From 8ff384d36f48022a4717ca214e9c03e6efc85276 Mon Sep 17 00:00:00 2001 From: Michael Jerris Date: Wed, 20 Jun 2007 10:41:15 +0000 Subject: [PATCH] Update sofia-sip from darcs: Mon May 14 12:43:07 EDT 2007 martti.mela@nokia.com * su_base_port.c: fixed a double free in su_base_port_start_shared(). Fri May 25 13:56:23 EDT 2007 Pekka.Pessi@nokia.com * soa: added SOATAG_ORDERED_USER(), SOATAG_REUSE_REJECTED(). Allow replacing existing m=lines. Sun May 27 14:52:13 EDT 2007 Pekka.Pessi@nokia.com * msg_parser.c: fixed bug #1726034 Mon May 28 04:57:08 EDT 2007 Pekka.Pessi@nokia.com * test_nth.c: using non-blocking connect in test program, too. Mon May 28 04:58:05 EDT 2007 Pekka.Pessi@nokia.com * su.c: making all sockets non-blocking by default. Mon May 28 04:59:28 EDT 2007 Pekka.Pessi@nokia.com * m4/sac-su.m4: moved contents into sac-s2.m4 Mon May 28 05:32:26 EDT 2007 Pekka.Pessi@nokia.com * RELEASE: updated. Wed May 30 10:37:53 EDT 2007 Pekka.Pessi@nokia.com * m4/sac-su2.m4: added configure option --disable-tag-cast. Added SU_INLINE_TAG_CAST into sofia-sip/su_configure.h{,.in}. Using SU_INLINE_TAG_CAST in sofia-sip/sip_tag.h{,.in} sofia-sip/http_tag.h{,.in} sofia-sip/su_tag.h sofia-sip/su_tag_io.h sofia-sip/auth_module.h sofia-sip/nth_tag.h sofia-sip/nua_tag.h Fri Jun 1 15:11:52 EDT 2007 Pekka.Pessi@nokia.com * tport.c: fixed tport_set_params() with secondary transports Fri Jun 1 15:13:23 EDT 2007 Pekka.Pessi@nokia.com * tport_type_tcp.c: checking for end-of-stream even if su_getmsgsize() promised more data Fri Jun 1 15:15:34 EDT 2007 Pekka.Pessi@nokia.com * tport: added tport_is_clear_to_send(), allow use of tport_pending() without msg The error callback from tport can now be registered even if there is no request pending on transport (e.g., when keeping a transport connection open for inbound messages). Fri Jun 1 15:16:43 EDT 2007 Pekka.Pessi@nokia.com * nta: not retrying after an transport error if application provided the transport Fri Jun 1 15:17:23 EDT 2007 Pekka.Pessi@nokia.com * sip: do not accept empty URIs (<>) in From, To, and other headers expecting name-addr format Fri Jun 1 15:17:43 EDT 2007 Pekka.Pessi@nokia.com * torture_url.c: added test for parsing empty URLs. Fri Jun 1 15:19:27 EDT 2007 Pekka.Pessi@nokia.com * nua/test_proxy.[hc]: use registered connections for outbound with TCP. Added test_proxy_close_tports() used testing recovering from TCP failures. Fri Jun 1 15:20:33 EDT 2007 Pekka.Pessi@nokia.com * test_nua.c: added --print-tags and --tags-a, --tags=b and --tags=c options Added more functions for handling events Fri Jun 1 15:22:08 EDT 2007 Pekka.Pessi@nokia.com * test_nua: fixed some tests depending on delivery of responses in correct order Reordering might happen if some messages are sent over TCP, other over UDP. Fri Jun 1 15:27:55 EDT 2007 Pekka.Pessi@nokia.com * nua_register.c: re-registering in case the TCP connection towards proxy is closed In test_nua, Mr. B is now using TCP with the test proxy. Fri Jun 1 15:35:39 EDT 2007 Pekka.Pessi@nokia.com * nua/test_refer.c: fixed SIP payload checks Fri Jun 1 15:36:08 EDT 2007 Pekka.Pessi@nokia.com * nta_internal.h: added orq_user_tport field Tue Jun 5 06:16:43 EDT 2007 Pekka.Pessi@nokia.com * hide_email.sh: now fixing links, too. Fri Jun 15 05:34:29 EDT 2007 Pekka.Pessi@nokia.com * nua: fixed documentation entries for API functions left out from doxygen Thanks for Jerry Ricahrds for pointing this out. Wed May 23 10:26:26 EDT 2007 Mikhail Zabaluev * Correct documentation for parameter type of NUTAG_WITH_SAVED Mon Jun 18 12:34:58 EDT 2007 Mikhail Zabaluev * Make nua_saved_event_request() and hence NUTAG_WITH_SAVED resilient to a NULL event content git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@5413 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- libs/sofia-sip/Makefile.am | 2 +- libs/sofia-sip/RELEASE | 3 + libs/sofia-sip/configure.ac | 1 - .../libsofia-sip-ua/docs/hide_emails.sh | 10 +- .../http/sofia-sip/http_tag.h.in | 6 +- .../iptsec/sofia-sip/auth_module.h | 2 +- .../libsofia-sip-ua/msg/msg_parser.c | 2 + libs/sofia-sip/libsofia-sip-ua/msg/test_msg.c | 34 +- libs/sofia-sip/libsofia-sip-ua/nta/nta.c | 24 +- .../libsofia-sip-ua/nta/nta_internal.h | 1 + .../libsofia-sip-ua/nth/sofia-sip/nth_tag.h | 4 +- libs/sofia-sip/libsofia-sip-ua/nth/test_nth.c | 1 + libs/sofia-sip/libsofia-sip-ua/nua/nua.c | 2 +- .../libsofia-sip-ua/nua/nua_message.c | 5 +- .../libsofia-sip-ua/nua/nua_register.c | 82 +++- .../libsofia-sip-ua/nua/nua_session.c | 4 +- .../libsofia-sip-ua/nua/nua_subnotref.c | 12 +- .../libsofia-sip-ua/nua/sofia-sip/nua_tag.h | 4 +- .../libsofia-sip-ua/nua/test_100rel.c | 156 +++++-- .../libsofia-sip-ua/nua/test_basic_call.c | 2 +- .../libsofia-sip-ua/nua/test_call_reject.c | 2 +- .../libsofia-sip-ua/nua/test_cancel_bye.c | 50 +- .../sofia-sip/libsofia-sip-ua/nua/test_init.c | 14 +- libs/sofia-sip/libsofia-sip-ua/nua/test_nua.c | 14 +- libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h | 5 + libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c | 24 +- .../libsofia-sip-ua/nua/test_proxy.c | 114 ++++- .../libsofia-sip-ua/nua/test_proxy.h | 2 + .../libsofia-sip-ua/nua/test_refer.c | 40 +- .../libsofia-sip-ua/nua/test_register.c | 9 +- .../libsofia-sip-ua/nua/test_simple.c | 35 +- .../libsofia-sip-ua/nua/test_sip_events.c | 108 ++--- .../sofia-sip/libsofia-sip-ua/sip/sip_basic.c | 3 + .../sip/sofia-sip/sip_tag.h.in | 6 +- .../libsofia-sip-ua/sip/torture_sip.c | 2 + .../libsofia-sip-ua/soa/soa_static.c | 431 +++++++++++++----- libs/sofia-sip/libsofia-sip-ua/soa/soa_tag.c | 29 ++ .../libsofia-sip-ua/soa/sofia-sip/soa_tag.h | 14 + libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c | 127 +++++- .../su/sofia-sip/su_configure.h.in | 3 + .../libsofia-sip-ua/su/sofia-sip/su_tag.h | 2 +- .../libsofia-sip-ua/su/sofia-sip/su_tag_io.h | 2 +- libs/sofia-sip/libsofia-sip-ua/su/su.c | 8 +- .../libsofia-sip-ua/su/su_base_port.c | 1 - .../sofia-sip/libsofia-sip-ua/su/torture_su.c | 2 + .../libsofia-sip-ua/tport/sofia-sip/tport.h | 3 + .../tport/sofia-sip/tport_tag.h | 4 +- libs/sofia-sip/libsofia-sip-ua/tport/tport.c | 89 ++-- .../libsofia-sip-ua/tport/tport_type_tcp.c | 4 +- .../libsofia-sip-ua/url/torture_url.c | 9 + libs/sofia-sip/m4/sac-su2.m4 | 34 +- 51 files changed, 1159 insertions(+), 388 deletions(-) diff --git a/libs/sofia-sip/Makefile.am b/libs/sofia-sip/Makefile.am index 1fdf8606e2..a8a7e9f189 100644 --- a/libs/sofia-sip/Makefile.am +++ b/libs/sofia-sip/Makefile.am @@ -24,7 +24,7 @@ EXTRA_DIST = AUTHORS COPYING COPYRIGHTS ChangeLog.ext-trees \ ACLOCAL_AMFLAGS = -I m4 -EXTRA_DIST += m4/sac-general.m4 m4/sac-su.m4 m4/sac-coverage.m4 \ +EXTRA_DIST += m4/sac-general.m4 m4/sac-coverage.m4 \ m4/sac-su2.m4 m4/sac-tport.m4 m4/sac-openssl.m4 EXTRA_DIST += docs/build_system.txt \ diff --git a/libs/sofia-sip/RELEASE b/libs/sofia-sip/RELEASE index e39470e990..bb7d836cfc 100644 --- a/libs/sofia-sip/RELEASE +++ b/libs/sofia-sip/RELEASE @@ -24,6 +24,9 @@ API/ABI changes and versioning New features in API are marked with Doxytag macro @VERSION_1_12_7. libsofia-sip-ua: +- Removed extra system headers from +- Added global variable su_socket_blocking. If it is set to true, + sockets are created as blocking. - Added accessor function nta_outgoing_branch() - **template**: Added foobar() function (sofia-sip/foobar.h). - This release is ABI/API compatible with applications linked against diff --git a/libs/sofia-sip/configure.ac b/libs/sofia-sip/configure.ac index 7496a43a1c..aa2b9cfba1 100644 --- a/libs/sofia-sip/configure.ac +++ b/libs/sofia-sip/configure.ac @@ -69,7 +69,6 @@ AM_CONDITIONAL([HAVE_DOXYGEN], [test $DOXYGEN != echo]) SAC_SOFIA_SU SAC_OPENSSL SAC_TPORT -SAC_SU ### internal modules ### ---------------- diff --git a/libs/sofia-sip/libsofia-sip-ua/docs/hide_emails.sh b/libs/sofia-sip/libsofia-sip-ua/docs/hide_emails.sh index c82261934c..3060e0c059 100644 --- a/libs/sofia-sip/libsofia-sip-ua/docs/hide_emails.sh +++ b/libs/sofia-sip/libsofia-sip-ua/docs/hide_emails.sh @@ -29,9 +29,13 @@ # # -------------------------------------------------------------------- -echo "Hiding email addresses in ${1:-.}" +echo "Postprocessing HTML in ${1:-.}: hiding email addresses, fixing links" find ${1:-.} -name '*.html' -print0 | xargs -0 \ -sed -r -i 's/([:>;][a-z][-a-z.]*)(@[a-z][a-z]*)\.[a-z][a-z]*(["<\&])/\1\2-email.address.hidden\3/gi' - +sed -r -i ' +# Hide e-mail addresses +s/([:>;][a-z][-a-z.]*)(@[a-z][a-z]*)\.[a-z][a-z]*(["<\&])/\1\2-email.address.hidden\3/gi; +# Fix cross-module links +s!doxygen="([a-z]*).doxytags:../\1/" href="../\1/\1_index.html"!doxygen="\1.doxytags:../\1/" href="../\1/index.html"!g; +' diff --git a/libs/sofia-sip/libsofia-sip-ua/http/sofia-sip/http_tag.h.in b/libs/sofia-sip/libsofia-sip-ua/http/sofia-sip/http_tag.h.in index 82972e0d53..f057ef3d2d 100644 --- a/libs/sofia-sip/libsofia-sip-ua/http/sofia-sip/http_tag.h.in +++ b/libs/sofia-sip/libsofia-sip-ua/http/sofia-sip/http_tag.h.in @@ -102,7 +102,7 @@ SOFIAPUBVAR tag_typedef_t httptag_http; #define HTTPTAG_HTTP_REF(x) httptag_http_ref, httptag_http_vr(&(x)) SOFIAPUBVAR tag_typedef_t httptag_http_ref; -#if SU_HAVE_INLINE +#if SU_INLINE_TAG_CAST su_inline tag_value_t httptag_http_v(http_t const *v) { return (tag_value_t)v; } su_inline @@ -150,7 +150,7 @@ SOFIAPUBVAR tag_typedef_t httptag_header; #define HTTPTAG_HEADER_REF(x) httptag_header_ref, httptag_header_vr(&(x)) SOFIAPUBVAR tag_typedef_t httptag_header_ref; -#if SU_HAVE_INLINE +#if SU_INLINE_TAG_CAST su_inline tag_value_t httptag_header_v(http_header_t const *v) { return (tag_value_t)v; } @@ -220,7 +220,7 @@ SOFIAPUBVAR tag_typedef_t httptag_#xxxxxx#_ref; #define HTTPTAG_#XXXXXX#_STR_REF(x) HTTPTAG_STR_REF(#xxxxxx#, x) SOFIAPUBVAR tag_typedef_t httptag_#xxxxxx#_str_ref; -#if SU_HAVE_INLINE +#if SU_INLINE_TAG_CAST su_inline tag_value_t httptag_#xxxxxx#_v(http_#xxxxxx#_t const *v) { return (tag_value_t)v; } diff --git a/libs/sofia-sip/libsofia-sip-ua/iptsec/sofia-sip/auth_module.h b/libs/sofia-sip/libsofia-sip-ua/iptsec/sofia-sip/auth_module.h index dfb4906143..8a3563934a 100644 --- a/libs/sofia-sip/libsofia-sip-ua/iptsec/sofia-sip/auth_module.h +++ b/libs/sofia-sip/libsofia-sip-ua/iptsec/sofia-sip/auth_module.h @@ -253,7 +253,7 @@ SOFIAPUBVAR tag_typedef_t authtag_module; #define AUTHTAG_MODULE_REF(x) authtag_module_ref, authtag_module_vr((&x)) SOFIAPUBVAR tag_typedef_t authtag_module_ref; -#if SU_HAVE_INLINE +#if SU_INLINE_TAG_CAST su_inline tag_value_t authtag_module_v(auth_mod_t *v) { return (tag_value_t)v; } diff --git a/libs/sofia-sip/libsofia-sip-ua/msg/msg_parser.c b/libs/sofia-sip/libsofia-sip-ua/msg/msg_parser.c index 355f9e4f2c..715434c3ba 100644 --- a/libs/sofia-sip/libsofia-sip-ua/msg/msg_parser.c +++ b/libs/sofia-sip/libsofia-sip-ua/msg/msg_parser.c @@ -1840,6 +1840,8 @@ char *msg_as_string(su_home_t *home, msg_t *msg, msg_pub_t *pub, int flags, return NULL; } + b = b2; + continue; } } diff --git a/libs/sofia-sip/libsofia-sip-ua/msg/test_msg.c b/libs/sofia-sip/libsofia-sip-ua/msg/test_msg.c index 9bbee0e2cd..66f9af8290 100644 --- a/libs/sofia-sip/libsofia-sip-ua/msg/test_msg.c +++ b/libs/sofia-sip/libsofia-sip-ua/msg/test_msg.c @@ -797,7 +797,21 @@ int test_msg_parsing(void) { size_t size = SIZE_MAX; - char *s = msg_as_string(msg_home(msg), msg, NULL, 0, &size); + char *s; + char body[66 * 15 + 1]; + int i; + msg_payload_t *pl; + + /* Bug #1726034 */ + for (i = 0; i < 15; i++) + strcpy(body + i * 66, + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"); + pl = msg_payload_make(msg_home(msg), body); + + TEST(msg_header_insert(msg, (msg_pub_t *)tst, (void *)pl), 0); + + s = msg_as_string(msg_home(msg), msg, NULL, 0, &size); TEST_S(s, "GET a-wife HTTP/1.1" CRLF "Foo: bar" CRLF @@ -806,7 +820,23 @@ int test_msg_parsing(void) "Content-Language: se-FI, fi-FI, sv-FI\r\n" "Accept-Language: se, fi, sv\r\n" CRLF -"test" CRLF); +"test" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF +); } msg_destroy(msg); diff --git a/libs/sofia-sip/libsofia-sip-ua/nta/nta.c b/libs/sofia-sip/libsofia-sip-ua/nta/nta.c index a81d4ed14f..f4efd7a332 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nta/nta.c +++ b/libs/sofia-sip/libsofia-sip-ua/nta/nta.c @@ -2198,7 +2198,8 @@ void agent_recv_request(nta_agent_t *agent, TPTAG_SDWN_AFTER(stream), TAG_END()); } - } else { + } + else { msg_destroy(msg); if (stream) /* Send FIN */ tport_shutdown(tport, 1); @@ -6172,13 +6173,6 @@ size_t incoming_mass_destroy(nta_agent_t *sa, incoming_queue_t *q) HTABLE_BODIES_WITH(outgoing_htable, oht, nta_outgoing_t, HTABLE_HASH_ORQ, size_t, hash_value_t); -static nta_outgoing_t *outgoing_create(nta_agent_t *agent, - nta_response_f *callback, - nta_outgoing_magic_t *magic, - url_string_t const *route_url, - tp_name_t const *tpn, - msg_t *msg, - tag_type_t tag, tag_value_t value, ...); static int outgoing_features(nta_agent_t *agent, nta_outgoing_t *orq, msg_t *msg, sip_t *sip, tagi_t *tags); @@ -6815,7 +6809,11 @@ nta_outgoing_t *outgoing_create(nta_agent_t *agent, /* select the tport to use for the outgoing message */ if (override_tport) { /* note: no ref taken to the tport as its only used once here */ - tpn = tport_name(override_tport); + if (tport_is_secondary(override_tport)) { + tpn = tport_name(override_tport); + orq->orq_user_tport = 1; + } + } if (route_url) { @@ -7095,8 +7093,10 @@ outgoing_send(nta_outgoing_t *orq, int retransmit) if (cc) nta_compartment_decref(&cc); + if (orq->orq_user_tport) + /* No retries */; /* RFC3261, 18.1.1 */ - if (err == EMSGSIZE && !orq->orq_try_tcp_instead) { + else if (err == EMSGSIZE && !orq->orq_try_tcp_instead) { if (strcasecmp(tpn->tpn_proto, "udp") == 0 || strcasecmp(tpn->tpn_proto, "*") == 0) { outgoing_try_tcp_instead(orq); @@ -10217,6 +10217,10 @@ nta_transport_(nta_agent_t *agent, } +/** Return a new reference to the transaction transport. + * + * @note The referenced transport must be unreferenced with tport_unref() + */ tport_t * nta_incoming_transport(nta_agent_t *agent, nta_incoming_t *irq, 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 1a8440b343..478a74b53f 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nta/nta_internal.h +++ b/libs/sofia-sip/libsofia-sip-ua/nta/nta_internal.h @@ -504,6 +504,7 @@ struct nta_outgoing_s unsigned orq_completed : 1; unsigned orq_delayed : 1; unsigned orq_stripped_uri : 1; + unsigned orq_user_tport : 1; /**< Application provided tport - don't retry */ unsigned orq_try_tcp_instead : 1; unsigned orq_try_udp_instead : 1; unsigned orq_reliable : 1; /**< Transport is reliable */ diff --git a/libs/sofia-sip/libsofia-sip-ua/nth/sofia-sip/nth_tag.h b/libs/sofia-sip/libsofia-sip-ua/nth/sofia-sip/nth_tag.h index 47e2613e36..76626b4be5 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nth/sofia-sip/nth_tag.h +++ b/libs/sofia-sip/libsofia-sip-ua/nth/sofia-sip/nth_tag.h @@ -101,7 +101,7 @@ NTH_DLL extern tag_typedef_t nthtag_error_msg; NTH_DLL extern tag_typedef_t nthtag_error_msg_ref; #define NTHTAG_ERROR_MSG_REF(x) nthtag_error_msg_ref, tag_bool_vr(&(x)) -#if SU_HAVE_INLINE +#if SU_INLINE_TAG_CAST struct nth_client_s; su_inline tag_value_t nthtag_template_v(struct nth_client_s const *v) { return (tag_value_t)v; } @@ -119,7 +119,7 @@ NTH_DLL extern tag_typedef_t nthtag_template; NTH_DLL extern tag_typedef_t nthtag_template_ref; #define NTHTAG_TEMPLATE_REF(x) nthtag_template_ref, nthtag_template_vr(&(x)) -#if SU_HAVE_INLINE +#if SU_INLINE_TAG_CAST su_inline tag_value_t nthtag_message_v(struct msg_s *v) { return (tag_value_t)v; } su_inline tag_value_t nthtag_message_vr(struct msg_s **vp) diff --git a/libs/sofia-sip/libsofia-sip-ua/nth/test_nth.c b/libs/sofia-sip/libsofia-sip-ua/nth/test_nth.c index f7f6b63306..b9e1c8b2d8 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nth/test_nth.c +++ b/libs/sofia-sip/libsofia-sip-ua/nth/test_nth.c @@ -570,6 +570,7 @@ static int send_request(tester_t *t, char const *req, size_t reqlen, if (c == INVALID_SOCKET) { c = su_socket(t->t_addr->su_family, SOCK_STREAM, 0); TEST_1(c != SOCK_STREAM); + TEST_1(su_setblocking(c, 1) != -1); TEST_1(connect(c, &t->t_addr->su_sa, t->t_addrlen) != -1); while (su_root_step(t->t_root, 1) == 0); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua.c index 6c44ff920c..b3f304f6b8 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua.c @@ -1049,7 +1049,7 @@ msg_t *nua_current_request(nua_t const *nua) /** Get request message from saved nua event. @NEW_1_12_4. */ msg_t *nua_saved_event_request(nua_saved_event_t const *saved) { - return saved ? su_msg_data(saved)->e_msg : NULL; + return saved && saved[0] ? su_msg_data(saved)->e_msg : NULL; } /** Save nua event and its arguments */ diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_message.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_message.c index b58f60497c..e34ba8ef0f 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_message.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_message.c @@ -48,7 +48,10 @@ /* ======================================================================== */ /* MESSAGE */ -/** Send an instant message. +/**@fn void nua_message( \ + * nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); + * + * Send an instant message. * * Send an instant message using SIP MESSAGE method. * 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 44c32e0050..4bfb7f466d 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c @@ -37,6 +37,8 @@ /** @internal SU network changed detector argument pointer type */ #define SU_NETWORK_CHANGED_MAGIC_T struct nua_s +#define TP_CLIENT_T struct register_usage + #include #include #include @@ -46,7 +48,7 @@ #include #include -#define NTA_UPDATE_MAGIC_T struct nua_s +#define NTA_UPDATE_MAGIC_T struct nua_handle_s #include "nua_stack.h" @@ -126,6 +128,7 @@ struct register_usage { /** Status of registration */ unsigned nr_ready:1; + /** Kind of registration. * * If nr_default is true, this is not a real registration but placeholder @@ -138,7 +141,11 @@ struct register_usage { unsigned nr_default:1, nr_secure:1, nr_public:1, nr_ip4:1, nr_ip6:1; /** Stack-generated contact */ - unsigned nr_by_stack:1, :0; + unsigned nr_by_stack:1; + + unsigned:0; + + int nr_error_report_id; /**< ID used to ask for error reports from tport */ sip_route_t *nr_route; /**< Outgoing Service-Route */ sip_path_t *nr_path; /**< Incoming Path */ @@ -205,6 +212,12 @@ static void nua_register_usage_remove(nua_handle_t *nh, nr->nr_compartment = NULL; #endif + if (nr->nr_error_report_id) + tport_release(nr->nr_tport, nr->nr_error_report_id, NULL, NULL, nr, 0); + + if (nr->nr_tport) + tport_unref(nr->nr_tport), nr->nr_tport = NULL; + ds->ds_has_register = 0; /* There can be only one */ } @@ -222,6 +235,12 @@ static void nua_register_usage_peer_info(nua_dialog_usage_t *du, /* ======================================================================== */ /* REGISTER */ +static void nua_register_connection_closed(tp_stack_t *sip_stack, + nua_registration_t *nr, + tport_t *tport, + msg_t *msg, + int error); + /* Interface towards outbound_t */ sip_contact_t *nua_handle_contact_by_via(nua_handle_t *nh, su_home_t *home, @@ -764,6 +783,7 @@ int nua_register_client_request(nua_client_request_t *cr, TAG_IF(unreg, NTATAG_SIGCOMP_CLOSE(1)), TAG_IF(!unreg, NTATAG_COMP("sigcomp")), #endif + NTATAG_TPORT(nr->nr_tport), TAG_NEXT(tags)); } @@ -827,6 +847,8 @@ static int nua_register_client_response(nua_client_request_t *cr, msg_t *_reqmsg = nta_outgoing_getrequest(cr->cr_orq); sip_t *req = sip_object(_reqmsg); + tport_t *tport; + msg_destroy(_reqmsg); assert(nr); assert(sip); assert(req); @@ -927,10 +949,26 @@ static int nua_register_client_response(nua_client_request_t *cr, outbound_start_keepalive(nr->nr_ob, cr->cr_orq); } - /* persistant connection for registration */ - if (!nr->nr_tport) - /* note: nta_outgoing_transport() takes a ref */ - nr->nr_tport = nta_outgoing_transport (cr->cr_orq); + tport = nta_outgoing_transport (cr->cr_orq); + + /* cache persistant connection for registration */ + if (tport && tport != nr->nr_tport) { + if (nr->nr_error_report_id) { + if (tport_release(nr->nr_tport, nr->nr_error_report_id, NULL, NULL, nr, 0) < 0) + SU_DEBUG_1(("nua_register: tport_release() failed\n")); + nr->nr_error_report_id = 0; + } + tport_unref(nr->nr_tport); + nr->nr_tport = tport; + + if (tport_is_secondary(tport)) { + tport_set_params(tport, TPTAG_SDWN_ERROR(1), TAG_END()); + nr->nr_error_report_id = + tport_pend(tport, NULL, nua_register_connection_closed, nr); + } + } + else + tport_unref(tport); /* note: nta_outgoing_transport() makes a ref */ nua_registration_set_ready(nr, 1); } @@ -943,9 +981,15 @@ static int nua_register_client_response(nua_client_request_t *cr, outbound_stop_keepalive(nr->nr_ob); /* release the persistant transport for registration */ - if (nr->nr_tport) - tport_decref(&nr->nr_tport), nr->nr_tport = NULL; + if (nr->nr_tport) { + if (nr->nr_error_report_id) { + if (tport_release(nr->nr_tport, nr->nr_error_report_id, NULL, NULL, nr, 0) < 0) + SU_DEBUG_1(("nua_register: tport_release() failed\n")); + nr->nr_error_report_id = 0; + } + tport_unref(nr->nr_tport), nr->nr_tport = NULL; + } nua_registration_set_ready(nr, 0); } @@ -953,6 +997,24 @@ static int nua_register_client_response(nua_client_request_t *cr, return nua_base_client_response(cr, status, phrase, sip, NULL); } +static +void nua_register_connection_closed(tp_stack_t *sip_stack, + nua_registration_t *nr, + tport_t *tport, + msg_t *msg, + int error) +{ + if (tport_release(nr->nr_tport, nr->nr_error_report_id, NULL, NULL, nr, 0) < 0) + SU_DEBUG_1(("nua_register: tport_release() failed\n")); + + nr->nr_error_report_id = 0; + tport_unref(nr->nr_tport), nr->nr_tport = NULL; + + /* Schedule re-REGISTER immediately */ + nua_dialog_usage_refresh_at(nua_dialog_usage_public(nr), sip_now()); +} + + static void nua_register_usage_refresh(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du, @@ -978,8 +1040,8 @@ static void nua_register_usage_refresh(nua_handle_t *nh, * @retval <0 try again later */ static int nua_register_usage_shutdown(nua_handle_t *nh, - nua_dialog_state_t *ds, - nua_dialog_usage_t *du) + nua_dialog_state_t *ds, + nua_dialog_usage_t *du) { nua_client_request_t *cr = du->du_cr; nua_registration_t *nr = nua_dialog_usage_private(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 301fd287e3..323254f8ce 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c @@ -1627,7 +1627,8 @@ static int nua_prack_client_report(nua_client_request_t *cr, * (maybe NULL if call handle was created for this call) * @param sip incoming INVITE request * @param tags SOATAG_ACTIVE_AUDIO(), SOATAG_ACTIVE_VIDEO() - * + * + * @par * @par Responding to INVITE with nua_respond() * * If @a status in #nua_i_invite event is below 200, the application should @@ -1700,6 +1701,7 @@ static int nua_prack_client_report(nua_client_request_t *cr, * #nua_i_prack, #nua_i_update, nua_update(), * nua_invite(), #nua_r_invite * + * @par * @par Third Party Call Control * * When so called 2rd party call control is used, the initial @b INVITE may 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 e33da8cc6e..2db1ca8c3a 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c @@ -121,7 +121,9 @@ void nua_subscribe_usage_remove(nua_handle_t *nh, /* ====================================================================== */ /* SUBSCRIBE */ -/** Subscribe to a SIP event. +/**@fn void nua_subscribe(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); + * + * Subscribe to a SIP event. * * Subscribe a SIP event using the SIP SUBSCRIBE request. If the * SUBSCRBE is successful a subscription state is established and @@ -145,7 +147,9 @@ void nua_subscribe_usage_remove(nua_handle_t *nh, * @sa NUTAG_SUBSTATE(), @RFC3265 */ -/** Unsubscribe an event. +/**@fn void nua_unsubscribe(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); + * + * Unsubscribe an event. * * Unsubscribe an active or pending subscription with SUBSCRIBE request * containing Expires: header with value 0. The dialog associated with @@ -689,7 +693,9 @@ int nua_notify_server_report(nua_server_request_t *sr, tagi_t const *tags) /* ======================================================================== */ /* REFER */ -/** Transfer a call. +/**@fn void nua_refer(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); + * + * Transfer a call. * * Send a REFER request asking the recipient to transfer the call. * diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h b/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h index 34fab1a8d1..7ef53d290e 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h +++ b/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h @@ -166,7 +166,7 @@ SOFIAPUBVAR tag_typedef_t nutag_with; * nua_respond() * * @par Parameter type - * msg_t * + * nua_saved_event_t * * * @par Values * Pointer to a saved event. @@ -2270,7 +2270,7 @@ SOFIAPUBVAR tag_typedef_t nutag_detect_network_updates; SOFIAPUBVAR tag_typedef_t nutag_detect_network_updates_ref; /* Pass nua handle as tagged argument */ -#if SU_HAVE_INLINE +#if SU_INLINE_TAG_CAST su_inline tag_value_t nutag_handle_v(nua_handle_t *v) { return (tag_value_t)v; } su_inline tag_value_t nutag_handle_vr(nua_handle_t **vp) {return(tag_value_t)vp;} #else 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 37840dd762..592124b09b 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c @@ -128,7 +128,7 @@ int test_180rel(struct context *ctx) struct endpoint *a = &ctx->a, *b = &ctx->b; struct call *a_call = a->call, *b_call = b->call; - struct event *e; + struct event *e, *ep, *ei; sip_t *sip; if (print_headings) @@ -193,10 +193,17 @@ int test_180rel(struct context *ctx) TEST_1(is_answer_recv(e->data->e_tags)); TEST_1(!is_offer_sent(e->data->e_tags)); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack); + 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, 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 = e->next); TEST_E(e->data->e_event, nua_r_invite); + TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite); TEST(e->data->e_status, 200); TEST_1(sip = sip_object(e->data->e_msg)); TEST_1(sip->sip_content_type); @@ -206,7 +213,8 @@ int test_180rel(struct context *ctx) 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); + + TEST_1(!e->next || !ep->next); free_events_in_list(ctx, a->events); /* @@ -321,13 +329,13 @@ int test_prack_auth(struct context *ctx) struct endpoint *c = &ctx->c, *b = &ctx->b; struct call *c_call = c->call, *b_call = b->call; - struct event *e; + struct event *e, *ep, *ei; sip_t *sip; sip_proxy_authenticate_t *au; char const *md5 = NULL, *md5sess = NULL; if (print_headings) - printf("TEST NUA-10.1.1: Call with 100rel and 180\n"); + printf("TEST NUA-10.1.3: Call with 100rel and 180\n"); /* Test for authentication during 100rel @@ -405,8 +413,14 @@ int test_prack_auth(struct context *ctx) TEST_1(is_answer_recv(e->data->e_tags)); TEST_1(!is_offer_sent(e->data->e_tags)); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack); + 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, 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); if (e->data->e_status != 200 && md5 && !md5sess) { if (e->data->e_status != 100) { TEST(e->data->e_status, 407); @@ -419,16 +433,15 @@ int test_prack_auth(struct context *ctx) 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_r_invite); + 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); + TEST_1(!e->next || !ep->next || (ep->data->e_status != 200 && !e->next->next->next)); free_events_in_list(ctx, c->events); /* @@ -466,10 +479,10 @@ int test_prack_auth(struct context *ctx) free_events_in_list(ctx, b->events); if (print_headings) - printf("TEST NUA-10.1.1: PASSED\n"); + printf("TEST NUA-10.1.3: PASSED\n"); if (print_headings) - printf("TEST NUA-10.1.2: terminate call\n"); + printf("TEST NUA-10.1.4: terminate call\n"); BYE(b, b_call, b_call->nh, TAG_END()); run_bc_until(ctx, -1, until_terminated, -1, until_terminated); @@ -496,7 +509,7 @@ int test_prack_auth(struct context *ctx) nua_handle_destroy(b_call->nh), b_call->nh = NULL; if (print_headings) - printf("TEST NUA-10.1.2: PASSED\n"); + printf("TEST NUA-10.1.4: PASSED\n"); END(); } @@ -558,7 +571,7 @@ int test_183rel(struct context *ctx) struct endpoint *a = &ctx->a, *b = &ctx->b; struct call *a_call = a->call, *b_call = b->call; - struct event *e; + struct event *e, *ei, *ep; if (print_headings) printf("TEST NUA-10.2.1: Call with 100rel, 183 and 180\n"); @@ -590,26 +603,43 @@ int test_183rel(struct context *ctx) TEST_1(is_answer_recv(e->data->e_tags)); 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_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite); + ep = e->data->e_event == nua_r_prack ? e : NULL; + + if (ep) { + TEST(ep->data->e_status, 200); 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_offer_answer_done(e->data->e_tags)); + + if (!ep) { + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack); + TEST(e->data->e_status, 200); + } + + 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, until_final_response); + ep = event_by_type(e->next, nua_r_prack); + } - TEST_1(e = e->next); TEST_E(e->data->e_event, 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 = e->next); TEST_E(e->data->e_event, nua_r_invite); + 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); + TEST_1(!e->next || !ep->next); free_events_in_list(ctx, a->events); /* @@ -879,7 +909,7 @@ int test_preconditions(struct context *ctx) struct endpoint *a = &ctx->a, *b = &ctx->b; struct call *a_call = a->call, *b_call = b->call; - struct event *e; + struct event *e, *ep, *ei; sip_t *sip; if (print_headings) @@ -969,7 +999,10 @@ int test_preconditions(struct context *ctx) TEST_1(!is_answer_recv(e->data->e_tags)); TEST_1(is_offer_sent(e->data->e_tags)); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_update); + ei = event_by_type(e->next, nua_r_invite); + ep = event_by_type(e->next, nua_r_update); + + TEST_1(e = ep); TEST_E(e->data->e_event, nua_r_update); TEST(e->data->e_status, 200); TEST_1(sip = sip_object(e->data->e_msg)); TEST_1(sip->sip_session_expires); @@ -979,17 +1012,27 @@ int test_preconditions(struct context *ctx) TEST_1(is_answer_recv(e->data->e_tags)); TEST_1(!is_offer_sent(e->data->e_tags)); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite); + 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)); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack); + if (e == ep) /* invite was responded before update */ + e = ep->next->next; + + 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, 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); /* Does not have effect on call state */ - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite); + TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite); TEST(e->data->e_status, 200); TEST_1(sip = sip_object(e->data->e_msg)); if (ctx->proxy_tests) { @@ -1001,7 +1044,7 @@ int test_preconditions(struct context *ctx) 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); + TEST_1(!e->next || !ep->next); free_events_in_list(ctx, a->events); /* @@ -1169,7 +1212,7 @@ int test_preconditions2(struct context *ctx) struct endpoint *a = &ctx->a, *b = &ctx->b; struct call *a_call = a->call, *b_call = b->call; - struct event *e; + struct event *e, *eu, *ei; if (print_headings) printf("TEST NUA-10.4.1: Call with preconditions and non-100rel 180\n"); @@ -1253,22 +1296,25 @@ int test_preconditions2(struct context *ctx) TEST_1(!is_answer_recv(e->data->e_tags)); TEST_1(is_offer_sent(e->data->e_tags)); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_update); - TEST(e->data->e_status, 200); + 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); 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)); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite); + 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)); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite); - TEST(e->data->e_status, 200); + TEST_1(e = eu->next->next == ei ? ei->next->next : eu->next->next); + + 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 */ @@ -1445,7 +1491,7 @@ int test_update_by_uas(struct context *ctx) struct endpoint *a = &ctx->a, *b = &ctx->b; struct call *a_call = a->call, *b_call = b->call; - struct event *e; + struct event *e, *ep, *ei; sip_t *sip; /* -------------------------------------------------------------------- */ @@ -1563,11 +1609,18 @@ int test_update_by_uas(struct context *ctx) TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */ TEST_1(!is_offer_answer_done(e->data->e_tags)); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack); + 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, 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); /* Does not have effect on call state */ - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite); + TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite); TEST(e->data->e_status, 200); if (ctx->proxy_tests) { TEST_1(sip = sip_object(e->data->e_msg)); @@ -1579,7 +1632,7 @@ int test_update_by_uas(struct context *ctx) 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); + TEST_1(!e->next || !ep->next); free_events_in_list(ctx, a->events); /* @@ -1846,7 +1899,7 @@ int test_180rel_cancel2(struct context *ctx) struct endpoint *a = &ctx->a, *b = &ctx->b; struct call *a_call = a->call, *b_call = b->call; - struct event *e; + struct event *e, *ep, *ec; if (print_headings) printf("TEST NUA-10.7: CANCEL after 100rel 180\n"); @@ -1911,17 +1964,21 @@ int test_180rel_cancel2(struct context *ctx) TEST_1(is_answer_recv(e->data->e_tags)); TEST_1(!is_offer_sent(e->data->e_tags)); -#define NEXT_SKIP_PRACK_CANCEL() \ +#define NEXT_SKIP(x) \ do { TEST_1(e = e->next); } \ - while (e->data->e_event == nua_r_prack || e->data->e_event == nua_r_cancel) + while (x); - NEXT_SKIP_PRACK_CANCEL(); + NEXT_SKIP(e->data->e_event == nua_r_prack || + e->data->e_event == nua_r_cancel || + e->data->e_event == nua_i_state); TEST_E(e->data->e_event, nua_r_invite); if (e->data->e_status == 487) { TEST(e->data->e_status, 487); TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); TEST(callstate(e->data->e_tags), nua_callstate_terminated); + if (e->next) + NEXT_SKIP(e->data->e_event == nua_r_prack || e->data->e_event == nua_r_cancel); TEST_1(!e->next); } else { @@ -1932,7 +1989,8 @@ int test_180rel_cancel2(struct context *ctx) BYE(a, a_call, a_call->nh, TAG_END()); run_ab_until(ctx, -1, until_terminated, -1, until_terminated); - NEXT_SKIP_PRACK_CANCEL(); TEST_E(e->data->e_event, nua_r_bye); + NEXT_SKIP(e->data->e_event == nua_r_prack || e->data->e_event == nua_r_cancel); + TEST_E(e->data->e_event, nua_r_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 */ @@ -1963,18 +2021,20 @@ int test_180rel_cancel2(struct context *ctx) 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 */ + ec = event_by_type(e->next, nua_i_cancel); - if (e->next->data->e_event == nua_i_cancel) { - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_cancel); + if (ec) { + TEST_1(e = ec); TEST_E(e->data->e_event, nua_i_cancel); TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */ } else { - /* Respond with 200 OK */ - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + /* 180 is PRACKed, PRACK does not have effect on call state */ + ep = event_by_type(e->next, nua_i_prack); + if (ep) e = ep; + /* Responded with 200 OK */ + TEST_1(e = event_by_type(e->next, nua_i_state)); + 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); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_basic_call.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_basic_call.c index 8d7fa30e33..6b82bfc910 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_basic_call.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_basic_call.c @@ -1100,7 +1100,7 @@ int test_basic_call_5(struct context *ctx) nua_handle_destroy(b_call->nh), b_call->nh = NULL; if (print_headings) - printf("TEST NUA-3.4: PASSED\n"); + printf("TEST NUA-3.5: PASSED\n"); END(); } diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_call_reject.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_call_reject.c index f5e40b6fee..a3bf75e179 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_call_reject.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_call_reject.c @@ -1520,7 +1520,7 @@ int test_rejects(struct context *ctx) test_reject_302(ctx) || test_reject_401(ctx) || test_mime_negotiation(ctx) || - test_call_timeouts(ctx) || test_reject_401_aka(ctx) || + test_call_timeouts(ctx) || 0; } diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c index c0bc8e8b80..fbce171774 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c @@ -1349,11 +1349,14 @@ int test_bye_to_invalid_contact(struct context *ctx) struct endpoint *a = &ctx->a, *b = &ctx->b; struct call *a_call = a->call, *b_call = b->call; struct event *e; + sip_t *sip = NULL; + + int seen_401; a_call->sdp = "m=audio 5008 RTP/AVP 8"; b_call->sdp = "m=audio 5010 RTP/AVP 0 8"; -/* Early BYE 2 +/* Bad Contact URI A B |-------INVITE------>| @@ -1362,14 +1365,17 @@ int test_bye_to_invalid_contact(struct context *ctx) |<----180 Ringing----| |<-------200---------| | | - |--------BYE-------->| - |<------200 OK-------| |--------ACK-------->| | | + |<-------BYE---------| + |--------400-------->| + | | + |--------BYE-------->| + |<------200 OK-------| | | */ if (print_headings) - printf("TEST NUA-6.4.3: BYE call when completing\n"); + printf("TEST NUA-6.4.3: Test dialog with bad Contact info\n"); TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END())); @@ -1448,6 +1454,42 @@ int test_bye_to_invalid_contact(struct context *ctx) if (print_headings) printf("TEST NUA-6.4.3: PASSED\n"); + if (!ctx->p) + return 0; + + if (print_headings) + printf("TEST NUA-6.4.4: Wait for re-REGISTER after connection has been closed\n"); + + /* B is supposed to re-register pretty soon, wait for re-registration */ + + run_b_until(ctx, -1, save_until_final_response); + + seen_401 = 0; + + for (e = b->events->head; e; e = e->next) { + TEST_E(e->data->e_event, nua_r_register); + TEST_1(sip = sip_object(e->data->e_msg)); + + if (e->data->e_status == 200) { + TEST(e->data->e_status, 200); + TEST_1(seen_401); + TEST_1(sip->sip_contact); + } + else if (sip->sip_status && sip->sip_status->st_status == 401) { + seen_401 = 1; + } + + if (!e->next) + break; + } + TEST_1(e); + TEST_S(sip->sip_contact->m_expires, "3600"); + TEST_1(!e->next); + free_events_in_list(ctx, b->events); + + if (print_headings) + printf("TEST NUA-6.4.4: PASSED\n"); + END(); } diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c index b389b9d927..ce7b145191 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c @@ -70,8 +70,9 @@ int test_nua_init(struct context *ctx, sip_allow_t const *allow = NULL; sip_supported_t const *supported = NULL; char const *appl_method = NULL; - url_t const *p_uri, *a_uri; /* Proxy URI */ + url_t const *p_uri, *a_uri, *b_uri; /* Proxy URI */ char const *a_bind, *a_bind2; + url_t b_proxy[1]; a_bind = a_bind2 = "sip:0.0.0.0:*"; @@ -121,7 +122,7 @@ int test_nua_init(struct context *ctx, printf("TEST NUA-2.1.1: PASSED\n"); } - p_uri = a_uri = test_proxy_uri(ctx->p); + p_uri = a_uri = b_uri = test_proxy_uri(ctx->p); if (start_nat && p_uri == NULL) p_uri = url_hdup(ctx->home, (void *)o_proxy); @@ -257,8 +258,15 @@ int test_nua_init(struct context *ctx, ctx->b.instance = nua_generate_instance_identifier(ctx->home); + if (ctx->p) { + /* B uses TCP when talking with proxy */ + *b_proxy = *b_uri; + b_uri = b_proxy; + b_proxy->url_params = "transport=tcp"; + } + ctx->b.nua = nua_create(ctx->root, b_callback, ctx, - NUTAG_PROXY(p_uri ? p_uri : o_proxy), + NUTAG_PROXY(b_uri ? b_uri : o_proxy), SIPTAG_FROM_STR("sip:bob@example.org"), NUTAG_URL("sip:0.0.0.0:*"), SOATAG_USER_SDP_STR("m=audio 5006 RTP/AVP 8 0"), diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.c index 9e633cb361..efcaa7263e 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.c @@ -211,6 +211,18 @@ int main(int argc, char *argv[]) else if (strcmp(argv[i], "--loop") == 0) { o_alarm = 0, o_loop = 1; } + else if (strcmp(argv[i], "--print-tags") == 0) { + ctx->print_tags = 1; + } + else if (strcmp(argv[i], "--tags=a") == 0) { + ctx->a.print_tags = 1; + } + else if (strcmp(argv[i], "--tags=b") == 0) { + ctx->b.print_tags = 1; + } + else if (strcmp(argv[i], "--tags=c") == 0) { + ctx->c.print_tags = 1; + } else if (strcmp(argv[i], "--log=a") == 0) { ctx->a.logging = 1; } @@ -317,8 +329,8 @@ int main(int argc, char *argv[]) retval |= test_rejects(ctx); SINGLE_FAILURE_CHECK(); retval |= test_call_cancel(ctx); SINGLE_FAILURE_CHECK(); retval |= test_call_destroy(ctx); SINGLE_FAILURE_CHECK(); - retval |= test_offer_answer(ctx); SINGLE_FAILURE_CHECK(); retval |= test_early_bye(ctx); SINGLE_FAILURE_CHECK(); + retval |= test_offer_answer(ctx); SINGLE_FAILURE_CHECK(); retval |= test_reinvites(ctx); SINGLE_FAILURE_CHECK(); retval |= test_session_timer(ctx); SINGLE_FAILURE_CHECK(); retval |= test_refer(ctx); SINGLE_FAILURE_CHECK(); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h b/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h index 8a299f2976..9f169de9ad 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h @@ -124,6 +124,7 @@ struct context su_root_t *root; int threading, proxy_tests, expensive, quit_on_single_failure, osx_runloop; + int print_tags; char const *external_proxy; int proxy_logging; @@ -133,6 +134,7 @@ struct context struct context *ctx; /* Backpointer */ int logging; + int print_tags; int running; @@ -196,6 +198,9 @@ void free_event_in_list(struct context *ctx, struct eventlist *list, struct event *e); +struct event *event_by_type(struct event *e, nua_event_t); +size_t count_events(struct event const *e); + #define CONDITION_PARAMS \ nua_event_t event, \ int status, char const *phrase, \ diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c index 80d3f30c41..1e89252126 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c @@ -190,7 +190,8 @@ void print_event(nua_event_t event, ep->name, (void *)nh, operation); } - if ((tstflags & tst_verbatim) && tags) + if (tags && + ((tstflags & tst_verbatim) || ctx->print_tags || ep->print_tags)) tl_print(stderr, "", tags); } @@ -499,6 +500,27 @@ void free_event_in_list(struct context *ctx, } } +struct event *event_by_type(struct event *e, nua_event_t etype) +{ + for (; e; e = e->next) { + if (e->data->e_event == etype) + break; + } + + return e; +} + +size_t count_events(struct event const *e) +{ + size_t n; + + for (n = 0; e; e = e->next) + n++; + + return n; +} + + int is_special(nua_event_t e) { if (e == nua_i_active || e == nua_i_terminated) diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c index 5cf9e8a4af..23e89bbca8 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c @@ -53,6 +53,8 @@ struct binding; #include #include #include +#include +#include #include #include @@ -111,6 +113,8 @@ struct proxy { sip_time_t min_expires, expires, max_expires; sip_time_t session_expires, min_se; + + int outbound_tcp; /**< Use inbound TCP connection as outbound */ } prefs; }; @@ -119,7 +123,6 @@ static struct registration_entry *registration_entry_new(struct proxy *, url_t const *); static void registration_entry_destroy(struct registration_entry *e); - struct registration_entry { struct registration_entry *next, **prev; @@ -132,14 +135,16 @@ struct registration_entry struct binding { struct binding *next, **prev; - sip_contact_t *contact; /* bindings */ + sip_contact_t *contact; /* binding */ sip_time_t registered, expires; /* When registered and when expires */ - sip_call_id_t *call_id; + sip_call_id_t *call_id; uint32_t cseq; + tport_t *tport; /**< Reference to tport */ }; static struct binding *binding_new(su_home_t *home, sip_contact_t *contact, + tport_t *tport, sip_call_id_t *call_id, uint32_t cseq, sip_time_t registered, @@ -147,7 +152,9 @@ static struct binding *binding_new(su_home_t *home, static void binding_destroy(su_home_t *home, struct binding *b); static int binding_is_active(struct binding const *b) { - return b->expires > sip_now(); + return + b->expires > sip_now() && + (b->tport == NULL || tport_is_clear_to_send(b->tport)); } LIST_PROTOS(static, proxy_transaction, struct proxy_transaction); @@ -195,6 +202,8 @@ static int process_options(struct proxy *proxy, static struct registration_entry * registration_entry_find(struct proxy const *proxy, url_t const *uri); +static int close_tports(void *proxy); + static auth_challenger_t registrar_challenger[1]; static auth_challenger_t proxy_challenger[1]; @@ -268,6 +277,8 @@ test_proxy_init(su_root_t *root, struct proxy *proxy) proxy->prefs.session_expires = 180; proxy->prefs.min_se = 90; + proxy->prefs.outbound_tcp = 1; + if (!proxy->defleg || !proxy->example_net || !proxy->example_org || !proxy->example_com) return -1; @@ -302,11 +313,11 @@ test_proxy_deinit(su_root_t *root, struct proxy *proxy) nta_outgoing_destroy(t->client), t->client = NULL; } - nta_agent_destroy(proxy->agent); - while (proxy->entries) registration_entry_destroy(proxy->entries); + nta_agent_destroy(proxy->agent); + free(proxy->tags); } @@ -396,6 +407,38 @@ void test_proxy_get_session_timer(struct proxy *p, } } +void test_proxy_set_outbound(struct proxy *p, + int use_outbound) +{ + if (p) { + p->prefs.outbound_tcp = use_outbound; + } +} + +void test_proxy_get_outbound(struct proxy *p, + int *return_use_outbound) +{ + if (p) { + if (return_use_outbound) + *return_use_outbound = p->prefs.outbound_tcp; + } +} + +int test_proxy_close_tports(struct proxy *p) +{ + if (p) { + int retval = -EPROTO; + + su_task_execute(su_clone_task(p->clone), close_tports, p, &retval); + + if (retval < 0) + return errno = -retval, -1; + else + return 0; + } + return errno = EFAULT, -1; +} + /* ---------------------------------------------------------------------- */ static sip_contact_t *create_transport_contacts(struct proxy *p) @@ -449,6 +492,7 @@ int proxy_request(struct proxy *proxy, sip_session_expires_t *x = NULL, x0[1]; sip_min_se_t *min_se = NULL, min_se0[1]; char const *require = NULL; + tport_t *tport = NULL; mf = sip->sip_max_forwards; @@ -529,8 +573,9 @@ int proxy_request(struct proxy *proxy, nta_incoming_treply(irq, SIP_480_TEMPORARILY_UNAVAILABLE, TAG_END()); return 480; } - + target = b->contact->m_url; + tport = b->tport; } t = proxy_transaction_new(proxy); @@ -560,6 +605,7 @@ int proxy_request(struct proxy *proxy, SIPTAG_SESSION_EXPIRES(x), SIPTAG_MIN_SE(min_se), SIPTAG_REQUIRE_STR(require), + NTATAG_TPORT(tport), TAG_END()); if (t->client == NULL) { proxy_transaction_destroy(t); @@ -734,9 +780,10 @@ static int validate_contacts(struct proxy *p, auth_status_t *as, static int check_out_of_order(struct proxy *p, auth_status_t *as, struct registration_entry *e, sip_t const *); static int binding_update(struct proxy *p, - auth_status_t *as, - struct registration_entry *e, - sip_t const *sip); + auth_status_t *as, + struct registration_entry *e, + nta_incoming_t *irq, + sip_t const *sip); sip_contact_t *binding_contacts(su_home_t *home, struct binding *bindings); @@ -768,10 +815,11 @@ int process_register(struct proxy *proxy, assert(as->as_status >= 200); nta_incoming_treply(irq, - as->as_status, as->as_phrase, - SIPTAG_HEADER((void *)as->as_info), - SIPTAG_HEADER((void *)as->as_response), - TAG_END()); + as->as_status, as->as_phrase, + SIPTAG_HEADER((void *)as->as_info), + SIPTAG_HEADER((void *)as->as_response), + TAG_END()); + status = as->as_status; su_home_unref(as->as_home); @@ -816,7 +864,7 @@ static int process_register2(struct proxy *p, if (!e) return set_status(as, SIP_500_INTERNAL_SERVER_ERROR); - if (binding_update(p, as, e, sip)) + if (binding_update(p, as, e, irq, sip)) return as->as_status; msg_header_free(p->home, (void *)e->contacts); @@ -954,6 +1002,7 @@ LIST_BODIES(static, registration_entry, struct registration_entry, next, prev); static struct binding *binding_new(su_home_t *home, sip_contact_t *contact, + tport_t *tport, sip_call_id_t *call_id, uint32_t cseq, sip_time_t registered, @@ -968,6 +1017,7 @@ struct binding *binding_new(su_home_t *home, *m = *contact; m->m_next = NULL; b->contact = sip_contact_dup(home, m); + b->tport = tport_ref(tport); b->call_id = sip_call_id_dup(home, call_id); b->cseq = cseq; b->registered = registered; @@ -992,6 +1042,7 @@ void binding_destroy(su_home_t *home, struct binding *b) } msg_header_free(home, (void *)b->contact); msg_header_free(home, (void *)b->call_id); + tport_unref(b->tport); su_free(home, b); } @@ -999,16 +1050,21 @@ static int binding_update(struct proxy *p, auth_status_t *as, struct registration_entry *e, + nta_incoming_t *irq, sip_t const *sip) { struct binding *b, *old, *next, *last, *bindings = NULL, **bb = &bindings; sip_contact_t *m; sip_time_t expires; - sip_time_t now = sip_now(); + tport_t *tport = NULL; assert(sip->sip_contact); + if (p->prefs.outbound_tcp && + str0casecmp(sip->sip_via->v_protocol, sip_transport_tcp) == 0) + tport = nta_incoming_transport(p->agent, irq, NULL); + /* Create new bindings */ for (m = sip->sip_contact; m; m = m->m_next) { if (m->m_url->url_type == url_any) @@ -1022,7 +1078,7 @@ int binding_update(struct proxy *p, msg_header_remove_param(m->m_common, "expires"); - b = binding_new(p->home, m, sip->sip_call_id, sip->sip_cseq->cs_seq, + b = binding_new(p->home, m, tport, sip->sip_call_id, sip->sip_cseq->cs_seq, now, now + expires); if (!b) break; @@ -1030,6 +1086,8 @@ int binding_update(struct proxy *p, *bb = b, b->prev = bb, bb = &b->next; } + tport_unref(tport); + last = NULL; if (m == NULL) { @@ -1095,3 +1153,25 @@ sip_contact_t *binding_contacts(su_home_t *home, struct binding *bindings) return retval; } + +/* ---------------------------------------------------------------------- */ + +static int close_tports(void *_proxy) +{ + struct proxy *p = _proxy; + struct registration_entry *e; + struct binding *b; + + /* Close all outbound transports */ + for (e = p->entries; e; e = e->next) { + for (b = e->bindings; b; b = b->next) { + if (b->tport) { + tport_shutdown(b->tport, 2); + tport_unref(b->tport); + b->tport = NULL; + } + } + } + + return 0; +} diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.h b/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.h index bd7551ea61..9f74a533a8 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.h +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.h @@ -56,6 +56,8 @@ void test_proxy_get_session_timer(struct proxy *p, sip_time_t *return_session_expires, sip_time_t *return_min_se); +int test_proxy_close_tports(struct proxy *p); + SOFIA_END_DECLS #endif 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 8cc97c1d59..56e0d91af0 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_refer.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_refer.c @@ -113,7 +113,7 @@ int test_refer0(struct context *ctx, char const *tests, struct call *a_call = a->call, *b_call = b->call, *c_call = c->call; struct call *a_refer, *a_c2, *b_refer; struct eventlist *a_revents, *b_revents; - struct event *e; + struct event *e, *notify_e; sip_t const *sip; sip_event_t const *a_event, *b_event; sip_refer_to_t const *refer_to; @@ -280,21 +280,38 @@ int test_refer0(struct context *ctx, char const *tests, TAG_END()), 1); TEST_1(b_event); TEST_1(b_event->o_id); TEST_1(b_event = sip_event_dup(tmphome, b_event)); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_refer); + + notify_e = NULL; + + TEST_1(e = e->next); + if (e->data->e_event == nua_i_notify) { + notify_e = e; + TEST_1(e = e->next); + } + TEST_E(e->data->e_event, nua_r_refer); TEST(e->data->e_status, 202); TEST_1(sip = sip_object(e->data->e_msg)); TEST_SIZE(strtoul(b_event->o_id, NULL, 10), sip->sip_cseq->cs_seq); if (a_refer != a_call) { - if (b_revents->head->next->next == NULL) - run_ab_until(ctx, -1, save_until_received, nua_i_notify, save_events); - else if (a_revents->head->next == NULL) + while (!notify_e) { + for (e = b_revents->head; e; e = e->next) { + if (e->data->e_event == nua_i_notify) { + notify_e = e; + break; + } + } + if (!notify_e) + run_ab_until(ctx, -1, save_until_received, nua_i_notify, save_events); + } + + if (a_revents->head->next == NULL) run_a_until(ctx, -1, save_until_received); TEST_1(e = a_revents->head->next); TEST_E(e->data->e_event, nua_r_notify); TEST_1(!e->next); - TEST_1(e = b_revents->head->next->next); + TEST_1(e = notify_e); TEST_E(e->data->e_event, nua_i_notify); TEST(e->data->e_status, 200); TEST_1(sip = sip_object(e->data->e_msg)); @@ -304,8 +321,8 @@ int test_refer0(struct context *ctx, char const *tests, TEST_1(sip->sip_subscription_state); TEST_S(sip->sip_subscription_state->ss_substate, "pending"); TEST_1(sip->sip_payload && sip->sip_payload->pl_data); - TEST_S(sip->sip_payload->pl_data, "SIP/2.0 100 Trying\r\n"); - TEST_1(!e->next); + TEST_M(sip->sip_payload->pl_data, "SIP/2.0 100 Trying\r\n", + sip->sip_payload->pl_len); } free_events_in_list(ctx, a_revents); @@ -363,7 +380,8 @@ int test_refer0(struct context *ctx, char const *tests, TEST_1(sip->sip_subscription_state); TEST_S(sip->sip_subscription_state->ss_substate, "pending"); TEST_1(sip->sip_payload && sip->sip_payload->pl_data); - TEST_S(sip->sip_payload->pl_data, "SIP/2.0 100 Trying\r\n"); + TEST_M(sip->sip_payload->pl_data, "SIP/2.0 100 Trying\r\n", + sip->sip_payload->pl_len); TEST_1(e = e->next); } TEST_E(e->data->e_event, nua_r_subscribe); @@ -476,7 +494,7 @@ int test_refer0(struct context *ctx, char const *tests, TEST_1(sip->sip_subscription_state); TEST_S(sip->sip_subscription_state->ss_substate, "active"); TEST_1(sip->sip_payload && sip->sip_payload->pl_data); - TEST_S(sip->sip_payload->pl_data, "SIP/2.0 180 Ringing\r\n"); + TEST_M(sip->sip_payload->pl_data, "SIP/2.0 180 Ringing\r\n", sip->sip_payload->pl_len); TEST_1(sip->sip_event); if (refer_with_id) TEST_S(sip->sip_event->o_id, b_event->o_id); @@ -486,7 +504,7 @@ int test_refer0(struct context *ctx, char const *tests, TEST_1(sip->sip_subscription_state); TEST_S(sip->sip_subscription_state->ss_substate, "terminated"); TEST_1(sip->sip_payload && sip->sip_payload->pl_data); - TEST_S(sip->sip_payload->pl_data, "SIP/2.0 200 OK\r\n"); + TEST_M(sip->sip_payload->pl_data, "SIP/2.0 200 OK\r\n", sip->sip_payload->pl_len); TEST_1(sip->sip_event); if (refer_with_id) TEST_S(sip->sip_event->o_id, b_event->o_id); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_register.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_register.c index 4a6d287652..ed1ab1520d 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_register.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_register.c @@ -200,6 +200,10 @@ int test_register_to_proxy(struct context *ctx) m->m_display = "B"; m->m_url->url_user = "b"; + /* Include "tcp" transport parameter in Contact */ + if (ctx->p) + m->m_url->url_params = "transport=tcp"; + REGISTER(b, b_reg, b_reg->nh, SIPTAG_TO(b->to), SIPTAG_CONTACT(m), /* Do not include credentials unless challenged */ @@ -229,11 +233,14 @@ int test_register_to_proxy(struct context *ctx) TEST_S(sip->sip_contact->m_display, "B"); TEST_S(sip->sip_contact->m_url->url_user, "b"); free_events_in_list(ctx, b->events); + if (print_headings) printf("TEST NUA-2.3.2: PASSED\n"); - if (ctx->p) + if (ctx->p) { + test_proxy_close_tports(ctx->p); test_proxy_set_expiration(ctx->p, 600, 3600, 36000); + } if (print_headings) printf("TEST NUA-2.3.3: REGISTER c\n"); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c index 700ba9dcd3..ead1658f96 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c @@ -1194,7 +1194,7 @@ int test_subscription_timeout(struct context *ctx) struct endpoint *a = &ctx->a, *b = &ctx->b; struct call *a_call = a->call, *b_call = b->call; - struct event *e; + struct event *e, *en, *es; sip_t const *sip; tagi_t const *n_tags, *r_tags; @@ -1220,22 +1220,16 @@ int test_subscription_timeout(struct context *ctx) /* Client events: nua_method(), nua_i_notify/nua_r_method, nua_i_notify */ - TEST_1(e = a->events->head); - if (e->data->e_event == nua_i_notify) { - TEST_E(e->data->e_event, nua_i_notify); - TEST_1(sip = sip_object(e->data->e_msg)); - n_tags = e->data->e_tags; - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_subscribe); - r_tags = e->data->e_tags; - } - else { - TEST_E(e->data->e_event, nua_r_method); - TEST(e->data->e_status, 202); - r_tags = e->data->e_tags; - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); - TEST_1(sip = sip_object(e->data->e_msg)); - n_tags = e->data->e_tags; - } + TEST_1(en = event_by_type(a->events->head, nua_i_notify)); + TEST_1(es = event_by_type(a->events->head, nua_r_method)); + + TEST_1(e = en); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + n_tags = e->data->e_tags; + + TEST_1(e = es); TEST_E(e->data->e_event, nua_r_method); + r_tags = e->data->e_tags; + TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence"); TEST_1(sip->sip_content_type); TEST_S(sip->sip_content_type->c_type, "application/pidf+xml"); @@ -1245,7 +1239,12 @@ int test_subscription_timeout(struct context *ctx) TEST_1(tl_find(n_tags, nutag_substate)); TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_pending); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); + if (es->next == en) + e = en->next; + else + e = es->next; + + TEST_1(e); TEST_E(e->data->e_event, nua_i_notify); n_tags = e->data->e_tags; TEST_1(tl_find(n_tags, nutag_substate)); TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c index 34b5ee85fe..6c005c1fcc 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c @@ -89,9 +89,9 @@ int test_events(struct context *ctx) struct endpoint *a = &ctx->a, *b = &ctx->b; struct call *a_call = a->call, *b_call = b->call; - struct event *e; + struct event *e, *en, *es; sip_t const *sip; - tagi_t const *n_tags, *r_tags; + tagi_t const *t, *n_tags, *r_tags; url_t b_url[1]; nea_sub_t *sub = NULL; @@ -206,26 +206,25 @@ int test_events(struct context *ctx) /* Client events: nua_subscribe(), nua_i_notify/nua_r_subscribe */ - TEST_1(e = a->events->head); - if (e->data->e_event == nua_i_notify) { - TEST_E(e->data->e_event, nua_i_notify); - TEST_1(sip = sip_object(e->data->e_msg)); - n_tags = e->data->e_tags; - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_subscribe); - r_tags = e->data->e_tags; - TEST_1(tl_find(r_tags, nutag_substate)); - TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active); + TEST_1(en = event_by_type(a->events->head, nua_i_notify)); + TEST_1(es = event_by_type(a->events->head, nua_r_subscribe)); + + TEST_1(e = es); TEST_E(e->data->e_event, nua_r_subscribe); + r_tags = e->data->e_tags; + TEST_1(tl_find(r_tags, nutag_substate)); + if (es->next == en) { + TEST_1(200 <= e->data->e_status && e->data->e_status < 300); + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_embryonic); } else { - TEST_E(e->data->e_event, nua_r_subscribe); - TEST(e->data->e_status, 202); - r_tags = e->data->e_tags; - TEST_1(tl_find(r_tags, nutag_substate)); - TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_embryonic); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); - TEST_1(sip = sip_object(e->data->e_msg)); - n_tags = e->data->e_tags; + TEST_1(200 <= e->data->e_status && e->data->e_status < 300); + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active); } + + TEST_1(e = en); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + n_tags = e->data->e_tags; + TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence"); TEST_1(sip->sip_content_type); TEST_S(sip->sip_content_type->c_type, "application/pidf+xml"); @@ -234,7 +233,7 @@ int test_events(struct context *ctx) TEST_1(sip->sip_subscription_state->ss_expires); TEST_1(tl_find(n_tags, nutag_substate)); TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active); - TEST_1(!e->next); + TEST_1(!en->next || !es->next); free_events_in_list(ctx, a->events); if (print_headings) @@ -302,7 +301,7 @@ int test_events(struct context *ctx) UNSUBSCRIBE(a, a_call, a_call->nh, TAG_END()); - run_ab_until(ctx, -1, save_until_notified_and_responded, + run_ab_until(ctx, -1, save_until_final_response, -1, NULL /* XXX save_until_received */); /* Client events: @@ -313,26 +312,21 @@ int test_events(struct context *ctx) TEST_E(e->data->e_event, nua_i_notify); TEST_1(sip = sip_object(e->data->e_msg)); n_tags = e->data->e_tags; - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_unsubscribe); - TEST_1(tl_find(e->data->e_tags, nutag_substate)); - TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, - nua_substate_terminated); + TEST_1(sip->sip_event); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "terminated"); + TEST_1(!sip->sip_subscription_state->ss_expires); + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated); + TEST_1(e = e->next); } - else { - TEST_E(e->data->e_event, nua_r_unsubscribe); - TEST(e->data->e_status, 202); - TEST_1(tl_find(e->data->e_tags, nutag_substate)); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); - TEST_1(sip = sip_object(e->data->e_msg)); - n_tags = e->data->e_tags; - } - TEST_1(sip->sip_event); - TEST_1(sip->sip_subscription_state); - TEST_S(sip->sip_subscription_state->ss_substate, "terminated"); - TEST_1(!sip->sip_subscription_state->ss_expires); - TEST_1(tl_find(n_tags, nutag_substate)); - TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated); - TEST_1(!e->next); + TEST_E(e->data->e_event, nua_r_unsubscribe); + TEST_1(tl_find(e->data->e_tags, nutag_substate)); + TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, + nua_substate_terminated); + /* Currently, NOTIFY is dropped after successful response to unsubscribe */ + /* But we don't really care.. */ + /* TEST_1(!e->next); */ free_events_in_list(ctx, a->events); if (print_headings) @@ -383,26 +377,18 @@ int test_events(struct context *ctx) /* Client events: nua_subscribe(), nua_i_notify/nua_r_subscribe */ - TEST_1(e = a->events->head); - if (e->data->e_event == nua_i_notify) { - TEST_E(e->data->e_event, nua_i_notify); - TEST_1(sip = sip_object(e->data->e_msg)); - n_tags = e->data->e_tags; - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_subscribe); - TEST_1(tl_find(e->data->e_tags, nutag_substate)); - TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, - nua_substate_pending); - } - else { - TEST_E(e->data->e_event, nua_r_subscribe); - TEST(e->data->e_status, 202); - TEST_1(tl_find(e->data->e_tags, nutag_substate)); - TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, - nua_substate_embryonic); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); - TEST_1(sip = sip_object(e->data->e_msg)); - n_tags = e->data->e_tags; - } + TEST_1(en = event_by_type(a->events->head, nua_i_notify)); + TEST_1(es = event_by_type(a->events->head, nua_r_subscribe)); + + e = es; TEST_E(e->data->e_event, nua_r_subscribe); + TEST_1(t = tl_find(e->data->e_tags, nutag_substate)); + TEST_1(t->t_value == nua_substate_pending || + t->t_value == nua_substate_embryonic); + + e = en; TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + n_tags = e->data->e_tags; + TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence"); TEST_S(sip->sip_event->o_id, "1"); TEST_1(sip->sip_content_type); @@ -417,7 +403,7 @@ int test_events(struct context *ctx) TEST_1(tl_find(n_tags, nutag_substate)); TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_pending); - TEST_1(!e->next); + TEST_1(!en->next || !es->next); free_events_in_list(ctx, a->events); /* diff --git a/libs/sofia-sip/libsofia-sip-ua/sip/sip_basic.c b/libs/sofia-sip/libsofia-sip-ua/sip/sip_basic.c index 25a9cea6ac..acb6a54546 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sip/sip_basic.c +++ b/libs/sofia-sip/libsofia-sip-ua/sip/sip_basic.c @@ -718,6 +718,9 @@ issize_t sip_name_addr_d(su_home_t *home, * it is like "Contact: url:foo,sip:bar,sip:zunk" */ c = *s; *s = '\0'; /* terminate temporarily */ + /* Do not accept an empty URL */ + if (addr_spec[0] == '\0') + return -1; if (url_d(return_url, addr_spec) == -1) return -1; *s = c; /* return terminator */ diff --git a/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_tag.h.in b/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_tag.h.in index 0d65d3694e..671e18e18a 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_tag.h.in +++ b/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_tag.h.in @@ -104,7 +104,7 @@ SOFIAPUBVAR tag_typedef_t siptag_sip; #define SIPTAG_SIP_REF(x) siptag_sip_ref, siptag_sip_vr(&(x)) SOFIAPUBVAR tag_typedef_t siptag_sip_ref; -#if SU_HAVE_INLINE +#if SU_INLINE_TAG_CAST su_inline tag_value_t siptag_sip_v(sip_t const *v) { return (tag_value_t)v; } su_inline @@ -136,7 +136,7 @@ SOFIAPUBVAR tag_typedef_t siptag_header; #define SIPTAG_HEADER_REF(x) siptag_header_ref, siptag_header_vr(&(x)) SOFIAPUBVAR tag_typedef_t siptag_header_ref; -#if SU_HAVE_INLINE +#if SU_INLINE_TAG_CAST su_inline tag_value_t siptag_header_v(sip_header_t const *v) { return (tag_value_t)v; } @@ -240,7 +240,7 @@ SOFIAPUBVAR tag_typedef_t siptag_#xxxxxx#_str; #define SIPTAG_#XXXXXX#_STR_REF(x) siptag_#xxxxxx#_str_ref, tag_str_vr(&(x)) SOFIAPUBVAR tag_typedef_t siptag_#xxxxxx#_str_ref; -#if SU_HAVE_INLINE +#if SU_INLINE_TAG_CAST su_inline tag_value_t siptag_#xxxxxx#_v(sip_#xxxxxx#_t const *v) { return (tag_value_t)v; } 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 deda739c69..90462095be 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sip/torture_sip.c +++ b/libs/sofia-sip/libsofia-sip-ua/sip/torture_sip.c @@ -405,6 +405,8 @@ int test_basic(void) TEST_1(!sip_from_create(home, (void *)"sip:joe@[baa")); + TEST_1(!sip_from_make(home, (void *)"tester <>;tag=fasjfuios")); + TEST_1(f = sip_from_make(home, (void *)"sip:joe@bar (foo)")); su_free(home, f); 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 47a74fb3dd..fdeecdb556 100644 --- a/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c +++ b/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c @@ -80,9 +80,19 @@ typedef struct soa_static_session { soa_session_t sss_session[1]; char *sss_audio_aux; + int sss_ordered_user; /**< User SDP is ordered */ + int sss_reuse_rejected; /**< Try to reuse rejected media line slots */ + + /** Mapping from user SDP m= lines to session SDP m= lines */ + int *sss_u2s; + /** Mapping from session SDP m= lines to user SDP m= lines */ + int *sss_s2u; } soa_static_session_t; +#define U2S_NOT_USED (-1) +#define U2S_SENTINEL (-2) + static int soa_static_init(char const *, soa_session_t *, soa_session_t *); static void soa_static_deinit(soa_session_t *); static int soa_static_set_params(soa_session_t *ss, tagi_t const *tags); @@ -152,10 +162,14 @@ static int soa_static_set_params(soa_session_t *ss, tagi_t const *tags) { soa_static_session_t *sss = (soa_static_session_t *)ss; char const *audio_aux = sss->sss_audio_aux; + int ordered_user = sss->sss_ordered_user; + int reuse_rejected = sss->sss_reuse_rejected; int n, m; n = tl_gets(tags, SOATAG_AUDIO_AUX_REF(audio_aux), + SOATAG_ORDERED_USER_REF(ordered_user), + SOATAG_REUSE_REJECTED_REF(reuse_rejected), TAG_END()); if (n > 0 && str0casecmp(audio_aux, sss->sss_audio_aux)) { @@ -167,6 +181,9 @@ static int soa_static_set_params(soa_session_t *ss, tagi_t const *tags) su_free(ss->ss_home, tbf); } + sss->sss_ordered_user = ordered_user != 0; + sss->sss_reuse_rejected = reuse_rejected != 0; + m = soa_base_set_params(ss, tags); if (m < 0) return m; @@ -182,6 +199,8 @@ static int soa_static_get_params(soa_session_t const *ss, tagi_t *tags) n = tl_tgets(tags, SOATAG_AUDIO_AUX(sss->sss_audio_aux), + SOATAG_ORDERED_USER(sss->sss_ordered_user), + SOATAG_REUSE_REJECTED(sss->sss_reuse_rejected), TAG_END()); m = soa_base_get_params(ss, tags); if (m < 0) @@ -204,6 +223,10 @@ static tagi_t *soa_static_get_paramlist(soa_session_t const *ss, tl = soa_base_get_paramlist(ss, TAG_IF(sss->sss_audio_aux, SOATAG_AUDIO_AUX(sss->sss_audio_aux)), + TAG_IF(sss->sss_ordered_user, + SOATAG_ORDERED_USER(1)), + TAG_IF(sss->sss_reuse_rejected, + SOATAG_REUSE_REJECTED(1)), TAG_NEXT(ta_args(ta))); ta_end(ta); @@ -239,6 +262,7 @@ static int soa_static_set_user_sdp(soa_session_t *ss, } /** Generate a rejected m= line */ +static sdp_media_t *soa_sdp_make_rejected_media(su_home_t *home, sdp_media_t const *m, sdp_session_t *sdp, @@ -263,6 +287,7 @@ sdp_media_t *soa_sdp_make_rejected_media(su_home_t *home, /** Expand a @a truncated SDP. */ +static sdp_session_t *soa_sdp_expand_media(su_home_t *home, sdp_session_t const *truncated, sdp_session_t const *complete) @@ -312,6 +337,7 @@ int soa_sdp_upgrade_is_needed(sdp_session_t const *session, } /** Check if codec is in auxiliary list */ +static int soa_sdp_is_auxiliary_codec(sdp_rtpmap_t const *rm, char const *auxiliary) { char const *codec; @@ -342,68 +368,84 @@ int soa_sdp_is_auxiliary_codec(sdp_rtpmap_t const *rm, char const *auxiliary) return 0; } +static +sdp_rtpmap_t *soa_sdp_media_matching_rtpmap(sdp_rtpmap_t const *from, + sdp_rtpmap_t const *anylist, + char const *auxiliary) +{ + sdp_rtpmap_t const *rm; -/** Find first matching media in table. */ -sdp_media_t *soa_sdp_matching(soa_session_t *ss, - sdp_media_t *mm[], - sdp_media_t const *with, - int *return_common_codecs) + for (rm = anylist; rm; rm = rm->rm_next) { + /* Ignore auxiliary codecs */ + if (auxiliary && soa_sdp_is_auxiliary_codec(rm, auxiliary)) + continue; + + if (sdp_rtpmap_find_matching(from, rm)) + return (sdp_rtpmap_t *)rm; + } + + return NULL; +} + +#define SDP_MEDIA_NONE ((sdp_media_t *)-1) + +/** Find first matching media in table @a mm. + * + * - if allow_rtp_mismatch == 0, search for a matching codec + * - if allow_rtp_mismatch == 1, prefer m=line with matching codec + * - if allow_rtp_mismatch > 1, ignore codecs + */ +static +int soa_sdp_matching_mindex(soa_session_t *ss, + sdp_media_t *mm[], + sdp_media_t const *with, + int *return_codec_mismatch) { int i, j = -1; - sdp_media_t *m; - sdp_rtpmap_t const *rm; soa_static_session_t *sss = (soa_static_session_t *)ss; - char const *auxiliary; + int rtp = sdp_media_uses_rtp(with), dummy; + char const *auxiliary = NULL; - auxiliary = with->m_type == sdp_media_audio ? sss->sss_audio_aux : NULL; + if (return_codec_mismatch == NULL) + return_codec_mismatch = &dummy; - /* Looking for a single codec */ - if (with->m_rtpmaps && with->m_rtpmaps->rm_next == NULL) - auxiliary = NULL; + if (with->m_type == sdp_media_audio) { + auxiliary = sss->sss_audio_aux; + /* Looking for a single codec */ + if (with->m_rtpmaps && with->m_rtpmaps->rm_next == NULL) + auxiliary = NULL; + } for (i = 0; mm[i]; i++) { + if (mm[i] == SDP_MEDIA_NONE) + continue; + if (!sdp_media_match_with(mm[i], with)) continue; - if (!sdp_media_uses_rtp(with)) + if (!rtp) break; - if (!return_common_codecs) + if (soa_sdp_media_matching_rtpmap(with->m_rtpmaps, + mm[i]->m_rtpmaps, + auxiliary)) break; - /* Check also rtpmaps */ - for (rm = mm[i]->m_rtpmaps; rm; rm = rm->rm_next) { - /* Ignore auxiliary codecs */ - if (auxiliary && soa_sdp_is_auxiliary_codec(rm, auxiliary)) - continue; - - if (sdp_rtpmap_find_matching(with->m_rtpmaps, rm)) - break; - } - if (rm) - break; if (j == -1) j = i; } - if (return_common_codecs) - *return_common_codecs = mm[i] != NULL; - - if (mm[i] == NULL && j != -1) - i = j; /* return m= line without common codecs */ - - m = mm[i]; - - for (; mm[i]; i++) - mm[i] = mm[i + 1]; - - return m; + if (mm[i]) + return *return_codec_mismatch = 0, i; + else + return *return_codec_mismatch = 1, j; } /** Set payload types in @a l_m according to the values in @a r_m. * * @retval number of common codecs */ +static int soa_sdp_set_rtpmap_pt(sdp_media_t *l_m, sdp_media_t const *r_m) { @@ -511,6 +553,7 @@ int soa_sdp_set_rtpmap_pt(sdp_media_t *l_m, * * @return Number of common codecs */ +static int soa_sdp_sort_rtpmap(sdp_rtpmap_t **inout_list, sdp_rtpmap_t const *rrm, char const *auxiliary) @@ -564,6 +607,7 @@ int soa_sdp_sort_rtpmap(sdp_rtpmap_t **inout_list, * * @return Number of common codecs */ +static int soa_sdp_select_rtpmap(sdp_rtpmap_t **inout_list, sdp_rtpmap_t const *rrm, char const *auxiliary, @@ -597,139 +641,220 @@ int soa_sdp_select_rtpmap(sdp_rtpmap_t **inout_list, return common_codecs; } -/** Sort and select rtpmaps within session */ -int soa_sdp_upgrade_rtpmaps(soa_session_t *ss, - sdp_session_t *session, - sdp_session_t const *remote) + +/** Sort and select rtpmaps */ +static +int soa_sdp_media_upgrade_rtpmaps(soa_session_t *ss, + sdp_media_t *sm, + sdp_media_t const *rm) { soa_static_session_t *sss = (soa_static_session_t *)ss; + char const *auxiliary = NULL; + int common_codecs; + + common_codecs = soa_sdp_set_rtpmap_pt(sm, rm); + + if (rm->m_type == sdp_media_audio) + auxiliary = sss->sss_audio_aux; + + if (ss->ss_rtp_sort == SOA_RTP_SORT_REMOTE || + (ss->ss_rtp_sort == SOA_RTP_SORT_DEFAULT && + rm->m_mode == sdp_recvonly)) { + soa_sdp_sort_rtpmap(&sm->m_rtpmaps, rm->m_rtpmaps, auxiliary); + } + + if (common_codecs == 0) + ; + else if (ss->ss_rtp_select == SOA_RTP_SELECT_SINGLE) { + soa_sdp_select_rtpmap(&sm->m_rtpmaps, rm->m_rtpmaps, auxiliary, 1); + } + else if (ss->ss_rtp_select == SOA_RTP_SELECT_COMMON) { + soa_sdp_select_rtpmap(&sm->m_rtpmaps, rm->m_rtpmaps, auxiliary, 0); + } + + return common_codecs; +} + + +/** Sort and select rtpmaps within session */ +static +int soa_sdp_session_upgrade_rtpmaps(soa_session_t *ss, + sdp_session_t *session, + sdp_session_t const *remote) +{ sdp_media_t *sm; sdp_media_t const *rm; for (sm = session->sdp_media, rm = remote->sdp_media; sm && rm; sm = sm->m_next, rm = rm->m_next) { - if (sm->m_rejected) - continue; - if (sdp_media_uses_rtp(sm)) { - int common_codecs = soa_sdp_set_rtpmap_pt(sm, rm); - - char const *auxiliary = - rm->m_type == sdp_media_audio ? sss->sss_audio_aux : NULL; - - if (ss->ss_rtp_sort == SOA_RTP_SORT_REMOTE || - (ss->ss_rtp_sort == SOA_RTP_SORT_DEFAULT && - rm->m_mode == sdp_recvonly)) { - soa_sdp_sort_rtpmap(&sm->m_rtpmaps, rm->m_rtpmaps, auxiliary); - } - - if (common_codecs == 0) - ; - else if (ss->ss_rtp_select == SOA_RTP_SELECT_SINGLE) { - soa_sdp_select_rtpmap(&sm->m_rtpmaps, rm->m_rtpmaps, auxiliary, 1); - } - else if (ss->ss_rtp_select == SOA_RTP_SELECT_COMMON) { - soa_sdp_select_rtpmap(&sm->m_rtpmaps, rm->m_rtpmaps, auxiliary, 0); - } - } + if (!sm->m_rejected && sdp_media_uses_rtp(sm)) + soa_sdp_media_upgrade_rtpmaps(ss, sm, rm); } return 0; } - /** Upgrade m= lines within session */ +static int soa_sdp_upgrade(soa_session_t *ss, su_home_t *home, sdp_session_t *session, - sdp_session_t const *caps, - sdp_session_t const *upgrader) + sdp_session_t const *user, + sdp_session_t const *remote, + int **return_u2s, + int **return_s2u) { soa_static_session_t *sss = (soa_static_session_t *)ss; - int Ns, Nc, Nu, size, i, j; - sdp_media_t *m, **mm, *cm; - sdp_media_t **s_media, **o_media, **c_media; - sdp_media_t const **u_media; + int Ns, Nu, Nr, size, i, j; + sdp_media_t *m, **mm, *um; + sdp_media_t **s_media, **o_media, **u_media; + sdp_media_t const *rm, **r_media; + int *u2s = NULL, *s2u = NULL; + + if (session == NULL || user == NULL) + return (errno = EFAULT), -1; Ns = sdp_media_count(session, sdp_media_any, 0, 0, 0); - Nc = sdp_media_count(caps, sdp_media_any, 0, 0, 0); - Nu = sdp_media_count(upgrader, sdp_media_any, 0, 0, 0); + Nu = sdp_media_count(user, sdp_media_any, 0, 0, 0); + Nr = sdp_media_count(remote, sdp_media_any, 0, 0, 0); - if (caps == upgrader) - size = Ns + Nc + 1; - else if (Ns < Nu) - size = Nu + 1; + if (remote == NULL) + size = Ns + Nu + 1; + else if (Ns < Nr) + size = Nr + 1; else size = Ns + 1; s_media = su_zalloc(home, size * (sizeof *s_media)); o_media = su_zalloc(home, (Ns + 1) * (sizeof *o_media)); - c_media = su_zalloc(home, (Nc + 1) * (sizeof *c_media)); u_media = su_zalloc(home, (Nu + 1) * (sizeof *u_media)); + r_media = su_zalloc(home, (Nr + 1) * (sizeof *r_media)); - cm = sdp_media_dup_all(home, caps->sdp_media, session); + um = sdp_media_dup_all(home, user->sdp_media, session); - if (!s_media || !c_media || !u_media || !cm) + if (!s_media || !u_media || !r_media || !um) return -1; + u2s = su_alloc(home, (Nu + 1) * sizeof(*u2s)); + s2u = su_alloc(home, size * sizeof(*s2u)); + if (!u2s || !s2u) + return -1; + + for (i = 0; i < Nu; i++) + u2s[i] = U2S_NOT_USED; + u2s[i] = U2S_SENTINEL; + + for (i = 0; i <= size; i++) + s2u[i] = U2S_NOT_USED; + s2u[i] = U2S_SENTINEL; + for (i = 0, m = session->sdp_media; m && i < Ns; m = m->m_next) o_media[i++] = m; assert(i == Ns); - for (i = 0, m = cm; m && i < Nc; m = m->m_next) - c_media[i++] = m; - assert(i == Nc); - for (i = 0, m = upgrader->sdp_media; m && i < Nu; m = m->m_next) + for (i = 0, m = um; m && i < Nu; m = m->m_next) u_media[i++] = m; assert(i == Nu); + m = remote ? remote->sdp_media : NULL; + for (i = 0; m && i < Nr; m = m->m_next) + r_media[i++] = m; + assert(i == Nr); - if (caps != upgrader) { + if (sss->sss_ordered_user && sss->sss_u2s) { /* User SDP is ordered */ + for (j = 0; sss->sss_u2s[j] != U2S_SENTINEL; j++) { + i = sss->sss_u2s[j]; + if (i == U2S_NOT_USED) + continue; + if (j >= Nu) /* lines removed from user SDP */ + continue; + s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE; + u2s[j] = i, s2u[i] = j; + } + } + + if (remote) { /* Update session according to remote */ - for (i = 0; i < Nu; i++) { - int common_codecs = 0; + for (i = 0; i < Nr; i++) { + rm = r_media[i]; + m = s_media[i]; - m = soa_sdp_matching(ss, c_media, u_media[i], &common_codecs); + if (!m) { + int codec_mismatch = 0; - if (!m || u_media[i]->m_rejected) { - m = soa_sdp_make_rejected_media(home, u_media[i], session, 0); - } - else if (sdp_media_uses_rtp(m)) { - /* Process rtpmaps */ - char const *auxiliary = - m->m_type == sdp_media_audio ? sss->sss_audio_aux : NULL; + if (!rm->m_rejected) + j = soa_sdp_matching_mindex(ss, u_media, rm, &codec_mismatch); + else + j = -1; - if (!common_codecs && !ss->ss_rtp_mismatch) - m = soa_sdp_make_rejected_media(home, m, session, 1); - soa_sdp_set_rtpmap_pt(m, u_media[i]); - - if (ss->ss_rtp_sort == SOA_RTP_SORT_REMOTE || - (ss->ss_rtp_sort == SOA_RTP_SORT_DEFAULT && - u_media[i]->m_mode == sdp_recvonly)) { - soa_sdp_sort_rtpmap(&m->m_rtpmaps, u_media[i]->m_rtpmaps, auxiliary); + if (j == -1) { + s_media[i] = soa_sdp_make_rejected_media(home, rm, session, 0); + continue; + } + else if (codec_mismatch && !ss->ss_rtp_mismatch) { + m = soa_sdp_make_rejected_media(home, u_media[j], session, 1); + soa_sdp_set_rtpmap_pt(s_media[i] = m, rm); + continue; } - if (common_codecs && - (ss->ss_rtp_select == SOA_RTP_SELECT_SINGLE || - ss->ss_rtp_select == SOA_RTP_SELECT_COMMON)) { - soa_sdp_select_rtpmap(&m->m_rtpmaps, u_media[i]->m_rtpmaps, auxiliary, - ss->ss_rtp_select == SOA_RTP_SELECT_SINGLE); - } + s_media[i] = m = u_media[j]; u_media[j] = SDP_MEDIA_NONE; + u2s[j] = i, s2u[i] = j; } - s_media[i] = m; + if (sdp_media_uses_rtp(rm)) + soa_sdp_media_upgrade_rtpmaps(ss, m, rm); + } + } + else if (sss->sss_ordered_user) { + /* Update session with unused media in u_media */ + + if (!sss->sss_reuse_rejected) { + /* Mark previously used slots */ + for (i = 0; i < Ns; i++) { + if (s_media[i]) + continue; + s_media[i] = soa_sdp_make_rejected_media(home, o_media[i], session, 0); + } + } + + for (j = 0; j < Nu; j++) { + if (u_media[j] == SDP_MEDIA_NONE) + continue; + + for (i = 0; i < size - 1; i++) { + if (s_media[i] != NULL) + continue; + s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE; + u2s[j] = i, s2u[i] = j; + } + + assert(i != size); } } else { - /* Update session according to local */ + /* Match unused user media by media types with the existing session */ for (i = 0; i < Ns; i++) { - m = soa_sdp_matching(ss, c_media, o_media[i], NULL); - if (!m) - m = soa_sdp_make_rejected_media(home, o_media[i], session, 0); - s_media[i] = m; + if (s_media[i]) + continue; + + j = soa_sdp_matching_mindex(ss, u_media, o_media[i], NULL); + if (j == -1) { + s_media[i] = soa_sdp_make_rejected_media(home, o_media[i], session, 0); + continue; + } + + s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE; + u2s[j] = i, s2u[i] = j; } + /* Here we just append new media at the end */ - for (j = 0; c_media[j]; j++) - s_media[i++] = c_media[j]; + for (j = 0; j < Nu; j++) { + if (u_media[j] != SDP_MEDIA_NONE) { + s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE; + u2s[j] = i, s2u[i] = j; + i++; + } + } assert(i <= size); } @@ -739,10 +864,46 @@ int soa_sdp_upgrade(soa_session_t *ss, } *mm = NULL; +#ifndef NDEBUG + for (j = i; j < size; j++) + assert(s2u[j] == U2S_NOT_USED); +#endif + + s2u[size = i] = U2S_SENTINEL; + *return_u2s = u2s; + *return_s2u = s2u; + +#ifndef NDEBUG /* X check */ + for (j = 0; j < Nu; j++) { + i = u2s[j]; + assert(i == U2S_NOT_USED || s2u[i] == j); + } + for (i = 0; i < size; i++) { + j = s2u[i]; + assert(j == U2S_NOT_USED || u2s[j] == i); + } +#endif + return 0; } +int *u2s_alloc(su_home_t *home, int const *u2s) +{ + if (u2s) { + int i, *a; + for (i = 0; u2s[i] != U2S_SENTINEL; i++) + ; + a = su_alloc(home, (i + 1) * (sizeof *u2s)); + if (a) + memcpy(a, u2s, (i + 1) * (sizeof *u2s)); + return a; + } + + return NULL; +} + /** Check if @a session contains media that are rejected by @a remote. */ +static int soa_sdp_reject_is_needed(sdp_session_t const *session, sdp_session_t const *remote) { @@ -774,6 +935,7 @@ int soa_sdp_reject_is_needed(sdp_session_t const *session, } /** If m= line is rejected by remote mark m= line rejected within session */ +static int soa_sdp_reject(su_home_t *home, sdp_session_t *session, sdp_session_t const *remote) @@ -813,6 +975,7 @@ int soa_sdp_reject(su_home_t *home, } /** Check if @a session mode should be changed. */ +static int soa_sdp_mode_set_is_needed(sdp_session_t const *session, sdp_session_t const *remote, char const *hold) @@ -855,6 +1018,7 @@ int soa_sdp_mode_set_is_needed(sdp_session_t const *session, /** Update mode within session */ +static int soa_sdp_mode_set(sdp_session_t *session, sdp_session_t const *remote, char const *hold) @@ -908,6 +1072,8 @@ static int offer_answer_step(soa_session_t *ss, enum offer_answer_action action, char const *by) { + soa_static_session_t *sss = (soa_static_session_t *)ss; + char c_address[64]; sdp_session_t *local = ss->ss_local->ssd_sdp; sdp_session_t local0[1]; @@ -922,6 +1088,8 @@ static int offer_answer_step(soa_session_t *ss, sdp_connection_t *c, c0[1] = {{ sizeof(c0) }}; sdp_time_t t[1] = {{ sizeof(t) }}; + int *u2s = NULL, *s2u = NULL, *tbf; + char const *phrase = "Internal Media Error"; su_home_t tmphome[SU_HOME_AUTO_SIZE(8192)]; @@ -991,7 +1159,7 @@ static int offer_answer_step(soa_session_t *ss, *local0 = *local, local = local0; SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "upgrade with local description")); - soa_sdp_upgrade(ss, tmphome, local, user, user); + soa_sdp_upgrade(ss, tmphome, local, user, NULL, &u2s, &s2u); break; case generate_answer: /* Upgrade local SDP based on remote SDP */ @@ -1003,7 +1171,7 @@ static int offer_answer_step(soa_session_t *ss, *local0 = *local, local = local0; SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "upgrade with remote description")); - soa_sdp_upgrade(ss, tmphome, local, user, remote); + soa_sdp_upgrade(ss, tmphome, local, user, remote, &u2s, &s2u); } break; case process_answer: @@ -1074,7 +1242,7 @@ static int offer_answer_step(soa_session_t *ss, *local0 = *local, local = local0; DUP_LOCAL(local); } - soa_sdp_upgrade_rtpmaps(ss, local, remote); + soa_sdp_session_upgrade_rtpmaps(ss, local, remote); } break; case generate_offer: @@ -1125,9 +1293,16 @@ static int offer_answer_step(soa_session_t *ss, soa_description_free(ss, ss->ss_previous); + if (u2s) { + u2s = u2s_alloc(ss->ss_home, u2s); + s2u = u2s_alloc(ss->ss_home, s2u); + if (!u2s || !s2u) + goto internal_error; + } + if (ss->ss_local->ssd_sdp != local && sdp_session_cmp(ss->ss_local->ssd_sdp, local)) { - /* We have modfied local session: update origin-line */ + /* We have modified local session: update origin-line */ if (local->sdp_origin != o) *o = *local->sdp_origin, local->sdp_origin = o; o->o_version++; @@ -1151,10 +1326,24 @@ static int offer_answer_step(soa_session_t *ss, /* Update the unparsed and pretty-printed descriptions */ if (soa_description_set(ss, ss->ss_local, local, NULL, 0) < 0) { + if (action == generate_offer) { + /* Remove 2nd reference to local session state */ + memset(ss->ss_previous, 0, (sizeof *ss->ss_previous)); + ss->ss_previous_user_version = 0; + ss->ss_previous_remote_version = 0; + } + + su_free(ss->ss_home, u2s), su_free(ss->ss_home, s2u); + goto internal_error; } } + if (u2s) { + tbf = sss->sss_u2s, sss->sss_u2s = u2s, su_free(ss->ss_home, tbf); + tbf = sss->sss_s2u, sss->sss_s2u = s2u, su_free(ss->ss_home, tbf); + } + /* Update version numbers */ switch (action) { case generate_offer: 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 fb03206c72..638e2812f3 100644 --- a/libs/sofia-sip/libsofia-sip-ua/soa/soa_tag.c +++ b/libs/sofia-sip/libsofia-sip-ua/soa/soa_tag.c @@ -604,3 +604,32 @@ tag_typedef_t soatag_srtp_integrity = BOOLTAG_TYPEDEF(srtp_integrity); * @sa soa_set_params(), nua_invite(), @ref nua_event_diagram_call_hold */ tag_typedef_t soatag_hold = STRTAG_TYPEDEF(hold); + + +/**@def SOATAG_ORDERED_USER(x) + * + * Take account strict ordering of user SDP m=lines. If user SDP has been + * updated, the new media lines replace old ones even if the media type has + * been changed. This allows the application to replace @b m=audio with + * @b m=image/t38, for instance. + * + * @par Used with + * soa_set_params(), soa_get_params(), soa_get_paramlist() \n + * + * @par Parameter type + * boolean + * + * @par Values + * - false (0) - update session with user SDP based on media type + * - true (1) - update session with m= line in user SDP based on their order + * + * The default value is false and session are updated based on media types. + * + * + * Corresponding tag taking a reference parameter is SOATAG_RTP_SELECT_REF(). + * + * @sa @RFC3264 section 8.3.3, T.38 + */ +tag_typedef_t soatag_ordered_user = BOOLTAG_TYPEDEF(ordered_user); + +tag_typedef_t soatag_reuse_rejected = BOOLTAG_TYPEDEF(reuse_rejected); diff --git a/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_tag.h b/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_tag.h index a75baa9116..3f9dcc7b21 100644 --- a/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_tag.h +++ b/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_tag.h @@ -244,6 +244,20 @@ SOFIAPUBVAR tag_typedef_t soatag_hold; #define SOATAG_HOLD_REF(x) soatag_hold_ref, tag_str_vr(&(x)) SOFIAPUBVAR tag_typedef_t soatag_hold_ref; +#define SOATAG_ORDERED_USER(x) soatag_ordered_user, tag_bool_v(x) +SOFIAPUBVAR tag_typedef_t soatag_ordered_user; + +#define SOATAG_ORDERED_USER_REF(x) \ + soatag_ordered_user_ref, tag_bool_vr(&(x)) +SOFIAPUBVAR tag_typedef_t soatag_ordered_user_ref; + +#define SOATAG_REUSE_REJECTED(x) soatag_reuse_rejected, tag_bool_v(x) +SOFIAPUBVAR tag_typedef_t soatag_reuse_rejected; + +#define SOATAG_REUSE_REJECTED_REF(x) \ + soatag_reuse_rejected_ref, tag_bool_vr(&(x)) +SOFIAPUBVAR tag_typedef_t soatag_reuse_rejected_ref; + SOFIA_END_DECLS #endif /* SOA_TAG_H */ 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 4bd0dbce8b..8b4995a024 100644 --- a/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c +++ b/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c @@ -519,7 +519,7 @@ int test_static_offer_answer(struct context *ctx) /* 'B' will reject. */ TEST(soa_set_params(a, SOATAG_HOLD(NULL), /* 'A' will release hold. */ - SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8\r\n" + SOATAG_USER_SDP_STR("m=audio 5008 RTP/AVP 0 8\r\ni=x\r\n" "m=video 5006 RTP/AVP 34\r\n"), TAG_END()), 2); @@ -663,7 +663,7 @@ int test_codec_selection(struct context *ctx) TEST_1(m = b_sdp->sdp_media); TEST_1(m->m_rejected); TEST_1(rm = m->m_rtpmaps); TEST(rm->rm_pt, 96); TEST_S(rm->rm_encoding, "G7231"); - /* Not using payload type 97 from offer */ + /* Not reusing payload type 97 from offer */ TEST_1(rm = rm->rm_next); TEST(rm->rm_pt, 98); TEST_S(rm->rm_encoding, "G729"); TEST_1(!rm->rm_next); @@ -1141,6 +1141,128 @@ int test_codec_selection(struct context *ctx) END(); } +int test_media_replace(struct context *ctx) +{ + BEGIN(); + int n; + + soa_session_t *a, *b; + + char const *offer = NONE, *answer = NONE; + isize_t offerlen = (isize_t)-1, answerlen = (isize_t)-1; + + sdp_session_t const *a_sdp, *b_sdp; + sdp_media_t const *m; + + char const a_caps[] = + "v=0\r\n" + "o=left 219498671 2 IN IP4 127.0.0.2\r\n" + "c=IN IP4 127.0.0.2\r\n" + "m=audio 5008 RTP/AVP 0 8\r\n" + ; + + char const b_caps[] = + "m=audio 5004 RTP/AVP 0 8\n" + "a=rtpmap:96 G7231/8000\n" + "a=rtpmap:97 G729/8000\n" + "m=image 5556 UDPTL t38\r\n" + "a=T38FaxVersion:0\r\n" + "a=T38MaxBitRate:9600\r\n" + "a=T38FaxFillBitRemoval:0\r\n" + "a=T38FaxTranscodingMMR:0\r\n" + "a=T38FaxTranscodingJBIG:0\r\n" + "a=T38FaxRateManagement:transferredTCF\r\n" + "a=T38FaxMaxDatagram:400\r\n"; + + TEST_1(a = soa_create("static", ctx->root, ctx)); + TEST_1(b = soa_create("static", ctx->root, ctx)); + + TEST(soa_set_user_sdp(a, 0, a_caps, strlen(a_caps)), 1); + TEST(soa_set_user_sdp(b, 0, b_caps, strlen(b_caps)), 1); + + n = soa_generate_offer(a, 1, test_completed); TEST(n, 0); + n = soa_get_local_sdp(a, NULL, &offer, &offerlen); TEST(n, 1); + TEST_1(offer != NULL && offer != NONE); + n = soa_set_remote_sdp(b, 0, offer, offerlen); TEST(n, 1); + n = soa_get_local_sdp(b, NULL, &answer, &answerlen); TEST(n, 0); + n = soa_generate_answer(b, test_completed); TEST(n, 0); + n = soa_get_local_sdp(b, &b_sdp, &answer, &answerlen); TEST(n, 1); + TEST_1(answer != NULL && answer != NONE); + n = soa_set_remote_sdp(a, 0, answer, -1); TEST(n, 1); + n = soa_process_answer(a, test_completed); TEST(n, 0); + + TEST_1(soa_is_complete(b)); + TEST(soa_activate(b, NULL), 0); + + TEST_1(soa_is_complete(a)); + TEST(soa_activate(a, NULL), 0); + + TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDRECV); + TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDRECV); + + /* ---------------------------------------------------------------------- */ + + /* Re-O/A: replace media stream */ + + /* Accept media without common codecs */ + TEST_1(soa_set_params(a, SOATAG_RTP_MISMATCH(0), + SOATAG_ORDERED_USER(1), + SOATAG_USER_SDP_STR( + "v=0\r\n" + "o=left 219498671 2 IN IP4 127.0.0.2\r\n" + "c=IN IP4 127.0.0.2\r\n" + "m=image 16384 UDPTL t38\r\n" + "a=T38FaxVersion:0\r\n" + "a=T38MaxBitRate:9600\r\n" + "a=T38FaxFillBitRemoval:0\r\n" + "a=T38FaxTranscodingMMR:0\r\n" + "a=T38FaxTranscodingJBIG:0\r\n" + "a=T38FaxRateManagement:transferredTCF\r\n" + "a=T38FaxMaxDatagram:400\r\n" + ), + TAG_END())); + + n = soa_generate_offer(a, 1, test_completed); TEST(n, 0); + n = soa_get_local_sdp(a, &a_sdp, &offer, &offerlen); TEST(n, 1); + TEST_1(offer != NULL && offer != NONE); + n = soa_set_remote_sdp(b, 0, offer, offerlen); TEST(n, 1); + n = soa_generate_answer(b, test_completed); TEST(n, 0); + n = soa_get_local_sdp(b, &b_sdp, &answer, &answerlen); TEST(n, 1); + TEST_1(answer != NULL && answer != NONE); + n = soa_set_remote_sdp(a, 0, answer, -1); TEST(n, 1); + n = soa_process_answer(a, test_completed); TEST(n, 0); + n = soa_get_local_sdp(a, &a_sdp, NULL, NULL); TEST(n, 1); + + TEST_1(soa_is_complete(b)); + TEST(soa_activate(b, NULL), 0); + + TEST_1(soa_is_complete(a)); + TEST(soa_activate(a, NULL), 0); + + TEST_1(m = a_sdp->sdp_media); TEST_1(!m->m_rejected); + TEST(m->m_type, sdp_media_image); + TEST(m->m_proto, sdp_proto_udptl); + TEST_1(m->m_format); + TEST_S(m->m_format->l_text, "t38"); + + TEST_1(m = b_sdp->sdp_media); TEST_1(!m->m_rejected); + TEST(m->m_type, sdp_media_image); + TEST(m->m_proto, sdp_proto_udptl); + TEST_1(m->m_format); + TEST_S(m->m_format->l_text, "t38"); + + TEST(soa_is_audio_active(a), SOA_ACTIVE_DISABLED); + TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_DISABLED); + + TEST_VOID(soa_terminate(a, NULL)); + TEST_VOID(soa_terminate(b, NULL)); + + TEST_VOID(soa_destroy(a)); + TEST_VOID(soa_destroy(b)); + + END(); +} + int test_asynch_offer_answer(struct context *ctx) { @@ -1332,6 +1454,7 @@ int main(int argc, char *argv[]) retval |= test_params(ctx); SINGLE_FAILURE_CHECK(); retval |= test_static_offer_answer(ctx); SINGLE_FAILURE_CHECK(); retval |= test_codec_selection(ctx); SINGLE_FAILURE_CHECK(); + retval |= test_media_replace(ctx); SINGLE_FAILURE_CHECK(); retval |= test_asynch_offer_answer(ctx); SINGLE_FAILURE_CHECK(); } diff --git a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_configure.h.in b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_configure.h.in index 687463a883..b15ac1a793 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_configure.h.in +++ b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_configure.h.in @@ -88,6 +88,9 @@ /** Define as suitable declarator static inline functions */ #undef su_inline +/** Define as 1 the tag value casts use inlined functions */ +#undef SU_INLINE_TAG_CAST + /** Define this as 1 if we can use tags directly from stack. */ #undef SU_HAVE_TAGSTACK diff --git a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_tag.h b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_tag.h index 47f05d9e03..ad9f9786cb 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_tag.h +++ b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_tag.h @@ -171,7 +171,7 @@ SOFIAPUBFUN void tl_vfree(tagi_t *t); #define SU_ALIGN(x) \ ((sizeof(void *) - ((intptr_t)(x) & (sizeof(void *) - 1))) & (sizeof(void *) - 1)) -#if SU_HAVE_INLINE +#if SU_INLINE_TAG_CAST su_inline tag_value_t tag_int_v(int v) { return (tag_value_t)v; } su_inline tag_value_t tag_int_vr(int *vp) { return (tag_value_t)vp; } su_inline tag_value_t tag_uint_v(unsigned v) { return (tag_value_t)v; } diff --git a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_tag_io.h b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_tag_io.h index 72a1a889d1..2cefff003d 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_tag_io.h +++ b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_tag_io.h @@ -48,7 +48,7 @@ SOFIA_BEGIN_DECLS SOFIAPUBFUN void tl_print(FILE *f, char const *title, tagi_t const lst[]); -#if SU_HAVE_INLINE +#if SU_INLINE_TAG_CAST su_inline tag_value_t tag_socket_v(su_socket_t v) { return (tag_value_t)v; } diff --git a/libs/sofia-sip/libsofia-sip-ua/su/su.c b/libs/sofia-sip/libsofia-sip-ua/su/su.c index 1b97828341..6375b0fe86 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/su.c +++ b/libs/sofia-sip/libsofia-sip-ua/su/su.c @@ -51,14 +51,18 @@ #endif int su_socket_close_on_exec = 0; +int su_socket_blocking = 0; /** Create an endpoint for communication. */ su_socket_t su_socket(int af, int socktype, int proto) { su_socket_t s = socket(af, socktype, proto); #if SU_HAVE_BSDSOCK - if (s != INVALID_SOCKET && su_socket_close_on_exec) { - fcntl(s, F_SETFD, FD_CLOEXEC); /* Close on exec */ + if (s != INVALID_SOCKET) { + if (su_socket_close_on_exec) + fcntl(s, F_SETFD, FD_CLOEXEC); /* Close on exec */ + if (!su_socket_blocking) /* All sockets are born blocking */ + su_setblocking(s, 0); } #endif return s; diff --git a/libs/sofia-sip/libsofia-sip-ua/su/su_base_port.c b/libs/sofia-sip/libsofia-sip-ua/su/su_base_port.c index 3e8668d6b2..e00ec29675 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/su_base_port.c +++ b/libs/sofia-sip/libsofia-sip-ua/su/su_base_port.c @@ -540,7 +540,6 @@ int su_base_port_start_shared(su_root_t *parent, init(child, magic) == 0) return 0; - deinit(child, magic); su_msg_destroy(return_clone); su_root_destroy(child); return -1; 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 569bdd88a5..5087660f51 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/torture_su.c +++ b/libs/sofia-sip/libsofia-sip-ua/su/torture_su.c @@ -204,6 +204,8 @@ int test_sendrecv(void) TEST(getsockname(l, &su.su_sa, &sulen), 0); TEST(listen(l, 5), 0); + + TEST(su_setblocking(s, 1), 0); TEST(connect(s, &su.su_sa, sulen), 0); a = accept(l, &csu.su_sa, &csulen); TEST_1(a != -1); diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport.h b/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport.h index 8928ca5b55..b04111e4b2 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport.h @@ -279,6 +279,9 @@ TPORT_DLL int tport_is_shutdown(tport_t const *self); /** Test if transport is connected. @NEW_1_12_5 */ TPORT_DLL int tport_is_connected(tport_t const *self); +/** Test if transport can be used to send message. @NEW_1_12_7 */ +TPORT_DLL int tport_is_clear_to_send(tport_t const *self); + /** Set transport magic. */ TPORT_DLL void tport_set_magic(tport_t *self, tp_magic_t *magic); diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport_tag.h b/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport_tag.h index 099fb2e534..500a898319 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport_tag.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport_tag.h @@ -403,7 +403,7 @@ TPORT_DLL extern tag_typedef_t tptag_log; * Use with tport_tcreate(), nua_create(), nta_agent_create(), * nth_engine_create(), or initial nth_site_create(). * - * @sa #TPORT_DUMP, TPTAG_DUMP() + * @sa #TPORT_LOG environment variable, TPTAG_DUMP() * * @NEW_1_12_5 */ @@ -418,7 +418,7 @@ TPORT_DLL extern tag_typedef_t tptag_dump; * Use with tport_tcreate(), nta_agent_create(), nua_create(), * nth_engine_create(), or initial nth_site_create(). * - * @sa #TPORT_DUMP, TPTAG_LOG(). + * @sa #TPORT_DUMP environment variable, TPTAG_LOG(). * * @NEW_1_12_5 */ diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport.c index 0a2b46678a..ea924b5aa6 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport.c @@ -166,7 +166,7 @@ int tport_is_secondary(tport_t const *self) self->tp_pri->pri_primary != self; } -/** Test if transport has been registered */ +/** Test if transport has been registered to su_root_t */ int tport_is_registered(tport_t const *self) { return self->tp_index != 0; @@ -291,6 +291,19 @@ int tport_is_connected(tport_t const *self) return self->tp_is_connected; } +/** Test if transport can be used to send message. @NEW_1_12_7 */ +int tport_is_clear_to_send(tport_t const *self) +{ + return + tport_is_master(self) || + tport_is_primary(self) || + (tport_is_secondary(self) && + tport_is_registered(self) && + self->tp_reusable && + !self->tp_closed && + !self->tp_send_close); +} + /** MTU for transport */ su_inline unsigned tport_mtu(tport_t const *self) { @@ -1164,7 +1177,7 @@ int tport_set_params(tport_t *self, if (self == NULL) return su_seterrno(EINVAL); - memcpy(tpp, tpp0 = self->tp_params, sizeof *tpp); + memcpy(tpp, tpp0 = self->tp_params, sizeof tpp); mtu = tpp->tpp_mtu; connect = tpp->tpp_conn_orient; @@ -1220,9 +1233,10 @@ int tport_set_params(tport_t *self, if (tport_is_secondary(self) && self->tp_params == self->tp_pri->pri_primary->tp_params) { tpp0 = su_zalloc(self->tp_home, sizeof *tpp0); if (!tpp0) return -1; + self->tp_params = tpp0; } - memcpy(tpp0, tpp, sizeof *tpp); + memcpy(tpp0, tpp, sizeof tpp); return n; } @@ -1963,7 +1977,7 @@ int tport_addrinfo_copy(su_addrinfo_t *dst, void *addr, socklen_t addrlen, */ void tport_close(tport_t *self) { - SU_DEBUG_5(("%s(%p): " TPN_FORMAT "\n", "tport_close", (void *)self, + SU_DEBUG_5(("%s(%p): " TPN_FORMAT "\n", "tport_close", (void *)self, TPN_ARGS(self->tp_name))); self->tp_closed = 1; @@ -2360,6 +2374,7 @@ void tport_error_report(tport_t *self, int errcode, else if (errcode > 0) errmsg = su_strerror(errcode); else + /* Should be something like ENOTCONN */ errcode = 0, errmsg = "stream closed"; if (addr && addr->su_family == AF_UNSPEC) @@ -2369,12 +2384,12 @@ void tport_error_report(tport_t *self, int errcode, if (errcode > 0 && tport_has_connection(self)) self->tp_reusable = 0; - if (addr == NULL && tport_is_connection_oriented(self)) - addr = self->tp_addr; - /* Report error */ if (addr && tport_pending_error(self, addr, errcode)) ; + else if (tport_is_secondary(self) && + tport_pending_error(self, NULL, errcode) > 0) + ; else if (self->tp_master->mr_tpac->tpac_error) { char *dstname = NULL; char hp[TPORT_HOSTPORTSIZE]; @@ -3070,30 +3085,24 @@ tport_t *tport_tsend(tport_t *self, /* Select a primary protocol, make a fresh connection */ self = primary->pri_primary; } + else if (tport_is_secondary(self) && tport_is_clear_to_send(self)) { + self = self; + } + /* + * Try to find an already open connection to the destination, + * or get a primary protocol + */ else { - if (tport_is_secondary(self) && - tport_is_registered(self) && - self->tp_reusable && - !self->tp_closed && - !self->tp_send_close) { - self = self; + /* If primary, resolve the destination address, store it in the msg */ + if (tport_resolve(primary->pri_primary, msg, tpn) < 0) { + return NULL; } - /* - * Try to find an already open connection to the destination, - * or get a primary protocol - */ - else { - /* If primary, resolve the destination address, store it in the msg */ - if (tport_resolve(primary->pri_primary, msg, tpn) < 0) { - return NULL; - } - resolved = 1; + resolved = 1; + + self = tport_by_addrinfo(primary, msg_addrinfo(msg), tpn); - self = tport_by_addrinfo(primary, msg_addrinfo(msg), tpn); - - if (!self) - self = primary->pri_primary; - } + if (!self) + self = primary->pri_primary; } if (tport_is_primary(self)) { @@ -3878,9 +3887,12 @@ int tport_pend(tport_t *self, { tport_pending_t *pending; - if (self == NULL || msg == NULL || callback == NULL || client == NULL) + if (self == NULL || callback == NULL || client == NULL) return -1; - + + if (msg == NULL && tport_is_primary(self)) + return -1; + SU_DEBUG_7(("tport_pend(%p): pending %p for %s/%s:%s (already %u)\n", (void *)self, (void *)msg, self->tp_protoname, self->tp_host, self->tp_port, @@ -3930,7 +3942,7 @@ int tport_release(tport_t *self, { tport_pending_t *pending; - if (self == NULL || msg == NULL || pendd <= 0 || pendd > (int)self->tp_plen) + if (self == NULL || pendd <= 0 || pendd > (int)self->tp_plen) return su_seterrno(EINVAL), -1; pending = self->tp_pending + (pendd - 1); @@ -3968,7 +3980,7 @@ tport_pending_error(tport_t *self, su_sockaddr_t const *dst, int error) msg_t *msg; su_addrinfo_t const *ai; - assert(self); assert(dst); + assert(self); callbacks = 0; reported = ++self->tp_reported; @@ -3979,22 +3991,25 @@ tport_pending_error(tport_t *self, su_sockaddr_t const *dst, int error) for (i = 0; i < self->tp_plen; i++) { pending = self->tp_pending + i; - if (!pending->p_callback || !pending->p_msg) + if (!pending->p_callback) continue; if (pending->p_reported == reported) continue; msg = pending->p_msg; - ai = msg_addrinfo(msg); - if (su_cmp_sockaddr(dst, (su_sockaddr_t *)ai->ai_addr) != 0) - continue; + if (dst && msg) { + ai = msg_addrinfo(msg); - pending->p_reported = reported; + if (su_cmp_sockaddr(dst, (su_sockaddr_t *)ai->ai_addr) != 0) + continue; + } msg_set_errno(msg, error); + pending->p_reported = reported; + pending->p_callback(self->TP_STACK, pending->p_client, self, msg, error); callbacks++; diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tcp.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tcp.c index a890b3f618..2916076841 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tcp.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tcp.c @@ -252,9 +252,9 @@ int tport_recv_stream(tport_t *self) tport_dump_iovec(self, msg, n, iovec, veclen, "recv", "from"); /* Mark buffer as used */ - msg_recv_commit(msg, n, 0); + msg_recv_commit(msg, n, n == 0); - return 1; + return n != 0; } ssize_t tport_send_stream(tport_t const *self, msg_t *msg, diff --git a/libs/sofia-sip/libsofia-sip-ua/url/torture_url.c b/libs/sofia-sip/libsofia-sip-ua/url/torture_url.c index 92c82fea05..b26f751bdc 100644 --- a/libs/sofia-sip/libsofia-sip-ua/url/torture_url.c +++ b/libs/sofia-sip/libsofia-sip-ua/url/torture_url.c @@ -214,6 +214,15 @@ int test_any(void) TEST_S(u->url_fragment, "foo"); } + { + url_t u[1]; + char b2[6] = ""; + + memset(u, 0xff, sizeof u); + TEST(url_d(u, b2), 0); + TEST(u->url_type, url_unknown); + } + su_home_deinit(home); END(); diff --git a/libs/sofia-sip/m4/sac-su2.m4 b/libs/sofia-sip/m4/sac-su2.m4 index 4ae3393f14..0f4d6a3640 100644 --- a/libs/sofia-sip/m4/sac-su2.m4 +++ b/libs/sofia-sip/m4/sac-su2.m4 @@ -2,11 +2,11 @@ dnl ====================================================================== dnl su module dnl ====================================================================== +AC_DEFUN([SAC_SU]) + AC_DEFUN([SAC_SOFIA_SU], [ # Beginning of SAC_SOFIA_SU -AC_REQUIRE([SAC_WITH_RT]) - # ====================================================================== # Check for features used by su @@ -66,6 +66,16 @@ AC_REQUIRE([AC_C_MACRO_FUNCTION]) AC_REQUIRE([AC_C_INLINE]) +AC_ARG_ENABLE(tag-cast, +[ --disable-tag-cast cast tag values with inlined functions [[enabled]]], + , enable_tag_cast=yes) + +if test "$enable_tag_cast" = "yes"; then + tag_cast=1 +else + tag_cast=0 +fi + case "$ac_cv_c_inline" in yes) SAC_SU_DEFINE(su_inline, static inline, [ Define to declarator for static inline functions. @@ -76,15 +86,20 @@ case "$ac_cv_c_inline" in SAC_SU_DEFINE(SU_HAVE_INLINE, 1, [ Define to 1 if you have inline functions. ])dnl + SAC_SU_DEFINE_UNQUOTED(SU_INLINE_TAG_CAST, $tag_cast, [ + Define to 1 if you use inline function to cast tag values. + ])dnl ;; no | "" ) SAC_SU_DEFINE(su_inline, static)dnl SAC_SU_DEFINE(SU_INLINE, /*inline*/)dnl SAC_SU_DEFINE(SU_HAVE_INLINE, 0)dnl + SAC_SU_DEFINE(SU_INLINE_TAG_CAST, 0)dnl ;; *) SAC_SU_DEFINE_UNQUOTED(su_inline, static $ac_cv_c_inline)dnl SAC_SU_DEFINE_UNQUOTED(SU_INLINE, $ac_cv_c_inline)dnl SAC_SU_DEFINE(SU_HAVE_INLINE, 1)dnl + SAC_SU_DEFINE_UNQUOTED(SU_INLINE_TAG_CAST, $tag_cast)dnl ;; esac @@ -368,7 +383,15 @@ fi # Checks for libraries # =========================================================================== -SAC_CHECK_SU_LIBS +AC_CHECK_LIB(pthread, pthread_create) +AC_CHECK_LIB(socket, socketpair,,,-lnsl) + +AC_ARG_WITH(rt, +[ --with-rt use POSIX realtime library [[used by default]]]) +if test "${with_rt}" != no; then + AC_SEARCH_LIBS(clock_gettime, rt) + AC_CHECK_FUNCS([clock_gettime clock_getcpuclockid]) +fi # No GLib path explicitly defined, use pkg-config AC_ARG_WITH(glib, @@ -470,11 +493,6 @@ if test $ac_cv_func_if_nameindex = yes ; then [Define to 1 if you have if_nameindex().]) fi -AC_REQUIRE([SAC_WITH_RT]) -if test "${with_rt}" != no; then - AC_CHECK_FUNCS([clock_gettime clock_getcpuclockid]) -fi - SAC_REPLACE_FUNCS([memmem memccpy memspn memcspn strcasestr strtoull \ inet_ntop inet_pton poll])