Merge changes from sofia-sip tree:

Mon Feb 12 21:22:39 EET 2007  Maxim Zaikin <Maxim at amsd.com>
  * su_pthread_port.c: destroying condition variable used when destroying the thread.

Tue Feb 13 01:10:38 EET 2007  kai.vehmanen@nokia.com
  * nua: Ignore CANCEL of incoming request if we have already sent a final response, part 2.

Tue Feb 13 01:02:00 EET 2007  kai.vehmanen@nokia.com
  * nua: Ignore CANCEL of incoming request if we have already sent a final response. Problem reported by Mike Jerris.

nua: fixed problems in state machines.
Pekka Pessi <first.lastname@nokia.com>**20070214201847
   
 These problems are mostly introduced in 1.12.5:
 - assert failed if INVITE was received when calling
   (reported by Michael Jerris)
 - assert failed if nua_invite() was called second time before receiving
   final response to first INVITE (ditto)
 - ACK is now always sent if session was terminated after receiving 2XX
 - if nua has a pending incoming INVITE request, return 491 to
   nua_invite()

nta.c: return 481 response to CANCEL if INVITE transaction was successful
According to the RFC 3261 state diagram the INVITE transaction is destroyed
 immediately when a final 2XX series response is sent. Now nta returns a 481
 response to CANCEL if it finds an INVITE server transaction that was
 responded with a 2XX success response.




git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@4284 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Michael Jerris 2007-02-15 05:18:37 +00:00
parent 6e12fb32cd
commit 85c8bcd5ed
17 changed files with 1053 additions and 289 deletions

View File

@ -1 +1 @@
Tue Feb 13 11:08:00 EST 2007
Thu Feb 15 00:08:41 EST 2007

View File

