/*
* This file is part of the Sofia-SIP package
*
* Copyright (C) 2005 Nokia Corporation.
*
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
/**@CFILE nta.c
* @brief Sofia SIP Transaction API implementation
*
* This source file has been divided into sections as follows:
* 1) agent
* 2) tport handling
* 3) dispatching messages received from network
* 4) message creation, message utility
* 5) stateless operation
* 6) dialogs (legs)
* 7) server transactions (incoming)
* 8) client transactions (outgoing)
* 9) resolving URLs for client transactions
* 10) 100rel reliable responses (reliable)
* 11) SigComp handling and public transport interface
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
*
* @date Created: Tue Jun 13 02:57:51 2000 ppessi
*/
#include "config.h"
#include <sofia-sip/string0.h>
/** @internal SU message argument structure type */
#define SU_MSG_ARG_T union sm_arg_u
/** @internal SU timer argument pointer type */
#define SU_TIMER_ARG_T struct nta_agent_s
#include <sofia-sip/su_alloc.h>
#include <sofia-sip/su.h>
#include <sofia-sip/su_time.h>
#include <sofia-sip/su_wait.h>
#include <sofia-sip/su_tagarg.h>
#include <sofia-sip/base64.h>
#include <sofia-sip/su_uniqueid.h>
#include <sofia-sip/sip.h>
#include <sofia-sip/sip_header.h>
#include <sofia-sip/sip_util.h>
#include <sofia-sip/sip_status.h>
#include <sofia-sip/hostdomain.h>
#include <sofia-sip/msg_addr.h>
#include <sofia-sip/msg_parser.h>
#include "nta_internal.h"
#include "sofia-sip/nta_stateless.h"
#include "sofia-sip/url_tag.h"
#if !defined(random) && defined(_WIN32)
#define random rand
#endif
#if !defined(EMSGSIZE) && defined(_WIN32)
#define EMSGSIZE WSAEMSGSIZE
#endif
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <limits.h>
#include <errno.h>
/* From AM_INIT/AC_INIT in our "config.h" */
char const nta_version[] = PACKAGE_VERSION;
#if HAVE_FUNC
#elif HAVE_FUNCTION
#define __func__ __FUNCTION__
#else
static char const __func__[] = "nta";
#endif
#define NONE ((void *)-1)
/* Internal tags */
/* Delay sending of request */
#define NTATAG_DELAY_SENDING(x) ntatag_delay_sending, tag_bool_v((x))
#define NTATAG_DELAY_SENDING_REF(x) \
ntatag_delay_sending_ref, tag_bool_vr(&(x))
extern tag_typedef_t ntatag_delay_sending;
extern tag_typedef_t ntatag_delay_sending_ref;
/* Allow sending incomplete responses */
#define NTATAG_INCOMPLETE(x) ntatag_incomplete, tag_bool_v((x))
#define NTATAG_INCOMPLETE_REF(x) \
ntatag_incomplete_ref, tag_bool_vr(&(x))
extern tag_typedef_t ntatag_incomplete;
extern tag_typedef_t ntatag_incomplete_ref;
nta_compressor_vtable_t *nta_compressor_vtable = NULL;
/* Agent */
static int agent_tag_init(nta_agent_t *self);
static int agent_timer_init(nta_agent_t *agent);
static void agent_timer(su_root_magic_t *rm, su_timer_t *, nta_agent_t *);
static int agent_launch_terminator(nta_agent_t *agent);
static void agent_kill_terminator(nta_agent_t *agent);
static int agent_set_params(nta_agent_t *agent, tagi_t *tags);
static void agent_set_udp_params(nta_agent_t *self, usize_t udp_mtu);
static int agent_get_params(nta_agent_t *agent, tagi_t *tags);
/* Transport interface */
static sip_via_t const *agent_tport_via(tport_t *tport);
static int outgoing_insert_via(nta_outgoing_t *orq, sip_via_t const *);
static int nta_tpn_by_via(tp_name_t *tpn, sip_via_t const *v, int *using_rport);
static msg_t *nta_msg_create_for_transport(nta_agent_t *agent, int flags,
char const data[], usize_t dlen,
tport_t const *tport,
tp_client_t *via);
static int complete_response(msg_t *response,
int status, char const *phrase,
msg_t const *request);
#define IF_SIGCOMP_TPTAG_COMPARTMENT(cc) TAG_IF(cc, TPTAG_COMPARTMENT(cc)),
#define IF_SIGCOMP_TPTAG_COMPARTMENT_REF(cc) TPTAG_COMPARTMENT_REF(cc),
struct sigcomp_compartment;
struct sigcomp_compartment *
nta_compartment_ref(struct sigcomp_compartment *cc);
static
struct sigcomp_compartment *
agent_compression_compartment(nta_agent_t *sa, tport_t *tp, tp_name_t const *tpn,
int new_if_needed);
static
int agent_accept_compressed(nta_agent_t *sa, msg_t *msg,
struct sigcomp_compartment *cc);
static int agent_close_compressor(nta_agent_t *sa,
struct sigcomp_compartment *cc);
static int agent_zap_compressor(nta_agent_t *sa,
struct sigcomp_compartment *cc);
static char const * stateful_branch(su_home_t *home, nta_agent_t *);
static char const * stateless_branch(nta_agent_t *, msg_t *, sip_t const *,
tp_name_t const *tp);
#define NTA_BRANCH_PRIME SU_U64_C(0xB9591D1C361C6521)
#define NTA_TAG_PRIME SU_U64_C(0xB9591D1C361C6521)
HTABLE_PROTOS_WITH(leg_htable, lht, nta_leg_t, size_t, hash_value_t);
static nta_leg_t *leg_find(nta_agent_t const *sa,
char const *method_name,
url_t const *request_uri,
sip_call_id_t const *i,
char const *from_tag,
url_t const *from_uri,
char const *to_tag,
url_t const *to_uri);
static nta_leg_t *dst_find(nta_agent_t const *sa, url_t const *u0,
char const *method);
static void leg_recv(nta_leg_t *, msg_t *, sip_t *, tport_t *);
static void leg_free(nta_agent_t *sa, nta_leg_t *leg);
#define NTA_HASH(i, cs) ((i)->i_hash + 26839U * (uint32_t)(cs))
HTABLE_PROTOS_WITH(incoming_htable, iht, nta_incoming_t, size_t, hash_value_t);
static nta_incoming_t *incoming_create(nta_agent_t *agent,
msg_t *request,
sip_t *sip,
tport_t *tport,
char const *tag);
static int incoming_callback(nta_leg_t *leg, nta_incoming_t *irq, sip_t *sip);
static void incoming_free(nta_incoming_t *irq);
static inline void incoming_cut_off(nta_incoming_t *irq);
static inline void incoming_reclaim(nta_incoming_t *irq);
static void incoming_queue_init(incoming_queue_t *,
unsigned timeout);
static void incoming_queue_adjust(nta_agent_t *sa,
incoming_queue_t *queue,
unsigned timeout);
static inline
nta_incoming_t *incoming_find(nta_agent_t const *agent, sip_t const *sip,
sip_via_t const *v,
nta_incoming_t **merge,
nta_incoming_t **ack);
static int incoming_reply(nta_incoming_t *irq, msg_t *msg, sip_t *sip);
static inline int incoming_recv(nta_incoming_t *irq, msg_t *msg, sip_t *sip,
tport_t *tport);
static inline int incoming_ack(nta_incoming_t *irq, msg_t *msg, sip_t *sip,
tport_t *tport);
static inline int incoming_cancel(nta_incoming_t *irq, msg_t *msg, sip_t *sip,
tport_t *tport);
static inline int incoming_merge(nta_incoming_t *irq, msg_t *msg, sip_t *sip,
tport_t *tport);
static inline int incoming_timestamp(nta_incoming_t *, msg_t *, sip_t *);
static inline su_duration_t incoming_timer(nta_agent_t *, su_duration_t);
static nta_reliable_t *reliable_mreply(nta_incoming_t *,
nta_prack_f *, nta_reliable_magic_t *,
msg_t *, sip_t *);
static int reliable_send(nta_incoming_t *, nta_reliable_t *, msg_t *, sip_t *);
static int reliable_final(nta_incoming_t *irq, msg_t *msg, sip_t *sip);
static msg_t *reliable_response(nta_incoming_t *irq);
static int reliable_recv(nta_incoming_t *, msg_t *, sip_t *, tport_t *);
static void reliable_flush(nta_incoming_t *irq);
static void