diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index b72b6e5f76..db17c315f4 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Sat Jan 19 01:47:55 GMT 2008 +Wed Jan 23 13:36:34 EST 2008 diff --git a/libs/sofia-sip/AUTHORS b/libs/sofia-sip/AUTHORS index 4adcf4e686..cc18cae9d7 100644 --- a/libs/sofia-sip/AUTHORS +++ b/libs/sofia-sip/AUTHORS @@ -16,13 +16,16 @@ Haataja, Mikko Jacobs, Remeres Jalava, Teemu Jerris, Michael -Legostayev Denis +Legostayev, Denis +Leuenberger, Stefan Prado, Dimitri E. Puolakka, Petteri Puustinen, Ismo Rinne-Rahkola, Pasi +Rondina, Daniele Saari, Mika Selin, Jari +Suttner, Bernhard Underwood, Steve Urpalainen, Jari Whittaker, Colin diff --git a/libs/sofia-sip/Makefile.am b/libs/sofia-sip/Makefile.am index 5539a0b267..6bdceebc9c 100644 --- a/libs/sofia-sip/Makefile.am +++ b/libs/sofia-sip/Makefile.am @@ -9,7 +9,7 @@ AUTOMAKE_OPTIONS = foreign 1.7 SUBDIRS = libsofia-sip-ua $(GLIB_SUBDIRS) utils packages tests DIST_SUBDIRS = libsofia-sip-ua libsofia-sip-ua-glib utils packages \ - win32 open_c + tests win32 open_c # note: when glib devel files are not available, make should not # enter the libsofia-sip-ua-glib subdir at all @@ -32,7 +32,8 @@ EXTRA_DIST += docs/build_system.txt \ docs/devel_platform_notes.txt \ docs/release_management.txt -EXTRA_DIST += scripts/lcov-report scripts/uncovered +EXTRA_DIST += scripts/lcov-report scripts/uncovered \ + scripts/hide_emails.sh dist_man_MANS = man/man1/sip-date.1 man/man1/sip-options.1 \ man/man1/localinfo.1 man/man1/addrinfo.1 \ diff --git a/libs/sofia-sip/RELEASE b/libs/sofia-sip/RELEASE index 611367c1e3..f514c261d8 100644 --- a/libs/sofia-sip/RELEASE +++ b/libs/sofia-sip/RELEASE @@ -5,26 +5,31 @@ Release notes for current version of Sofia-SIP Changes since last release -------------------------- - +Beside bugfixes, a server graylisting was added to nta and a few +improvements in event handling were made in nua interface. -Bugs in blaa and foo have been fixed. The stack now supports -use of foobar... +A check-based test program was added, too. API/ABI changes and versioning ------------------------------ - - -**template**: New features in API are marked with Doxytag macro @VERSION_1_XX_X. +New features in API are marked with Doxytag macro @NEW_1_12_8 or +@VERSION_1_12_8. libsofia-sip-ua: -- **template**: Added foobar() function (sofia-sip/foobar.h). +- Added nta_sip_is_internal(), nta_msg_is_internal() function in + . Deprecating nta_is_internal_msg(). +- Added su_msg_new(), su_msg_send_to() and su_msg_deinitializer() for more + robust and light-weight message passing in +- Added su_home_lock(), su_home_trylock(), and su_home_unlock() in + +- Added type-neutral template macros for hash tables HTABLE2_DECLARE2(), HTABLE2_PROTOS2(), + and HTABLE2_BODIES2(). +- Added sres_cache_set_srv_priority() in and + sres_set_cached_srv_priority() for graylisting + SRV records for inresponsive servers. +- nua_create()/nta_agent_create()/tport_bind() now joins to multicast group + if "maddr" parameter is specified - This release is ABI/API compatible with applications linked against any 1.12.x release. However, applications built against this release won't work against an older library. The ABI has been tested with the nua module @@ -38,32 +43,74 @@ libsofia-sip-ua-glib: Contributors to this release ---------------------------- - - -- **template**: First Surname (patch to nua/soa/msg) - +- Bernhard Suttner contributed patch to soa for using a=inactive attribute + with call hold +- Stefan Leuenberger from Netmodule contributed the hack for changing + priority of SRV records if a SIP server was inresponsive +- Daniele Rondina contributed patches fixing crash in STUN discovery cleanup + and for transport handling with nta_outgoing_*create() +- Youness Alaoui sent a iPhone build fix +- Michael Jerris and Stefan Knoblich from Freeswitch project reported + numerous bugs and contributed fixes + See the AUTHORS file in the distribution package. Notes on new features --------------------- - +SIP Server Graylisting +---------------------- + +The hack contributed by Stefan Leuenberger from Netmodule changes the +priority of SRV records which are used when SIP URI is resolved. + +If your domain has multiple servers, you can define separate SRV records for +them. If any of them becomes unresponsive, a SIP request sent to it times +out or gets rejected because of network error and Sofia SIP automatically +retries with another server. However, as Sofia SIP does not keep track of +failed servers with the next request it may again try first the failed +server. The graylisting repriotizes the SRV records so that the priority of +the failed server gets reduced and it won't be tried again until all other +servers have failed, too. Note that the SIP URI resolver may get confused +when using this kind of repriotizing if a single SRV record corresponds to +multiple servers or servers with multiple addresses. + +You can disable the reprioritizing hack by including tag NTATAG_GREYLIST(0) +with nua_create(), nua_set_params(), nta_agent_create() or +nta_agent_set_params(). Bugs fixed in this release -------------------------- +* Fixed su_home_init not initializing suh_lock. +* Fixed memory leak when increasing hash table size +* Fixed problems with multiple authentication challenges. +* Fixed sf.net bug #1816647: Outbound contact does not make it to dialogs. +* Fixed problem with tagargs, amd64 and Sun CC. +* Fixed nta_outgoing_*create() not using NTATAG_TPORT() if + NTATAG_DEFAULT_PROXY() was set. +* Fixed memory leak from nua operations. +* Fixed crash when INVITE destroyed session before UPDATE or PRACK completed +* Fixed crash in stun when destroying stun context during discovery callback +* Fixed nta timer interval calculation ignoring some transactions +* Fixed request merging when client is RFC 2543 proxy +* Fixed request matching and merging if To/From URI does not match +* Fixed problem with soa and overlapping INVITEs +* Fixed handling of RFC 2848 subscriptions (without Event header) +* Fixed problems with expiration time calculations if NOTIFY is sent before + SUBSCRIBE has been responded +* Fixed su_getaddrinfo() with ephemeral (NULL or "0") service +* Fixed IPv6 build with Vista SDK +* Fixed sf.net bug #1867753 (avoid duplicating initial route set +* Fixed SCTP receive +* Using tport_log and su_llog() when logging TLS warnings and errors +* Removed asserts on hairy dialog shutdown cases. +* Using remote activity mode derived from O/A result rather than mode based on remote + offer +* Fixed mode in SDP offer while on hold +* Fixed handling of REFER in nua when it gets redirected or challenged +* Fixed memory corruption when sending of ACK request failed. + < notable bugs fixed in this release - check the sf.net bug tracker; see closed bugs, sorted by closing date diff --git a/libs/sofia-sip/autogen.sh b/libs/sofia-sip/autogen.sh index 1cf6ebf518..e460abb662 100644 --- a/libs/sofia-sip/autogen.sh +++ b/libs/sofia-sip/autogen.sh @@ -1,6 +1,8 @@ #!/bin/sh set -x +AUTOMAKE=${AUTOMAKE:-automake-1.9} ACLOCAL=${ACLOCAL:-aclocal-1.9} +export AUTOMAKE ACLOCAL ${AUTORECONF:-autoreconf} -i find . \( -name 'run*' -o -name '*.sh' \) -a -type f | xargs chmod +x chmod +x scripts/* diff --git a/libs/sofia-sip/libsofia-sip-ua/docs/Doxyfile.aliases b/libs/sofia-sip/libsofia-sip-ua/docs/Doxyfile.aliases index 2162afb9eb..c46512123f 100644 --- a/libs/sofia-sip/libsofia-sip-ua/docs/Doxyfile.aliases +++ b/libs/sofia-sip/libsofia-sip-ua/docs/Doxyfile.aliases @@ -52,4 +52,7 @@ ALIASES = \ "VERSION_1_12_7=1.12.7" \ "NEW_1_12_7=@since New in 1.12.7" \ "EXP_1_12_7=@since Experimental in 1.12.7, available if --enable-experimental configuration option is given" \ + "VERSION_1_12_8=1.12.8" \ + "NEW_1_12_8=@since New in 1.12.8" \ + diff --git a/libs/sofia-sip/libsofia-sip-ua/docs/Makefile.am b/libs/sofia-sip/libsofia-sip-ua/docs/Makefile.am index 3aff4a29f7..75eee96648 100644 --- a/libs/sofia-sip/libsofia-sip-ua/docs/Makefile.am +++ b/libs/sofia-sip/libsofia-sip-ua/docs/Makefile.am @@ -2,7 +2,6 @@ EXTRA_DIST = Doxyfile.aliases Doxyfile.conf \ docguide.docs \ mainpage.docs \ conformance.docs \ - hide_emails.sh \ ChangeLog \ pictures/SIP_basic_incoming_operation.eps \ pictures/SIP_basic_incoming_operation.gif \ @@ -37,7 +36,7 @@ Doxyfile.rfc: print "RFC"i"=\"RFC "i"\" "b; \ }}' \ site=http://www.faqs.org/rfcs/rfc type=.html \ - from=700 to=4800 \ + from=700 to=5500 \ /dev/null > $@ CLEANFILES = Doxyfile.rfc *.doxytags diff --git a/libs/sofia-sip/libsofia-sip-ua/docs/conformance.docs b/libs/sofia-sip/libsofia-sip-ua/docs/conformance.docs index 5706cdca07..dc7a36ff18 100644 --- a/libs/sofia-sip/libsofia-sip-ua/docs/conformance.docs +++ b/libs/sofia-sip/libsofia-sip-ua/docs/conformance.docs @@ -49,6 +49,7 @@ below. RFC 4168
RFC 4320
RFC 4488
+RFC 5057
RFC 4566
RFC 2327
@@ -963,6 +964,23 @@ below. + + + + @RFC5057: Multiple Dialog Usages in SIP + + + Sofia-SIP provides function sip_response_terminates_dialog() that + can be used to determine the effect of error response with dialog. + + The nua UA engine uses sip_response_terminates_dialog(). + + + The client application must either use NUA or + sip_response_terminates_dialog(). + + + diff --git a/libs/sofia-sip/libsofia-sip-ua/iptsec/test_auth_digest.c b/libs/sofia-sip/libsofia-sip-ua/iptsec/test_auth_digest.c index b86779e4cd..c42b58dd38 100644 --- a/libs/sofia-sip/libsofia-sip-ua/iptsec/test_auth_digest.c +++ b/libs/sofia-sip/libsofia-sip-ua/iptsec/test_auth_digest.c @@ -520,6 +520,11 @@ int test_digest_client() sip_www_authenticate_class, sip_authentication_info_class }}; + auth_challenger_t pach[1] = + {{ 407, "Proxy Authorization required", + sip_proxy_authenticate_class, + sip_proxy_authentication_info_class + }}; TEST_1(home = su_home_new(sizeof(*home))); @@ -994,6 +999,46 @@ int test_digest_client() TEST(as->as_status, 0); auth_mod_destroy(am); aucs = NULL; + /* Test empty realm */ + TEST_1(am = auth_mod_create(root, + AUTHTAG_METHOD("Digest"), + AUTHTAG_REALM(""), + AUTHTAG_DB(testpasswd), + TAG_END())); + reinit_as(as); + auth_mod_check_client(am, as, NULL, ach); TEST(as->as_status, 401); + TEST(auc_challenge(&aucs, home, (msg_auth_t *)as->as_response, + sip_authorization_class), 1); + reinit_as(as); + + TEST(auc_all_credentials(&aucs, "Digest", "\"\"", "user1", "secret"), 1); + msg_header_remove(m2, (void *)sip, (void *)sip->sip_authorization); + TEST(auc_authorization(&aucs, m2, (msg_pub_t*)sip, rq->rq_method_name, + (url_t *)"sip:surf3@ims3.so.noklab.net", + sip->sip_payload), 1); + TEST_1(sip->sip_authorization); + + auth_mod_check_client(am, as, sip->sip_authorization, ach); + TEST(as->as_status, 0); + aucs = NULL; + reinit_as(as); + auth_mod_check_client(am, as, NULL, pach); TEST(as->as_status, 407); + TEST(auc_challenge(&aucs, home, (msg_auth_t *)as->as_response, + sip_proxy_authorization_class), 1); + reinit_as(as); + + TEST(auc_credentials(&aucs, as->as_home, "Digest:\"\":user1:secret"), 1); + msg_header_remove(m2, (void *)sip, (void *)sip->sip_proxy_authorization); + TEST(auc_authorization(&aucs, m2, (msg_pub_t*)sip, rq->rq_method_name, + (url_t *)"sip:surf3@ims3.so.noklab.net", + sip->sip_payload), 1); + TEST_1(sip->sip_proxy_authorization); + + auth_mod_check_client(am, as, sip->sip_proxy_authorization, pach); + TEST(as->as_status, 0); + + auth_mod_destroy(am); aucs = NULL; + /* Test Basic authentication scheme */ TEST_1(am = auth_mod_create(root, AUTHTAG_METHOD("Basic"), diff --git a/libs/sofia-sip/libsofia-sip-ua/nta/nta.c b/libs/sofia-sip/libsofia-sip-ua/nta/nta.c index 2fb7dccfd3..3107ca88b6 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nta/nta.c +++ b/libs/sofia-sip/libsofia-sip-ua/nta/nta.c @@ -176,9 +176,7 @@ static nta_leg_t *leg_find(nta_agent_t const *sa, 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); + char const *to_tag); 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 *); @@ -340,7 +338,7 @@ su_log_t nta_log[] = { SU_LOG_INIT("nta", "NTA_DEBUG", SU_DEBUG) }; * NTATAG_BAD_REQ_MASK(), NTATAG_BAD_RESP_MASK(), NTATAG_BLACKLIST(), * NTATAG_CANCEL_2543(), NTATAG_CANCEL_487(), NTATAG_CLIENT_RPORT(), * NTATAG_DEBUG_DROP_PROB(), NTATAG_DEFAULT_PROXY(), - * NTATAG_EXTRA_100(), + * NTATAG_EXTRA_100(), NTATAG_GRAYLIST(), * NTATAG_MAXSIZE(), NTATAG_MAX_FORWARDS(), NTATAG_MERGE_482(), NTATAG_MCLASS() * NTATAG_PASS_100(), NTATAG_PASS_408(), NTATAG_PRELOAD(), NTATAG_PROGRESS(), * NTATAG_REL100(), @@ -402,6 +400,7 @@ nta_agent_t *nta_agent_create(su_root_t *root, agent->sa_t4 = NTA_SIP_T4; agent->sa_t1x64 = 64 * NTA_SIP_T1; agent->sa_timer_c = 185 * 1000; + agent->sa_graylist = 600; agent->sa_drop_prob = 0; agent->sa_is_a_uas = 0; agent->sa_progress = 60 * 1000; @@ -896,7 +895,7 @@ void agent_kill_terminator(nta_agent_t *agent) * NTATAG_BAD_REQ_MASK(), NTATAG_BAD_RESP_MASK(), NTATAG_BLACKLIST(), * NTATAG_CANCEL_2543(), NTATAG_CANCEL_487(), NTATAG_CLIENT_RPORT(), * NTATAG_DEBUG_DROP_PROB(), NTATAG_DEFAULT_PROXY(), - * NTATAG_EXTRA_100(), + * NTATAG_EXTRA_100(), NTATAG_GRAYLIST(), * NTATAG_MAXSIZE(), NTATAG_MAX_FORWARDS(), NTATAG_MERGE_482(), NTATAG_MCLASS() * NTATAG_PASS_100(), NTATAG_PASS_408(), NTATAG_PRELOAD(), NTATAG_PROGRESS(), * NTATAG_REL100(), @@ -946,6 +945,7 @@ int agent_set_params(nta_agent_t *agent, tagi_t *tags) unsigned sip_t4 = agent->sa_t4; unsigned sip_t1x64 = agent->sa_t1x64; unsigned timer_c = agent->sa_timer_c; + unsigned graylist = agent->sa_graylist; unsigned blacklist = agent->sa_blacklist; int ua = agent->sa_is_a_uas; unsigned progress = agent->sa_progress; @@ -989,6 +989,7 @@ int agent_set_params(nta_agent_t *agent, tagi_t *tags) NTATAG_DEBUG_DROP_PROB_REF(drop_prob), NTATAG_DEFAULT_PROXY_REF(proxy), NTATAG_EXTRA_100_REF(extra_100), + NTATAG_GRAYLIST_REF(graylist), NTATAG_MAXSIZE_REF(maxsize), NTATAG_MAX_PROCEEDING_REF(max_proceeding), NTATAG_MAX_FORWARDS_REF(max_forwards), @@ -1142,6 +1143,12 @@ int agent_set_params(nta_agent_t *agent, tagi_t *tags) outgoing_queue_adjust(agent, agent->sa_out.inv_proceeding, timer_c); } + if (graylist > 24 * 60 * 60) + graylist = 24 * 60 * 60; + agent->sa_graylist = graylist; + + if (blacklist > 24 * 60 * 60) + blacklist = 24 * 60 * 60; agent->sa_blacklist = blacklist; if (progress == 0) @@ -1204,7 +1211,7 @@ void agent_set_udp_params(nta_agent_t *self, usize_t udp_mtu) * NTATAG_CANCEL_2543_REF(), NTATAG_CANCEL_487_REF(), * NTATAG_CLIENT_RPORT_REF(), NTATAG_CONTACT_REF(), * NTATAG_DEBUG_DROP_PROB_REF(), NTATAG_DEFAULT_PROXY_REF(), - * NTATAG_EXTRA_100_REF(), + * NTATAG_EXTRA_100_REF(), NTATAG_GRAYLIST_REF(), * NTATAG_MAXSIZE_REF(), NTATAG_MAX_FORWARDS_REF(), NTATAG_MCLASS_REF(), * NTATAG_MERGE_482_REF(), NTATAG_MAX_PROCEEDING_REF(), * NTATAG_PASS_100_REF(), NTATAG_PASS_408_REF(), NTATAG_PRELOAD_REF(), @@ -1253,6 +1260,7 @@ int agent_get_params(nta_agent_t *agent, tagi_t *tags) NTATAG_DEBUG_DROP_PROB(agent->sa_drop_prob), NTATAG_DEFAULT_PROXY(agent->sa_default_proxy), NTATAG_EXTRA_100(agent->sa_extra_100), + NTATAG_GRAYLIST(agent->sa_graylist), NTATAG_MAXSIZE(agent->sa_maxsize), NTATAG_MAX_PROCEEDING(agent->sa_max_proceeding), NTATAG_MAX_FORWARDS(agent->sa_max_forwards->mf_count), @@ -2416,8 +2424,8 @@ void agent_recv_request(nta_agent_t *agent, if ((leg = leg_find(agent, method_name, url, sip->sip_call_id, - sip->sip_from->a_tag, sip->sip_from->a_url, - sip->sip_to->a_tag, sip->sip_to->a_url))) { + sip->sip_from->a_tag, + sip->sip_to->a_tag))) { /* Try existing dialog */ SU_DEBUG_5(("nta: %s (%u) %s\n", method_name, cseq, "going to existing leg")); @@ -3991,12 +3999,12 @@ nta_leg_t *nta_leg_by_replaces(nta_agent_t *sa, sip_replaces_t const *rp) id->i_hash = msg_hash_string(id->i_id = rp->rp_call_id); - leg = leg_find(sa, NULL, NULL, id, from_tag, NULL, to_tag, NULL); + leg = leg_find(sa, NULL, NULL, id, from_tag, to_tag); if (leg == NULL && strcmp(from_tag, "0") == 0) - leg = leg_find(sa, NULL, NULL, id, NULL, NULL, to_tag, NULL); + leg = leg_find(sa, NULL, NULL, id, NULL, to_tag); if (leg == NULL && strcmp(to_tag, "0") == 0) - leg = leg_find(sa, NULL, NULL, id, from_tag, NULL, NULL, NULL); + leg = leg_find(sa, NULL, NULL, id, from_tag, NULL); } return leg; @@ -4120,11 +4128,14 @@ int addr_cmp(url_t const *a, url_t const *b) * @param call_id if non-NULL, must match with @CallID header contents * @param remote_tag if there is remote tag * associated with dialog, @a remote_tag must match - * @param remote_uri if there is no remote tag, the remote URI must match + * @param remote_uri ignored * @param local_tag if non-NULL and there is local tag associated with leg, * it must math - * @param local_uri if there is no local tag, the local URI must match + * @param local_uri ignored * + * @note + * If @a remote_tag or @a local_tag is an empty string (""), the tag is + * ignored when matching legs. */ nta_leg_t *nta_leg_by_dialog(nta_agent_t const *agent, url_t const *request_uri, @@ -4134,7 +4145,7 @@ nta_leg_t *nta_leg_by_dialog(nta_agent_t const *agent, char const *local_tag, url_t const *local_uri) { - void *to_be_freed = NULL, *tbf2 = NULL, *tbf3 = NULL; + void *to_be_freed = NULL; url_t *url; url_t url0[1]; nta_leg_t *leg; @@ -4158,21 +4169,18 @@ nta_leg_t *nta_leg_by_dialog(nta_agent_t const *agent, agent_aliases(agent, url, NULL); /* canonize url */ } - if (remote_uri && URL_IS_STRING(remote_uri)) - request_uri = tbf2 = url_hdup(NULL, remote_uri); + if (remote_tag && remote_tag[0] == '\0') + remote_tag = NULL; + if (local_tag && local_tag[0] == '\0') + local_tag = NULL; - if (local_uri && URL_IS_STRING(local_uri)) - local_uri = tbf3 = url_hdup(NULL, local_uri); - leg = leg_find(agent, NULL, url, call_id, - remote_tag, remote_uri, - local_tag, local_uri); + remote_tag, + local_tag); if (to_be_freed) su_free(NULL, to_be_freed); - if (tbf2) su_free(NULL, tbf2); - if (tbf3) su_free(NULL, tbf3); return leg; } @@ -4181,7 +4189,7 @@ nta_leg_t *nta_leg_by_dialog(nta_agent_t const *agent, * Find a leg corresponding to the request message. * * A leg matches to message if leg_match_request() returns true ("Call-ID", - * "To", and "From" match). + * "To"-tag, and "From"-tag match). */ static nta_leg_t *leg_find(nta_agent_t const *sa, @@ -4189,9 +4197,7 @@ nta_leg_t *leg_find(nta_agent_t const *sa, 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) + char const *to_tag) { hash_value_t hash = i->i_hash; leg_htable_t const *lht = sa->sa_dialogs; @@ -4201,10 +4207,9 @@ nta_leg_t *leg_find(nta_agent_t const *sa, (leg = *ll); ll = leg_htable_next(lht, ll)) { sip_call_id_t const *leg_i = leg->leg_id; - url_t const *remote_uri = leg->leg_remote->a_url; char const *remote_tag = leg->leg_remote->a_tag; - url_t const *local_uri = leg->leg_local->a_url; char const *local_tag = leg->leg_local->a_tag; + url_t const *leg_url = leg->leg_url; char const *leg_method = leg->leg_method; @@ -4212,6 +4217,7 @@ nta_leg_t *leg_find(nta_agent_t const *sa, continue; if (strcmp(leg_i->i_id, i->i_id) != 0) continue; + /* Do not match if the incoming To has tag, but the local does not */ if (!local_tag && to_tag) continue; @@ -4226,15 +4232,14 @@ nta_leg_t *leg_find(nta_agent_t const *sa, /* Do not match if incoming From has no tag but remote has a tag */ if (remote_tag && !from_tag) continue; + /* Avoid matching with itself */ if (!remote_tag != !from_tag && !local_tag != !to_tag) continue; - if (local_tag && to_tag ? - strcasecmp(local_tag, to_tag) : addr_cmp(local_uri, to_uri)) + if (local_tag && to_tag && strcasecmp(local_tag, to_tag) && to_tag[0]) continue; - if (remote_tag && from_tag ? - strcasecmp(remote_tag, from_tag) : addr_cmp(remote_uri, from_uri)) + if (remote_tag && from_tag && strcasecmp(remote_tag, from_tag) && from_tag[0]) continue; if (leg_url && request_uri && url_cmp(leg_url, request_uri)) @@ -6390,7 +6395,7 @@ static size_t outgoing_timer_c(outgoing_queue_t *q, char const *timer, su_duration_t now); -static void outgoing_ack(nta_outgoing_t *orq, msg_t *msg, sip_t *sip); +static void outgoing_ack(nta_outgoing_t *orq, sip_t *sip); static msg_t *outgoing_ackmsg(nta_outgoing_t *, sip_method_t, char const *, tagi_t const *tags); static void outgoing_retransmit(nta_outgoing_t *orq); @@ -7657,6 +7662,7 @@ void outgoing_queue(outgoing_queue_t *queue, if (outgoing_is_queued(orq)) outgoing_remove(orq); + assert(orq->orq_next == NULL); assert(*queue->q_tail == NULL); orq->orq_timeout = set_timeout(orq->orq_agent, queue->q_timeout); @@ -7680,7 +7686,7 @@ void outgoing_remove(nta_outgoing_t *orq) if ((*orq->orq_prev = orq->orq_next)) orq->orq_next->orq_prev = orq->orq_prev; else - orq->orq_queue->q_tail = orq->orq_prev, assert(!*orq->orq_queue->q_tail); + orq->orq_queue->q_tail = orq->orq_prev; orq->orq_queue->q_length--; orq->orq_next = NULL; @@ -8334,7 +8340,7 @@ int outgoing_recv(nta_outgoing_t *orq, else { /* Final response */ if (status >= 300 && !internal) - outgoing_ack(orq, msg, sip); + outgoing_ack(orq, sip); if (!orq->orq_completed) { if (outgoing_complete(orq)) @@ -8532,7 +8538,7 @@ static int outgoing_duplicate(nta_outgoing_t *orq, /** @internal ACK to a final response (300..699). * These messages are ACK'ed via the original URL (and tport) */ -void outgoing_ack(nta_outgoing_t *orq, msg_t *msg, sip_t *sip) +void outgoing_ack(nta_outgoing_t *orq, sip_t *sip) { nta_outgoing_t *ack; msg_t *ackmsg; @@ -8563,7 +8569,7 @@ void outgoing_ack(nta_outgoing_t *orq, msg_t *msg, sip_t *sip) TAG_END()))) ; else - msg_destroy(msg); + msg_destroy(ackmsg); } } @@ -8836,8 +8842,8 @@ struct sipdns_query char const *sq_proto; char const *sq_domain; char sq_port[6]; /* port number */ - - uint16_t sq_type; + uint16_t sq_otype; /* origin type of query data (0 means request) */ + uint16_t sq_type; /* query type */ uint16_t sq_priority; /* priority or preference */ uint16_t sq_weight; /* preference or weight */ }; @@ -9068,6 +9074,65 @@ outgoing_try_another(nta_outgoing_t *orq) outgoing_reset_timer(orq); outgoing_queue(orq->orq_agent->sa_out.resolving, orq); + if (orq->orq_status > 0) + /* PP: don't hack priority if a preliminary response has been received */ + ; + else if (orq->orq_agent->sa_graylist == 0) + /* PP: priority hacking disabled */ + ; + /* NetModule hack: + * Move server that did not work to end of queue in sres cache + * + * the next request does not try to use the server that is currently down + * + * @TODO: fix cases with only A or AAAA answering, or all servers down. + */ + else if (sr && sr->sr_target) { + struct sipdns_query *sq; + + /* find latest A/AAAA record */ + sq = sr->sr_head; + if (!sq || (sr->sr_a_aaaa1 != sr->sr_a_aaaa2 && sq->sq_type == sr->sr_a_aaaa1)) + sq = sr->sr_done; + + if (sq && sq->sq_otype == sres_type_srv) { + char const *target = sq->sq_domain, *proto = sq->sq_proto; + unsigned prio = sq->sq_priority, maxprio = prio; + + SU_DEBUG_5(("nta: no response from %s:%s;transport=%s\n", target, sq->sq_port, proto)); + + for (sq = sr->sr_head; sq; sq = sq->sq_next) + if (sq->sq_otype == sres_type_srv && sq->sq_priority > maxprio) + maxprio = sq->sq_priority; + + for (sq = sr->sr_done; sq; sq = sq->sq_next) + if (sq->sq_otype == sres_type_srv && sq->sq_priority > maxprio) + maxprio = sq->sq_priority; + + for (sq = sr->sr_done; sq; sq = sq->sq_next) { + int modified; + + if (sq->sq_type != sres_type_srv || strcmp(proto, sq->sq_proto)) + continue; + + /* modify the SRV record(s) corresponding to the latest A/AAAA record */ + modified = sres_set_cached_srv_priority( + orq->orq_agent->sa_resolver, + sq->sq_domain, + target, + sq->sq_port[0] ? (uint16_t)strtoul(sq->sq_port, NULL, 10) : 0, + orq->orq_agent->sa_graylist, + maxprio + 1); + + if (modified >= 0) + SU_DEBUG_3(("nta: reduced priority of %d %s SRV records (increase value to %u)\n", + modified, sq->sq_domain, maxprio + 1)); + else + SU_DEBUG_3(("nta: failed to reduce %s SRV priority\n", sq->sq_domain)); + } + } + } + return outgoing_resolve_next(orq); } @@ -9346,6 +9411,7 @@ void outgoing_answer_naptr(sres_context_t *orq, sq = su_zalloc(home, (sizeof *sq) + rlen); *tail = sq, tail = &sq->sq_next; + sq->sq_otype = sres_type_naptr; sq->sq_priority = na->na_prefer; sq->sq_weight = j; sq->sq_type = type; @@ -9446,11 +9512,11 @@ outgoing_answer_srv(sres_context_t *orq, sres_query_t *q, if (sq) { *tail = sq, tail = &sq->sq_next; + sq->sq_otype = sres_type_srv; sq->sq_type = sr->sr_a_aaaa1; sq->sq_proto = sq0->sq_proto; sq->sq_domain = memcpy(sq + 1, srv->srv_target, tlen); snprintf(sq->sq_port, sizeof(sq->sq_port), "%u", srv->srv_port); - sq->sq_priority = srv->srv_priority; sq->sq_weight = srv->srv_weight; } @@ -10143,8 +10209,8 @@ int nta_reliable_leg_prack(nta_reliable_magic_t *magic, if ((leg = leg_find(irq->irq_agent, method_name, url, sip->sip_call_id, - sip->sip_from->a_tag, sip->sip_from->a_url, - sip->sip_to->a_tag, sip->sip_to->a_url))) { + sip->sip_from->a_tag, + sip->sip_to->a_tag))) { /* Use existing dialog */ SU_DEBUG_5(("nta: %s (%u) %s\n", method_name, sip->sip_cseq->cs_seq, diff --git a/libs/sofia-sip/libsofia-sip-ua/nta/nta_internal.h b/libs/sofia-sip/libsofia-sip-ua/nta/nta_internal.h index 613c5313fc..b8e29ff18b 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nta/nta_internal.h +++ b/libs/sofia-sip/libsofia-sip-ua/nta/nta_internal.h @@ -167,6 +167,8 @@ struct nta_agent_s /** SIP timer C - interval between provisional responses receivedxs */ unsigned sa_timer_c; + /** Graylisting period */ + unsigned sa_graylist; /** Blacklisting period */ unsigned sa_blacklist; diff --git a/libs/sofia-sip/libsofia-sip-ua/nta/nta_tag.c b/libs/sofia-sip/libsofia-sip-ua/nta/nta_tag.c index 7b46e68ae3..58efc6f27a 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nta/nta_tag.c +++ b/libs/sofia-sip/libsofia-sip-ua/nta/nta_tag.c @@ -643,6 +643,31 @@ tag_typedef_t ntatag_progress = UINTTAG_TYPEDEF(progress); */ tag_typedef_t ntatag_timer_c = UINTTAG_TYPEDEF(timer_c); +/**@def NTATAG_GRAYLIST(x) + * + * Avoid failed servers. + * + * The NTATAG_GRAYLIST() provides the time that the servers are avoided + * after a request sent to them has been failed. Avoiding means that if a + * domain provides multiple servers, the failed servers are tried last. + * + * @par Used with + * nua_create(), nua_set_params(), + * nta_agent_create(), nta_agent_set_params() + * + * @par Parameter type + * unsigned int + * + * @par Values + * - Number of seconds that server is kept in graylist, from 0 to 86400. + * + * @par Default Value + * - 600 (graylist server for 10 minutes) + * + * @sa NTATAG_BLACKLIST(), NTATAG_TIMEOUT_408() + */ +tag_typedef_t ntatag_graylist = UINTTAG_TYPEDEF(graylist); + /**@def NTATAG_BLACKLIST(x) * * Add Retry-After header to error responses returned to application. @@ -660,7 +685,7 @@ tag_typedef_t ntatag_timer_c = UINTTAG_TYPEDEF(timer_c); * unsigned int * * @par Values - * - Value of delta-seconds in @RetryAfter header + * - Value of @i delta-seconds in @RetryAfter header, from 0 to 86400 * * @par Default Value * - 0 (no Retry-After is included) diff --git a/libs/sofia-sip/libsofia-sip-ua/nta/sofia-sip/nta_tag.h b/libs/sofia-sip/libsofia-sip-ua/nta/sofia-sip/nta_tag.h index 82e4b1c969..0b7ffb4e93 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nta/sofia-sip/nta_tag.h +++ b/libs/sofia-sip/libsofia-sip-ua/nta/sofia-sip/nta_tag.h @@ -203,6 +203,12 @@ NTA_DLL extern tag_typedef_t ntatag_timer_c; NTA_DLL extern tag_typedef_t ntatag_timer_c_ref; #define NTATAG_TIMER_C_REF(x) ntatag_timer_c_ref, tag_uint_vr(&(x)) +NTA_DLL extern tag_typedef_t ntatag_graylist; +#define NTATAG_GRAYLIST(x) ntatag_graylist, tag_uint_v((x)) + +NTA_DLL extern tag_typedef_t ntatag_graylist_ref; +#define NTATAG_GRAYLIST_REF(x) ntatag_graylist_ref, tag_uint_vr(&(x)) + NTA_DLL extern tag_typedef_t ntatag_blacklist; #define NTATAG_BLACKLIST(x) ntatag_blacklist, tag_uint_v((x)) diff --git a/libs/sofia-sip/libsofia-sip-ua/nta/test_nta.c b/libs/sofia-sip/libsofia-sip-ua/nta/test_nta.c index 1246e2925b..0cc7fe4d8a 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nta/test_nta.c +++ b/libs/sofia-sip/libsofia-sip-ua/nta/test_nta.c @@ -363,8 +363,16 @@ int new_leg_callback_200(agent_t *ag, return 200; } +static client_check_f client_check_to_tag; -static client_check_f * const default_checks[]; +static client_check_f * const default_checks[] = { + client_check_to_tag, + NULL +}; + +static client_check_f * const no_default_checks[] = { + NULL +}; int outgoing_callback(client_t *ctx, nta_outgoing_t *orq, @@ -1014,14 +1022,6 @@ int save_and_check_tcp(client_t *ctx, nta_outgoing_t *orq, sip_t const *sip) } -static client_check_f * const default_checks[] = { - client_check_to_tag, - NULL -}; - -static client_check_f * const no_default_checks[] = { - NULL -}; /* Test transports */ @@ -2256,8 +2256,23 @@ int test_dialog(agent_t *ag) END(); } -/* Test merging */ +static ssize_t recv_udp(agent_t *ag, void *b, size_t size) +{ + ssize_t n; + memset(b, size, 0); + + for (;;) { + su_root_step(ag->ag_root, 10L); + if (su_wait(ag->ag_sink_wait, 1, 0) == 0) { + n = su_recv(ag->ag_sink_socket, b, size, MSG_TRUNC); + if (n > 0) + return n; + } + } +} + +/* Test merging */ int test_merging(agent_t *ag) { BEGIN(); @@ -2289,11 +2304,17 @@ int test_merging(agent_t *ag) size_t len, l1, l2; su_sockaddr_t *su = ag->ag_su_nta; socklen_t sulen = ag->ag_su_nta_len; - int n; + +#ifndef MSG_TRUNC +#define MSG_TRUNC 0 +#endif /* Empty sink socket */ - while (su_wait(ag->ag_sink_wait, 1, 0) == 0) - su_recv(ag->ag_sink_socket, m1, sizeof m1, MSG_TRUNC); + su_setblocking(ag->ag_sink_socket, 0); + while (su_recv(ag->ag_sink_socket, m1, sizeof m1, MSG_TRUNC) >= 0) + ; + su_wait(ag->ag_sink_wait, 1, 0); + su_wait(ag->ag_sink_wait, 1, 0); { /* RFC 3261 8.2.2.2 Merged Requests: @@ -2338,16 +2359,9 @@ int test_merging(agent_t *ag) TEST_1((size_t)su_sendto(ag->ag_sink_socket, m1, l1, 0, su, sulen) == l1); TEST_1((size_t)su_sendto(ag->ag_sink_socket, m2, l2, 0, su, sulen) == l2); - for (n = 0; n < 2; ) { - su_root_step(ag->ag_root, 10L); - if (su_wait(ag->ag_sink_wait, 1, 0) == 0) { - if (n == 0) - su_recv(ag->ag_sink_socket, r1, sizeof r1, MSG_TRUNC); - else - su_recv(ag->ag_sink_socket, r2, sizeof r2, MSG_TRUNC); - n++; - } - } + recv_udp(ag, r1, sizeof r1); + recv_udp(ag, r2, sizeof r2); + len = strlen("SIP/2.0 200 "); TEST_1(memcmp(r1, "SIP/2.0 200 ", len) == 0); TEST_1(memcmp(r2, "SIP/2.0 482 ", len) == 0); @@ -2355,6 +2369,9 @@ int test_merging(agent_t *ag) TEST_P(ag->ag_latest_leg, ag->ag_server_leg); } + while (su_recv(ag->ag_sink_socket, m1, sizeof m1, MSG_TRUNC) >= 0) + ; + { /* * Check that request with same call-id, cseq and from-tag @@ -2392,16 +2409,8 @@ int test_merging(agent_t *ag) TEST_1((size_t)su_sendto(ag->ag_sink_socket, m1, l1, 0, su, sulen) == l1); TEST_1((size_t)su_sendto(ag->ag_sink_socket, m2, l2, 0, su, sulen) == l2); - for (n = 0; n < 2; ) { - su_root_step(ag->ag_root, 10L); - if (su_wait(ag->ag_sink_wait, 1, 0) == 0) { - if (n == 0) - su_recv(ag->ag_sink_socket, r1, sizeof r1, MSG_TRUNC); - else - su_recv(ag->ag_sink_socket, r2, sizeof r2, MSG_TRUNC); - n++; - } - } + recv_udp(ag, r1, sizeof r1); + recv_udp(ag, r2, sizeof r2); len = strlen("SIP/2.0 200 "); TEST_1(memcmp(r1, "SIP/2.0 200 ", len) == 0); @@ -2410,6 +2419,9 @@ int test_merging(agent_t *ag) TEST_P(ag->ag_latest_leg, ag->ag_server_leg); } + while (su_recv(ag->ag_sink_socket, m1, sizeof m1, MSG_TRUNC) >= 0) + ; + { /* test with rfc2543 */ @@ -2440,16 +2452,9 @@ int test_merging(agent_t *ag) TEST_1((size_t)su_sendto(ag->ag_sink_socket, m1, l1, 0, su, sulen) == l1); TEST_1((size_t)su_sendto(ag->ag_sink_socket, m2, l2, 0, su, sulen) == l2); - for (n = 0; n < 2; ) { - su_root_step(ag->ag_root, 10L); - if (su_wait(ag->ag_sink_wait, 1, 0) == 0) { - if (n == 0) - su_recv(ag->ag_sink_socket, r1, sizeof r1, MSG_TRUNC); - else - su_recv(ag->ag_sink_socket, r2, sizeof r2, MSG_TRUNC); - n++; - } - } + recv_udp(ag, r1, sizeof r1); + recv_udp(ag, r2, sizeof r2); + l1 = strlen("SIP/2.0 200 "); TEST_1(memcmp(r1, "SIP/2.0 200 ", l1) == 0); TEST_1(memcmp(r2, "SIP/2.0 482 ", l1) == 0); @@ -2457,6 +2462,9 @@ int test_merging(agent_t *ag) TEST_P(ag->ag_latest_leg, ag->ag_server_leg); } + while (su_recv(ag->ag_sink_socket, m1, sizeof m1, MSG_TRUNC) >= 0) + ; + { /* test with to-tag */ @@ -2487,16 +2495,9 @@ int test_merging(agent_t *ag) TEST_1((size_t)su_sendto(ag->ag_sink_socket, m1, l1, 0, su, sulen) == l1); TEST_1((size_t)su_sendto(ag->ag_sink_socket, m2, l2, 0, su, sulen) == l2); - for (n = 0; n < 2; ) { - su_root_step(ag->ag_root, 10L); - if (su_wait(ag->ag_sink_wait, 1, 0) == 0) { - if (n == 0) - su_recv(ag->ag_sink_socket, r1, sizeof r1, MSG_TRUNC); - else - su_recv(ag->ag_sink_socket, r2, sizeof r2, MSG_TRUNC); - n++; - } - } + recv_udp(ag, r1, sizeof r1); + recv_udp(ag, r2, sizeof r2); + l1 = strlen("SIP/2.0 200 "); TEST_1(memcmp(r1, "SIP/2.0 200 ", l1) == 0); TEST_1(memcmp(r2, "SIP/2.0 482 ", l1) != 0); @@ -2504,6 +2505,9 @@ int test_merging(agent_t *ag) TEST_P(ag->ag_latest_leg, ag->ag_server_leg); } + while (su_recv(ag->ag_sink_socket, m1, sizeof m1, MSG_TRUNC) >= 0) + ; + { /* test with rfc2543 and to-tag */ @@ -2532,16 +2536,9 @@ int test_merging(agent_t *ag) TEST_1((size_t)su_sendto(ag->ag_sink_socket, m1, l1, 0, su, sulen) == l1); TEST_1((size_t)su_sendto(ag->ag_sink_socket, m2, l2, 0, su, sulen) == l2); - for (n = 0; n < 2; ) { - su_root_step(ag->ag_root, 10L); - if (su_wait(ag->ag_sink_wait, 1, 0) == 0) { - if (n == 0) - su_recv(ag->ag_sink_socket, r1, sizeof r1, MSG_TRUNC); - else - su_recv(ag->ag_sink_socket, r2, sizeof r2, MSG_TRUNC); - n++; - } - } + recv_udp(ag, r1, sizeof r1); + recv_udp(ag, r2, sizeof r2); + l1 = strlen("SIP/2.0 200 "); TEST_1(memcmp(r1, "SIP/2.0 200 ", l1) == 0); TEST_1(memcmp(r2, "SIP/2.0 482 ", l1) != 0); diff --git a/libs/sofia-sip/libsofia-sip-ua/nta/test_nta_api.c b/libs/sofia-sip/libsofia-sip-ua/nta/test_nta_api.c index fa9f2da636..9862722abb 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nta/test_nta_api.c +++ b/libs/sofia-sip/libsofia-sip-ua/nta/test_nta_api.c @@ -1268,15 +1268,17 @@ static int api_test_dialog_matching(agent_t *ag) TEST_P(leg = nta_leg_by_dialog(nta, (url_t *)"sip:pc.al.us", i, "xyzzy", a2->a_url, a1->a_tag, a1->a_url), dialog1); +#if nomore /* remote url is required */ TEST_P(leg = nta_leg_by_dialog(nta, (url_t *)"sip:pc.al.us", i, a2->a_tag, a1->a_url, a1->a_tag, a1->a_url), NULL); +#endif TEST_P(leg = nta_leg_by_dialog(nta, (url_t *)"sip:pc.al.us", i, a2->a_tag, NULL, a1->a_tag, a1->a_url), dialog1); - /* local url is used if there is no local tag */ + /* local url is used if there is no local tag */ /* XXX - not really */ TEST_P(leg = nta_leg_by_dialog(nta, (url_t *)"sip:pc.al.us", i, a2->a_tag, a2->a_url, NULL, NULL), NULL); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c index d4cbd08128..2850de6220 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c @@ -183,22 +183,35 @@ void nua_dialog_store_peer_info(nua_owner_t *own, } } +/** Remove dialog information. */ +int nua_dialog_zap(nua_owner_t *own, + nua_dialog_state_t *ds) +{ + /* zap peer info */ + nua_dialog_store_peer_info(own, ds, NULL); + /* Local Contact */ + msg_header_free(own, (msg_header_t *)ds->ds_ltarget), ds->ds_ltarget = NULL; + /* Leg */ + nta_leg_destroy(ds->ds_leg), ds->ds_leg = NULL; + /* Remote tag */ + su_free(own, (void *)ds->ds_remote_tag), ds->ds_remote_tag = NULL; + /* Ready to set route/remote target */ + ds->ds_route = 0; + + return 0; +} + /** Remove dialog (if there is no other usages). */ int nua_dialog_remove(nua_owner_t *own, nua_dialog_state_t *ds, nua_dialog_usage_t *usage) { if (ds->ds_usage == usage && (usage == NULL || usage->du_next == NULL)) { - nua_dialog_store_peer_info(own, ds, NULL); /* zap peer info */ - msg_header_free(own, (msg_header_t *)ds->ds_ltarget), ds->ds_ltarget = NULL; - nta_leg_destroy(ds->ds_leg), ds->ds_leg = NULL; - su_free(own, (void *)ds->ds_remote_tag), ds->ds_remote_tag = NULL; - ds->ds_route = 0; + return nua_dialog_zap(own, ds); } return 0; } - /** @internal Get dialog usage slot. */ nua_dialog_usage_t ** nua_dialog_usage_at(nua_dialog_state_t const *ds, diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h b/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h index 88f3ffd608..63596206bd 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h @@ -422,6 +422,8 @@ void nua_dialog_uas_route(nua_owner_t *, nua_dialog_state_t *ds, sip_t const *sip, int rtag); void nua_dialog_store_peer_info(nua_owner_t *, nua_dialog_state_t *ds, sip_t const *sip); +int nua_dialog_zap(nua_owner_t *own, + nua_dialog_state_t *ds); int nua_dialog_remove(nua_owner_t *own, nua_dialog_state_t *ds, nua_dialog_usage_t *usage); @@ -545,6 +547,8 @@ su_inline void nua_client_terminating(nua_client_request_t *cr) int nua_client_init_request(nua_client_request_t *cr); +msg_t *nua_client_request_template(nua_client_request_t *cr); + int nua_client_restart_request(nua_client_request_t *cr, int terminating, tagi_t const *tags); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_event_server.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_event_server.c index 459159c29b..363bfbd76a 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_event_server.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_event_server.c @@ -262,7 +262,7 @@ void nua_stack_authorize(nua_t *nua, nua_stack_event(nua, nh, NULL, e, SIP_200_OK, NULL); } else { - nua_stack_event(nua, nh, NULL, e, NUA_INTERNAL_ERROR, NULL); + nua_stack_event(nua, nh, NULL, e, NUA_ERROR_AT(__FILE__, __LINE__), NULL); } } diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c index 26e4ccf9b0..a0e18efad7 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c @@ -775,7 +775,7 @@ static void nua_notify_usage_refresh(nua_handle_t *nh, return; } - nua_stack_tevent(nh->nh_nua, nh, NULL, e, NUA_INTERNAL_ERROR, + nua_stack_tevent(nh->nh_nua, nh, NULL, e, NUA_ERROR_AT(__FILE__, __LINE__), NUTAG_SUBSTATE(nua_substate_terminated), TAG_END()); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_publish.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_publish.c index dda89be2ba..80d3ad5fbe 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_publish.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_publish.c @@ -405,7 +405,7 @@ static int nua_publish_client_response(nua_client_request_t *cr, if (!ex || ex->ex_delta == 0) SET_STATUS(900, "Received Invalid Expiration Time"); else - SET_STATUS1(NUA_INTERNAL_ERROR); + SET_STATUS1(NUA_ERROR_AT(__FILE__, __LINE__)); } else nua_dialog_usage_set_refresh(du, ex->ex_delta); @@ -428,7 +428,7 @@ static void nua_publish_usage_refresh(nua_handle_t *nh, } nua_stack_event(nh->nh_nua, nh, NULL, - nua_r_publish, NUA_INTERNAL_ERROR, + nua_r_publish, NUA_ERROR_AT(__FILE__, __LINE__), NULL); nua_dialog_usage_remove(nh, ds, du); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c index 17ec33ae54..58c76aea88 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c @@ -1046,7 +1046,7 @@ static void nua_register_usage_refresh(nua_handle_t *nh, } /* Report that we have de-registered */ - nua_stack_event(nua, nh, NULL, nua_r_register, NUA_INTERNAL_ERROR, NULL); + nua_stack_event(nua, nh, NULL, nua_r_register, NUA_ERROR_AT(__FILE__, __LINE__), NULL); nua_dialog_usage_remove(nh, ds, du); } diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c index aa2a2d9b21..ad9f17ea30 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c @@ -1091,11 +1091,14 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags) assert(cr->cr_orq); + if (!ds->ds_leg) { - nta_outgoing_destroy(cr->cr_orq); - return -1; + /* XXX - fix nua_dialog_usage_remove_at() instead! */ + nta_outgoing_destroy(cr->cr_orq); + return -1; } + assert(ds->ds_leg); msg = nta_outgoing_getrequest(cr->cr_orq); sip = sip_object(msg); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c index d85f05f762..226fe5e398 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c @@ -650,7 +650,7 @@ void nua_stack_signal(nua_t *nua, su_msg_r msg, nua_ee_data_t *ee) } if (error < 0) { - nua_stack_event(nh->nh_nua, nh, NULL, event, NUA_INTERNAL_ERROR, NULL); + nua_stack_event(nh->nh_nua, nh, NULL, event, NUA_ERROR_AT(__FILE__, __LINE__), NULL); } su_msg_destroy(nua->nua_signal); @@ -1214,9 +1214,9 @@ nua_stack_authenticate(nua_t *nua, nua_handle_t *nh, nua_event_t e, cr->cr_waiting = cr->cr_wait_for_cred = 0; if (status < 0) - nua_client_response(cr, 900, "Cannot add credentials", NULL); + nua_client_response(cr, 900, "Operation cannot add credentials", NULL); else - nua_client_response(cr, 904, "No matching challenge", NULL); + nua_client_response(cr, 904, "Operation has no matching challenge ", NULL); } else if (status < 0) { nua_stack_event(nua, nh, NULL, e, 900, "Cannot add credentials", NULL); @@ -1984,7 +1984,7 @@ int nua_client_create(nua_handle_t *nh, return nua_stack_event(nh->nh_nua, nh, NULL, event, - NUA_INTERNAL_ERROR, + NUA_ERROR_AT(__FILE__, __LINE__), NULL); } @@ -2125,7 +2125,9 @@ int nua_client_bind(nua_client_request_t *cr, nua_dialog_usage_t *du) } if (du->du_cr && cr != du->du_cr) { - + /* This should never happen (but it does): + assert(!nua_client_is_queued(du->du_cr)); + */ if (nua_client_is_queued(du->du_cr)) return -1; if (nua_client_is_reporting(du->du_cr)) { @@ -2161,7 +2163,7 @@ int nua_client_init_request(nua_client_request_t *cr) int error = 0; if (!cr->cr_method_name) - return nua_client_return(cr, NUA_INTERNAL_ERROR, NULL); + return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), NULL); if (cr->cr_msg) return nua_client_request_try(cr); @@ -2186,12 +2188,22 @@ int nua_client_init_request(nua_client_request_t *cr) } if (!cr->cr_methods->crm_template || - !cr->cr_methods->crm_template(cr, &msg, cr->cr_tags)) - msg = nta_msg_create(nua->nua_nta, 0); + cr->cr_methods->crm_template(cr, &msg, cr->cr_tags) == 0) + msg = nua_client_request_template(cr); sip = sip_object(msg); if (!sip) - return nua_client_return(cr, NUA_INTERNAL_ERROR, msg); + return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); + + if (nh->nh_tags) { + for (t = nh->nh_tags; t; t = t_next(t)) { + if (t->t_tag == siptag_contact || + t->t_tag == siptag_contact_str) + has_contact = 1; + else if (t->t_tag == nutag_url) + url = (url_string_t const *)t->t_value; + } + } /**@par Populating SIP Request Message with Tagged Arguments * @@ -2211,30 +2223,6 @@ int nua_client_init_request(nua_client_request_t *cr) * However, if a tag value is #SIP_NONE (-1 casted as a void pointer), * the values from previous tags are ignored. */ - if (nh->nh_tags) { - for (t = nh->nh_tags; t; t = t_next(t)) { - if (t->t_tag == siptag_contact || - t->t_tag == siptag_contact_str) - has_contact = 1; - else if (t->t_tag == nutag_url) - url = (url_string_t const *)t->t_value; - } - - t = nh->nh_tags; - - /* Use the From header from the dialog */ - if (ds->ds_leg && t->t_tag == siptag_from) - t++; - - sip_add_tagis(msg, sip, &t); - } - - if (!ds->ds_route) { - sip_route_t *initial_route = NH_PGET(nh, initial_route); - if (initial_route) - sip_add_dup(msg, sip, (sip_header_t *)initial_route); - } - for (t = cr->cr_tags; t; t = t_next(t)) { if (t->t_tag == siptag_contact || t->t_tag == siptag_contact_str) @@ -2257,7 +2245,7 @@ int nua_client_init_request(nua_client_request_t *cr) if ((t = cr->cr_tags)) { if (sip_add_tagis(msg, sip, &t) < 0) - goto error; + return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); } /** @@ -2277,11 +2265,11 @@ int nua_client_init_request(nua_client_request_t *cr) if (!ds->ds_leg) { if (ds->ds_remote_tag && ds->ds_remote_tag[0] && sip_to_tag(nh->nh_home, sip->sip_to, ds->ds_remote_tag) < 0) - goto error; + return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (sip->sip_from == NULL && sip_add_dup(msg, sip, (sip_header_t *)nua->nua_from) < 0) - goto error; + return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (cr->cr_dialog) { ds->ds_leg = nta_leg_tcreate(nua->nua_nta, @@ -2292,12 +2280,12 @@ int nua_client_init_request(nua_client_request_t *cr) SIPTAG_CSEQ(sip->sip_cseq), TAG_END()); if (!ds->ds_leg) - goto error; + return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (!sip->sip_from->a_tag && sip_from_tag(msg_home(msg), sip->sip_from, nta_leg_tag(ds->ds_leg, NULL)) < 0) - goto error; + return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); } } else { @@ -2306,7 +2294,7 @@ int nua_client_init_request(nua_client_request_t *cr) } if (url && nua_client_set_target(cr, (url_t *)url) < 0) - goto error; + return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); cr->cr_has_contact = has_contact; @@ -2315,7 +2303,7 @@ int nua_client_init_request(nua_client_request_t *cr) if (error < -1) msg = NULL; if (error < 0) - goto error; + return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (error != 0) return error; } @@ -2325,8 +2313,34 @@ int nua_client_init_request(nua_client_request_t *cr) return nua_client_request_try(cr); - error: - return nua_client_return(cr, NUA_INTERNAL_ERROR, msg); +} + +msg_t *nua_client_request_template(nua_client_request_t *cr) +{ + nua_handle_t *nh = cr->cr_owner; + nua_t *nua = nh->nh_nua; + nua_dialog_state_t *ds = nh->nh_ds; + + msg_t *msg = nta_msg_create(nua->nua_nta, 0); + sip_t *sip = sip_object(msg); + + if (!sip) + return NULL; + + if (nh->nh_tags) { + tagi_t const *t = nh->nh_tags; + + /* Use the From header from the dialog. + From is always first tag in the handle */ + if (ds->ds_leg && t->t_tag == siptag_from) + t++; + + /* When the INVITE message (or any other SIP message) is + * created, the tagged values saved with nua_handle() are used first. */ + sip_add_tagis(msg, sip, &t); + } + + return msg; } @@ -2352,6 +2366,7 @@ int nua_client_restart_request(nua_client_request_t *cr, return nua_client_request_try(cr); } + return 0; } @@ -2415,7 +2430,7 @@ int nua_client_request_try(nua_client_request_t *cr) } if (error < 0) - error = nua_client_response(cr, NUA_INTERNAL_ERROR, NULL); + error = nua_client_response(cr, NUA_ERROR_AT(__FILE__, __LINE__), NULL); assert(error > 0); return error; @@ -2453,6 +2468,22 @@ int nua_client_request_sendmsg(nua_client_request_t *cr, msg_t *msg, sip_t *sip) while (sip->sip_route) sip_route_remove(msg, sip); } + else if (!ds->ds_route) { + sip_route_t *initial_route = NH_PGET(nh, initial_route); + + if (initial_route) { + initial_route = sip_route_dup(msg_home(msg), initial_route); + if (!initial_route) return -1; + msg_header_prepend(msg, (msg_pub_t*)sip, + /* This should be + (msg_header_t **)&sip->sip_route + * but directly casting pointer &sip->sip_route gives + * spurious type-punning warning */ + (msg_header_t **)((char *)sip + offsetof(sip_t, sip_route)), + (msg_header_t *)initial_route); + } + } + /** * For in-dialog requests, the request URI is taken from the @Contact @@ -2747,14 +2778,16 @@ int nua_base_client_check_restart(nua_client_request_t *cr, switch (status) { case 302: - if (nua_client_set_target(cr, sip->sip_contact->m_url) >= 0) + if (nua_dialog_zap(nh, nh->nh_ds) == 0 && + nua_client_set_target(cr, sip->sip_contact->m_url) >= 0) return nua_client_restart(cr, 100, "Redirected"); break; case 305: sip_route_init(r); *r->r_url = *sip->sip_contact->m_url; - if (sip_add_dup(cr->cr_msg, cr->cr_sip, (sip_header_t *)r) >= 0) + if (nua_dialog_zap(nh, nh->nh_ds) == 0 && + sip_add_dup(cr->cr_msg, cr->cr_sip, (sip_header_t *)r) >= 0) return nua_client_restart(cr, 100, "Redirected via a proxy"); break; @@ -2867,10 +2900,11 @@ int nua_client_restart(nua_client_request_t *cr, int status, char const *phrase) { nua_handle_t *nh = cr->cr_owner; + nua_dialog_state_t *ds = nh->nh_ds; nta_outgoing_t *orq; int error = -1, terminated, graceful; - msg_t *msg; - sip_t *sip; + msg_t *msg = NULL; + sip_t *sip = NULL; if (cr->cr_retry_count > NH_PGET(nh, retry_count)) return 0; @@ -2878,9 +2912,24 @@ int nua_client_restart(nua_client_request_t *cr, orq = cr->cr_orq, cr->cr_orq = NULL; assert(orq); terminated = cr->cr_terminated, cr->cr_terminated = 0; graceful = cr->cr_graceful, cr->cr_graceful = 0; + cr->cr_offer_sent = cr->cr_answer_recv = 0; + cr->cr_offer_recv = cr->cr_answer_sent = 0; - msg = msg_copy(cr->cr_msg); - sip = sip_object(msg); + if (!ds->ds_leg && cr->cr_dialog) { + sip_t const *sip = cr->cr_sip; + ds->ds_leg = nta_leg_tcreate(nh->nh_nua->nua_nta, + nua_stack_process_request, nh, + SIPTAG_CALL_ID(sip->sip_call_id), + SIPTAG_FROM(sip->sip_from), + SIPTAG_TO(sip->sip_to), + SIPTAG_CSEQ(sip->sip_cseq), + TAG_END()); + } + + if (ds->ds_leg || !cr->cr_dialog) { + msg = msg_copy(cr->cr_msg); + sip = sip_object(msg); + } if (msg && sip) { cr->cr_restarting = 1; diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.h b/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.h index e8a6226066..de99527258 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.h +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.h @@ -207,6 +207,7 @@ typedef struct nua_event_frame_s nua_event_frame_t; extern char const nua_internal_error[]; #define NUA_INTERNAL_ERROR 900, nua_internal_error +#define NUA_ERROR_AT(file, line) 900, "Internal error at " file ":" #line struct nua_s { su_home_t nua_home[1]; diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c index 948e2a2218..88ce79618f 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c @@ -451,7 +451,7 @@ static void nua_subscribe_usage_refresh(nua_handle_t *nh, if (!eu->eu_unsolicited) nua_stack_tevent(nh->nh_nua, nh, NULL, - nua_i_notify, NUA_INTERNAL_ERROR, + nua_i_notify, NUA_ERROR_AT(__FILE__, __LINE__), NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_EVENT(du->du_event), TAG_END()); @@ -831,22 +831,31 @@ static int nua_refer_client_request(nua_client_request_t *cr, tagi_t const *tags) { nua_handle_t *nh = cr->cr_owner; - nua_dialog_usage_t *du = cr->cr_usage; + nua_dialog_usage_t *du, *du0 = cr->cr_usage; struct event_usage *eu; sip_event_t *event; int error; cr->cr_usage = NULL; - if (du) - nua_dialog_usage_remove(nh, nh->nh_ds, du); - event = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq); if (!event) return -1; - du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, event); - if (!du) - return -1; + + if (du0 == NULL || + du0->du_event == NULL || + du0->du_event->o_id == NULL || + strcmp(du0->du_event->o_id, event->o_id)) { + du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, event); + if (!du) + return -1; + } + else { + du = du0, du0 = NULL; + } + + if (du0) + nua_dialog_usage_remove(nh, nh->nh_ds, du0); eu = nua_dialog_usage_private(cr->cr_usage = du); eu ->eu_refer = 1; diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c index 651630a217..5fd2f0c46f 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c @@ -1793,6 +1793,223 @@ int test_update_by_uas(struct context *ctx) END(); } + +int test_update_failure(struct context *ctx) +{ + BEGIN(); +#if 0 + struct endpoint *a = &ctx->a, *b = &ctx->b; + struct call *a_call = a->call, *b_call = b->call; + struct event *e, *eu, *ei; + enum nua_callstate ustate, istate; + + if (print_headings) + printf("TEST NUA-10.4.1: UPDATE failure terminating session\n"); + +/* Test UPDATE failing: + + A B + |-------INVITE------>| + |<----100 Trying-----| + | | + |<-------183---------| + |-------PRACK------->| + |<-------200---------| + | | + |<-------180---------| + | | + +------------------------+ + |<------200 OK-------| + |-------UPDATE------>| + | | + |<-------481---------| + | | + |--------ACK-------->| + | | + |<-------BYE---------| + |-------200 OK-------| + | | + +*/ + + a_call->sdp = "m=audio 5008 RTP/AVP 8"; + b_call->sdp = "m=audio 5010 RTP/AVP 0 8"; + + nua_set_params(ctx->a.nua, + NUTAG_EARLY_MEDIA(1), + SIPTAG_SUPPORTED_STR("100rel, precondition, timer"), + TAG_END()); + run_a_until(ctx, nua_r_set_params, until_final_response); + + nua_set_params(ctx->b.nua, + NUTAG_EARLY_MEDIA(1), + NUTAG_ONLY183_100REL(1), + SIPTAG_SUPPORTED_STR("100rel, precondition, timer"), + NUTAG_APPL_METHOD("UPDATE"), + TAG_END()); + run_b_until(ctx, nua_r_set_params, until_final_response); + + TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END())); + + INVITE(a, a_call, a_call->nh, + TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)), + SOATAG_USER_SDP_STR(a_call->sdp), + SIPTAG_SUPPORTED_STR("100rel"), + TAG_END()); + + run_ab_until(ctx, -1, save_until_final_response, -1, until_pracked); + + /* Client transitions: + INIT -(C1)-> CALLING: nua_invite(), nua_i_state + CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state + PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state + */ + TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */ + TEST_1(is_offer_sent(e->data->e_tags)); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite); + TEST(e->data->e_status, 183); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_proceeding); + TEST_1(is_answer_recv(e->data->e_tags)); + /* Offer is sent in PRACK */ + TEST_1(is_offer_sent(e->data->e_tags)); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack); + TEST(e->data->e_status, 200); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_proceeding); + TEST_1(is_answer_recv(e->data->e_tags)); + TEST_1(!is_offer_sent(e->data->e_tags)); + + /* Send UPDATE */ + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_proceeding); + TEST_1(!is_answer_recv(e->data->e_tags)); + TEST_1(is_offer_sent(e->data->e_tags)); + + /* The final response to the UPDATE and INVITE can be received in any order */ + eu = event_by_type(e->next, nua_r_update); + ei = event_by_type(e->next, nua_r_invite); + + TEST_1(e = eu); TEST_E(e->data->e_event, nua_r_update); + TEST(e->data->e_status, 200); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + ustate = callstate(e->data->e_tags); + TEST_1(is_answer_recv(e->data->e_tags)); + TEST_1(!is_offer_sent(e->data->e_tags)); + + TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite); + TEST(e->data->e_status, 180); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */ + TEST_1(!is_offer_answer_done(e->data->e_tags)); + + /* Final response to INVITE */ + TEST_1(ei = event_by_type(ei->next, nua_r_invite)); + + TEST_E(ei->data->e_event, nua_r_invite); TEST(ei->data->e_status, 200); + TEST_1(e = ei->next); TEST_E(e->data->e_event, nua_i_state); + istate = callstate(e->data->e_tags); + TEST_1(!is_offer_answer_done(e->data->e_tags)); + + if (eu == e->next) { + /* 200 OK to UPDATE is received after 200 OK to INVITE */ + TEST(ustate, nua_callstate_ready); + TEST(istate, nua_callstate_completing); + } + else { + /* 200 OK to UPDATE is received before 200 OK to INVITE */ + TEST(ustate, nua_callstate_proceeding); + TEST(istate, nua_callstate_ready); + } + + free_events_in_list(ctx, a->events); + + /* + Server transitions: + INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state + RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state + EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state + COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state + */ + TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite); + TEST(e->data->e_status, 100); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */ + + /* Responded with 183 Session Progress */ + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */ + TEST_1(is_answer_sent(e->data->e_tags)); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */ + TEST_1(is_offer_recv(e->data->e_tags)); + TEST_1(is_answer_sent(e->data->e_tags)); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_update); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */ + TEST_1(is_offer_recv(e->data->e_tags)); + TEST_1(is_answer_sent(e->data->e_tags)); + + /* Responded with 180 Ringing */ + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */ + TEST_1(!is_offer_answer_done(e->data->e_tags)); + + /* Responded with 200 OK */ + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */ + TEST_1(!is_offer_answer_done(e->data->e_tags)); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */ + TEST_1(!is_offer_answer_done(e->data->e_tags)); + TEST_1(!e->next); + free_events_in_list(ctx, b->events); + + if (print_headings) + printf("TEST NUA-10.4.1: PASSED\n"); + + if (print_headings) + printf("TEST NUA-10.4.2: terminate call\n"); + + BYE(b, b_call, b_call->nh, TAG_END()); + run_ab_until(ctx, -1, until_terminated, -1, until_terminated); + + /* B transitions: + READY --(T2)--> TERMINATING: nua_bye() + TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state + */ + TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */ + TEST_1(!e->next); + free_events_in_list(ctx, b->events); + + /* A: READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state */ + TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye); + TEST(e->data->e_status, 200); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */ + TEST_1(!e->next); + free_events_in_list(ctx, a->events); + + nua_handle_destroy(a_call->nh), a_call->nh = NULL; + nua_handle_destroy(b_call->nh), b_call->nh = NULL; + + if (print_headings) + printf("TEST NUA-10.4.2: PASSED\n"); +#endif + END(); +} int cancel_when_pracked(CONDITION_PARAMS); int alert_call(CONDITION_PARAMS); @@ -2105,19 +2322,281 @@ int test_180rel_cancel2(struct context *ctx) END(); } +int redirect_pracked(CONDITION_PARAMS); + +int test_180rel_redirected(struct context *ctx) +{ + BEGIN(); + + struct endpoint *a = &ctx->a, *b = &ctx->b; + struct call *a_call = a->call, *b_call = b->call; + struct event *e, *ep, *ei; + sip_t *sip; + + if (print_headings) + printf("TEST NUA-10.8.1: Call with 100rel and 180\n"); + +/* Test for 100rel: + + A B + |-------INVITE------>| + |<----100 Trying-----| + | | + |<-------180---------| + |-------PRACK------->| + |<-------200---------| + | | + |<----302 Moved------| + |--------ACK-------->| + | | + |-------INVITE------>| + |<----100 Trying-----| + | | + |<-------180---------| + |-------PRACK------->| + |<-------200---------| + | | + |<------200 OK-------| + |--------ACK-------->| + | | + |<-------BYE---------| + |-------200 OK-------| + | | + +*/ + + a_call->sdp = "m=audio 5008 RTP/AVP 8"; + b_call->sdp = "m=audio 5010 RTP/AVP 0 8"; + + nua_set_params(ctx->a.nua, + NUTAG_EARLY_MEDIA(1), + TAG_END()); + run_a_until(ctx, nua_r_set_params, until_final_response); + + nua_set_params(ctx->b.nua, + NUTAG_EARLY_MEDIA(1), + NUTAG_SESSION_TIMER(180), + TAG_END()); + run_b_until(ctx, nua_r_set_params, until_final_response); + + TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END())); + + INVITE(a, a_call, a_call->nh, + TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)), + SOATAG_USER_SDP_STR(a_call->sdp), + TAG_END()); + + run_ab_until(ctx, -1, until_ready, -1, redirect_pracked); + + /* Client transitions: + INIT -(C1)-> CALLING: nua_invite(), nua_i_state + CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack + PROCEEDING->(redirected)->CALLING: nua_r_invite, nua_i_state + CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack + PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state + */ + TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */ + TEST_1(is_offer_sent(e->data->e_tags)); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite); + TEST(e->data->e_status, 180); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_proceeding); + TEST_1(is_answer_recv(e->data->e_tags)); + TEST_1(!is_offer_sent(e->data->e_tags)); + + ei = event_by_type(e->next, nua_r_invite); /* 302 */ + ep = event_by_type(e->next, nua_r_prack); /* 200 */ + if (!ep) { + run_a_until(ctx, -1, save_until_final_response); + ep = event_by_type(e->next, nua_r_prack); + } + + TEST_1(e = ep); TEST_E(e->data->e_event, nua_r_prack); + TEST(e->data->e_status, 200); + + TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite); + TEST(e->data->e_status, 100); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST(sip->sip_status->st_status, 302); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */ + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite); + TEST(e->data->e_status, 180); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_proceeding); + TEST_1(is_answer_recv(e->data->e_tags)); + TEST_1(!is_offer_sent(e->data->e_tags)); + + ei = event_by_type(e->next, nua_r_invite); + ep = event_by_type(e->next, nua_r_prack); + if (!ep) { + run_a_until(ctx, -1, save_until_final_response); + ep = event_by_type(e->next, nua_r_prack); + } + + TEST_1(e = ep); TEST_E(e->data->e_event, nua_r_prack); + TEST(e->data->e_status, 200); + + TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite); + TEST(e->data->e_status, 200); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */ + TEST_1(!is_offer_answer_done(e->data->e_tags)); + + TEST_1(!e->next || !ep->next); + free_events_in_list(ctx, a->events); + + /* + Server transitions: + INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state + RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state + EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state + COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state + */ + TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite); + TEST(e->data->e_status, 100); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */ + TEST_1(is_offer_recv(e->data->e_tags)); + + /* Responded with 180 Ringing */ + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */ + TEST_1(is_answer_sent(e->data->e_tags)); + + /* 180 is PRACKed */ + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack); + /* 302 terminates call */ + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* Terminated */ + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_invite); + TEST(e->data->e_status, 100); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */ + TEST_1(is_offer_recv(e->data->e_tags)); + + /* Responded with 180 Ringing */ + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */ + TEST_1(is_answer_sent(e->data->e_tags)); + + /* 180 is PRACKed */ + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack); + /* Does not have effect on call state */ + + /* Respond with 200 OK */ + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */ + TEST_1(!is_offer_answer_done(e->data->e_tags)); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */ + TEST_1(!is_offer_answer_done(e->data->e_tags)); + TEST_1(!e->next); + free_events_in_list(ctx, b->events); + + if (print_headings) + printf("TEST NUA-10.8.1: PASSED\n"); + + if (print_headings) + printf("TEST NUA-10.8.2: terminate call\n"); + + BYE(b, b_call, b_call->nh, TAG_END()); + run_ab_until(ctx, -1, until_terminated, -1, until_terminated); + + /* B transitions: + READY --(T2)--> TERMINATING: nua_bye() + TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state + */ + TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */ + TEST_1(!e->next); + free_events_in_list(ctx, b->events); + + /* A: READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state */ + TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye); + TEST(e->data->e_status, 200); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */ + TEST_1(!e->next); + free_events_in_list(ctx, a->events); + + nua_handle_destroy(a_call->nh), a_call->nh = NULL; + nua_handle_destroy(b_call->nh), b_call->nh = NULL; + + if (print_headings) + printf("TEST NUA-10.8.2: PASSED\n"); + + END(); +} + +int redirect_pracked(CONDITION_PARAMS) +{ + if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR))) + return 0; + + save_event_in_list(ctx, event, ep, call); + + if (event == nua_i_prack) { + if (!ep->flags.bit0) { + sip_contact_t m[1]; + + ep->flags.bit0 = 1; + + *m = *ep->contact; + m->m_url->url_user = "302"; + RESPOND(ep, call, nh, SIP_302_MOVED_TEMPORARILY, SIPTAG_CONTACT(m), TAG_END()); + return 0; + } + else { + RESPOND(ep, call, nh, SIP_200_OK, + TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)), + TAG_END()); + } + } + + + switch (callstate(tags)) { + case nua_callstate_received: + RESPOND(ep, call, nh, SIP_180_RINGING, + TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)), + TAG_END()); + return 0; + case nua_callstate_ready: + return 1; + case nua_callstate_terminated: + if (call) + nua_handle_destroy(call->nh), call->nh = NULL; + return 0; + default: + return 0; + } +} int test_100rel(struct context *ctx) { int retval = 0; - + retval = test_180rel(ctx); RETURN_ON_SINGLE_FAILURE(retval); retval = test_prack_auth(ctx); RETURN_ON_SINGLE_FAILURE(retval); retval = test_183rel(ctx); RETURN_ON_SINGLE_FAILURE(retval); retval = test_preconditions(ctx); RETURN_ON_SINGLE_FAILURE(retval); retval = test_preconditions2(ctx); RETURN_ON_SINGLE_FAILURE(retval); retval = test_update_by_uas(ctx); RETURN_ON_SINGLE_FAILURE(retval); + retval = test_update_failure(ctx); RETURN_ON_SINGLE_FAILURE(retval); retval = test_180rel_cancel1(ctx); RETURN_ON_SINGLE_FAILURE(retval); retval = test_180rel_cancel2(ctx); RETURN_ON_SINGLE_FAILURE(retval); + retval = test_180rel_redirected(ctx); RETURN_ON_SINGLE_FAILURE(retval); nua_set_params(ctx->a.nua, NUTAG_EARLY_MEDIA(0), diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_refer.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_refer.c index 56e0d91af0..bb029c5fd6 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_refer.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_refer.c @@ -48,13 +48,14 @@ int test_refer0(struct context *ctx, char const *tests, int refer_with_id, int notify_by_appl); - int notify_until_terminated(CONDITION_PARAMS); +int test_challenge_refer(struct context *ctx); int test_refer(struct context *ctx) { /* test twice, once without id and once with id */ return + test_challenge_refer(ctx) || test_refer0(ctx, "NUA-9.1", 0, 0) || test_refer0(ctx, "NUA-9.2", 1, 0) || test_refer0(ctx, "NUA-9.3", 0, 1) || @@ -713,3 +714,139 @@ int notify_until_terminated(CONDITION_PARAMS) return 0; } } + +int authenticate_refer(CONDITION_PARAMS); +int reject_refer_after_notified(CONDITION_PARAMS); + +int test_challenge_refer(struct context *ctx) +{ + BEGIN(); + + struct endpoint *a = &ctx->a, *b = &ctx->b, *c = &ctx->c; + struct call *a_call = a->call, *c_call = c->call; + struct event *e; + sip_t const *sip; + + sip_refer_to_t r0[1]; + + if (!ctx->proxy_tests) + return 0; + + if (print_headings) + printf("TEST NUA-9.0.1: challenge REFER\n"); + + nua_set_params(ctx->a.nua, NUTAG_APPL_METHOD("REFER"), TAG_END()); + run_a_until(ctx, nua_r_set_params, until_final_response); + + *sip_refer_to_init(r0)->r_url = *b->contact->m_url; + r0->r_url->url_headers = "subject=referred"; + r0->r_display = "B"; + + TEST_1(c_call->nh = nua_handle(c->nua, c_call, SIPTAG_TO(a->to), TAG_END())); + + REFER(c, c_call, c_call->nh, + SIPTAG_FROM(c->to), + SIPTAG_REFER_TO(r0), + TAG_END()); + + run_abc_until(ctx, -1, reject_refer_after_notified, -1, NULL, -1, authenticate_refer); + + /* + Events in A: + nua_i_refer + */ + TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_refer); + TEST(e->data->e_status, 100); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_refer_to); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify); + TEST(e->data->e_status, 200); + TEST_1(!e->next); + /* + Events in C after nua_refer(): + nua_r_refer + */ + TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_r_refer); + TEST(e->data->e_status, 100); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_refer); + TEST(e->data->e_status, 407); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_refer); + TEST(e->data->e_status, 100); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); + TEST(e->data->e_status, 200); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_refer); + TEST(e->data->e_status, 480); + + TEST_1(!e->next); + + free_events_in_list(ctx, a->events); + free_events_in_list(ctx, c->events); + + nua_handle_destroy(a_call->nh), a_call->nh = NULL; + nua_handle_destroy(c_call->nh), c_call->nh = NULL; + + nua_set_params(ctx->a.nua, + NUTAG_APPL_METHOD(NULL), + NUTAG_APPL_METHOD("INVITE, REGISTER, PUBLISH, SUBSCRIBE"), + TAG_END()); + run_a_until(ctx, nua_r_set_params, until_final_response); + + if (print_headings) + printf("TEST NUA-9.0.1: PASSED\n"); + + END(); +} + +int reject_refer_after_notified(CONDITION_PARAMS) +{ + if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR))) + return 0; + + save_event_in_list(ctx, event, ep, call); + + if (event == nua_i_refer) { + } + + if (event == nua_r_notify) { + /* Respond to refer only after initial notify has been responded */ + struct eventlist *list; + struct event *e; + + if (call->events) + list = call->events; + else + list = ep->events; + + for (e = list->head; e; e = e->next) + if (e->data->e_event == nua_i_refer) + break; + + if (e) { + RESPOND(ep, call, nh, SIP_480_TEMPORARILY_UNAVAILABLE, + NUTAG_WITH(e->data->e_msg), + TAG_END()); + return 1; + } + return 0; + } + + return 0; +} + +int authenticate_refer(CONDITION_PARAMS) +{ + if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR))) + return 0; + + save_event_in_list(ctx, event, ep, call); + + if (status == 401 || status == 407) { + AUTHENTICATE(ep, call, nh, + NUTAG_AUTH("Digest:\"test-proxy\":charlie:secret"), + TAG_END()); + } + + return status == 480; +} diff --git a/libs/sofia-sip/libsofia-sip-ua/sip/sip_extra.c b/libs/sofia-sip/libsofia-sip-ua/sip/sip_extra.c index 4b1e6c4e45..a5f9db2bb4 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sip/sip_extra.c +++ b/libs/sofia-sip/libsofia-sip-ua/sip/sip_extra.c @@ -220,9 +220,13 @@ issize_t sip_error_info_e(char b[], isize_t bsiz, sip_header_t const *h, int f) * * The parsed Alert-Info header is stored in #sip_alert_info_t structure. * - * @NEW_1_12_7. In order to use @b Alert-Info header, - * initialize the SIP parser with, e.g., - * sip_update_default_mclass(sip_extend_mclass(NULL)) + * @NEW_1_12_7. In order to use @b Alert-Info header, initialize the SIP + * parser before calling nta_agent_create() or nua_create() with, e.g., + * sip_update_default_mclass(sip_extend_mclass(NULL)). + * + * The #sip_t structure does not contain a @a sip_alert_info field, but + * sip_alert_info() function should be used for accessing the @b Alert-Info + * header structure. */ /**@ingroup sip_alert_info @@ -283,8 +287,14 @@ issize_t sip_alert_info_e(char b[], isize_t bsiz, sip_header_t const *h, int f) * @sa sip_update_default_mclass() * * @NEW_1_12_7. In order to use @b Reply-To header, - * initialize the SIP parser with, e.g., + * initialize the SIP parser before calling nta_agent_create() or + * nua_create() with, e.g., * sip_update_default_mclass(sip_extend_mclass(NULL)). + * + * @note + * The #sip_t structure does not contain a @a sip_reply_to field, but + * sip_reply_to() function should be used for accessing the @b Reply-To + * header structure. */ /**@ingroup sip_reply_to @@ -933,7 +943,13 @@ char *sip_info_dup_one(sip_header_t *dst, * * @sa @RFC3265, draft-niemi-sip-subnot-etags-01.txt * - * @EXP_1_12_5. Note that #sip_t does not contain @a + * @EXP_1_12_5. + * In order to use @b Suppress-Body-If-Match header, + * initialize the SIP parser with, e.g., + * sip_update_default_mclass(sip_extend_mclass(NULL)). + * + * @note + * The #sip_t structure does not contain a @a * sip_suppress_body_if_match field, but sip_suppress_body_if_match() * function should be used for accessing the @b Suppress-Body-If-Match * header structure. @@ -999,10 +1015,15 @@ issize_t sip_suppress_body_if_match_e(char b[], isize_t bsiz, * * @sa @RFC3265, draft-niemi-sip-subnot-etag-01 * - * @EXP_1_12_5. Note that #sip_t does not contain @a - * sip_suppress_notify_if_match field, but sip_suppress_notify_if_match() - * function should be used for accessing the @b Suppress-Notify-If-Match - * header structure. + * @EXP_1_12_5. + * In order to use @b Suppress-Notify-If-Match header, + * initialize the SIP parser with, e.g., + * sip_update_default_mclass(sip_extend_mclass(NULL)). + * + * @note + * The #sip_t struct does not contain @a sip_suppress_notify_if_match field, + * but sip_suppress_notify_if_match() function should be used for accessing + * the @b Suppress-Notify-If-Match header structure. */ /**@ingroup sip_suppress_notify_if_match @@ -1085,8 +1106,14 @@ issize_t sip_suppress_notify_if_match_e(char b[], isize_t bsiz, * @sa sip_update_default_mclass(), draft-ietf-sip-privacy-04.txt, @RFC3325 * * @NEW_1_12_7. In order to use @b Remote-Party-ID header, - * initialize the SIP parser with, e.g., + * initialize the SIP parser before calling nta_agent_create() or + * nua_create() with, e.g., * sip_update_default_mclass(sip_extend_mclass(NULL)). + * + * @note + * The #sip_t structure does not contain @a sip_remote_party_id field, but + * sip_remote_party_id() function should be used for accessing the @b + * Remote-Party-ID header structure. */ /**@ingroup sip_remote_party_id @@ -1222,8 +1249,14 @@ static int sip_remote_party_id_update(msg_common_t *h, * @sa @RFC3325, @PPreferredIdentity * * @NEW_1_12_7. In order to use @b P-Asserted-Identity header, - * initialize the SIP parser with, e.g., + * initialize the SIP parser before calling nta_agent_create() or + * nua_create() with, e.g., * sip_update_default_mclass(sip_extend_mclass(NULL)). + * + * @note + * The #sip_t structure does not contain @a sip_p_asserted_identity field, + * but sip_p_asserted_identity() function should be used for accessing the + * @b P-Asserted-Identity header structure. */ /**@ingroup sip_p_asserted_identity @@ -1325,8 +1358,14 @@ char *sip_p_asserted_identity_dup_one(sip_header_t *dst, * @sa @RFC3325, @PAssertedIdentity * * @NEW_1_12_7. In order to use @b P-Preferred-Identity header, - * initialize the SIP parser with, e.g., + * initialize the SIP parser before calling nta_agent_create() or + * nua_create() with, e.g., * sip_update_default_mclass(sip_extend_mclass(NULL)). + * + * @note + * The #sip_t structure does not contain @a sip_p_preferred_identity field, + * but sip_p_preferred_identity() function should be used for accessing the + * @b P-Preferred-Identity header structure. */ /**@ingroup sip_p_preferred_identity diff --git a/libs/sofia-sip/libsofia-sip-ua/sip/sip_parser.c b/libs/sofia-sip/libsofia-sip-ua/sip/sip_parser.c index 9f0976fa3c..1051354b84 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sip/sip_parser.c +++ b/libs/sofia-sip/libsofia-sip-ua/sip/sip_parser.c @@ -90,6 +90,8 @@ msg_mclass_t const *sip_default_mclass(void) * @retval 0 when successful * @retval -1 upon an error * + * @sa sip_extend_mclass() + * * @NEW_1_12_7. */ int sip_update_default_mclass(msg_mclass_t const *mclass) @@ -103,12 +105,22 @@ int sip_update_default_mclass(msg_mclass_t const *mclass) /**Extend SIP parser class with extension headers. * * Extend given SIP parser class with extension headers. If the given parser - * class is the default one or NULL, make a clone of it before extending it. + * (message class) is the default one or NULL, make a clone of default + * parser before extending it. * * @param input pointer to a SIP message class (may be NULL) * * @return Pointer to extended mclass, or NULL upon an error. * + * @sa + * @AlertInfo, + * @ReplyTo, + * @RemotePartyId, + * @PAssertedIdentity, + * @PPreferredIdentity, + * @SuppressBodyIfMatch, + * @SuppressNotifyIfMatch + * * @NEW_1_12_7. */ msg_mclass_t *sip_extend_mclass(msg_mclass_t *input) diff --git a/libs/sofia-sip/libsofia-sip-ua/sip/sip_util.c b/libs/sofia-sip/libsofia-sip-ua/sip/sip_util.c index 5d371ea98e..3862de7f73 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sip/sip_util.c +++ b/libs/sofia-sip/libsofia-sip-ua/sip/sip_util.c @@ -883,8 +883,7 @@ sip_security_client_select(sip_security_client_t const *client, * decide whether to gracefully terminate or not, the * @a *return_graceful_terminate_usage is left unmodified. * - * @sa - * http://www.ietf.org/internet-drafts/draft-ietf-sipping-dialogusage-02.txt + * @RFC 5057 */ int sip_response_terminates_dialog(int response_code, sip_method_t method, @@ -1060,8 +1059,6 @@ int sip_response_terminates_dialog(int response_code, usage in an existing dialog, no new usage is created and existing usages are unaffected. */ - *return_graceful_terminate_usage = 0; - return 0; case 423: /** @par 423 Interval Too Brief @@ -1070,8 +1067,6 @@ int sip_response_terminates_dialog(int response_code, subscribe usage is not destroyed (or otherwise affected). No other usages of the dialog are affected. */ - *return_graceful_terminate_usage = 0; - return sip_method_subscribe == method ? terminate_usage : no_effect; case 428: /** @par 428 Use Identity Header @@ -1079,8 +1074,6 @@ int sip_response_terminates_dialog(int response_code, the usage. The usage is not affected. The dialog is only affected by a change in its local @CSeq. No other usages of the dialog are affected. */ - *return_graceful_terminate_usage = 0; - return 0; case 429: /** @par 429 Provide Referrer Identity diff --git a/libs/sofia-sip/libsofia-sip-ua/sip/torture_sip.c b/libs/sofia-sip/libsofia-sip-ua/sip/torture_sip.c index 332aed0c52..f48ee56f52 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sip/torture_sip.c +++ b/libs/sofia-sip/libsofia-sip-ua/sip/torture_sip.c @@ -77,6 +77,9 @@ static msg_t *read_message(int flags, char const string[]); static int test_identity(void) { su_home_t *home; + sip_alert_info_t *ai; + sip_reply_to_t *rplyto; + sip_remote_party_id_t *rpid; sip_p_asserted_identity_t *paid; sip_p_preferred_identity_t *ppid; @@ -115,15 +118,21 @@ static int test_identity(void) "Call-ID: 0ha0isndaksdj@10.1.2.3\r\n" "CSeq: 8 SUBSCRIBE\r\n" "Via: SIP/2.0/UDP 135.180.130.133\r\n" + "Alert-Info: ;walz, \r\n" + "Reply-To: Arska ;humppa\r\n" "P-Asserted-Identity: \r\n" "P-Preferred-Identity: , \r\n" + "Remote-Party-ID: \r\n" "Content-Length: 0\r\n" "\r\n"); sip = sip_object(msg); + TEST_1(!sip_alert_info(sip)); + TEST_1(!sip_reply_to(sip)); TEST_1(!sip_p_asserted_identity(sip)); TEST_1(!sip_p_preferred_identity(sip)); + TEST_1(!sip_remote_party_id(sip)); msg_destroy(msg); @@ -139,13 +148,22 @@ static int test_identity(void) "Call-ID: 0ha0isndaksdj@10.1.2.3\r\n" "CSeq: 8 SUBSCRIBE\r\n" "Via: SIP/2.0/UDP 135.180.130.133\r\n" + "Alert-Info: ;walz, \r\n" + "Reply-To: Arska ;humppa\r\n" "P-Asserted-Identity: \r\n" "P-Preferred-Identity: , \r\n" + "Remote-Party-ID: \r\n" "Content-Length: 0\r\n" "\r\n"); sip = sip_object(msg); + TEST_1(!sip_alert_info(sip)); + TEST_1(!sip_reply_to(sip)); + TEST_1(sip_p_asserted_identity(sip)); + TEST_1(sip_p_preferred_identity(sip)); + TEST_1(!sip_remote_party_id(sip)); + TEST_1(home = msg_home(msg)); TEST_1((paid = sip_p_asserted_identity_make(home, "sip:joe@example.com"))); @@ -161,6 +179,34 @@ static int test_identity(void) /* Now with extensions */ TEST_1(test_mclass = msg_mclass_clone(def1, 0, 0)); + msg = read_message(MSG_DO_EXTRACT_COPY, + "BYE sip:foo@bar SIP/2.0\r\n" + "To: ;tag=deadbeef\r\n" + "From: ;\r\n" + "Call-ID: 0ha0isndaksdj@10.1.2.3\r\n" + "CSeq: 8 SUBSCRIBE\r\n" + "Via: SIP/2.0/UDP 135.180.130.133\r\n" + "Alert-Info: ;walz, \r\n" + "Reply-To: Arska ;humppa\r\n" + "P-Asserted-Identity: \r\n" + "P-Preferred-Identity: , \r\n" + "Remote-Party-ID: \r\n" + "Content-Length: 0\r\n" + "\r\n"); + + sip = sip_object(msg); + + TEST_1(ai = sip_alert_info(sip)); + TEST_S(ai->ai_url->url_host, "test.com"); + TEST_1(rplyto = sip_reply_to(sip)); + TEST_S(rplyto->rplyto_url->url_host, "gov.ca.us"); + TEST_1(paid = sip_p_asserted_identity(sip)); + TEST_1(ppid = sip_p_preferred_identity(sip)); + TEST_1(rpid = sip_remote_party_id(sip)); + TEST_S(rpid->rpid_url->url_host, "test.domain.com"); + + msg_destroy(msg); + { su_home_t *home = su_home_clone(NULL, sizeof *home); diff --git a/libs/sofia-sip/libsofia-sip-ua/soa/soa.c b/libs/sofia-sip/libsofia-sip-ua/soa/soa.c index e5a6e2e7d6..8689800514 100644 --- a/libs/sofia-sip/libsofia-sip-ua/soa/soa.c +++ b/libs/sofia-sip/libsofia-sip-ua/soa/soa.c @@ -70,10 +70,6 @@ static char const __func__[] = "soa"; /* ======================================================================== */ /* Internal prototypes */ -void soa_set_activity(soa_session_t *ss, - sdp_media_t const *, - int remote); - su_inline int soa_media_is_ready(soa_session_t const *ss); enum soa_sdp_kind { @@ -1171,7 +1167,7 @@ int soa_base_set_remote_sdp(soa_session_t *ss, if (!new_version) return 0; - soa_set_activity(ss, sdp->sdp_media, 1); + soa_set_activity(ss, sdp->sdp_media, soa_activity_remote); ss->ss_remote_version++; @@ -1454,7 +1450,7 @@ int soa_base_generate_offer(soa_session_t *ss, if (!sdp) return -1; - soa_set_activity(ss, sdp->sdp_media, 0); + soa_set_activity(ss, sdp->sdp_media, soa_activity_local); /* Wanted activity */ ss->ss_offer_sent = 1; ss->ss_answer_recv = 0; @@ -1542,8 +1538,7 @@ int soa_base_generate_answer(soa_session_t *ss, su_free(ss->ss_home, ss->ss_rsession); ss->ss_rsession = rsession; - soa_set_activity(ss, l_sdp->sdp_media, 0); - soa_set_activity(ss, r_sdp->sdp_media, 1); + soa_set_activity(ss, l_sdp->sdp_media, soa_activity_session); ss->ss_offer_recv = 1; ss->ss_answer_sent = 1; @@ -1624,8 +1619,7 @@ int soa_base_process_answer(soa_session_t *ss, su_free(ss->ss_home, ss->ss_rsession); ss->ss_rsession = rsession; - soa_set_activity(ss, l_sdp->sdp_media, 0); - soa_set_activity(ss, r_sdp->sdp_media, 1); + soa_set_activity(ss, l_sdp->sdp_media, soa_activity_session); ss->ss_answer_recv = 1; ss->ss_complete = 1; @@ -1692,7 +1686,7 @@ int soa_base_process_reject(soa_session_t *ss, if (!l_sdp) return -1; - soa_set_activity(ss, l_sdp->sdp_media, 0); + soa_set_activity(ss, l_sdp->sdp_media, soa_activity_session); ss->ss_offer_sent = 0; @@ -1783,8 +1777,7 @@ void soa_base_terminate(soa_session_t *ss, char const *option) ss->ss_oa_rounds = 0; soa_description_free(ss, ss->ss_remote); - soa_set_activity(ss, NULL, 0); - soa_set_activity(ss, NULL, 1); + soa_set_activity(ss, NULL, soa_activity_session); } /** Return true if the SDP Offer/Answer negotation is complete. @@ -1889,56 +1882,75 @@ int soa_media_is_ready(soa_session_t const *ss) void soa_set_activity(soa_session_t *ss, sdp_media_t const *m, - int remote) + enum soa_activity activity) { struct soa_media_activity *ma; sdp_connection_t const *c; - int mode; - int ma_audio = SOA_ACTIVE_DISABLED; - int ma_video = SOA_ACTIVE_DISABLED; - int ma_chat = SOA_ACTIVE_DISABLED; - int ma_image = SOA_ACTIVE_DISABLED; - int *p; + int mode, swap; + int l_audio = SOA_ACTIVE_DISABLED, r_audio = SOA_ACTIVE_DISABLED; + int l_video = SOA_ACTIVE_DISABLED, r_video = SOA_ACTIVE_DISABLED; + int l_chat = SOA_ACTIVE_DISABLED, r_chat = SOA_ACTIVE_DISABLED; + int l_image = SOA_ACTIVE_DISABLED, r_image = SOA_ACTIVE_DISABLED; - remote = !!remote; - - ma = remote ? ss->ss_remote_activity : ss->ss_local_activity; + int *l, *r; for (; m; m = m->m_next) { if (m->m_type == sdp_media_audio) - p = &ma_audio; + l = &l_audio, r = &r_audio; else if (m->m_type == sdp_media_video) - p = &ma_video; + l = &l_video, r = &r_video; else if (m->m_type == sdp_media_image) - p = &ma_image; + l = &l_image, r = &r_image; else if (strcasecmp(m->m_type_name, "message") == 0) - p = &ma_chat; + l = &l_chat, r = &r_chat; else continue; if (m->m_rejected) { - if (*p < 0) - *p = SOA_ACTIVE_REJECTED; + if (*l < 0) *l = SOA_ACTIVE_REJECTED; + if (*r < 0) *r = SOA_ACTIVE_REJECTED; continue; } - mode = m->m_mode; + mode = m->m_mode, swap = ((mode << 1) & 2) | ((mode >> 1) & 1); c = sdp_media_connections((sdp_media_t *)m); - if (remote != (c && c->c_mcast)) - mode = ((mode << 1) & 2) | ((mode >> 1) & 1); - - if (*p < 0) - *p = mode; - else - *p |= mode; + switch (activity) { + case soa_activity_local: + *l &= SOA_ACTIVE_SENDRECV; + *l |= c && c->c_mcast ? swap : mode; + break; + case soa_activity_remote: + *r &= SOA_ACTIVE_SENDRECV; + *r = c && c->c_mcast ? mode : swap; + break; + case soa_activity_session: + *l &= SOA_ACTIVE_SENDRECV; + *l |= c && c->c_mcast ? swap : mode; + *r &= SOA_ACTIVE_SENDRECV; + *r = c && c->c_mcast ? swap : mode; + break; + } } - ma->ma_audio = ma_audio; - ma->ma_video = ma_video; - ma->ma_image = ma_image; - ma->ma_chat = ma_chat; + if (activity == soa_activity_local || + activity == soa_activity_session) { + ma = ss->ss_local_activity; + ma->ma_audio = l_audio; + ma->ma_video = l_video; + ma->ma_image = l_image; + ma->ma_chat = l_chat; + } + + if (activity == soa_activity_remote || + activity == soa_activity_session) { + ma = ss->ss_remote_activity; + ma->ma_audio = r_audio; + ma->ma_video = r_video; + ma->ma_image = r_image; + ma->ma_chat = r_chat; + } } /* ----------------------------------------------------------------------*/ diff --git a/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c b/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c index fdeecdb556..99642a6070 100644 --- a/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c +++ b/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c @@ -974,58 +974,28 @@ int soa_sdp_reject(su_home_t *home, return 0; } -/** Check if @a session mode should be changed. */ + +/** Update mode within session. + * + * @sa soatag_hold + * + * @retval 1 if session was changed (or to be changed, if @a dryrun is nonzero) + */ static -int soa_sdp_mode_set_is_needed(sdp_session_t const *session, - sdp_session_t const *remote, - char const *hold) -{ - sdp_media_t const *sm, *rm, *rm_next; - int hold_all; - sdp_mode_t recv_mode; - - SU_DEBUG_7(("soa_sdp_mode_set_is_needed(%p, %p, \"%s\"): called\n", - (void *)session, (void *)remote, hold ? hold : "")); - - if (!session ) - return 0; - - hold_all = str0cmp(hold, "*") == 0; - - rm = remote ? remote->sdp_media : NULL, rm_next = NULL; - - for (sm = session->sdp_media; sm; sm = sm->m_next, rm = rm_next) { - rm_next = rm ? rm->m_next : NULL; - - if (sm->m_rejected) - continue; - - if (rm) { - /* Mode bits do not match */ - if (((rm->m_mode & sdp_recvonly) == sdp_recvonly) - != ((sm->m_mode & sdp_sendonly) == sdp_sendonly)) - return 1; - } - - recv_mode = sm->m_mode & sdp_recvonly; - if (recv_mode && hold && - (hold_all || strcasestr(hold, sm->m_type_name))) - return 1; - } - - return 0; -} - - -/** Update mode within session */ -static -int soa_sdp_mode_set(sdp_session_t *session, +int soa_sdp_mode_set(sdp_session_t const *user, + int const *s2u, + sdp_session_t *session, sdp_session_t const *remote, - char const *hold) + char const *hold, + int dryrun) { sdp_media_t *sm; - sdp_media_t const *rm, *rm_next; + sdp_media_t const *rm, *rm_next, *um; + int retval = 0, i, j; int hold_all; + int inactive_all; + int inactive = 0; + char const *hold_media = NULL; sdp_mode_t send_mode, recv_mode; SU_DEBUG_7(("soa_sdp_mode_set(%p, %p, \"%s\"): called\n", @@ -1037,25 +1007,58 @@ int soa_sdp_mode_set(sdp_session_t *session, rm = remote ? remote->sdp_media : NULL, rm_next = NULL; hold_all = str0cmp(hold, "*") == 0; + inactive_all = str0cmp(hold, "#") == 0; - for (sm = session->sdp_media; sm; sm = sm->m_next, rm = rm_next) { + i = 0; + + for (sm = session->sdp_media; sm; sm = sm->m_next, rm = rm_next, i++) { rm_next = rm ? rm->m_next : NULL; + inactive = 0; if (sm->m_rejected) continue; - send_mode = sdp_sendonly; + assert(s2u); + + for (j = 0, um = user->sdp_media; j != s2u[i]; um = um->m_next, j++) + assert(um); + assert(um); + + send_mode = um->m_mode & sdp_sendonly; if (rm) send_mode = (rm->m_mode & sdp_recvonly) ? sdp_sendonly : 0; - recv_mode = sm->m_mode & sdp_recvonly; - if (recv_mode && hold && (hold_all || strcasestr(hold, sm->m_type_name))) - recv_mode = 0; + recv_mode = um->m_mode & sdp_recvonly; - sm->m_mode = recv_mode | send_mode; + if (rm && rm->m_mode == sdp_inactive) { + send_mode = recv_mode = 0; + } + else if (inactive_all) { + send_mode = recv_mode = 0; + } + else if (hold_all) { + recv_mode = 0; + } + else if (hold && (hold_media = strcasestr(hold, sm->m_type_name))) { + recv_mode = 0; + hold_media += strlen(sm->m_type_name); + hold_media += strspn(hold_media, " \t"); + if (hold_media[0] == '=') { + hold_media += strspn(hold, " \t"); + if (strncasecmp(hold_media, "inactive", strlen("inactive")) == 0) + recv_mode = send_mode = 0; + } + } + + if (sm->m_mode != (unsigned)(recv_mode | send_mode)) + retval = 1; + + if (!dryrun) { + sm->m_mode = recv_mode | send_mode; + } } - return 0; + return retval; } enum offer_answer_action { @@ -1213,16 +1216,22 @@ static int offer_answer_step(soa_session_t *ss, /* Step D: Set media mode bits */ switch (action) { + int const *s2u_; + case generate_offer: case generate_answer: case process_answer: - if (soa_sdp_mode_set_is_needed(local, remote, ss->ss_hold)) { + s2u_ = s2u; + + if (!s2u_) s2u_ = sss->sss_s2u; + + if (soa_sdp_mode_set(user, s2u_, local, remote, ss->ss_hold, 1)) { if (local != local0) { *local0 = *local, local = local0; DUP_LOCAL(local); } - soa_sdp_mode_set(local, remote, ss->ss_hold); + soa_sdp_mode_set(user, s2u_, local, remote, ss->ss_hold, 0); } break; default: diff --git a/libs/sofia-sip/libsofia-sip-ua/soa/soa_tag.c b/libs/sofia-sip/libsofia-sip-ua/soa/soa_tag.c index 29da494cdc..f3e000fb3f 100644 --- a/libs/sofia-sip/libsofia-sip-ua/soa/soa_tag.c +++ b/libs/sofia-sip/libsofia-sip-ua/soa/soa_tag.c @@ -585,12 +585,22 @@ tag_typedef_t soatag_srtp_integrity = BOOLTAG_TYPEDEF(srtp_integrity); /**@def SOATAG_HOLD(x) * - * Hold media stream or streams. When putting a SIP session on hold, the - * application can include, e.g., SOATAG_HOLD("audio") or - * SOATAG_HOLD("video") or SOATAG_HOLD("audio, video") or SOATAG_HOLD("*") - * as @soa parameters. When resuming the session, it can include - * SOATAG_HOLD(NULL). Note that last SOATAG_HOLD() in the tag list will - * override the SOATAG_HOLD() tags before it. + * Hold media stream or streams. + * + * The hold media stream will have the attribute a=sendonly (meaning that + * some hold announcements or pause music is sent to the held party but that + * the held party should not generate any media) or a=inactive (meaning that + * no media is sent). + * + * When putting a SIP session on hold with sendonly, the application can + * include, e.g., SOATAG_HOLD("audio") or SOATAG_HOLD("video") or + * SOATAG_HOLD("audio, video") or SOATAG_HOLD("*") as @soa parameters. When + * using inactive instead, the application should use "#" or + * "audio=inactive" instead. When resuming the session, application should + * include the tag SOATAG_HOLD(NULL). + * + * Note that last SOATAG_HOLD() in the tag list will override the + * SOATAG_HOLD() tags before it. * * @par Used with * soa_set_params(), soa_get_params(), soa_get_paramlist() \n diff --git a/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_session.h b/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_session.h index 8ff75d0773..f61bd74013 100644 --- a/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_session.h +++ b/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_session.h @@ -238,8 +238,11 @@ SOFIAPUBFUN int soa_has_received_sdp(soa_session_t const *ss); SOFIAPUBFUN int soa_set_status(soa_session_t *ss, int status, char const *phrase); +enum soa_activity { soa_activity_local, soa_activity_remote, soa_activity_session }; + SOFIAPUBFUN void soa_set_activity(soa_session_t *ss, - sdp_media_t const *m, int remote); + sdp_media_t const *m, + enum soa_activity activity); SOFIAPUBFUN int soa_description_set(soa_session_t *ss, struct soa_description *ssd, diff --git a/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c b/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c index 4054f18920..cbd808a468 100644 --- a/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c +++ b/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c @@ -451,20 +451,67 @@ int test_static_offer_answer(struct context *ctx) TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDONLY); TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDONLY); - /* 'A' will release hold. */ - TEST(soa_set_params(a, SOATAG_HOLD(NULL), TAG_END()), 1); + /* 'A' will put call inactive */ + offer = NONE; + TEST(soa_set_params(a, SOATAG_HOLD("#"), TAG_END()), 1); TEST(soa_generate_offer(a, 1, test_completed), 0); TEST(soa_get_local_sdp(a, NULL, &offer, &offerlen), 1); TEST_1(offer != NULL && offer != NONE); - TEST_1(!strstr(offer, "a=sendonly")); + TEST_1(strstr(offer, "a=inactive")); TEST(soa_set_remote_sdp(b, 0, offer, offerlen), 1); TEST(soa_generate_answer(b, test_completed), 0); TEST_1(soa_is_complete(b)); TEST(soa_activate(b, NULL), 0); TEST(soa_get_local_sdp(b, NULL, &answer, &answerlen), 1); TEST_1(answer != NULL && answer != NONE); - TEST_1(!strstr(answer, "a=recvonly")); + TEST_1(strstr(answer, "a=inactive")); + TEST(soa_set_remote_sdp(a, 0, answer, -1), 1); + TEST(soa_process_answer(a, test_completed), 0); + TEST(soa_activate(a, NULL), 0); + + TEST(soa_is_audio_active(a), SOA_ACTIVE_INACTIVE); + TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_INACTIVE); + + /* B will send an offer to A, but there is no change in O/A status */ + TEST(soa_generate_offer(b, 1, test_completed), 0); + TEST(soa_get_local_sdp(b, NULL, &offer, &offerlen), 1); + TEST_1(offer != NULL && offer != NONE); + TEST_1(!strstr(offer, "a=inactive")); + printf("offer:\n%s", offer); + TEST(soa_set_remote_sdp(a, 0, offer, offerlen), 1); + TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDRECV); + TEST(soa_generate_answer(a, test_completed), 0); + TEST(soa_is_audio_active(a), SOA_ACTIVE_INACTIVE); + TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_INACTIVE); + TEST_1(soa_is_complete(a)); + TEST(soa_activate(a, NULL), 0); + TEST(soa_get_local_sdp(a, NULL, &answer, &answerlen), 1); + TEST_1(answer != NULL && answer != NONE); + TEST_1(strstr(answer, "a=inactive")); + printf("answer:\n%s", answer); + TEST(soa_set_remote_sdp(b, 0, answer, -1), 1); + TEST(soa_process_answer(b, test_completed), 0); + TEST(soa_activate(b, NULL), 0); + + + TEST(soa_is_audio_active(b), SOA_ACTIVE_INACTIVE); + TEST(soa_is_remote_audio_active(b), SOA_ACTIVE_INACTIVE); + + /* 'A' will release hold. */ + TEST(soa_set_params(a, SOATAG_HOLD(NULL), TAG_END()), 1); + + TEST(soa_generate_offer(a, 1, test_completed), 0); + TEST(soa_get_local_sdp(a, NULL, &offer, &offerlen), 1); + TEST_1(offer != NULL && offer != NONE); + TEST_1(!strstr(offer, "a=sendonly") && !strstr(offer, "a=inactive")); + TEST(soa_set_remote_sdp(b, 0, offer, offerlen), 1); + TEST(soa_generate_answer(b, test_completed), 0); + TEST_1(soa_is_complete(b)); + TEST(soa_activate(b, NULL), 0); + TEST(soa_get_local_sdp(b, NULL, &answer, &answerlen), 1); + TEST_1(answer != NULL && answer != NONE); + TEST_1(!strstr(answer, "a=recvonly") && !strstr(answer, "a=inactive")); TEST(soa_set_remote_sdp(a, 0, answer, -1), 1); TEST(soa_process_answer(a, test_completed), 0); TEST(soa_activate(a, NULL), 0); @@ -1329,7 +1376,7 @@ int test_asynch_offer_answer(struct context *ctx) { BEGIN(); -#if 0 +#if 0 /* This has never been implemented */ int n; char const *caps = NONE, *offer = NONE, *answer = NONE; diff --git a/libs/sofia-sip/libsofia-sip-ua/sresolv/sofia-resolv/sres.h b/libs/sofia-sip/libsofia-sip-ua/sresolv/sofia-resolv/sres.h index 64138dcfb3..b8d33b59bd 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sresolv/sofia-resolv/sres.h +++ b/libs/sofia-sip/libsofia-sip-ua/sresolv/sofia-resolv/sres.h @@ -218,6 +218,16 @@ sres_record_t **sres_cached_answers_sockaddr(sres_resolver_t *res, uint16_t type, struct sockaddr const *addr); +/**Modify the priority of the specified SRV records. */ +SRESPUBFUN +int sres_set_cached_srv_priority(sres_resolver_t *res, + char const *domain, + char const *target, + uint16_t port, + uint32_t newttl, + uint16_t newprio); + + /** Send a query, wait for answer, return results. */ SRESPUBFUN int sres_blocking_query(sres_resolver_t *res, diff --git a/libs/sofia-sip/libsofia-sip-ua/sresolv/sofia-resolv/sres_cache.h b/libs/sofia-sip/libsofia-sip-ua/sresolv/sofia-resolv/sres_cache.h index 7598bc2e65..2639ce0c37 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sresolv/sofia-resolv/sres_cache.h +++ b/libs/sofia-sip/libsofia-sip-ua/sresolv/sofia-resolv/sres_cache.h @@ -102,6 +102,14 @@ SRESPUBFUN void sres_cache_free_record(sres_cache_t *cache, void *rr); /** Store a record to cache */ SRESPUBFUN void sres_cache_store(sres_cache_t *, sres_record_t *, time_t now); +/** Modify the priority in the specified SRV record */ +SRESPUBFUN int sres_cache_set_srv_priority(sres_cache_t *, + char const *domain, + char const *target, + uint16_t port, + uint32_t newttl, + uint16_t newprio); + #ifdef __cplusplus } #endif diff --git a/libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c b/libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c index e7c722cb7b..a5bb9dc1ed 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c +++ b/libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c @@ -1347,6 +1347,47 @@ sres_cached_answers_sockaddr(sres_resolver_t *res, return result; } +/** Set the priority of the matching cached SRV record. + * + * The SRV records with the domain name, target and port are matched and + * their priority value is adjusted. This function is used to implement + * greylisting of SIP servers. + * + * @param res pointer to resolver + * @param domain domain name of the SRV record(s) to modify + * @param target SRV target of the SRV record(s) to modify + * @param port port number of SRV record(s) to modify + * (in host byte order) + * @param ttl new ttl for SRV records of the domain + * @param priority new priority value (0=highest, 65535=lowest) + * + * @sa sres_cache_set_srv_priority() + * + * @NEW_1_12_8 + */ +int sres_set_cached_srv_priority(sres_resolver_t *res, + char const *domain, + char const *target, + uint16_t port, + uint32_t ttl, + uint16_t priority) +{ + char rooted_domain[SRES_MAXDNAME]; + + if (res == NULL || res->res_cache == NULL) + return su_seterrno(EFAULT); + + domain = sres_toplevel(rooted_domain, sizeof rooted_domain, domain); + + if (!domain) + return -1; + + return sres_cache_set_srv_priority(res->res_cache, + domain, target, port, + ttl, priority); +} + + /** Sort answers. */ int sres_sort_answers(sres_resolver_t *res, sres_record_t **answers) diff --git a/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_cache.c b/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_cache.c index 8896b35250..5c2d6d2602 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_cache.c +++ b/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_cache.c @@ -471,6 +471,75 @@ void sres_cache_clean(sres_cache_t *cache, time_t now) } } +/** Set the priority of the matching cached SRV record. + * + * The SRV records with the domain name, target and port are matched and + * their priority value is adjusted. This function is used to implement + * greylisting of SIP servers. + * + * @param cache pointer to DNS cache object + * @param domain domain name of the SRV record(s) to modify + * (including final dot) + * @param target SRV target of the SRV record(s) to modify + * @param port port number of SRV record(s) to modify + * (in host byte order) + * @param ttl new ttl + * @param priority new priority value (0=highest, 65535=lowest) + * + * @sa sres_set_cached_srv_priority() + * + * @NEW_1_12_8 + */ +int sres_cache_set_srv_priority(sres_cache_t *cache, + char const *domain, + char const *target, + uint16_t port, + uint32_t ttl, + uint16_t priority) +{ + int ret = 0; + unsigned hash; + sres_rr_hash_entry_t **iter; + time_t expires; + + if (cache == NULL || domain == NULL || target == NULL) + return -1; + + hash = sres_hash_key(domain); + + if (!LOCK(cache)) + return -1; + + time(&expires); + expires += ttl; + + for (iter = sres_htable_hash(cache->cache_hash, hash); + iter && *iter; + iter = sres_htable_next(cache->cache_hash, iter)) { + sres_record_t *rr = (*iter)->rr; + + if (rr && rr->sr_name && + sres_type_srv == rr->sr_type && + strcasecmp(rr->sr_name, domain) == 0) { + + (*iter)->rr_expires = expires; + + if ((port == 0 || rr->sr_srv->srv_port == port) && + rr->sr_srv->srv_target && + strcasecmp(rr->sr_srv->srv_target, target) == 0) { + /* record found --> change priority of server */ + rr->sr_srv->srv_priority = priority; + ret++; + } + } + } + + UNLOCK(cache); + + /** @return number of modified entries or -1 upon an error. */ + return ret; +} + HTABLE_BODIES_WITH(sres_htable, ht, sres_rr_hash_entry_t, SRES_HENTRY_HASH, unsigned, size_t); diff --git a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/htable2.h b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/htable2.h index 98cee22aef..b063fdfe46 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/htable2.h +++ b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/htable2.h @@ -34,30 +34,29 @@ * always storing a pointer to element). It can be used without * . * - * This file contain a hash table template for C. The hash tables are - * resizeable, and they usually contain pointers to entries. The - * declaration for template datatypes is instantiated with macro - * HTABLE2_DECLARE(). The prototypes for hashing functions are instantiated - * with macro HTABLE2_PROTOS(). The implementation is instantiated with - * macro HTABLE2_BODIES(). + * This file contain a hash table template for C. The hash tables are + * resizeable, and they usually contain pointers to entries. The declaration + * for template datatypes is instantiated with macro HTABLE2_DECLARE2(). The + * prototypes for hashing functions are instantiated with macro + * HTABLE2_PROTOS2(). The implementation is instantiated with macro + * HTABLE2_BODIES2(). * * The hash table template is most efficient when the hash value is * precalculated and stored in each entry. The hash "function" given to the - * HTABLE2_BODIES() would then be something like macro + * HTABLE2_BODIES2() would then be something like macro * @code - * #define HTABLE2_ENTRY_HASH(e) ((e).e_hash_value) + * #define ENTRY_HASH(e) ((e).e_hash_value) * @endcode * - * When a entry with new identical hash key is added to the table, it can be - * either @e inserted (before any other entry with same key value) or - * @e appended. + * When a entry with new identical key is added to the table, it can be + * either @e inserted (before any other entry with same key value) or @e + * appended. * * Example code can be found from . * * @author Pekka Pessi . * * @date Created: Tue Sep 25 17:42:40 2001 ppessi - * */ typedef unsigned long hash_value_t; @@ -75,6 +74,8 @@ typedef unsigned long hash_value_t; * @param sname name of struct * @param pr hash table field prefix * @param entrytype entry type + * + * @NEW_1_12_8 */ #define HTABLE2_DECLARE2(type, sname, pr, entrytype, size_t) \ typedef struct sname { \ @@ -101,6 +102,8 @@ typedef struct sname { \ * @param prefix function prefix * @param pr hash table field prefix (not used) * @param entrytype entry type + * + * @NEW_1_12_8 */ #define HTABLE2_PROTOS2(type, prefix, pr, entrytype, size_t) \ HTABLE2_SCOPE int prefix##_resize(void *a, type *, size_t); \ @@ -131,6 +134,8 @@ HTABLE2_SCOPE int prefix##_remove(type *, entrytype const) * @param reclaim function or macro zeroing entry * @param is_equal equality test * @param halloc function allocating or freeing memory + * + * @NEW_1_12_8 */ #define HTABLE2_BODIES2(type, prefix, pr, entrytype, size_t, \ hfun, is_used, reclaim, is_equal, halloc) \ diff --git a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_addrinfo.h b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_addrinfo.h index 45368a98ff..b13a695247 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_addrinfo.h +++ b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_addrinfo.h @@ -126,7 +126,7 @@ struct addrinfo { #define AI_MASK (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST) #endif -/** RFC 1576 address info structure. */ +/** @RFC1576 address info structure. */ typedef struct addrinfo su_addrinfo_t; /** Translate address and service. */ diff --git a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_wait.h b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_wait.h index e35a92a57d..f8453baf8d 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_wait.h +++ b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_wait.h @@ -398,8 +398,8 @@ typedef void su_msg_function(su_root_magic_t *magic, su_msg_r msg, su_msg_arg_t *arg); -/** Message deinitialize. */ -typedef void su_msg_deinit_func(su_msg_arg_t *arg); +/** Message deinitializer function type. @NEW_1_12_8 */ +typedef void su_msg_deinit_function(su_msg_arg_t *arg); /** Message delivery function pointer type. */ typedef su_msg_function *su_msg_f; @@ -529,7 +529,7 @@ SOFIAPUBFUN int su_msg_create(su_msg_r msg, su_task_r const to, su_task_r const from, su_msg_f wakeup, isize_t size); SOFIAPUBFUN int su_msg_report(su_msg_r msg, su_msg_f report); -SOFIAPUBFUN int su_msg_deinitializer(su_msg_r msg, su_msg_deinit_func *); +SOFIAPUBFUN int su_msg_deinitializer(su_msg_r msg, su_msg_deinit_function *); SOFIAPUBFUN int su_msg_reply(su_msg_r reply, su_msg_cr msg, su_msg_f wakeup, isize_t size); SOFIAPUBFUN void su_msg_destroy(su_msg_r msg); diff --git a/libs/sofia-sip/libsofia-sip-ua/su/su_addrinfo.c b/libs/sofia-sip/libsofia-sip-ua/su/su_addrinfo.c index 5ce50e0d96..f24e4f7dbb 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/su_addrinfo.c +++ b/libs/sofia-sip/libsofia-sip-ua/su/su_addrinfo.c @@ -893,6 +893,7 @@ int su_getaddrinfo(char const *node, char const *service, { int retval; su_addrinfo_t *ai; + char const *realservice = service; if (!service || service[0] == '\0') service = "0"; @@ -932,6 +933,9 @@ int su_getaddrinfo(char const *node, char const *service, retval = getaddrinfo(node, service, hints, res); + if (service != realservice && retval == EAI_SERVICE) + retval = getaddrinfo(node, realservice, hints, res); + if (retval == 0) { for (ai = *res; ai; ai = ai->ai_next) { if (ai->ai_protocol) diff --git a/libs/sofia-sip/libsofia-sip-ua/su/su_alloc.c b/libs/sofia-sip/libsofia-sip-ua/su/su_alloc.c index b54b71a7ab..ad735077fd 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/su_alloc.c +++ b/libs/sofia-sip/libsofia-sip-ua/su/su_alloc.c @@ -1494,7 +1494,10 @@ int su_home_mutex_lock(su_home_t *home) return 0; } -/** Release exclusive lock on home and decrease refcount (if home is threadsafe) */ +/** Release exclusive lock on home and decrease refcount (if home is threadsafe). + * + * @sa su_home_unlock(). + */ int su_home_mutex_unlock(su_home_t *home) { if (home == NULL) @@ -1516,9 +1519,15 @@ int su_home_mutex_unlock(su_home_t *home) /** Obtain exclusive lock on home without increasing refcount. + * + * Unless su_home_threadsafe() has been used to intialize locking on home + * object the function just returns -1. * * @return 0 if successful, -1 if not threadsafe, error code otherwise. * + * @sa su_home_mutex_lock(), su_home_unlock(), su_home_trylock(). + * + * @NEW_1_12_8 */ int su_home_lock(su_home_t *home) { @@ -1537,6 +1546,9 @@ int su_home_lock(su_home_t *home) * @return 0 if successful, -1 if not threadsafe, * EBUSY if already locked, error code otherwise. * + * @sa su_home_lock(), su_home_unlock(). + * + * @NEW_1_12_8 */ int su_home_trylock(su_home_t *home) { @@ -1550,9 +1562,15 @@ int su_home_trylock(su_home_t *home) } -/** Release exclusive lock on home. +/** Release exclusive lock on home. + * + * Release lock without decreasing refcount. * * @return 0 if successful, -1 if not threadsafe, error code otherwise. + * + * @sa su_home_lock(), su_home_trylock(), su_home_mutex_unlock(). + * + * @NEW_1_12_8 */ int su_home_unlock(su_home_t *home) { diff --git a/libs/sofia-sip/libsofia-sip-ua/su/su_global_log.c b/libs/sofia-sip/libsofia-sip-ua/su/su_global_log.c index 70aa470c6a..144c7f48ff 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/su_global_log.c +++ b/libs/sofia-sip/libsofia-sip-ua/su/su_global_log.c @@ -38,6 +38,7 @@ #include #include +#ifdef DOXYGEN_ONLY /**@var SU_DEBUG * * Environment variable determining the debug log level for @b su module. diff --git a/libs/sofia-sip/libsofia-sip-ua/su/su_port.h b/libs/sofia-sip/libsofia-sip-ua/su/su_port.h index adb7716031..00e491bc82 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/su_port.h +++ b/libs/sofia-sip/libsofia-sip-ua/su/su_port.h @@ -67,7 +67,7 @@ struct su_msg_s { su_task_r sum_from; su_msg_f sum_func; su_msg_f sum_report; - su_msg_deinit_func *sum_deinit; + su_msg_deinit_function *sum_deinit; su_msg_arg_t sum_data[1]; /* minimum size, may be extended */ }; diff --git a/libs/sofia-sip/libsofia-sip-ua/su/su_root.c b/libs/sofia-sip/libsofia-sip-ua/su/su_root.c index daf176cefb..9b0db85420 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/su_root.c +++ b/libs/sofia-sip/libsofia-sip-ua/su/su_root.c @@ -885,6 +885,8 @@ int su_root_has_thread(su_root_t *root) * * @retval 0 if successful, * @retval -1 if message allocation fails. + * + * @NEW_1_12_8 */ int su_msg_new(su_msg_r rmsg, size_t size) { @@ -957,9 +959,11 @@ int su_msg_report(su_msg_r msg, * * @param rmsg message reference * @param deinit pointer to deinitializer function + * + * @NEW_1_12_8 */ int su_msg_deinitializer(su_msg_r rmsg, - su_msg_deinit_func *deinit) + su_msg_deinit_function *deinit) { if (rmsg && rmsg[0]) { rmsg[0]->sum_deinit = deinit; @@ -1156,7 +1160,10 @@ int su_msg_send(su_msg_r rmsg) return 0; } -/** Send message to the @a to_task and mark @a from_task as sender */ +/** Send message to the @a to_task and mark @a from_task as sender. + * + * @NEW_1_12_8 + */ SOFIAPUBFUN int su_msg_send_to(su_msg_r rmsg, su_task_r const to_task, su_msg_f wakeup) diff --git a/libs/sofia-sip/libsofia-sip-ua/su/test_htable2.c b/libs/sofia-sip/libsofia-sip-ua/su/test_htable2.c index a11b2fd396..2d6fdd3057 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/test_htable2.c +++ b/libs/sofia-sip/libsofia-sip-ua/su/test_htable2.c @@ -83,15 +83,15 @@ int main(int argc, char *argv[]) typedef struct hentry_s entry_t; -HTABLE2_DECLARE2(htable2_t, htable2_s, ht2_, entry_t, size_t); -HTABLE2_PROTOS2(htable2_t, htable2, ht2_, entry_t, size_t); - struct hentry_s { - unsigned long e_hash; + hash_value_t e_hash; unsigned long e_n; }; +HTABLE2_DECLARE2(htable2_t, htable2_s, ht2_, entry_t, size_t); +HTABLE2_PROTOS2(htable2_t, htable2, ht2_, entry_t, size_t); + #define HENTRY_HASH(e) ((e).e_hash) #define HENTRY_IS_USED(e) ((e).e_n != 0) #define HENTRY_REMOVE(e) ((e)->e_n = 0, (e)->e_hash = 0) diff --git a/libs/sofia-sip/libsofia-sip-ua/su/torture_su.c b/libs/sofia-sip/libsofia-sip-ua/su/torture_su.c index 68d297aa5c..f7d5318d24 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/torture_su.c +++ b/libs/sofia-sip/libsofia-sip-ua/su/torture_su.c @@ -155,7 +155,7 @@ static int test_sockaddr(void) int test_sendrecv(void) { su_socket_t s, l, a; - int n; + ssize_t n; su_sockaddr_t su, csu; socklen_t sulen = sizeof su.su_sin, csulen = sizeof csu.su_sin; char b1[8], b2[8], b3[8]; @@ -172,6 +172,7 @@ int test_sendrecv(void) BEGIN(); s = su_socket(AF_INET, SOCK_DGRAM, 0); TEST_1(s != -1); + su_setblocking(s, 1); memset(&su, 0, sulen); su.su_len = sulen; @@ -209,6 +210,8 @@ int test_sendrecv(void) TEST(connect(s, &su.su_sa, sulen), 0); a = accept(l, &csu.su_sa, &csulen); TEST_1(a != -1); + TEST(su_setblocking(a, 1), 0); + n = su_vsend(s, sv, 3, 0, NULL, 0); TEST(n, 8 + 8 + 6); n = su_vrecv(a, rv, 3, 0, NULL, NULL); TEST(n, 8 + 8 + 6); diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/test_tport.c b/libs/sofia-sip/libsofia-sip-ua/tport/test_tport.c index d8bed43f99..8b6c2bf04c 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/test_tport.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/test_tport.c @@ -1009,18 +1009,19 @@ static int test_incomplete(tp_test_t *tt) s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); TEST_1(s != SOCKET_ERROR); - + + su_setblocking(s, 1); connected = connect(s, ai->ai_addr, (socklen_t)ai->ai_addrlen); su_root_step(tt->tt_root, 50); - TEST(send(s, "F", 1, 0), 1); + TEST(su_send(s, "F", 1, 0), 1); su_root_step(tt->tt_root, 50); - TEST(send(s, "O", 1, 0), 1); + TEST(su_send(s, "O", 1, 0), 1); su_root_step(tt->tt_root, 50); - TEST(send(s, "O", 1, 0), 1); + TEST(su_send(s, "O", 1, 0), 1); su_root_step(tt->tt_root, 50); - TEST(send(s, " ", 1, 0), 1); + TEST(su_send(s, " ", 1, 0), 1); su_root_step(tt->tt_root, 50); tt->tt_received = 0; diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h b/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h index 2517fa9d5f..7bf5ab0509 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h @@ -371,7 +371,7 @@ struct tport_vtable int tport_register_type(tport_vtable_t const *vtp); -/** @internal Test if transport is needs connect() before sending. */ +/** @internal Test if transport needs connect() before sending. */ su_inline int tport_is_connection_oriented(tport_t const *self) { return self->tp_conn_orient; diff --git a/libs/sofia-sip/tests/Makefile.am b/libs/sofia-sip/tests/Makefile.am index fedc6333d5..ac1cf3adfb 100644 --- a/libs/sofia-sip/tests/Makefile.am +++ b/libs/sofia-sip/tests/Makefile.am @@ -2,7 +2,7 @@ # Tests using check # -EXTRA_DIST = check_nua.c +EXTRA_DIST = check_sofia.h check_sofia.c suite_for_nua.c TESTS = check_PROGRAMS = diff --git a/libs/sofia-sip/win32/libsofia-sip-ua-static/libsofia_sip_ua_static.vcproj b/libs/sofia-sip/win32/libsofia-sip-ua-static/libsofia_sip_ua_static.vcproj index 275fb8fd09..a6d648090e 100644 --- a/libs/sofia-sip/win32/libsofia-sip-ua-static/libsofia_sip_ua_static.vcproj +++ b/libs/sofia-sip/win32/libsofia-sip-ua-static/libsofia_sip_ua_static.vcproj @@ -47,10 +47,10 @@ MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" - PrecompiledHeaderFile=".\Debug/libsofia_sip_ua_static.pch" - AssemblerListingLocation=".\Debug/" - ObjectFile=".\Debug/" - ProgramDataBaseFileName=".\Debug/" + PrecompiledHeaderFile=".\Debug\libsofia_sip_ua_static.pch" + AssemblerListingLocation=".\Debug\" + ObjectFile=".\Debug\" + ProgramDataBaseFileName=".\Debug\" BrowseInformation="1" WarningLevel="4" WarnAsError="true" @@ -84,7 +84,7 @@ @@ -94,7 +94,7 @@ @@ -188,7 +188,7 @@ @@ -93,7 +93,7 @@ @@ -186,7 +186,7 @@ @@ -93,7 +93,7 @@ @@ -188,7 +188,7 @@ @@ -94,7 +94,7 @@ @@ -188,7 +188,7 @@ @@ -94,7 +94,7 @@ @@ -188,7 +188,7 @@ @@ -93,7 +93,7 @@ @@ -186,7 +186,7 @@ @@ -93,7 +93,7 @@ @@ -188,7 +188,7 @@ @@ -93,7 +93,7 @@ @@ -188,7 +188,7 @@ @@ -93,7 +93,7 @@ @@ -188,7 +188,7 @@ @@ -94,7 +94,7 @@ @@ -188,7 +188,7 @@ @@ -91,7 +91,7 @@ @@ -185,7 +185,7 @@ @@ -94,7 +94,7 @@ @@ -188,7 +188,7 @@ @@ -94,7 +94,7 @@ @@ -188,7 +188,7 @@ @@ -93,7 +93,7 @@ @@ -188,7 +188,7 @@ @@ -94,7 +94,7 @@ @@ -188,7 +188,7 @@ @@ -98,7 +98,7 @@ @@ -196,7 +196,7 @@ @@ -96,7 +96,7 @@ @@ -194,7 +194,7 @@ @@ -96,7 +96,7 @@ @@ -194,7 +194,7 @@ @@ -96,7 +96,7 @@ @@ -194,7 +194,7 @@ @@ -97,7 +97,7 @@ @@ -194,7 +194,7 @@