@ -5,135 +5,69 @@ Release notes for current version of Sofia-SIP
Changes since last release
--------------------------
Support for request queuing has been added to the high-level user-agent
API. Various portability improvements have been made related to Linux,
Mac OS X, Windows and Solaris ports. SIP registrations have been
modified to utilize persistent transport connections. The GObject
interface to 'nua' (nua-glib) has been dropped from the package and is
now distributed separately. Severe bugs in 'su', 'nua', 'nta', 'stun' and
'su-glib' modules have been fixed.
<changes since last written in freshmeat.net "Changes:" style;
and in less than 10 lines, written in 3rd person English, with
complete sentences />
Bugs in blaa and foo have been fixed. The stack now supports
use of foobar...
API/ABI changes and versioning
------------------------------
New features in API are marked with Doxytag macro @NEW_1_12_5.
Experimental features are marked with Doxytag macro @EXP_1_12_5.
<see previous release notes at
http://sofia-sip.sourceforge.net/relnotes/ for examples ;
- should include all changes to public headers, and
other important information to developers;
- and should be updated _continuously_! />
**template**: New features in API are marked with Doxytag macro @NEW_1_XX_X.
**template**: New features in API are marked with Doxytag macro @VERSION_1_XX_X.
libsofia-sip-ua:
- **template**: Added foobar() function (sofia-sip/foobar.h).
- Added nua tags NUTAG_APPL_METHOD() and NUTAG_NEWSUB()
- Added nua_substate_make(), nua_substate_name()
- Added nta_incoming_create_response() function
- Added tport tags TPTAG_TOS(), TPTAG_DUMP(), TPTAG_LOG()
- Added tport predicate function tport_is_connected()
- Added authentication functions auc_info() and auc_has_authorization(),
added type msg_auth_info_t for Authentication-Info header
- Added msg_header_join_items() function
- Added sip_is_allowed() function and k_bitmap field to the
sip_allow_t structure
- Added sl_header_log implementation, updated its prototype
- Added experimental SIP headers Suppress-Notify-If-Match and
Suppress-Body-If-Match and functions related to them,
enabled with ./configure option --enable-experimental
- Added SIP header Refer-Sub and related functions
- Added <sofia-sip/sip_extra.h> include file
- Added auc_info() function (sofia-sip/auth_client.h)
- Added alternative implementations to event reactor object (su_port_t,
referenced by su_root_t) that can be changed at runtime using SU_PORT
environment variable, for instance
- Internal semantics of su_port_t reference counting have changed:
now su_port_create() has one reference, and su_root_create_with_port()
always consumes one reference
- Changed return type of bm_memmem() and bm_memcasemem() to non-const
- This release is ABI/API compatible with applications linked against
any 1.12.x release. However, applications built against this release won't
work against an older library. The ABI has been tested with the nua module
unit test (test_nua) built against original 1.12.0 release.
libsofia-sip-ua-glib:
- The 'nua-glib' module has been removed from the library and moved
to a separate package 'sofia-nua-glib'. The remaining library (su-glib)
is now considered stable and will be API/ABI compatible with later
releases in the 1.12.x series.
- ABI has been modified and applications built against 1.12.4 and earlier
releases, need to be rebuilt.
- Added su_glib_prefer_gsource() which makes glib-based su_port_t
implementation the default choice when su_root_create() is called
- No ABI/API changes, compatible with 1.12.0. Note, libsofia-sip-ua-glib
interface is not considered stable and may change in a future 1.12.x
release.
Contributors to this release
----------------------------
- Petteri Puolakka (patch to stun)
- Mikhail Zabaluev (patch to su-glib mainloop integration)
- Michael Jerris (patch to url parsing # in sip/sips userpart)
- Colin Whittaker (TPTAG_TOS())
- Roman Filonenko (TPTAG_LOG(), TPTAG_DUMP(),
patch to query DNS-servers with IP-Helper on win32)
- Remi Denis-Courmont (patch to network change API)
- Martti Mela (poll() emulation with select(), IPv6 and OS X fixes)
- Kai Vehmanen (persistent registrations, release management)
- Pekka Pessi (all the rest)
<list of people who contributed to _this_ release
- update as people's patches are added, or when you commit stuff
- current development team members (see AUTHORS) may be omitted,
or listed at the end of the contribur list (depending on the scope
of the work done since the last release)
- name of the contributor should be enough (email addresses in AUTHORS),
plus a _brief_ description of what was contributed
- roughly sorted by number of patches accepted
/>
- **template**: First Surname (patch to nua/soa/msg)
See the AUTHORS file in the distribution package.
Notes on new features
---------------------
- The su_root_t reactor uses different implementation (epoll, poll or select
on Linux), depending on SU_PORT environment variable.
- nua now supports request queuing, for instance, an application can send
overlapping BYE and NOTIFY requests. The stack engine takes care of
sending the new request only after the previous one has been responded.
- RFC 4488 defines the Refer-Sub header. Its datatypes, related functions and
methods declared in <sofia-sip/sip_extra.h> include file. The Refer-Sub
header structure can be accessed from sip_t structure with sip_refer_sub()
method, e.g.,
if (sip_refer_sub(sip) &&
strcasecmp("false", sip_refer_sub(sip)->rs_value) == 0) {
/* Do not create implicit subscription */
}
- Unsolicited NOTIFYs are now supported. The application can accept incoming
NOTIFYs and send NOTIFYs without existing subscription.
- Transport connections used for client registrations are now maintained
in a persistent fashion. For example a TCP connection used for
registration is not closed until client is unregisters, or the whole stack
is shut down.
- New build time options have been added: ability to build without
STUN and HTTP support. See 'docs/devel_platform_notes.txt' for some
additional notes to distributors.
<information about major new features
- new/changed/removed functionality
- links to further documentation
- section may be omitted for minor releases
/>
Bugs fixed in this release
--------------------------
- Fixed su_from_create() returning a sip_to_t instance. Problem reported by
Ludovico Cavedon.
- Partially fixed problem #1633969 with too frequent timer
- Fixed problem in dialog matching. Problem reported by Fabio Margarido.
- Fixed #1624446 - su_wait_clone() (and nua_destroy()) blocking for ever if
the root object was created using su_glib
- Fixed #1626330 - leak in nta.c which happened if INVITE never got a final
response nor timed out.
- Fixed handle leak (pthread_*_init without pthread_*_destroy). Problem
reported by Maxim Zaikin.
- Fixed crash when nua_bye() was called while a NOTIFY client transaction
was in progress. Problem reported by Anthony Minnessale.
- Not using close() with sockets in sres.c. Problem reported by
Roman Filonenko.
- Bug in zero-padding STUN messages with a message integrity
attribute. Patch by Petteri Puolakka.
- Fixed a severe problem with timer accuracy, when sofia-sip timers
where used under glib's mainloop.
- Improved glib mainloop integration to avoid warnings about already
active mainloop context, and potentially other issue. Patch by
Mikhail Zabaluev. Closes sf.net item #1606786.
- Ignore harmless syntax errors in incoming requests/responses by default
- If a TCP connection cannot be made within SIP T4 (because of a
firewall, for instance) nta now tries to use UDP instead.
< notable bugs fixed in this release
- check the sf.net bug tracker; see closed bugs,
sorted by closing date
- other bugs as fixed in CVS/darcs
/>
- **template**: #9499652 sf.net bug item title

View File

@ -11,7 +11,7 @@ dnl information on the package
dnl ---------------------------
dnl update both the version for AC_INIT and the LIBSOFIA_SIP_UA_MAJOR_MINOR
AC_INIT([sofia-sip], [1.12.5])
AC_INIT([sofia-sip], [1.12.5work])
AC_CONFIG_SRCDIR([libsofia-sip-ua/sip/sofia-sip/sip.h])
AC_SUBST(VER_LIBSOFIA_SIP_UA_MAJOR_MINOR, [1.12])
dnl Includedir specific to this sofia version

View File

@ -61,7 +61,8 @@ Making the release tarball
sh> cvs tag rel-sofia-sip-x_y_z
- take a fresh checkout of the release using the release tag
sh> darcs get http://sofia-sip.org/repos/sofia-sip --tag=rel-sofia-sip-1_yy_z
- create the release tarball with "make distcheck"
- create the release tarball with "make distcheck" (make sure depcomp et
al libtool scripts are correctly created)
- calculate md5 and sha1 hashes using md5sum and sha1sum utilities,
and copy the values to the release-notes (see below)

View File

@ -5056,10 +5056,16 @@ nta_incoming_t *incoming_find(nta_agent_t const *agent,
if (irq->irq_method == rq->rq_method)
break; /* found */
if (return_ack && rq->rq_method == sip_method_cancel)
if (!return_ack)
continue;
if (irq->irq_method == sip_method_invite) {
if (rq->rq_method == sip_method_cancel)
*return_ack = irq;
else if (return_ack && rq->rq_method == sip_method_ack &&
irq->irq_method == sip_method_invite)
else if (rq->rq_method == sip_method_ack)
*return_ack = irq;
}
else if (rq->rq_method == sip_method_cancel && !irq->irq_terminated)
*return_ack = irq;
}
@ -5179,10 +5185,18 @@ int incoming_cancel(nta_incoming_t *irq, msg_t *msg, sip_t *sip,
nta_agent_t *agent = irq->irq_agent;
/* Respond to the CANCEL */
if (200 <= irq->irq_status && irq->irq_status < 300) {
nta_msg_treply(agent, msg_ref_create(msg), SIP_481_NO_TRANSACTION,
NTATAG_TPORT(tport),
TAG_END());
}
else
nta_msg_treply(agent, msg_ref_create(msg), SIP_200_OK,
NTATAG_TPORT(tport),
TAG_END());
/* We have already sent final response */
if (irq->irq_completed || irq->irq_method != sip_method_invite) {
msg_destroy(msg);
return 0;

View File

@ -345,6 +345,15 @@ void nua_dialog_usage_remove_at(nua_owner_t *own,
nua_client_request_t *cr, *cr_next;
nua_server_request_t *sr, *sr_next;
*at = du->du_next;
o = du->du_event;
SU_DEBUG_5(("nua(%p): removing %s usage%s%s\n",
(void *)own, nua_dialog_usage_name(du),
o ? " with event " : "", o ? o->o_type :""));
du->du_class->usage_remove(own, ds, du);
/* Destroy saved client request */
if (nua_client_is_bound(du->du_cr)) {
nua_client_bind(cr = du->du_cr, NULL);
@ -366,26 +375,20 @@ void nua_dialog_usage_remove_at(nua_owner_t *own,
nua_server_request_destroy(sr);
}
*at = du->du_next;
o = du->du_event;
SU_DEBUG_5(("nua(%p): removing %s usage%s%s\n",
(void *)own, nua_dialog_usage_name(du),
o ? " with event " : "", o ? o->o_type :""));
du->du_class->usage_remove(own, ds, du);
su_home_unref(own);
su_free(own, du);
}
/* Zap dialog if there are no more usages */
if (ds->ds_usage == NULL) {
if (ds->ds_terminated)
;
else if (ds->ds_usage == NULL) {
nua_dialog_remove(own, ds, NULL);
ds->ds_has_events = 0;
ds->ds_terminated = 0;
return;
}
else if (!ds->ds_terminated) {
else {
nua_dialog_log_usage(own, ds);
}
}
@ -561,6 +564,8 @@ int nua_dialog_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
{
nua_dialog_usage_t *du;
ds->ds_terminated = 1;
do {
for (du = ds->ds_usage; du; du = du->du_next) {
if (!du->du_shutdown) {

View File

@ -115,6 +115,8 @@ struct nua_server_request {
unsigned sr_terminating:1; /**< Terminate usage after final response */
unsigned sr_gracefully:1; /**< Terminate usage gracefully */
unsigned sr_neutral:1; /**< No effect on session or other usage */
/* Flags used with offer-answer */
unsigned sr_offer_recv:1; /**< We have received an offer */
unsigned sr_answer_sent:2; /**< We have answered (reliably, if >1) */
@ -284,6 +286,9 @@ struct nua_client_request
unsigned cr_offer_recv:1; /**< Recv offer in a response */
unsigned cr_answer_sent:1; /**< Sent answer in (PR)ACK */
/* Flags with usage */
unsigned cr_neutral:1; /**< No effect on session or other usage */
/* Lifelong flags? */
unsigned cr_auto:1; /**< Request was generated by stack */
unsigned cr_has_contact:1; /**< Request has user Contact */
@ -318,6 +323,7 @@ struct nua_dialog_state
unsigned ds_has_register:1; /**< We have registration */
unsigned ds_has_publish:1; /**< We have publish */
unsigned ds_got_session:1; /**< We have (or have had) session */
unsigned ds_got_referrals:1; /**< We have (or have had) referrals */
unsigned :0;
@ -573,6 +579,10 @@ int nua_client_report(nua_client_request_t *cr,
nua_client_request_t *nua_client_request_pending(nua_client_request_t const *);
int nua_client_init_requests(nua_client_request_t *cr,
void const *cr0,
int invite);
/* ---------------------------------------------------------------------- */
extern nua_server_methods_t const
@ -590,6 +600,7 @@ extern nua_server_methods_t const
nua_refer_server_methods, /**< REFER */
nua_publish_server_methods; /**< PUBLISH */
/** Return true if we have not sent final response to request */
static inline
int nua_server_request_is_pending(nua_server_request_t const *sr)
{

View File

@ -177,6 +177,9 @@ static int nua_session_usage_shutdown(nua_owner_t *,
nua_dialog_state_t *,
nua_dialog_usage_t *);
static int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags);
static int nua_invite_client_deinit(nua_client_request_t *cr);
static nua_usage_class const nua_session_usage[1] = {
{
sizeof (nua_session_usage_t),
@ -202,6 +205,7 @@ int nua_session_usage_add(nua_handle_t *nh,
if (ds->ds_has_session)
return -1;
ds->ds_has_session = 1;
ds->ds_got_session = 1;
return 0;
}
@ -212,10 +216,44 @@ void nua_session_usage_remove(nua_handle_t *nh,
nua_dialog_usage_t *du)
{
nua_session_usage_t *ss = nua_dialog_usage_private(du);
nua_client_request_t *cr, *cr_next;
cr = du->du_cr;
if (cr && cr->cr_orq && cr->cr_status >= 200) {
ss->ss_reporting = 1;
nua_invite_client_ack(cr, NULL);
ss->ss_reporting = 0;
}
/* Destroy queued INVITE transactions */
for (cr = ds->ds_cr; cr; cr = cr_next) {
cr_next = cr->cr_next;
if (cr->cr_method != sip_method_invite)
continue;
if (cr == du->du_cr)
continue;
nua_stack_event(nh->nh_nua, nh,
NULL,
cr->cr_event,
SIP_481_NO_TRANSACTION,
NULL);
nua_client_request_destroy(cr);
cr_next = ds->ds_cr;
}
ds->ds_has_session = 0;
nh->nh_has_invite = 0;
nh->nh_active_call = 0;
nh->nh_hold_remote = 0;
(void)ss;
if (nh->nh_soa)
soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
}
static
@ -228,7 +266,7 @@ nua_dialog_usage_t *nua_dialog_usage_for_session(nua_dialog_state_t const *ds)
}
static
nua_session_usage_t *nua_session_usage_get(nua_dialog_state_t const *ds)
nua_session_usage_t *nua_session_usage_for_dialog(nua_dialog_state_t const *ds)
{
nua_dialog_usage_t *du;
@ -245,13 +283,6 @@ static
void nua_session_usage_destroy(nua_handle_t *nh,
nua_session_usage_t *ss)
{
nh->nh_has_invite = 0;
nh->nh_active_call = 0;
nh->nh_hold_remote = 0;
if (nh->nh_soa)
soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
/* Remove usage */
nua_dialog_usage_remove(nh, nh->nh_ds, nua_dialog_usage_public(ss));
@ -482,13 +513,6 @@ static int nua_invite_client_report(nua_client_request_t *cr,
nta_outgoing_t *orq,
tagi_t const *tags);
static int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags);
static int nua_invite_client_ack_msg(nua_client_request_t *cr,
msg_t *msg, sip_t *sip,
tagi_t const *tags);
static int nua_invite_client_deinit(nua_client_request_t *cr);
nua_client_methods_t const nua_invite_client_methods = {
SIP_METHOD_INVITE,
0,
@ -527,6 +551,9 @@ static int nua_invite_client_init(nua_client_request_t *cr,
nua_dialog_usage_t *du;
cr->cr_usage = du = nua_dialog_usage_for_session(nh->nh_ds);
/* Errors returned by nua_invite_client_init()
are neutral to session state */
cr->cr_neutral = 1;
if (nh_is_special(nh) ||
nua_stack_set_handle_special(nh, nh_has_invite, nua_i_error))
@ -534,7 +561,15 @@ static int nua_invite_client_init(nua_client_request_t *cr,
else if (nh_referral_check(nh, tags) < 0)
return nua_client_return(cr, 900, "Invalid referral", msg);
if (!du)
if (du) {
nua_server_request_t *sr;
for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next)
/* INVITE in progress? */
if (sr->sr_usage == du && sr->sr_method == sip_method_invite &&
nua_server_request_is_pending(sr))
return nua_client_return(cr, SIP_491_REQUEST_PENDING, msg);
}
else
du = nua_dialog_usage_add(nh, nh->nh_ds, nua_session_usage, NULL);
if (!du)
return -1;
@ -547,6 +582,8 @@ static int nua_invite_client_init(nua_client_request_t *cr,
NH_PGET(nh, min_se),
NH_PGET(nh, refresher));
cr->cr_neutral = 0;
return 0;
}
@ -568,7 +605,7 @@ static int nua_invite_client_request(nua_client_request_t *cr,
invite_timeout = NH_PGET(nh, invite_timeout);
if (invite_timeout == 0)
invite_timeout = UINT_MAX;
/* Cancel if we don't get response within timeout*/
/* Send CANCEL if we don't get response within timeout*/
nua_dialog_usage_set_expires(du, invite_timeout);
nua_dialog_usage_set_refresh(du, 0);
@ -808,12 +845,20 @@ static int nua_invite_client_report(nua_client_request_t *cr,
if (orq != cr->cr_orq && status != 100)
return 1;
if (ss == NULL) {
signal_call_state_change(nh, ss, status, phrase, nua_callstate_terminated);
return 1;
}
ss->ss_reporting = 1;
if (ss == NULL) {
next_state = nua_callstate_terminated;
if (cr->cr_neutral) {
signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
ss->ss_reporting = 0;
return 1;
}
else if (status == 100) {
if (status == 100) {
next_state = nua_callstate_calling;
}
else if (status < 300 && cr->cr_graceful) {
@ -927,11 +972,10 @@ int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
{
nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds);
nua_session_usage_t *ss = nua_dialog_usage_private(du);
nua_client_request_t *cr = du ? du->du_cr : NULL;
int error;
if (!du ||
!du->du_cr ||
du->du_cr->cr_orq == NULL ||
du->du_cr->cr_status < 200) {
if (!cr || cr->cr_orq == NULL || cr->cr_status < 200) {
UA_EVENT2(nua_i_error, 900, "No response to ACK");
return 1;
}
@ -942,8 +986,9 @@ int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
soa_set_params(nh->nh_soa, TAG_NEXT(tags));
}
if (nua_invite_client_ack(du->du_cr, tags) < 0) {
int error;
error = nua_invite_client_ack(cr, tags);
if (error < 0) {
ss->ss_reason = "SIP;cause=500;text=\"Internal Error\"";
ss->ss_reporting = 1; /* We report state here if BYE fails */
error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL);
@ -953,6 +998,12 @@ int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
? nua_callstate_terminated
: nua_callstate_terminating);
}
else {
if (!nua_client_is_queued(cr) && !nua_client_is_bound(cr))
nua_client_request_destroy(cr);
nua_client_init_requests(nh->nh_ds->ds_cr, cr, 1);
}
return 0;
}
@ -967,6 +1018,7 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
{
nua_handle_t *nh = cr->cr_owner;
nua_dialog_state_t *ds = nh->nh_ds;
nua_session_usage_t *ss = nua_dialog_usage_private(cr->cr_usage);
msg_t *msg;
sip_t *sip;
@ -974,6 +1026,9 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
sip_authorization_t *wa;
sip_proxy_authorization_t *pa;
sip_cseq_t *cseq;
nta_outgoing_t *ack;
int status = 200;
char const *phrase = "OK", *reason = NULL;
assert(ds->ds_leg);
assert(cr->cr_orq);
@ -1009,35 +1064,7 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
;
else if (nta_msg_request_complete(msg, ds->ds_leg, SIP_METHOD_ACK, NULL) < 0)
;
else
error = nua_invite_client_ack_msg(cr, msg, sip, tags);
nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
if (error == -1)
msg_destroy(msg);
return error;
}
/** Send ACK, destroy INVITE transaction.
*
* @retval 1 if successful
* @retval -2 if an error occurred
*/
static
int nua_invite_client_ack_msg(nua_client_request_t *cr,
msg_t *msg, sip_t *sip,
tagi_t const *tags)
{
nua_handle_t *nh = cr->cr_owner;
nua_dialog_usage_t *du = cr->cr_usage;
nua_session_usage_t *ss = nua_dialog_usage_private(du);
nta_outgoing_t *ack;
int status = 200;
char const *phrase = "OK", *reason = NULL;
else {
/* Remove extra headers */
while (sip->sip_allow)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_allow);
@ -1052,7 +1079,7 @@ int nua_invite_client_ack_msg(nua_client_request_t *cr,
while (sip->sip_supported)
sip_header_remove(msg, sip, (sip_header_t*)sip->sip_supported);
if (!nh->nh_soa)
if (!nh->nh_soa || ss == NULL)
;
else if (cr->cr_offer_recv && !cr->cr_answer_sent) {
if (soa_generate_answer(nh->nh_soa, NULL) < 0 ||
@ -1088,15 +1115,24 @@ int nua_invite_client_ack_msg(nua_client_request_t *cr,
reason = "SIP;cause=500;text=\"Internal Error\"";
}
if (ss) {
if (reason)
if (ss && reason)
ss->ss_reason = reason;
if (!ss->ss_reporting && status < 300)
if (status < 300)
error = 1;
else
error = -2;
}
nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
nua_client_request_remove(cr);
if (ss) {
if (!ss->ss_reporting && error >= 0)
signal_call_state_change(nh, ss, status, phrase, nua_callstate_ready);
}
return status < 300 ? 1 : -2;
return error;
}
/** Deinitialize client request */
@ -1220,7 +1256,7 @@ static void nua_session_usage_refresh(nua_handle_t *nh,
if (cr->cr_method == sip_method_update)
return;
/* INVITE or UPDATE in progress or being authenticated */
/* INVITE or UPDATE in progress */
for (sr = ds->ds_sr; sr; sr = sr->sr_next)
if (sr->sr_usage == du &&
(sr->sr_method == sip_method_invite ||
@ -1258,7 +1294,7 @@ static int nua_session_usage_shutdown(nua_handle_t *nh,
nua_server_request_t *sr, *sr_next;
nua_client_request_t *cri;
assert(ss == nua_session_usage_get(nh->nh_ds));
assert(ss == nua_session_usage_for_dialog(nh->nh_ds));
/* Zap server-side transactions */
for (sr = ds->ds_sr; sr; sr = sr_next) {
@ -1663,6 +1699,8 @@ nua_invite_server_init(nua_server_request_t *sr)
nua_handle_t *nh = sr->sr_owner;
nua_t *nua = nh->nh_nua;
sr->sr_neutral = 1;
if (!NUA_PGET(nua, nh, invite_enable))
return SR_STATUS1(sr, SIP_403_FORBIDDEN);
@ -1688,9 +1726,10 @@ nua_invite_server_init(nua_server_request_t *sr)
break;
}
if (sr0)
if (sr0) {
/* Overlapping invites - RFC 3261 14.2 */
return nua_server_retry_after(sr, 500, "Overlapping Requests", 0, 10);
}
for (cr = nh->nh_ds->ds_cr; cr; cr = cr->cr_next) {
if (cr->cr_usage == sr->sr_usage && cr->cr_orq && cr->cr_offer_sent)
@ -1699,6 +1738,8 @@ nua_invite_server_init(nua_server_request_t *sr)
}
}
sr->sr_neutral = 0;
return 0;
}
@ -2024,6 +2065,7 @@ int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
nua_dialog_usage_t *du = sr->sr_usage;
nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
int initial = sr->sr_initial && !sr->sr_event;
int neutral = sr->sr_neutral;
int application = sr->sr_application;
int status = sr->sr_status; char const *phrase = sr->sr_phrase;
int retval;
@ -2036,18 +2078,18 @@ int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
if (retval >= 2 || ss == NULL) {
/* Session has been terminated. */
if (!initial)
if (!initial && !neutral)
signal_call_state_change(nh, NULL, status, phrase,
nua_callstate_terminated);
return retval;
}
assert(ss);
assert(ss->ss_state != nua_callstate_calling);
assert(ss->ss_state != nua_callstate_proceeding);
/* Update session state */
if (status < 300 || application != 0)
if (status < 300 || application != 0) {
assert(ss->ss_state != nua_callstate_calling);
assert(ss->ss_state != nua_callstate_proceeding);
signal_call_state_change(nh, ss, status, phrase,
status >= 300
? nua_callstate_init
@ -2056,6 +2098,7 @@ int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
: status > 100
? nua_callstate_early
: nua_callstate_received);
}
if (status == 180)
ss->ss_alerting = 1;
@ -2065,7 +2108,7 @@ int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
if (200 <= status && status < 300) {
du->du_ready = 1;
}
else if (300 <= status) {
else if (300 <= status && !neutral) {
if (nh->nh_soa)
soa_init_offer_answer(nh->nh_soa);
}
@ -2196,10 +2239,14 @@ int process_cancel(nua_server_request_t *sr,
{
nua_handle_t *nh = sr->sr_owner;
nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
assert(ss); assert(ss == nua_session_usage_for_dialog(nh->nh_ds)); (void)ss;
if (nua_server_request_is_pending(sr)) {
msg_t *cancel = nta_incoming_getrequest_ackcancel(irq);
if (nta_incoming_status(irq) < 200 && nua_server_request_is_pending(sr) &&
ss && (ss == nua_session_usage_get(nh->nh_ds))) {
assert(nta_incoming_status(irq) < 200);
nua_stack_event(nh->nh_nua, nh, cancel, nua_i_cancel, SIP_200_OK, NULL);
SR_STATUS1(sr, SIP_487_REQUEST_TERMINATED);
@ -2222,7 +2269,7 @@ int process_timeout(nua_server_request_t *sr,
char const *reason = "SIP;cause=408;text=\"ACK Timeout\"";
int error;
assert(ss); assert(ss == nua_session_usage_get(nh->nh_ds));
assert(ss); assert(ss == nua_session_usage_for_dialog(nh->nh_ds));
if (nua_server_request_is_pending(sr)) {
phrase = "PRACK Timeout";
@ -3328,7 +3375,7 @@ nua_client_methods_t const nua_bye_client_methods = {
int
nua_stack_bye(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags)
{
nua_session_usage_t *ss = nua_session_usage_get(nh->nh_ds);
nua_session_usage_t *ss = nua_session_usage_for_dialog(nh->nh_ds);
if (ss &&
nua_callstate_calling <= ss->ss_state &&
@ -3430,8 +3477,10 @@ static int nua_bye_client_report(nua_client_request_t *cr,
signal_call_state_change(nh, ss, status, "to BYE",
nua_callstate_terminated);
if (ss && !ss->ss_reporting && !nua_client_is_queued(du->du_cr)) {
/* Do not destroy session usage while INVITE is alive */
if (ss && !ss->ss_reporting) {
if (nua_client_is_queued(du->du_cr) && du->du_cr->cr_status < 200)
/* No final response to INVITE received yet */;
else
nua_session_usage_destroy(nh, ss);
}
}

View File

@ -743,14 +743,14 @@ void nh_destroy(nua_t *nua, nua_handle_t *nh)
if (nh->nh_notifier)
nea_server_destroy(nh->nh_notifier), nh->nh_notifier = NULL;
nua_dialog_deinit(nh, nh->nh_ds);
while (nh->nh_ds->ds_cr)
nua_client_request_destroy(nh->nh_ds->ds_cr);
while (nh->nh_ds->ds_sr)
nua_server_request_destroy(nh->nh_ds->ds_sr);
nua_dialog_deinit(nh, nh->nh_ds);
if (nh->nh_soa)
soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
@ -1090,6 +1090,9 @@ int nua_stack_process_request(nua_handle_t *nh,
nta_incoming_tag(irq, NULL);
if (method == sip_method_cancel)
return 481;
/* Hook to outbound */
if (method == sip_method_options) {
status = nua_registration_process_request(nua->nua_registrations,
@ -1485,6 +1488,8 @@ int nua_server_respond(nua_server_request_t *sr, tagi_t const *tags)
else if (next.msg)
msg_destroy(next.msg);
assert(sr->sr_status >= 200 || sr->sr_response.msg);
return retval;
}
@ -1521,7 +1526,7 @@ int nua_server_report(nua_server_request_t *sr)
if (sr)
return sr->sr_methods->sm_report(sr, NULL);
else
return 2;
return 1;
}
int nua_base_server_treport(nua_server_request_t *sr,
@ -1763,8 +1768,11 @@ inline int nua_client_request_queue(nua_client_request_t *cr)
}
}
else {
while (*queue)
while (*queue) {
queue = &(*queue)->cr_next;
if (cr->cr_method == sip_method_invite)
queued = 1;
}
}
if ((cr->cr_next = *queue))
@ -2543,7 +2551,7 @@ int nua_base_client_response(nua_client_request_t *cr,
tagi_t const *tags)
{
nua_handle_t *nh = cr->cr_owner;
int next;
sip_method_t method = cr->cr_method;
cr->cr_reporting = 1;
@ -2552,14 +2560,15 @@ int nua_base_client_response(nua_client_request_t *cr,
nua_client_report(cr, status, phrase, sip, cr->cr_orq, tags);
if (status >= 200)
nua_client_request_remove(cr);
if (cr->cr_method == sip_method_invite ? status < 300 : status < 200) {
if (status < 200 ||
/* Un-ACKed 2XX response to INVITE */
(cr->cr_method == sip_method_invite && status < 300 && cr->cr_orq)) {
cr->cr_reporting = 0;
return 1;
}
nua_client_request_remove(cr);
if (cr->cr_orq)
nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
@ -2585,16 +2594,12 @@ int nua_base_client_response(nua_client_request_t *cr,
if (nua_client_is_queued(cr))
return 1;
next = cr->cr_method != sip_method_invite && cr->cr_method != sip_method_cancel;
if (!nua_client_is_bound(cr))
nua_client_request_destroy(cr);
if (next && nh->nh_ds->ds_cr != NULL && nh->nh_ds->ds_cr != cr) {
cr = nh->nh_ds->ds_cr;
if (cr->cr_method != sip_method_invite && cr->cr_method != sip_method_cancel)
nua_client_init_request(cr);
}
if (method != sip_method_cancel)
return nua_client_init_requests(nh->nh_ds->ds_cr, cr,
method == sip_method_invite);
return 1;
}
@ -2638,6 +2643,29 @@ int nua_client_treport(nua_client_request_t *cr,
return retval;
}
int nua_client_init_requests(nua_client_request_t *cr,
void const *cr0,
int invite)
{
if (cr0 == cr) /* already initialized! */
return 1;
for (; cr; cr = cr->cr_next) {
if (cr->cr_method == sip_method_cancel)
continue;
if (invite
? cr->cr_method == sip_method_invite
: cr->cr_method != sip_method_invite)
break;
}
if (cr)
nua_client_init_request(cr);
return 1;
}
nua_client_request_t *
nua_client_request_pending(nua_client_request_t const *cr)
{

View File

@ -1578,6 +1578,310 @@ int test_update_by_uas(struct context *ctx)
END();
}
int cancel_when_pracked(CONDITION_PARAMS);
int alert_call(CONDITION_PARAMS);
int test_180rel_cancel1(struct context *ctx)
{
BEGIN();
struct endpoint *a = &ctx->a, *b = &ctx->b;
struct call *a_call = a->call, *b_call = b->call;
struct event *e;
if (print_headings)
printf("TEST NUA-10.6: CANCEL after PRACK\n");
/* Test for 100rel:
A B
|-------INVITE------>|
|<----100 Trying-----|
| |
|<-------180---------|
|-------PRACK------->|
|<-------200---------|
| |
|------CANCEL------->|
|<------200 OK-------|
| |
|<-------487---------|
|--------ACK-------->|
| |
*/
a_call->sdp = "m=audio 5008 RTP/AVP 8";
b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
nua_set_params(ctx->a.nua,
NUTAG_EARLY_MEDIA(1),
NUTAG_ONLY183_100REL(0),
TAG_END());
run_a_until(ctx, nua_r_set_params, until_final_response);
nua_set_params(ctx->b.nua,
NUTAG_EARLY_MEDIA(1),
NUTAG_ONLY183_100REL(0),
TAG_END());
run_b_until(ctx, nua_r_set_params, until_final_response);
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
INVITE(a, a_call, a_call->nh,
TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
SOATAG_USER_SDP_STR(a_call->sdp),
TAG_END());
run_ab_until(ctx, -1, cancel_when_pracked, -1, alert_call);
/* Client transitions:
INIT -(C1)-> CALLING: nua_invite(), nua_i_state
CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
PROCEEDING -(C3+C4)-> TERMINATED: nua_r_invite, nua_i_state
*/
TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
TEST_1(is_offer_sent(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 180);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
TEST_1(is_answer_recv(e->data->e_tags));
TEST_1(!is_offer_sent(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_cancel);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
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);
TEST_1(!e->next);
free_events_in_list(ctx, a->events);
/*
Server transitions:
INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
Option A:
EARLY -(S10)-> TERMINATED: nua_i_cancel, nua_i_state
Option B:
EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
*/
TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
TEST(e->data->e_status, 100);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
TEST_1(is_offer_recv(e->data->e_tags));
/* Responded with 180 Ringing */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
TEST_1(is_answer_sent(e->data->e_tags));
/* 180 is PRACKed */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
/* Does not have effect on call state */
TEST_1(e = e->next); 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 */
free_events_in_list(ctx, b->events);
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
if (print_headings)
printf("TEST NUA-10.6: PASSED\n");
END();
}
int cancel_when_pracked(CONDITION_PARAMS)
{
if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
return 0;
save_event_in_list(ctx, event, ep, call);
if (event == nua_r_prack)
CANCEL(ep, call, nh, TAG_END());
switch (callstate(tags)) {
case nua_callstate_proceeding:
return 0;
case nua_callstate_ready:
return 1;
case nua_callstate_terminated:
return 1;
default:
return 0;
}
}
int test_180rel_cancel2(struct context *ctx)
{
BEGIN();
struct endpoint *a = &ctx->a, *b = &ctx->b;
struct call *a_call = a->call, *b_call = b->call;
struct event *e;
if (print_headings)
printf("TEST NUA-10.7: CANCEL after 100rel 180\n");
/* Test for 100rel:
A B
|-------INVITE------>|
|<----100 Trying-----|
| |
|<-------180---------|
|-------PRACK------->|
|<-------200---------|
| |
|------CANCEL------->|
|<------200 OK-------|
| |
|<-------487---------|
|--------ACK-------->|
| |
*/
a_call->sdp = "m=audio 5008 RTP/AVP 8";
b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
nua_set_params(ctx->a.nua,
NUTAG_EARLY_MEDIA(1),
NUTAG_ONLY183_100REL(0),
TAG_END());
run_a_until(ctx, nua_r_set_params, until_final_response);
nua_set_params(ctx->b.nua,
NUTAG_EARLY_MEDIA(1),
NUTAG_ONLY183_100REL(0),
TAG_END());
run_b_until(ctx, nua_r_set_params, until_final_response);
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
INVITE(a, a_call, a_call->nh,
TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
SOATAG_USER_SDP_STR(a_call->sdp),
TAG_END());
run_ab_until(ctx, -1, cancel_when_ringing, -1, accept_pracked2);
/* Client transitions:
INIT -(C1)-> CALLING: nua_invite(), nua_i_state
CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
PROCEEDING -(C3+C4)-> TERMINATED: nua_r_invite, nua_i_state
*/
TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
TEST_1(is_offer_sent(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 180);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
TEST_1(is_answer_recv(e->data->e_tags));
TEST_1(!is_offer_sent(e->data->e_tags));
#define NEXT_SKIP_PRACK_CANCEL() \
do { TEST_1(e = e->next); } \
while (e->data->e_event == nua_r_prack || e->data->e_event == nua_r_cancel)
NEXT_SKIP_PRACK_CANCEL();
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);
TEST_1(!e->next);
}
else {
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);
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);
TEST(e->data->e_status, 200);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
TEST_1(!e->next);
}
free_events_in_list(ctx, a->events);
/*
Server transitions:
INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
Option A:
EARLY -(S10)-> TERMINATED: nua_i_cancel, nua_i_state
Option B:
EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
*/
TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
TEST(e->data->e_status, 100);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
TEST_1(is_offer_recv(e->data->e_tags));
/* Responded with 180 Ringing */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
TEST_1(is_answer_sent(e->data->e_tags));
/* 180 is PRACKed */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
/* Does not have effect on call state */
if (e->next->data->e_event == nua_i_cancel) {
TEST_1(e = e->next); 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);
TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
TEST_1(!is_offer_answer_done(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
TEST_1(!is_offer_answer_done(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
}
free_events_in_list(ctx, b->events);
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
if (print_headings)
printf("TEST NUA-10.7: PASSED\n");
END();
}
int test_100rel(struct context *ctx)
{
@ -1589,6 +1893,8 @@ int test_100rel(struct context *ctx)
retval = test_preconditions(ctx); RETURN_ON_SINGLE_FAILURE(retval);
retval = test_preconditions2(ctx); RETURN_ON_SINGLE_FAILURE(retval);
retval = test_update_by_uas(ctx); RETURN_ON_SINGLE_FAILURE(retval);
retval = test_180rel_cancel1(ctx); RETURN_ON_SINGLE_FAILURE(retval);
retval = test_180rel_cancel2(ctx); RETURN_ON_SINGLE_FAILURE(retval);
nua_set_params(ctx->a.nua,
NUTAG_EARLY_MEDIA(0),
@ -1612,3 +1918,4 @@ int test_100rel(struct context *ctx)
return retval;
}

View File

@ -467,7 +467,7 @@ int test_call_hold(struct context *ctx)
*/
if (print_headings)
printf("TEST NUA-7.6: re-INVITE without auto-ack\n");
printf("TEST NUA-7.6.1: re-INVITE without auto-ack\n");
/* Turn off auto-ack */
nua_set_hparams(b_call->nh, NUTAG_AUTOACK(0), TAG_END());
@ -517,7 +517,7 @@ int test_call_hold(struct context *ctx)
free_events_in_list(ctx, a->events);
if (print_headings)
printf("TEST NUA-7.6: PASSED\n");
printf("TEST NUA-7.6.1: PASSED\n");
/* ---------------------------------------------------------------------- */
@ -528,7 +528,7 @@ int test_call_hold(struct context *ctx)
*/
if (print_headings)
printf("TEST NUA-7.6: terminate call\n");
printf("TEST NUA-7.6.2: terminate call\n");
BYE(a, a_call, a_call->nh, TAG_END());
run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
@ -555,7 +555,7 @@ int test_call_hold(struct context *ctx)
free_events_in_list(ctx, b->events);
if (print_headings)
printf("TEST NUA-7.6: PASSED\n");
printf("TEST NUA-7.6.2: PASSED\n");
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
@ -725,9 +725,6 @@ int test_reinvite(struct context *ctx)
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
if (print_headings)
printf("TEST NUA-7.8: PASSED\n");
END();
}
@ -784,6 +781,211 @@ int ringing_until_terminated(CONDITION_PARAMS)
}
}
/* ======================================================================== */
int accept_and_attempt_reinvite(CONDITION_PARAMS);
int until_ready2(CONDITION_PARAMS);
/* test_reinvite2 message sequence looks like this:
A B
| |
|-------INVITE------>|
|<----100 Trying-----|
| |
|<----180 Ringing----|
| |
| /-INVITE-|
| \---900->|
| |
|<--------200--------|
|---------ACK------->|
: :
: queue INVITE :
: :
|-----re-INVITE----->|
|<--------200--------|
|---------ACK------->|
|-----re-INVITE----->|
|<--------200--------|
|---------ACK------->|
: :
: glare :
: :
|-----re-INVITE----->|
|<----re-INVITE------|
|<--------491--------|
|---------491------->|
|---------ACK------->|
|<--------ACK--------|
: :
|---------BYE------->|
|<--------200--------|
*/
int test_reinvite2(struct context *ctx)
{
BEGIN();
struct endpoint *a = &ctx->a, *b = &ctx->b;
struct call *a_call = a->call, *b_call = b->call;
struct event *e;
if (print_headings)
printf("TEST NUA-7.8.1: Test re-INVITE glare\n");
a_call->sdp = "m=audio 5008 RTP/AVP 0 8\n";
b_call->sdp = "m=audio 5010 RTP/AVP 8\n";
TEST_1(a_call->nh =
nua_handle(a->nua, a_call,
SIPTAG_FROM_STR("Alice <sip:alice@example.com>"),
SIPTAG_TO(b->to),
TAG_END()));
INVITE(a, a_call, a_call->nh,
TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
SOATAG_USER_SDP_STR(a_call->sdp),
TAG_END());
run_ab_until(ctx, -1, until_ready, -1, accept_and_attempt_reinvite);
TEST_1(nua_handle_has_active_call(a_call->nh));
TEST_1(nua_handle_has_active_call(b_call->nh));
free_events_in_list(ctx, a->events);
free_events_in_list(ctx, b->events);
/* Check that we can queue INVITEs */
INVITE(a, a_call, a_call->nh, TAG_END());
INVITE(a, a_call, a_call->nh, TAG_END());
run_ab_until(ctx, -1, until_ready2, -1, until_ready2);
free_events_in_list(ctx, a->events);
free_events_in_list(ctx, b->events);
/* Check that INVITE glare works */
INVITE(a, a_call, a_call->nh, TAG_END());
INVITE(b, b_call, b_call->nh, TAG_END());
a->flags.n = 0, b->flags.n = 0;
run_ab_until(ctx, -1, until_ready2, -1, until_ready2);
free_events_in_list(ctx, a->events);
free_events_in_list(ctx, b->events);
if (print_headings)
printf("TEST NUA-7.8.1: PASSED\n");
/* ---------------------------------------------------------------------- */
/*
A B
|---------BYE------->|
|<--------200--------|
*/
if (print_headings)
printf("TEST NUA-7.8.2: terminate call\n");
BYE(a, a_call, a_call->nh, TAG_END());
run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
/*
Transitions of A:
READY --(T2)--> TERMINATING: nua_bye()
TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
*/
TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_bye);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
TEST_1(!e->next);
free_events_in_list(ctx, a->events);
/* Transitions of B:
READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
*/
TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_bye);
TEST(e->data->e_status, 200);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
TEST_1(!e->next);
free_events_in_list(ctx, b->events);
if (print_headings)
printf("TEST NUA-7.8.2: PASSED\n");
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
END();
}
int accept_and_attempt_reinvite(CONDITION_PARAMS)
{
if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
return 0;
save_event_in_list(ctx, event, ep, call);
if (event == nua_i_prack) {
INVITE(ep, call, nh, TAG_END());
RESPOND(ep, call, nh, SIP_200_OK,
TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
TAG_END());
}
else switch (callstate(tags)) {
case nua_callstate_received:
RESPOND(ep, call, nh, SIP_180_RINGING,
SIPTAG_REQUIRE_STR("100rel"),
TAG_END());
return 0;
case nua_callstate_early:
return 0;
case nua_callstate_ready:
return 1;
case nua_callstate_terminated:
if (call)
nua_handle_destroy(call->nh), call->nh = NULL;
return 1;
default:
return 0;
}
return 0;
}
/*
X INVITE
| |
|-------INVITE------>|
|<--------200--------|
|---------ACK------->|
*/
int until_ready2(CONDITION_PARAMS)
{
if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
return 0;
save_event_in_list(ctx, event, ep, call);
if (event == nua_r_invite && status == 491) {
if (ep == &ctx->a && ++ctx->b.flags.n >= 2) ctx->b.running = 0;
if (ep == &ctx->b && ++ctx->a.flags.n >= 2) ctx->a.running = 0;
}
switch (callstate(tags)) {
case nua_callstate_ready:
return ++ep->flags.n >= 2;
case nua_callstate_terminated:
if (call)
nua_handle_destroy(call->nh), call->nh = NULL;
return 1;
default:
return 0;
}
}
int test_reinvites(struct context *ctx)
{
int retval = 0;
@ -794,7 +996,10 @@ int test_reinvites(struct context *ctx)
retval = test_call_hold(ctx);
if (retval == 0)
retval |= test_reinvite(ctx);
retval = test_reinvite(ctx);
if (retval == 0)
retval = test_reinvite2(ctx);
if (print_headings && retval == 0)
printf("TEST NUA-7: PASSED\n");

View File

@ -118,6 +118,8 @@ int cancel_when_ringing(CONDITION_PARAMS)
case nua_callstate_proceeding:
CANCEL(ep, call, nh, TAG_END());
return 0;
case nua_callstate_ready:
return 1;
case nua_callstate_terminated:
return 1;
default:
@ -673,6 +675,82 @@ int test_call_destroy_4(struct context *ctx)
END();
}
/* Destroy when one INVITE is queued. */
int test_call_destroy_5(struct context *ctx)
{
BEGIN();
struct endpoint *a = &ctx->a, *b = &ctx->b;
struct call *a_call = a->call, *b_call = b->call;
struct event *e;
if (print_headings)
printf("TEST NUA-5.7: destroy when re-INVITE is queued\n");
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
INVITE(a, a_call, a_call->nh,
TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
NUTAG_AUTOACK(0),
SOATAG_USER_SDP_STR(a_call->sdp),
TAG_END());
INVITE(a, a_call, a_call->nh, TAG_END());
run_ab_until(ctx, -1, until_terminated, -1, destroy_when_completed);
/* Client transitions:
INIT -(C1)-> CALLING: nua_invite(), ...
*/
TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
TEST_1(is_offer_sent(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 180);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
TEST_1(e = e->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_completing); /* COMPLETING */
TEST_1(is_answer_recv(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 481);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
TEST_1(!e->next);
free_events_in_list(ctx, a->events);
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
/*
Server transitions:
INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state ... DESTROY
*/
TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
TEST(e->data->e_status, 100);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
TEST_1(is_offer_recv(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
TEST_1(is_answer_sent(e->data->e_tags));
TEST_1(!e->next);
free_events_in_list(ctx, b->events);
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
if (print_headings)
printf("TEST NUA-5.7: PASSED\n");
END();
}
int test_call_destroy(struct context *ctx)
{
struct endpoint *a = &ctx->a, *b = &ctx->b;
@ -685,10 +763,12 @@ int test_call_destroy(struct context *ctx)
test_call_destroy_1(ctx) ||
test_call_destroy_2(ctx) ||
test_call_destroy_3(ctx) ||
test_call_destroy_4(ctx);
test_call_destroy_4(ctx) ||
test_call_destroy_5(ctx);
}
/* ======================================================================== */
/* Early BYE
A B
@ -731,12 +811,27 @@ int bye_when_ringing(CONDITION_PARAMS)
}
}
int bye_when_completing(CONDITION_PARAMS);
static int ack_sent = 0;
size_t count_acks(void *message, size_t len)
{
if (strncasecmp(message, "ACK sip:", 8) == 0)
ack_sent++;
return len;
}
int test_early_bye(struct context *ctx)
{
BEGIN();
struct endpoint *a = &ctx->a, *b = &ctx->b;
struct call *a_call = a->call, *b_call = b->call;
struct event *e;
struct nat_filter *f = NULL;
a_call->sdp = "m=audio 5008 RTP/AVP 8";
b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
if (print_headings)
printf("TEST NUA-6.1: BYE call when ringing\n");
@ -806,6 +901,119 @@ int test_early_bye(struct context *ctx)
if (print_headings)
printf("TEST NUA-6.1: PASSED\n");
/* Early BYE 2
A B
|-------INVITE------>|
|<----100 Trying-----|
| |
|<----180 Ringing----|
|<-------200---------|
| |
|--------BYE-------->|
|<------200 OK-------|
|--------ACK-------->|
| |
| |
*/
if (print_headings)
printf("TEST NUA-6.2: BYE call when completing\n");
if (ctx->nat)
TEST_1(f = test_nat_add_filter(ctx->nat, count_acks, nat_outbound));
ack_sent = 0;
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
INVITE(a, a_call, a_call->nh,
TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
SOATAG_USER_SDP_STR(a_call->sdp),
NUTAG_AUTOACK(0),
TAG_END());
run_ab_until(ctx, -1, bye_when_completing, -1, accept_until_terminated);
/* Client transitions:
INIT -(C1)-> CALLING: nua_invite(), nua_i_state
CALLING -(C2)-> PROCEEDING: nua_r_invite(180, nua_i_state, nua_cancel()
PROCEEDING -(C6b)-> TERMINATED: nua_r_invite(487), nua_i_state
*/
TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_calling);
TEST_1(is_offer_sent(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 180);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
TEST_1(e = e->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_completing);
TEST_1(e = e->next);
TEST_E(e->data->e_event, nua_r_bye); TEST(e->data->e_status, 200);
TEST_1(e->data->e_msg);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
TEST_1(!e->next);
if (ctx->nat) {
while (ack_sent == 0)
su_root_step(ctx->root, 100);
TEST_1(ack_sent > 0);
TEST_1(test_nat_remove_filter(ctx->nat, f) == 0);
}
/*
Server transitions:
INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
RECEIVED -(S2a)-> EARLY: nua_respond(180), nua_i_state
EARLY -(S6b)--> TERMINATED: nua_i_cancel, nua_i_state
*/
TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
TEST(e->data->e_status, 100);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
TEST_1(is_offer_recv(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_completed); /* EARLY */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
TEST(e->data->e_status, 200);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
TEST_1(!e->next);
free_events_in_list(ctx, a->events);
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
free_events_in_list(ctx, b->events);
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
if (print_headings)
printf("TEST NUA-6.2: PASSED\n");
END();
}
int bye_when_completing(CONDITION_PARAMS)
{
if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
return 0;
save_event_in_list(ctx, event, ep, call);
switch (callstate(tags)) {
case nua_callstate_completing:
ack_sent = 0;
BYE(ep, call, nh, TAG_END());
return 0;
case nua_callstate_terminated:
return 1;
default:
return 0;
}
}

View File

@ -166,12 +166,11 @@ struct context
struct eventlist specials[1];
/* State flags for complex scenarios */
union {
struct {
unsigned n;
unsigned bit0:1, bit1:1, bit2:1, bit3:1;
unsigned bit4:1, bit5:1, bit6:1, bit7:1;
} b;
unsigned n;
unsigned :0;
} flags;
} a, b, c;
@ -215,6 +214,7 @@ int save_until_special(CONDITION_PARAMS);
int until_terminated(CONDITION_PARAMS);
int until_ready(CONDITION_PARAMS);
int accept_call(CONDITION_PARAMS);
int cancel_when_ringing(CONDITION_PARAMS);
int accept_notify(CONDITION_PARAMS);

View File

@ -272,21 +272,21 @@ void run_abc_until(struct context *ctx,
a->last_event = -1;
a->running = a_condition != NULL && a_condition != save_events;
a->running |= a_event != -1;
a->flags.n = 0;
memset(&a->flags, 0, sizeof a->flags);
b->next_event = b_event;
b->next_condition = b_condition;
b->last_event = -1;
b->running = b_condition != NULL && b_condition != save_events;
b->running |= b_event != -1;
b->flags.n = 0;
memset(&b->flags, 0, sizeof b->flags);
c->next_event = c_event;
c->next_condition = c_condition;
c->last_event = -1;
c->running = c_condition != NULL && c_condition != save_events;
c->running |= c_event != -1;
c->flags.n = 0;
memset(&c->flags, 0, sizeof c->flags);
for (; a->running || b->running || c->running;) {
su_root_step(ctx->root, 1000);

View File

@ -228,6 +228,7 @@ test_proxy_init(su_root_t *root, struct proxy *proxy)
URL_STRING_MAKE("sip:0.0.0.0:*"),
NULL, NULL,
NTATAG_UA(0),
NTATAG_CANCEL_487(0),
NTATAG_SERVER_RPORT(1),
NTATAG_CLIENT_RPORT(1),
TAG_NEXT(proxy->tags));

View File

@ -64,15 +64,15 @@ int save_until_notified(CONDITION_PARAMS)
int save_until_notified_and_responded(CONDITION_PARAMS)
{
save_event_in_list(ctx, event, ep, call);
if (event == nua_i_notify) ep->flags.b.bit0 = 1;
if (event == nua_i_notify) ep->flags.bit0 = 1;
if (event == nua_r_subscribe || event == nua_r_unsubscribe) {
if (status >= 300)
return 1;
else if (status >= 200)
ep->flags.b.bit1 = 1;
ep->flags.bit1 = 1;
}
return ep->flags.b.bit0 && ep->flags.b.bit1;
return ep->flags.bit0 && ep->flags.bit1;
}

View File

@ -422,6 +422,7 @@ void su_pthread_port_wait(su_clone_r rclone)
pthread_mutex_unlock(mom->mutex);
pthread_mutex_destroy(mom->mutex);
pthread_cond_destroy(mom->cv);
}
struct su_pthread_port_execute