, #nta_message_f, nta_msg_ackbye(),
* NTATAG_S_TRLESS_RESPONSE_REF().
diff --git a/libs/sofia-sip/libsofia-sip-ua/nta/test_nta_api.c b/libs/sofia-sip/libsofia-sip-ua/nta/test_nta_api.c
index 30c71244a7..429d425ca0 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nta/test_nta_api.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nta/test_nta_api.c
@@ -453,7 +453,7 @@ int api_test_params(agent_t *ag)
sip_contact_t const *aliases = (void *)-1;
msg_mclass_t *mclass = (void *)-1;
- sip_contact_t *contact = (void *)-1;
+ sip_contact_t const *contact = (void *)-1;
url_string_t const *default_proxy = (void *)-1;
void *smime = (void *)-1;
@@ -859,6 +859,7 @@ static int api_test_default(agent_t *ag)
nta_incoming_t *irq;
nta_outgoing_t *orq;
sip_via_t via[1];
+ su_nanotime_t nano;
TEST_1(nta = ag->ag_agent);
@@ -876,7 +877,9 @@ static int api_test_default(agent_t *ag)
TEST_S(nta_incoming_method_name(irq), "*");
TEST_P(nta_incoming_url(irq), NULL);
TEST(nta_incoming_cseq(irq), 0);
-
+
+ TEST(nta_incoming_received(irq, &nano), nano / 1000000000);
+
TEST(nta_incoming_set_params(irq, TAG_END()), 0);
TEST_P(nta_incoming_getrequest(irq), NULL);
@@ -944,6 +947,7 @@ static int api_test_errors(agent_t *ag)
nta_agent_t *nta;
su_root_t *root;
su_home_t home[1];
+ su_nanotime_t nano;
BEGIN();
@@ -1044,6 +1048,8 @@ static int api_test_errors(agent_t *ag)
TEST_P(nta_incoming_method_name(NULL), NULL);
TEST_P(nta_incoming_url(NULL), NULL);
TEST(nta_incoming_cseq(NULL), 0);
+ TEST(nta_incoming_received(NULL, &nano), 0);
+ TEST64(nano, 0);
TEST(nta_incoming_set_params(NULL, TAG_END()), -1);
diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua.docs b/libs/sofia-sip/libsofia-sip-ua/nua/nua.docs
index fb241b7fe2..f27c793cb7 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua.docs
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua.docs
@@ -1174,9 +1174,9 @@ follows:
terminating* |
- |
- The cannot be terminated with BYE before the dialog is established with a
- non-100 preliminary response. So, instead of @b BYE, stack sends a @b
- CANCEL request, and enters terminating state.
+ The call cannot be terminated with BYE before the dialog is established
+ with a non-100 preliminary response. So, instead of a @b BYE, stack sends
+ a @b CANCEL request, and enters terminating state.
However, there is a race condition and the server can respond with a
succesful 2XX response before receiving CANCEL. If the server responds with
diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_common.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_common.c
index 6e7499636e..a37d57c6cf 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_common.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_common.c
@@ -114,6 +114,7 @@ nua_handle_t *nh_create_handle(nua_t *nua,
nh->nh_nua = nua;
nh->nh_magic = hmagic;
nh->nh_prefs = nua->nua_dhandle->nh_prefs;
+ nh->nh_ds->ds_owner = nh;
if (nua_handle_save_tags(nh, tags) < 0) {
SU_DEBUG_5(("nua(%p): creating handle %p failed\n",
diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c
index 185c35c5f9..d4cbd08128 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c
@@ -43,6 +43,7 @@
#include
#include
+#include
#define NUA_OWNER_T su_home_t
@@ -127,9 +128,9 @@ void nua_dialog_store_peer_info(nua_owner_t *own,
nua_dialog_state_t *ds,
sip_t const *sip)
{
- nua_remote_t *nr = ds->ds_remote_ua;
+ nua_dialog_peer_info_t *nr = ds->ds_remote_ua;
nua_dialog_usage_t *du;
- nua_remote_t old[1];
+ nua_dialog_peer_info_t old[1];
*old = *nr;
@@ -189,6 +190,7 @@ int nua_dialog_remove(nua_owner_t *own,
{
if (ds->ds_usage == usage && (usage == NULL || usage->du_next == NULL)) {
nua_dialog_store_peer_info(own, ds, NULL); /* zap peer info */
+ msg_header_free(own, (msg_header_t *)ds->ds_ltarget), ds->ds_ltarget = NULL;
nta_leg_destroy(ds->ds_leg), ds->ds_leg = NULL;
su_free(own, (void *)ds->ds_remote_tag), ds->ds_remote_tag = NULL;
ds->ds_route = 0;
@@ -287,6 +289,8 @@ nua_dialog_usage_t *nua_dialog_usage_add(nua_owner_t *own,
du = su_zalloc(own, sizeof *du + uclass->usage_size);
if (du) {
+ su_home_ref(own);
+ du->du_dialog = ds;
du->du_class = uclass;
du->du_event = o;
@@ -300,7 +304,6 @@ nua_dialog_usage_t *nua_dialog_usage_add(nua_owner_t *own,
(void *)own, nua_dialog_usage_name(du),
o ? " with event " : "", o ? o->o_type :""));
- su_home_ref(own);
du->du_next = ds->ds_usage, ds->ds_usage = du;
return du;
@@ -450,29 +453,28 @@ void nua_dialog_deinit(nua_owner_t *own,
* if @a delta is less than 5 minutes but longer than 90 seconds, 30..60
* seconds before end of interval.
*
- * If @a delta is 0, the refresh time is set at the end of the world
- * (maximum time, for 32-bit systems sometimes during 2036).
+ * If @a delta is 0, the dialog usage is never refreshed.
*/
void nua_dialog_usage_set_refresh(nua_dialog_usage_t *du, unsigned delta)
{
if (delta == 0)
- du->du_refresh = 0;
+ nua_dialog_usage_reset_refresh(du);
else if (delta > 90 && delta < 5 * 60)
/* refresh 30..60 seconds before deadline */
- nua_dialog_usage_refresh_range(du, delta - 60, delta - 30);
+ nua_dialog_usage_set_refresh_range(du, delta - 60, delta - 30);
else {
/* By default, refresh around half time before deadline */
unsigned min = (delta + 2) / 4;
unsigned max = (delta + 2) / 4 + (delta + 1) / 2;
if (min == 0)
min = 1;
- nua_dialog_usage_refresh_range(du, min, max);
+ nua_dialog_usage_set_refresh_range(du, min, max);
}
}
/**@internal Set refresh in range min..max seconds in the future. */
-void nua_dialog_usage_refresh_range(nua_dialog_usage_t *du,
- unsigned min, unsigned max)
+void nua_dialog_usage_set_refresh_range(nua_dialog_usage_t *du,
+ unsigned min, unsigned max)
{
sip_time_t now = sip_now(), target;
unsigned delta;
@@ -493,12 +495,12 @@ void nua_dialog_usage_refresh_range(nua_dialog_usage_t *du,
SU_DEBUG_7(("nua(): refresh %s after %lu seconds (in [%u..%u])\n",
nua_dialog_usage_name(du), target - now, min, max));
- du->du_refresh = target;
+ nua_dialog_usage_set_refresh_at(du, target);
}
/** Set absolute refresh time */
-void nua_dialog_usage_refresh_at(nua_dialog_usage_t *du,
- sip_time_t target)
+void nua_dialog_usage_set_refresh_at(nua_dialog_usage_t *du,
+ sip_time_t target)
{
SU_DEBUG_7(("nua(): refresh %s after %lu seconds\n",
nua_dialog_usage_name(du), target - sip_now()));
@@ -512,25 +514,14 @@ void nua_dialog_usage_reset_refresh(nua_dialog_usage_t *du)
du->du_refresh = 0;
}
-/** @internal Refresh usage or shutdown usage if @a now is 0. */
+/** @internal Refresh usage. */
void nua_dialog_usage_refresh(nua_owner_t *owner,
nua_dialog_state_t *ds,
nua_dialog_usage_t *du,
sip_time_t now)
{
- if (du) {
- du->du_refresh = 0;
-
- if (now > 0) {
- assert(du->du_class->usage_refresh);
- du->du_class->usage_refresh(owner, ds, du, now);
- }
- else {
- du->du_shutdown = 1;
- assert(du->du_class->usage_shutdown);
- du->du_class->usage_shutdown(owner, ds, du);
- }
- }
+ assert(du && du->du_class->usage_refresh);
+ du->du_class->usage_refresh(owner, ds, du, now);
}
/** Terminate all dialog usages gracefully. */
@@ -552,18 +543,18 @@ int nua_dialog_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
return 1;
}
-/** (Gracefully) terminate usage.
+/** Shutdown (gracefully terminate) usage.
*
* @retval >0 shutdown done
* @retval 0 shutdown in progress
* @retval <0 try again later
*/
int nua_dialog_usage_shutdown(nua_owner_t *owner,
- nua_dialog_state_t *ds,
- nua_dialog_usage_t *du)
+ nua_dialog_state_t *ds,
+ nua_dialog_usage_t *du)
{
if (du) {
- du->du_refresh = 0;
+ nua_dialog_usage_reset_refresh(du);
du->du_shutdown = 1;
assert(du->du_class->usage_shutdown);
return du->du_class->usage_shutdown(owner, ds, du);
@@ -571,3 +562,41 @@ int nua_dialog_usage_shutdown(nua_owner_t *owner,
else
return 200;
}
+
+/** Repeat shutdown of all usages.
+ *
+ * @note Caller must have a reference to nh
+ */
+int nua_dialog_repeat_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
+{
+ nua_dialog_usage_t *du;
+ nua_server_request_t *sr, *sr_next;
+
+ for (sr = ds->ds_sr; sr; sr = sr_next) {
+ sr_next = sr->sr_next;
+
+ if (nua_server_request_is_pending(sr)) {
+ SR_STATUS1(sr, SIP_410_GONE); /* 410 terminates dialog */
+ nua_server_respond(sr, NULL);
+ nua_server_report(sr);
+ }
+ }
+
+ for (du = ds->ds_usage; du ;) {
+ nua_dialog_usage_t *du_next = du->du_next;
+
+ nua_dialog_usage_shutdown(owner, ds, du);
+
+ if (du_next == NULL)
+ break;
+
+ for (du = ds->ds_usage; du; du = du->du_next) {
+ if (du == du_next)
+ break;
+ else if (!du->du_shutdown)
+ break;
+ }
+ }
+
+ return ds->ds_usage != NULL;
+}
diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h b/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h
index fc667f864b..77d264d896 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h
@@ -35,10 +35,6 @@
* @date Created: Wed Mar 8 11:38:18 EET 2006 ppessi
*/
-typedef struct nua_dialog_state nua_dialog_state_t;
-typedef struct nua_dialog_usage nua_dialog_usage_t;
-typedef struct nua_remote_s nua_remote_t;
-
#ifndef NUA_OWNER_T
#define NUA_OWNER_T struct nua_owner_s
#endif
@@ -48,10 +44,13 @@ typedef NUA_OWNER_T nua_owner_t;
#include
#endif
-typedef su_msg_r nua_saved_signal_t;
-
+typedef struct nua_dialog_state nua_dialog_state_t;
+typedef struct nua_dialog_usage nua_dialog_usage_t;
typedef struct nua_server_request nua_server_request_t;
typedef struct nua_client_request nua_client_request_t;
+typedef struct nua_dialog_peer_info nua_dialog_peer_info_t;
+
+typedef su_msg_r nua_saved_signal_t;
typedef struct {
sip_method_t sm_method;
@@ -272,6 +271,8 @@ struct nua_client_request
nta_outgoing_t *cr_orq;
+ su_timer_t *cr_timer; /**< Expires or retry timer */
+
/*nua_event_t*/ int cr_event; /**< Request event */
sip_method_t cr_method;
char const *cr_method_name;
@@ -303,8 +304,10 @@ struct nua_client_request
unsigned cr_dialog:1; /**< Request can initiate dialog */
/* Current state */
+ unsigned cr_waiting:1; /**< Request is waiting */
unsigned cr_challenged:1; /**< Request was challenged */
unsigned cr_wait_for_cred:1; /**< Request is pending authentication */
+ unsigned cr_wait_for_timer:1; /**< Request is waiting for a timer to expire */
unsigned cr_restarting:1; /**< Request is being restarted */
unsigned cr_reporting:1; /**< Reporting in progress */
unsigned cr_terminating:1; /**< Request terminates the usage */
@@ -316,6 +319,9 @@ struct nua_client_request
struct nua_dialog_state
{
+ /** Dialog owner */
+ nua_owner_t *ds_owner;
+
/** Dialog usages. */
nua_dialog_usage_t *ds_usage;
@@ -346,12 +352,13 @@ struct nua_dialog_state
sip_from_t const *ds_local; /**< Local address */
sip_to_t const *ds_remote; /**< Remote address */
nta_leg_t *ds_leg;
+ sip_contact_t *ds_ltarget; /**< Local target */
char const *ds_remote_tag; /**< Remote tag (if any).
* Should be non-NULL
* if dialog is established.
*/
-
- struct nua_remote_s {
+
+ struct nua_dialog_peer_info {
sip_allow_t *nr_allow;
sip_accept_t *nr_accept;
sip_require_t *nr_require;
@@ -360,10 +367,6 @@ struct nua_dialog_state
} ds_remote_ua[1];
};
-typedef void nh_pending_f(nua_owner_t *,
- nua_dialog_usage_t *du,
- sip_time_t now);
-
/** Virtual function pointer table for dialog usage. */
typedef struct {
unsigned usage_size, usage_class_size;
@@ -388,6 +391,7 @@ typedef struct {
struct nua_dialog_usage {
nua_dialog_usage_t *du_next;
nua_usage_class const *du_class;
+ nua_dialog_state_t *du_dialog;
nua_client_request_t *du_cr; /**< Client request bound with usage */
unsigned du_ready:1; /**< Established usage */
@@ -440,13 +444,16 @@ void nua_dialog_deinit(nua_owner_t *own,
int nua_dialog_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds);
+int nua_dialog_repeat_shutdown(nua_owner_t *owner,
+ nua_dialog_state_t *ds);
+
void nua_dialog_usage_set_refresh(nua_dialog_usage_t *du, unsigned delta);
-void nua_dialog_usage_refresh_range(nua_dialog_usage_t *du,
- unsigned min, unsigned max);
+void nua_dialog_usage_set_refresh_range(nua_dialog_usage_t *du,
+ unsigned min, unsigned max);
-void nua_dialog_usage_refresh_at(nua_dialog_usage_t *du,
- sip_time_t target);
+void nua_dialog_usage_set_refresh_at(nua_dialog_usage_t *du,
+ sip_time_t target);
void nua_dialog_usage_reset_refresh(nua_dialog_usage_t *du);
diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c
index fcb37f8c68..be3376ebb4 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c
@@ -696,7 +696,7 @@ static int nua_notify_client_report(nua_client_request_t *cr,
nua_client_resend_request(cr, 0);
}
else if (nu->nu_expires) {
- nua_dialog_usage_refresh_at(du, nu->nu_expires);
+ nua_dialog_usage_set_refresh_at(du, nu->nu_expires);
}
}
diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c
index 92f1d00915..25458c799b 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c
@@ -1158,7 +1158,7 @@ int nua_handle_save_tags(nua_handle_t *nh, tagi_t *tags)
url = (url_string_t *)t->t_value;
}
/* NUTAG_SIPS_URL_REF(url) */
- else if (t->t_tag == nutag_url) {
+ else if (t->t_tag == nutag_sips_url) {
url = (url_string_t *)t->t_value;
}
}
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 9c31ba6ec6..d16b4e378c 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c
@@ -973,7 +973,7 @@ static int nua_register_client_response(nua_client_request_t *cr,
nua_registration_set_ready(nr, 1);
}
else if (du) {
- nua_dialog_usage_set_refresh(du, 0);
+ nua_dialog_usage_reset_refresh(du);
su_free(nh->nh_home, nr->nr_route);
nr->nr_route = NULL;
@@ -1004,14 +1004,31 @@ void nua_register_connection_closed(tp_stack_t *sip_stack,
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"));
+ tp_name_t const *tpn;
+ int pending = nr->nr_error_report_id;
+ assert(tport == nr->nr_tport);
+
+ if (!nr->nr_tport)
+ return;
+
+ if (tport_release(nr->nr_tport, pending, NULL, NULL, nr, 0) < 0)
+ SU_DEBUG_1(("nua_register: tport_release() failed\n"));
nr->nr_error_report_id = 0;
+
+ tpn = tport_name(nr->nr_tport);
+
+ SU_DEBUG_5(("nua_register(%p): tport to %s/%s:%s%s%s closed %s\n",
+ nua_dialog_usage_public(nr)->du_dialog->ds_owner,
+ tpn->tpn_proto, tpn->tpn_host, tpn->tpn_port,
+ tpn->tpn_comp ? ";comp=" : "",
+ tpn->tpn_comp ? tpn->tpn_comp : "",
+ error != 0 ? su_strerror(error) : ""));
+
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());
+ nua_dialog_usage_set_refresh_range(nua_dialog_usage_public(nr), 0, 0);
}
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 6508e72d1f..a68d35a3e8 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c
@@ -173,6 +173,8 @@ typedef struct nua_session_usage
char const *ss_oa_recv, *ss_oa_sent;
} nua_session_usage_t;
+static char const Offer[] = "offer", Answer[] = "answer";
+
static char const *nua_session_usage_name(nua_dialog_usage_t const *du);
static int nua_session_usage_add(nua_handle_t *nh,
nua_dialog_state_t *ds,
@@ -579,7 +581,7 @@ static int nua_invite_client_init(nua_client_request_t *cr,
cr->cr_usage = du = nua_dialog_usage_for_session(nh->nh_ds);
/* Errors returned by nua_invite_client_init()
- are neutral to session state */
+ do not change the session state */
cr->cr_neutral = 1;
if (nh_is_special(nh) ||
@@ -640,7 +642,7 @@ static int nua_invite_client_request(nua_client_request_t *cr,
invite_timeout = UINT_MAX;
/* Send CANCEL if we don't get response within timeout*/
/* nua_dialog_usage_set_expires(du, invite_timeout); Xyzzy */
- nua_dialog_usage_set_refresh(du, 0);
+ nua_dialog_usage_reset_refresh(du);
/* Add session timer headers */
if (session_timer_is_supported(ss->ss_timer))
@@ -689,10 +691,10 @@ static int nua_invite_client_request(nua_client_request_t *cr,
NTATAG_REL100(ss->ss_100rel),
TAG_NEXT(tags));
if (retval == 0) {
- cr->cr_offer_sent = offer_sent;
- ss->ss_oa_sent = offer_sent ? "offer" : NULL;
+ if ((cr->cr_offer_sent = offer_sent))
+ ss->ss_oa_sent = Offer;
- if (!cr->cr_restarting)
+ if (!cr->cr_restarting) /* Restart logic calls nua_invite_client_report */
signal_call_state_change(nh, ss, 0, "INVITE sent",
nua_callstate_calling);
}
@@ -807,9 +809,9 @@ static int nua_session_client_response(nua_client_request_t *cr,
sdp = NULL;
}
else if (cr->cr_offer_sent) {
- /* case 1: incoming answer */
+ /* case 1: answer to our offer */
cr->cr_answer_recv = status;
- received = "answer";
+ received = Answer;
if (nh->nh_soa == NULL)
LOG5("got SDP");
@@ -836,9 +838,9 @@ static int nua_session_client_response(nua_client_request_t *cr,
sdp = NULL;
}
else {
- /* case 2: answer to our offer */
+ /* case 2: new offer */
cr->cr_offer_recv = 1, cr->cr_answer_sent = 0;
- received = "offer";
+ received = Offer;
if (nh->nh_soa && soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0) {
LOG3("error parsing SDP");
@@ -868,12 +870,13 @@ static int nua_invite_client_report(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_dialog_usage_t *du = cr->cr_usage;
nua_session_usage_t *ss = nua_dialog_usage_private(du);
unsigned next_state;
int error;
- nh_referral_respond(nh, status, phrase);
+ nh_referral_respond(nh, status, phrase); /* XXX - restarting after 401/407 */
nua_stack_event(nh->nh_nua, nh,
nta_outgoing_getresponse(orq),
@@ -881,7 +884,8 @@ static int nua_invite_client_report(nua_client_request_t *cr,
status, phrase,
tags);
- if (orq != cr->cr_orq && status != 100)
+ if (cr->cr_waiting)
+ /* Do not report call state change if waiting for restart */
return 1;
if (ss == NULL) {
@@ -897,7 +901,10 @@ static int nua_invite_client_report(nua_client_request_t *cr,
return 1;
}
- if (status == 100) {
+ if (orq != cr->cr_orq && cr->cr_orq) { /* Being restarted */
+ next_state = nua_callstate_calling;
+ }
+ else if (status == 100) {
next_state = nua_callstate_calling;
}
else if (status < 300 && cr->cr_graceful) {
@@ -953,8 +960,16 @@ static int nua_invite_client_report(nua_client_request_t *cr,
/* Auto-ACK response to re-INVITE unless auto_ack is set to 0 */
(ss->ss_state == nua_callstate_ready &&
!NH_PISSET(nh, auto_ack))) {
+ nua_client_request_t *cru;
- if (nua_invite_client_ack(cr, NULL) > 0)
+ for (cru = ds->ds_cr; cru; cru = cru->cr_next) {
+ if (cr != cru && cru->cr_offer_sent && !cru->cr_answer_recv)
+ break;
+ }
+
+ if (cru)
+ /* A final response to UPDATE or PRACK with answer on its way? */;
+ else if (nua_invite_client_ack(cr, NULL) > 0)
next_state = nua_callstate_ready;
else
next_state = nua_callstate_terminating;
@@ -1129,7 +1144,7 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
else if (cr->cr_offer_recv && !cr->cr_answer_sent) {
if (nh->nh_soa == NULL) {
if (session_get_description(sip, NULL, NULL))
- cr->cr_answer_sent = 1, ss->ss_oa_sent = "answer";
+ cr->cr_answer_sent = 1, ss->ss_oa_sent = Answer;
}
else if (soa_generate_answer(nh->nh_soa, NULL) < 0 ||
session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
@@ -1138,23 +1153,33 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
/* reason = soa_error_as_sip_reason(nh->nh_soa); */
}
else {
- cr->cr_answer_sent = 1, ss->ss_oa_sent = "answer";
+ cr->cr_answer_sent = 1, ss->ss_oa_sent = Answer;
}
}
if (ss == NULL || ss->ss_state >= nua_callstate_ready || reason)
;
- else if (nh->nh_soa
- ? soa_is_complete(nh->nh_soa)
- : !(cr->cr_offer_sent && !cr->cr_answer_recv)) {
- /* signal that O/A round(s) is (are) complete */
- if (nh->nh_soa)
+ else if (nh->nh_soa && soa_is_complete(nh->nh_soa)) {
+ /* signal SOA that O/A round(s) is (are) complete */
soa_activate(nh->nh_soa, NULL);
}
+ else if (nh->nh_soa == NULL && !(cr->cr_offer_sent && !cr->cr_answer_recv)) {
+ ;
+ }
else {
- /* No SDP answer -> terminate call */
- status = 988, phrase = "Incomplete offer/answer";
- reason = "SIP;cause=488;text=\"Incomplete offer/answer\"";
+ nua_client_request_t *cru;
+
+ /* Final response to UPDATE or PRACK may be on its way ... */
+ for (cru = ds->ds_cr; cru; cru = cru->cr_next) {
+ if (cr != cru && cru->cr_offer_sent && !cru->cr_answer_recv)
+ break;
+ }
+
+ if (cru == NULL) {
+ /* No SDP answer -> terminate call */
+ status = 988, phrase = "Incomplete offer/answer";
+ reason = "SIP;cause=488;text=\"Incomplete offer/answer\"";
+ }
}
if ((ack = nta_outgoing_mcreate(nh->nh_nua->nua_nta, NULL, NULL, NULL,
@@ -1561,15 +1586,13 @@ static int nua_prack_client_request(nua_client_request_t *cr,
cr->cr_offer_sent = offer_sent;
cr->cr_answer_sent = answer_sent;
- if (!cr->cr_restarting) {
- if (offer_sent)
- ss->ss_oa_sent = "offer";
- else if (answer_sent)
- ss->ss_oa_sent = "answer";
+ if (offer_sent)
+ ss->ss_oa_sent = Offer;
+ else if (answer_sent)
+ ss->ss_oa_sent = Answer;
- if (!ss->ss_reporting)
- signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
- }
+ if (!cr->cr_restarting) /* Restart logic calls nua_prack_client_report */
+ signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
}
return retval;
@@ -1591,7 +1614,8 @@ static int nua_prack_client_report(nua_client_request_t *cr,
tagi_t const *tags)
{
nua_handle_t *nh = cr->cr_owner;
- nua_session_usage_t *ss = nua_dialog_usage_private(cr->cr_usage);
+ nua_dialog_usage_t *du = cr->cr_usage;
+ nua_session_usage_t *ss = nua_dialog_usage_private(du);
nua_stack_event(nh->nh_nua, nh,
nta_outgoing_getresponse(orq),
@@ -1599,11 +1623,34 @@ static int nua_prack_client_report(nua_client_request_t *cr,
status, phrase,
tags);
- if (!ss || orq != cr->cr_orq || cr->cr_terminated || cr->cr_graceful)
+ if (!ss || cr->cr_terminated || cr->cr_graceful)
return 1;
- if (cr->cr_offer_sent)
- signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
+ if (cr->cr_waiting)
+ /* Do not report call state change if restarting later */
+ return 1;
+
+ if (cr->cr_offer_sent || cr->cr_answer_sent) {
+ unsigned next_state = ss->ss_state;
+
+ if (status < 200)
+ ;
+ else if (du->du_cr && du->du_cr->cr_orq && du->du_cr->cr_status >= 200) {
+ /* There is an un-ACK-ed INVITE there */
+ assert(du->du_cr->cr_method == sip_method_invite);
+ if (NH_PGET(nh, auto_ack) ||
+ /* Auto-ACK response to re-INVITE unless auto_ack is set to 0 */
+ (ss->ss_state == nua_callstate_ready && !NH_PISSET(nh, auto_ack))) {
+ /* No UPDATE with offer/answer if PRACK with offer/answer was ongoing! */
+ if (nua_invite_client_ack(du->du_cr, NULL) > 0)
+ next_state = nua_callstate_ready;
+ else
+ next_state = nua_callstate_terminating;
+ }
+ }
+
+ signal_call_state_change(nh, ss, status, phrase, next_state);
+ }
if (ss->ss_update_needed && 200 <= status && status < 300 &&
!SIP_IS_ALLOWED(NH_PGET(nh, appl_method), sip_method_update))
@@ -1904,7 +1951,7 @@ int nua_invite_server_preprocess(nua_server_request_t *sr)
ss = nua_dialog_usage_private(sr->sr_usage);
if (sr->sr_offer_recv)
- ss->ss_oa_recv = "offer";
+ ss->ss_oa_recv = Offer;
ss->ss_100rel = NH_PGET(nh, early_media);
ss->ss_precondition = sip_has_feature(request->sip_require, "precondition");
@@ -2049,9 +2096,9 @@ int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *tags)
if (nh->nh_soa && session_include_description(nh->nh_soa, 1, msg, sip) < 0)
SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
else if (offer)
- sr->sr_offer_sent = 1 + reliable, ss->ss_oa_sent = "offer";
+ sr->sr_offer_sent = 1 + reliable, ss->ss_oa_sent = Offer;
else if (answer)
- sr->sr_answer_sent = 1 + reliable, ss->ss_oa_sent = "answer";
+ sr->sr_answer_sent = 1 + reliable, ss->ss_oa_sent = Answer;
}
if (reliable && sr->sr_status < 200) {
@@ -2236,7 +2283,7 @@ int process_ack(nua_server_request_t *sr,
int error;
if (session_get_description(sip, &sdp, &len))
- recv = "answer";
+ recv = Answer;
if (recv) {
assert(ss->ss_oa_recv == NULL);
@@ -2474,14 +2521,14 @@ int nua_prack_server_init(nua_server_request_t *sr)
/* XXX - check for overlap? */
if (sri->sr_offer_sent)
- sr->sr_answer_recv = 1, ss->ss_oa_recv = "answer";
+ sr->sr_answer_recv = 1, ss->ss_oa_recv = Answer;
else
- sr->sr_offer_recv = 1, ss->ss_oa_recv = "offer";
+ sr->sr_offer_recv = 1, ss->ss_oa_recv = Offer;
if (nh->nh_soa &&
soa_set_remote_sdp(nh->nh_soa, NULL, sr->sr_sdp, sr->sr_sdp_len) < 0) {
SU_DEBUG_5(("nua(%p): %s server: error parsing %s\n", (void *)nh,
- "PRACK", "offer"));
+ "PRACK", Offer));
return
sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase);
}
@@ -2504,21 +2551,21 @@ int nua_prack_server_respond(nua_server_request_t *sr, tagi_t const *tags)
if (nh->nh_soa == NULL) {
if (sr->sr_offer_recv && session_get_description(sip, NULL, NULL))
- sr->sr_answer_sent = 1, ss->ss_oa_sent = "answer";
+ sr->sr_answer_sent = 1, ss->ss_oa_sent = Answer;
}
else if ((sr->sr_offer_recv && soa_generate_answer(nh->nh_soa, NULL) < 0) ||
(sr->sr_answer_recv && soa_process_answer(nh->nh_soa, NULL) < 0)) {
SU_DEBUG_5(("nua(%p): %s server: %s %s\n",
(void *)nh, "PRACK",
"error processing",
- sr->sr_offer_recv ? "offer" : "answer"));
+ sr->sr_offer_recv ? Offer : Answer));
sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase);
}
else if (sr->sr_offer_recv) {
if (session_include_description(nh->nh_soa, 1, msg, sip) < 0)
sr_status(sr, SIP_500_INTERNAL_SERVER_ERROR);
else
- sr->sr_answer_sent = 1, ss->ss_oa_sent = "answer";
+ sr->sr_answer_sent = 1, ss->ss_oa_sent = Answer;
}
}
@@ -2976,20 +3023,18 @@ static int nua_update_client_request(nua_client_request_t *cr,
retval = nua_base_client_request(cr, msg, sip, NULL);
if (retval == 0) {
+ enum nua_callstate state = ss->ss_state;
cr->cr_offer_sent = offer_sent;
ss->ss_update_needed = 0;
- if (!cr->cr_restarting) {
- enum nua_callstate state = ss->ss_state;
+ if (state == nua_callstate_ready)
+ state = nua_callstate_calling; /* XXX */
- if (state == nua_callstate_ready)
- state = nua_callstate_calling;
-
- if (offer_sent)
- ss->ss_oa_sent = "offer";
+ if (offer_sent)
+ ss->ss_oa_sent = Offer;
+ if (!cr->cr_restarting) /* Restart logic calls nua_update_client_report */
signal_call_state_change(nh, ss, 0, "UPDATE sent", state);
- }
}
return retval;
@@ -3057,6 +3102,7 @@ static int nua_update_client_report(nua_client_request_t *cr,
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);
+ unsigned next_state = ss->ss_state;
nua_stack_event(nh->nh_nua, nh,
nta_outgoing_getresponse(orq),
@@ -3064,11 +3110,32 @@ static int nua_update_client_report(nua_client_request_t *cr,
status, phrase,
tags);
- if (!ss || orq != cr->cr_orq ||
- cr->cr_terminated || cr->cr_graceful || !cr->cr_offer_sent)
+ if (!ss || cr->cr_terminated || cr->cr_graceful)
return 1;
- signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
+ if (cr->cr_waiting)
+ /* Do not report call state change if restarting later */
+ return 1;
+
+ if (cr->cr_offer_sent) {
+ if (status < 200)
+ ;
+ else if (du->du_cr && du->du_cr->cr_orq && du->du_cr->cr_status >= 200) {
+ /* There is an un-ACK-ed INVITE there */
+ assert(du->du_cr->cr_method == sip_method_invite);
+
+ if (NH_PGET(nh, auto_ack) ||
+ /* Auto-ACK response to re-INVITE unless auto_ack is set to 0 */
+ (ss->ss_state == nua_callstate_ready && !NH_PISSET(nh, auto_ack))) {
+ if (nua_invite_client_ack(du->du_cr, NULL) > 0)
+ next_state = nua_callstate_ready;
+ else
+ next_state = nua_callstate_terminating;
+ }
+ }
+
+ signal_call_state_change(nh, ss, status, phrase, next_state);
+ }
return 1;
}
@@ -3148,13 +3215,13 @@ int nua_update_server_init(nua_server_request_t *sr)
if (nh->nh_soa &&
soa_set_remote_sdp(nh->nh_soa, NULL, sr->sr_sdp, sr->sr_sdp_len) < 0) {
SU_DEBUG_5(("nua(%p): %s server: error parsing %s\n", (void *)nh,
- "UPDATE", "offer"));
+ "UPDATE", Offer));
return
sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase);
}
sr->sr_offer_recv = 1;
- ss->ss_oa_recv = "offer";
+ ss->ss_oa_recv = Offer;
}
return 0;
@@ -3172,11 +3239,11 @@ int nua_update_server_respond(nua_server_request_t *sr, tagi_t const *tags)
if (200 <= sr->sr_status && sr->sr_status < 300 && sr->sr_sdp) {
if (nh->nh_soa == NULL) {
- sr->sr_answer_sent = 1, ss->ss_oa_sent = "answer";
+ sr->sr_answer_sent = 1, ss->ss_oa_sent = Answer;
}
else if (soa_generate_answer(nh->nh_soa, NULL) < 0) {
SU_DEBUG_5(("nua(%p): %s server: %s %s\n",
- (void *)nh, "UPDATE", "error processing", "offer"));
+ (void *)nh, "UPDATE", "error processing", Offer));
sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase);
}
else if (soa_activate(nh->nh_soa, NULL) < 0) {
@@ -3188,7 +3255,7 @@ int nua_update_server_respond(nua_server_request_t *sr, tagi_t const *tags)
sr_status(sr, SIP_500_INTERNAL_SERVER_ERROR);
}
else {
- sr->sr_answer_sent = 1, ss->ss_oa_sent = "answer";
+ sr->sr_answer_sent = 1, ss->ss_oa_sent = Answer;
}
}
@@ -3639,14 +3706,17 @@ static void signal_call_state_change(nua_handle_t *nh,
oa_recv = ss->ss_oa_recv, ss->ss_oa_recv = NULL;
oa_sent = ss->ss_oa_sent, ss->ss_oa_sent = NULL;
+ assert(oa_sent == Offer || oa_sent == Answer || oa_sent == NULL);
+ assert(oa_recv == Offer || oa_recv == Answer || oa_recv == NULL);
+
if (oa_recv) {
- offer_recv = strcasecmp(oa_recv, "offer") == 0;
- answer_recv = strcasecmp(oa_recv, "answer") == 0;
+ offer_recv = oa_recv == Offer;
+ answer_recv = oa_recv == Answer;
}
if (oa_sent) {
- offer_sent = strcasecmp(oa_sent, "offer") == 0;
- answer_sent = strcasecmp(oa_sent, "answer") == 0;
+ offer_sent = oa_sent == Offer;
+ answer_sent = oa_sent == Answer;
}
}
@@ -4061,7 +4131,7 @@ session_timer_set(nua_session_usage_t *ss)
if (t->interval >= 90)
low -=5, high += 5;
- nua_dialog_usage_refresh_range(du, low, high);
+ nua_dialog_usage_set_refresh_range(du, low, high);
t->timer_set = 1;
}
else if (t->refresher == nua_remote_refresher) {
@@ -4074,7 +4144,7 @@ session_timer_set(nua_session_usage_t *ss)
interval -= 32 > interval / 6 ? interval / 3 : 32 + interval / 3;
- nua_dialog_usage_refresh_range(du, interval, interval);
+ nua_dialog_usage_set_refresh_range(du, interval, interval);
t->timer_set = 1;
}
else {
diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c
index 123187bf2d..498b5e011b 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c
@@ -46,6 +46,7 @@
#define SU_ROOT_MAGIC_T struct nua_s
#define SU_MSG_ARG_T struct event_s
+#define SU_TIMER_ARG_T struct nua_client_request
#define NUA_SAVED_EVENT_T su_msg_t *
@@ -271,9 +272,7 @@ int nua_stack_event(nua_t *nua, nua_handle_t *nh, msg_t *msg,
if ((event > nua_r_authenticate && event <= nua_r_ack)
|| event < nua_i_error
|| (nh && !nh->nh_valid)
- /* disable hiding all events after shutdown so that we can get state callbacks to properly tear down our calls */
- /* || (nua->nua_shutdown && event != nua_r_shutdown) */
- ) {
+ || (nua->nua_shutdown && event != nua_r_shutdown)) {
if (msg)
msg_destroy(msg);
return event;
@@ -486,7 +485,8 @@ void nua_stack_timer(nua_t *nua, su_timer_t *t, su_timer_arg_t *a)
su_timer_set(t, nua_stack_timer, a);
if (nua->nua_shutdown) {
- nua_stack_shutdown(nua);
+ nua_stack_shutdown(nua);
+ return;
}
for (nh = nua->nua_handles; nh; nh = nh_next) {
@@ -605,21 +605,10 @@ void nua_stack_shutdown(nua_t *nua)
for (nh = nua->nua_handles; nh; nh = nh_next) {
nua_dialog_state_t *ds = nh->nh_ds;
- nua_server_request_t *sr, *sr_next;
nh_next = nh->nh_next;
- for (sr = ds->ds_sr; sr; sr = sr_next) {
- sr_next = sr->sr_next;
-
- if (nua_server_request_is_pending(sr)) {
- SR_STATUS1(sr, SIP_410_GONE); /* 410 terminates dialog */
- nua_server_respond(sr, NULL);
- nua_server_report(sr);
- }
- }
-
- busy += nh_call_pending(nh, 0);
+ busy += nua_dialog_repeat_shutdown(nh, ds);
if (nh->nh_soa) {
soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
@@ -992,6 +981,7 @@ nua_stack_authenticate(nua_t *nua, nua_handle_t *nh, nua_event_t e,
if (status > 0) {
if (cr && cr->cr_wait_for_cred) {
+ cr->cr_waiting = cr->cr_wait_for_cred = 0;
nua_client_restart_request(cr, cr->cr_terminating, tags);
}
else {
@@ -1001,8 +991,8 @@ nua_stack_authenticate(nua_t *nua, nua_handle_t *nh, nua_event_t e,
}
}
else if (cr && cr->cr_wait_for_cred) {
- cr->cr_wait_for_cred = 0;
-
+ cr->cr_waiting = cr->cr_wait_for_cred = 0;
+
if (status < 0)
nua_client_response(cr, 900, "Cannot add credentials", NULL);
else
@@ -1439,9 +1429,10 @@ int nua_server_trespond(nua_server_request_t *sr,
int nua_server_respond(nua_server_request_t *sr, tagi_t const *tags)
{
nua_handle_t *nh = sr->sr_owner;
+ nua_dialog_state_t *ds = nh->nh_ds;
sip_method_t method = sr->sr_method;
struct { msg_t *msg; sip_t *sip; } next = { NULL, NULL };
- int retval;
+ int retval, user_contact = 1;
#if HAVE_OPEN_C
/* Nice. And old arm symbian compiler; see below. */
tagi_t next_tags[2];
@@ -1496,15 +1487,26 @@ int nua_server_respond(nua_server_request_t *sr, tagi_t const *tags)
sip_add_dup(msg, sip, (void *)NH_PGET(nh, allow_events)) < 0)
;
else if (!sip->sip_contact && sr->sr_status < 300 && sr->sr_add_contact &&
- nua_registration_add_contact_to_response(nh, msg, sip, NULL, m) < 0)
+ (user_contact = 0,
+ ds->ds_ltarget
+ ? sip_add_dup(msg, sip, (sip_header_t *)ds->ds_ltarget)
+ : nua_registration_add_contact_to_response(nh, msg, sip, NULL, m))
+ < 0)
;
else {
int term;
-
+ sip_contact_t *ltarget = NULL;
+
term = sip_response_terminates_dialog(sr->sr_status, sr->sr_method, NULL);
sr->sr_terminating = (term < 0) ? -1 : (term > 0 || sr->sr_terminating);
+ if (sr->sr_target_refresh && sr->sr_status < 300 && !sr->sr_terminating &&
+ user_contact && sip->sip_contact) {
+ /* Save Contact given by application */
+ ltarget = sip_contact_dup(nh->nh_home, sip->sip_contact);
+ }
+
retval = sr->sr_methods->sm_respond(sr, next_tags);
if (sr->sr_status < 200)
@@ -1514,6 +1516,16 @@ int nua_server_respond(nua_server_request_t *sr, tagi_t const *tags)
assert(sr->sr_status >= 200 || sr->sr_response.msg);
+ if (ltarget) {
+ if (sr->sr_status < 300) {
+ nua_dialog_state_t *ds = nh->nh_ds;
+ msg_header_free(nh->nh_home, (msg_header_t *)ds->ds_ltarget);
+ ds->ds_ltarget = ltarget;
+ }
+ else
+ msg_header_free(nh->nh_home, (msg_header_t *)ltarget);
+ }
+
return retval;
}
@@ -1714,6 +1726,9 @@ int nua_base_server_report(nua_server_request_t *sr, tagi_t const *tags)
static int nua_client_request_try(nua_client_request_t *cr);
static int nua_client_request_sendmsg(nua_client_request_t *cr,
msg_t *msg, sip_t *sip);
+static void nua_client_restart_after(su_root_magic_t *magic,
+ su_timer_t *timer,
+ nua_client_request_t *cr);
/**Create a client request.
*
@@ -1860,6 +1875,9 @@ void nua_client_request_destroy(nua_client_request_t *cr)
cr->cr_orq = NULL;
+ if (cr->cr_timer)
+ su_timer_destroy(cr->cr_timer), cr->cr_timer = NULL;
+
if (cr->cr_target)
su_free(nh->nh_home, cr->cr_target);
@@ -2255,9 +2273,20 @@ int nua_client_request_sendmsg(nua_client_request_t *cr, msg_t *msg, sip_t *sip)
* registrar is also added to the request message.
*/
if (cr->cr_method != sip_method_register) {
+ if (cr->cr_contactize && cr->cr_has_contact) {
+ sip_contact_t *ltarget = sip_contact_dup(nh->nh_home, sip->sip_contact);
+ if (ds->ds_ltarget)
+ msg_header_free(nh->nh_home, (msg_header_t *)ds->ds_ltarget);
+ ds->ds_ltarget = ltarget;
+ }
+
+ if (ds->ds_ltarget && !cr->cr_has_contact)
+ sip_add_dup(msg, sip, (sip_header_t *)ds->ds_ltarget);
+
if (nua_registration_add_contact_to_request(nh, msg, sip,
cr->cr_contactize &&
- !cr->cr_has_contact,
+ !cr->cr_has_contact &&
+ !ds->ds_ltarget,
!ds->ds_route) < 0)
return -1;
}
@@ -2443,7 +2472,7 @@ int nua_client_response(nua_client_request_t *cr,
/** Check if request should be restarted.
*
- * @retval 1 if restarted or waring for restart
+ * @retval 1 if restarted or waiting for restart
* @retval 0 otherwise
*/
int nua_client_check_restart(nua_client_request_t *cr,
@@ -2470,8 +2499,7 @@ int nua_base_client_check_restart(nua_client_request_t *cr,
sip_t const *sip)
{
nua_handle_t *nh = cr->cr_owner;
-
- /* XXX - handle Retry-After */
+ nta_outgoing_t *orq;
if (status == 302 || status == 305) {
sip_route_t r[1];
@@ -2520,7 +2548,6 @@ int nua_base_client_check_restart(nua_client_request_t *cr,
if ((status == 401 && sip->sip_www_authenticate) ||
(status == 407 && sip->sip_proxy_authenticate)) {
int server = 0, proxy = 0;
- nta_outgoing_t *orq;
if (sip->sip_www_authenticate)
server = auc_challenge(&nh->nh_auth, nh->nh_home,
@@ -2544,7 +2571,8 @@ int nua_base_client_check_restart(nua_client_request_t *cr,
return nua_client_restart(cr, 100, "Request Authorized by Cache");
orq = cr->cr_orq, cr->cr_orq = NULL;
- cr->cr_wait_for_cred = 1;
+
+ cr->cr_waiting = cr->cr_wait_for_cred = 1;
nua_client_report(cr, status, phrase, NULL, orq, NULL);
nta_outgoing_destroy(orq);
@@ -2552,9 +2580,44 @@ int nua_base_client_check_restart(nua_client_request_t *cr,
}
}
+ if (500 <= status && status < 600 &&
+ sip->sip_retry_after &&
+ sip->sip_retry_after->af_delta < 32) {
+ char phrase[18]; /* Retry-After: XXXX\0 */
+
+ if (cr->cr_timer == NULL)
+ cr->cr_timer = su_timer_create(su_root_task(nh->nh_nua->nua_root), 0);
+
+ if (su_timer_set_interval(cr->cr_timer, nua_client_restart_after, cr,
+ sip->sip_retry_after->af_delta * 1000) < 0)
+ return 0; /* Too bad */
+
+ snprintf(phrase, sizeof phrase, "Retry After %u",
+ (unsigned)sip->sip_retry_after->af_delta);
+
+ orq = cr->cr_orq, cr->cr_orq = NULL;
+ cr->cr_waiting = cr->cr_wait_for_timer = 1;
+ nua_client_report(cr, 100, phrase, NULL, orq, NULL);
+ nta_outgoing_destroy(orq);
+ return 1;
+ }
+
return 0; /* This was a final response that cannot be restarted. */
}
+/** Request restarted by timer */
+static
+void nua_client_restart_after(su_root_magic_t *magic,
+ su_timer_t *timer,
+ nua_client_request_t *cr)
+{
+ if (!cr->cr_wait_for_timer)
+ return;
+
+ cr->cr_waiting = cr->cr_wait_for_timer = 0;
+ nua_client_restart_request(cr, cr->cr_terminating, NULL);
+}
+
/** Restart request.
*
* @retval 1 if restarted
@@ -2587,7 +2650,6 @@ int nua_client_restart(nua_client_request_t *cr,
msg_destroy(msg);
}
-
if (error) {
cr->cr_graceful = graceful;
cr->cr_terminated = terminated;
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 9855fb7913..024df1cb6d 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c
@@ -389,7 +389,7 @@ static int nua_subscribe_client_response(nua_client_request_t *cr,
if (eu->eu_substate == nua_substate_terminated)
eu->eu_substate = nua_substate_embryonic;
- nua_dialog_usage_refresh_range(du, delta, delta);
+ nua_dialog_usage_set_refresh_range(du, delta, delta);
}
else {
eu->eu_substate = nua_substate_terminated;
@@ -635,6 +635,7 @@ int nua_notify_server_report(nua_server_request_t *sr, tagi_t const *tags)
sip_t const *sip = sr->sr_request.sip;
enum nua_substate substate = nua_substate_terminated;
sip_time_t delta = SIP_TIME_MAX;
+ sip_event_t const *o = sip->sip_event;
int retry = -1;
int retval;
@@ -670,6 +671,7 @@ int nua_notify_server_report(nua_server_request_t *sr, tagi_t const *tags)
retval = nua_base_server_treport(sr, /* can destroy sr */
NUTAG_SUBSTATE(substate),
+ SIPTAG_EVENT(o),
TAG_NEXT(tags));
if (retval != 1 || du == NULL)
@@ -681,7 +683,7 @@ int nua_notify_server_report(nua_server_request_t *sr, tagi_t const *tags)
else if (retry >= 0) { /* Try to subscribe again */
/* XXX - this needs through testing */
nua_dialog_remove(nh, nh->nh_ds, du); /* tear down */
- nua_dialog_usage_refresh_range(du, retry, retry + 5);
+ nua_dialog_usage_set_refresh_range(du, retry, retry + 5);
}
else {
nua_dialog_usage_set_refresh(du, delta);
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 592124b09b..019a600c4b 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c
@@ -196,7 +196,7 @@ int test_180rel(struct context *ctx)
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);
+ run_a_until(ctx, -1, save_until_final_response);
ep = event_by_type(e->next, nua_r_prack);
}
@@ -332,10 +332,9 @@ int test_prack_auth(struct context *ctx)
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.3: Call with 100rel and 180\n");
+ printf("TEST NUA-10.1.3: Call with 100rel, PRACK is challenged\n");
/* Test for authentication during 100rel
@@ -396,10 +395,6 @@ int test_prack_auth(struct context *ctx)
TEST(e->data->e_status, 407);
TEST_1(sip = sip_object(e->data->e_msg));
TEST_1(au = sip->sip_proxy_authenticate);
- TEST_1(auth_get_params(NULL, au->au_params,
- "algorithm=md5", &md5,
- "algorithm=md5-sess", &md5sess,
- NULL) > 0);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
@@ -415,24 +410,15 @@ int test_prack_auth(struct context *ctx)
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);
- }
-
- 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);
+ if (e->data->e_status == 100 || e->data->e_status == 407) {
+ /* The final response to PRACK may be received after ACK is sent */
+ if (!event_by_type(e->next, nua_r_prack))
+ run_bc_until(ctx, -1, save_events, -1, save_until_final_response);
+ TEST_1(e = ep = event_by_type(e->next, nua_r_prack));
}
+ TEST_E(e->data->e_event, nua_r_prack);
TEST(e->data->e_status, 200);
TEST_1(e = ei); TEST_E(e->data->e_event, nua_r_invite);
@@ -441,7 +427,7 @@ int test_prack_auth(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 || !ep->next || (ep->data->e_status != 200 && !e->next->next->next));
+ TEST_1(!e->next || !ep->next);
free_events_in_list(ctx, c->events);
/*
@@ -626,7 +612,7 @@ int test_183rel(struct context *ctx)
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);
+ run_a_until(ctx, -1, save_until_final_response);
ep = event_by_type(e->next, nua_r_prack);
}
@@ -1024,7 +1010,7 @@ int test_preconditions(struct context *ctx)
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);
+ run_a_until(ctx, -1, save_until_final_response);
ep = event_by_type(e->next, nua_r_prack);
}
@@ -1213,6 +1199,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, *eu, *ei;
+ enum nua_callstate ustate, istate;
if (print_headings)
printf("TEST NUA-10.4.1: Call with preconditions and non-100rel 180\n");
@@ -1227,15 +1214,23 @@ int test_preconditions2(struct context *ctx)
|-------PRACK------->|
|<-------200---------|
| |
- |<-------180---------|
- | |
- |<------200 OK-------|
+ |-------UPDATE------>|
+ +------------------------+
+ | |<-------200---------| |
+ | | | |
+ | |<-------180---------| |
+ | | | |
+ | |<------200 OK-------| |
+ +------------------------+
|--------ACK-------->|
| |
|<-------BYE---------|
|-------200 OK-------|
| |
+ Note that the boxed responses above can be re-ordered
+ (180 or 200 OK to INVITE is received before 200 OK to UPDATE).
+ ACK, however, is sent only after 200 OK to both UPDATE and INVITE.
*/
a_call->sdp = "m=audio 5008 RTP/AVP 8";
@@ -1280,6 +1275,7 @@ int test_preconditions2(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_proceeding);
TEST_1(is_answer_recv(e->data->e_tags));
+ /* Offer is sent in PRACK */
TEST_1(is_offer_sent(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack);
@@ -1296,13 +1292,14 @@ int test_preconditions2(struct context *ctx)
TEST_1(!is_answer_recv(e->data->e_tags));
TEST_1(is_offer_sent(e->data->e_tags));
+ /* The final response to the UPDATE and INVITE can be received in any order */
eu = event_by_type(e->next, nua_r_update);
ei = event_by_type(e->next, nua_r_invite);
TEST_1(e = eu); TEST_E(e->data->e_event, nua_r_update);
TEST(e->data->e_status, 200);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
- TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+ ustate = callstate(e->data->e_tags);
TEST_1(is_answer_recv(e->data->e_tags));
TEST_1(!is_offer_sent(e->data->e_tags));
@@ -1312,14 +1309,25 @@ int test_preconditions2(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 = eu->next->next == ei ? ei->next->next : eu->next->next);
+ /* Final response to INVITE */
+ TEST_1(ei = event_by_type(ei->next, nua_r_invite));
- 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_E(ei->data->e_event, nua_r_invite); TEST(ei->data->e_status, 200);
+ TEST_1(e = ei->next); TEST_E(e->data->e_event, nua_i_state);
+ istate = callstate(e->data->e_tags);
TEST_1(!is_offer_answer_done(e->data->e_tags));
- TEST_1(!e->next);
+
+ if (eu == e->next) {
+ /* 200 OK to UPDATE is received after 200 OK to INVITE */
+ TEST(ustate, nua_callstate_ready);
+ TEST(istate, nua_callstate_completing);
+ }
+ else {
+ /* 200 OK to UPDATE is received before 200 OK to INVITE */
+ TEST(ustate, nua_callstate_proceeding);
+ TEST(istate, nua_callstate_ready);
+ }
+
free_events_in_list(ctx, a->events);
/*
@@ -1499,7 +1507,7 @@ int test_update_by_uas(struct context *ctx)
if (print_headings)
printf("TEST NUA-10.5.1: Call with dual UPDATE\n");
-/* Test for precondition:
+/* Test for update by UAS.
A B
|-------INVITE------>|
@@ -1510,9 +1518,11 @@ int test_update_by_uas(struct context *ctx)
|<-------200---------|
| |
|-------UPDATE------>|
- |<-------200---------|
- | |
- |<------UPDATE-------|
+ +------------------------+
+ | |<-------200---------| |
+ | | | |
+ | |<------UPDATE-------| |
+ +------------------------+
|--------200-------->|
| |
|<-------180---------|
@@ -1526,6 +1536,9 @@ int test_update_by_uas(struct context *ctx)
|-------200 OK-------|
| |
+ Note that the 200 OK to UPDATE from A and UPDATE from B may be re-ordered
+ In that case, A will respond with 500/Retry-After and B will retry UPDATE.
+ See do {} while () loop below.
*/
a_call->sdp = "m=audio 5008 RTP/AVP 8";
@@ -1612,7 +1625,7 @@ int test_update_by_uas(struct context *ctx)
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);
+ run_a_until(ctx, -1, save_until_final_response);
ep = event_by_type(e->next, nua_r_prack);
}
@@ -1672,17 +1685,24 @@ int test_update_by_uas(struct context *ctx)
TEST_1(is_answer_sent(e->data->e_tags));
/* sent UPDATE */
- TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
- TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
- TEST_1(is_offer_sent(e->data->e_tags));
- TEST_1(!is_offer_recv(e->data->e_tags));
- TEST_1(!is_answer_sent(e->data->e_tags));
- TEST_1(!is_answer_recv(e->data->e_tags));
+ do {
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(!is_offer_recv(e->data->e_tags));
+ TEST_1(!is_answer_sent(e->data->e_tags));
+ TEST_1(!is_answer_recv(e->data->e_tags));
- TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_update);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_update);
+ if (e->data->e_status == 100) {
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(sip->sip_status->st_status, 500); TEST_1(sip->sip_retry_after);
+ }
+ } while (e->data->e_status == 100);
+ 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_early); /* EARLY */
- TEST_1(!is_offer_sent(e->data->e_tags));
+ TEST_1(!is_offer_sent(e->data->e_tags)); /* XXX */
TEST_1(!is_offer_recv(e->data->e_tags));
TEST_1(!is_answer_sent(e->data->e_tags));
TEST_1(is_answer_recv(e->data->e_tags));
@@ -2060,7 +2080,7 @@ int test_180rel_cancel2(struct context *ctx)
int test_100rel(struct context *ctx)
{
- int retval;
+ int retval = 0;
retval = test_180rel(ctx); RETURN_ON_SINGLE_FAILURE(retval);
retval = test_prack_auth(ctx); RETURN_ON_SINGLE_FAILURE(retval);
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 e5b9eb18e3..836e35e92f 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
@@ -1356,14 +1356,262 @@ int accept_upgrade(CONDITION_PARAMS)
}
}
+/* Basic call and re-INVITE with user-specified Contact:
+
+ A B
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |-----re-INVITE----->|
+ |<------200 OK-------|
+ |--------ACK-------->|
+ | |
+ |<-------BYE---------|
+ |-------200 OK------>|
+ | |
+
+ Client transitions:
+ INIT -(C1)-> CALLING -(C2a)-> PROCEEDING -(C3+C4)-> READY
+ Server transitions:
+ INIT -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S4)-> READY
+
+ Both client and server save Contact from nua_invite() and nua_respond(),
+ respectively.
+
+ INIT -(C1)-> CALLING -(C3a+C4)-> READY
+ INIT -(S3c)-> COMPLETED -(S4)-> READY
+
+ Both client and server use saved Contact.
+
+ B sends BYE:
+ READY -(T2)-> TERMINATING -(T3)-> TERMINATED
+ A receives BYE:
+ READY -(T1)-> TERMINATED
+
+ See @page nua_call_model in nua.docs for more information
+*/
+
+static sip_contact_t *contact_for_b;
+
+int accept_call_with_contact(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_received:
+ RESPOND(ep, call, nh, SIP_180_RINGING,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ SIPTAG_CONTACT(contact_for_b),
+ TAG_END());
+ return 0;
+ case nua_callstate_early:
+ RESPOND(ep, call, nh, SIP_200_OK,
+ TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+ SIPTAG_CONTACT(contact_for_b),
+ TAG_END());
+ return 0;
+ case nua_callstate_ready:
+ return 1;
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int test_basic_call_6(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;
+ sip_t *sip;
+
+ sip_contact_t ma[1], mb[1];
+
+ if (print_headings)
+ printf("TEST NUA-3.1: Basic call\n");
+
+ a_call->sdp = "m=audio 5008 RTP/AVP 8";
+ b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+ TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ *ma = *a->contact;
+ ma->m_display = "Alice B.";
+ ma->m_url->url_user = "a++a";
+
+ *mb = *b->contact;
+ mb->m_display = "Bob A.";
+ mb->m_url->url_user = "b++b";
+
+ contact_for_b = mb;
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ SIPTAG_CONTACT(ma),
+ TAG_END());
+
+ run_ab_until(ctx, -1, until_ready, -1, accept_call_with_contact);
+
+ TEST_1(nua_handle_has_active_call(a_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+ TEST_1(nua_handle_has_active_call(b_call->nh));
+ TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+ CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+ PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+ TEST(e->data->e_status, 180);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_payload);
+ 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_answer_recv(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(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_payload);
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Bob A.");
+ TEST_S(sip->sip_contact->m_url->url_user, "b++b");
+ /* Test that B uses application-specific contact */
+ TEST_1(sip->sip_contact->m_url->url_user);
+ 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(!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
+ EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+ COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Alice B.");
+ TEST_S(sip->sip_contact->m_url->url_user, "a++a");
+ 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(is_answer_sent(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_completed); /* COMPLETED */
+ TEST_1(is_answer_sent(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(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ /* re-INVITE */
+ INVITE(a, a_call, a_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_ready, -1, until_ready);
+
+ 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, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Bob A.");
+ TEST_S(sip->sip_contact->m_url->url_user, "b++b");
+ /* Test that B uses application-specific contact */
+ TEST_1(sip->sip_contact->m_url->url_user);
+ 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(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 200);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ TEST_S(sip->sip_contact->m_display, "Alice B.");
+ TEST_S(sip->sip_contact->m_url->url_user, "a++a");
+ 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 = 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(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ BYE(b, b_call, b_call->nh, TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+ /* B transitions:
+ READY --(T2)--> TERMINATING: nua_bye()
+ TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+ */
+ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, b->events);
+
+ TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+ /* A transitions:
+ READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+ */
+ TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+ TEST(e->data->e_status, 200);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(!e->next);
+ free_events_in_list(ctx, a->events);
+
+ TEST_1(!nua_handle_has_active_call(a_call->nh));
+
+ 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-3.1: PASSED\n");
+
+ END();
+}
+
int test_basic_call(struct context *ctx)
{
- return
- test_basic_call_1(ctx)
+ return 0
+ || test_basic_call_1(ctx)
|| test_basic_call_2(ctx)
|| test_basic_call_3(ctx)
|| test_basic_call_4(ctx)
|| test_basic_call_5(ctx)
+ || test_basic_call_6(ctx)
|| test_video_call_1(ctx)
;
}
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 a3bf75e179..c3be7ad0dd 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
@@ -262,6 +262,7 @@ int test_reject_b(struct context *ctx)
/* ------------------------------------------------------------------------ */
int reject_302(CONDITION_PARAMS), reject_305(CONDITION_PARAMS);
+int reject_500_retry_after(CONDITION_PARAMS);
int redirect_always(CONDITION_PARAMS);
int reject_604(CONDITION_PARAMS);
@@ -270,12 +271,21 @@ int reject_604(CONDITION_PARAMS);
| |
|-------INVITE------>|
|<----100 Trying-----|
- | |
|<-----302 Other-----|
|--------ACK-------->|
| |
|-------INVITE------>|
|<----100 Trying-----|
+ |<--305 Use Proxy----|
+ |--------ACK-------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |<----500 Retry------|
+ |--------ACK-------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
| |
|<----180 Ringing----|
| |
@@ -330,29 +340,33 @@ int reject_305(CONDITION_PARAMS)
case nua_callstate_terminated:
if (call)
nua_handle_destroy(call->nh), call->nh = NULL;
- ep->next_condition = reject_604;
+ ep->next_condition = reject_500_retry_after;
return 0;
default:
return 0;
}
}
-int redirect_always(CONDITION_PARAMS)
+int reject_500_retry_after(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_invite) {
- char user[30];
- sip_contact_t m[1];
- *m = *ep->contact;
- snprintf(user, sizeof user, "user-%u", ep->flags.n++);
- m->m_url->url_user = user;
- RESPOND(ep, call, nh, SIP_302_MOVED_TEMPORARILY,
- SIPTAG_CONTACT(m), TAG_END());
- nua_handle_destroy(nh);
- call->nh = NULL;
- return 1;
+ sip_retry_after_t af[1];
+ sip_retry_after_init(af)->af_delta = 1;
+ RESPOND(ep, call, nh, 500, "Retry After", SIPTAG_RETRY_AFTER(af), TAG_END());
+ }
+ else if (event == nua_i_state) switch (callstate(tags)) {
+ case nua_callstate_terminated:
+ if (call)
+ nua_handle_destroy(call->nh), call->nh = NULL;
+ ep->next_condition = reject_604;
+ break;
+ default:
+ break;
}
return 0;
@@ -381,6 +395,28 @@ int reject_604(CONDITION_PARAMS)
}
}
+int redirect_always(CONDITION_PARAMS)
+{
+ if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+ return 0;
+
+ if (event == nua_i_invite) {
+ char user[30];
+ sip_contact_t m[1];
+ *m = *ep->contact;
+ snprintf(user, sizeof user, "user-%u", ep->flags.n++);
+ m->m_url->url_user = user;
+ RESPOND(ep, call, nh, SIP_302_MOVED_TEMPORARILY,
+ SIPTAG_CONTACT(m), TAG_END());
+ nua_handle_destroy(nh);
+ call->nh = NULL;
+ return 1;
+ }
+
+ return 0;
+}
+
+
int test_reject_302(struct context *ctx)
{
BEGIN();
@@ -407,18 +443,29 @@ int test_reject_302(struct context *ctx)
/*
A reject-3 B
- | |
+ | |
|-------INVITE------>|
|<----100 Trying-----|
- | |
+ | |
|<-----302 Other-----|
|--------ACK-------->|
- | |
+ | |
|-------INVITE------>|
|<----100 Trying-----|
- | |
+ |<---305 Use Proxy---|
+ |--------ACK-------->|
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |<-----500 Retry-----|
+ |--------ACK-------->|
+ | |
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
|<----180 Ringing----|
- | |
+ | |
|<---604 Nowhere-----|
|--------ACK-------->|
*/
@@ -445,6 +492,12 @@ int test_reject_302(struct context *ctx)
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, 100);
+ TEST(sip_object(e->data->e_msg)->sip_status->st_status, 500);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+ TEST_1(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 */
@@ -475,6 +528,13 @@ int test_reject_302(struct context *ctx)
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_invite);
TEST(e->data->e_status, 100);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+ TEST_1(is_offer_recv(e->data->e_tags));
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_invite);
+ TEST(e->data->e_status, 100);
TEST_1(sip = sip_object(e->data->e_msg));
TEST_1(sip->sip_request);
TEST_S(sip->sip_request->rq_url->url_user, "302");
@@ -1430,8 +1490,8 @@ int test_call_timeouts(struct context *ctx)
|<--------200--------|
|--ACK-X |
| |
- |---------BYE------->|
- |<-------200 OK------|
+ |<--------BYE--------|
+ |--------200 OK----->|
*/
@@ -1484,6 +1544,109 @@ int test_call_timeouts(struct context *ctx)
if (print_headings)
printf("TEST NUA-4.7.3: PASSED\n");
+ if (!ctx->nat)
+ goto completed_4_7_4;
+
+ if (print_headings)
+ printf("TEST NUA-4.7.4: 200 OK timeout after client has timed out\n");
+
+ if (ctx->expensive)
+ nua_set_params(b->nua, NTATAG_SIP_T1X64(34000), TAG_END());
+ else
+ nua_set_params(b->nua, NTATAG_SIP_T1X64(4000), 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()));
+
+ TEST_1(f = test_nat_add_filter(ctx->nat, filter_ACK, NULL, nat_outbound));
+
+ INVITE(a, a_call, a_call->nh,
+ TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+ SIPTAG_SUBJECT_STR("NUA-4.7.4"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+ run_ab_until(ctx, -1, until_terminated, -1, accept_call);
+
+ /*
+ A accept_call B
+ | |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ | |
+ |<----180 Ringing----|
+ | |
+ |<--------200--------| Timer H'
+ |--------ACK-----X X--+
+ | | |
+ |<--------200--------| |
+ |--------ACK-----X | |
+ | | |
+ |<--------200--------| |
+ | | |
+ |<--------200--------| |
+ | | |
+ | |<-+
+ |<--------BYE--------|
+ |--------200 OK----->|
+
+ */
+
+ /*
+ */
+
+ 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(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_ready); /* READY */
+ 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);
+ TEST_1(!e->next);
+
+ /*
+ Server transitions:
+ -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S5)-> TERMINATING
+ -(S10)-> TERMINATED -X
+ */
+ 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 = e->next); TEST_E(e->data->e_event, nua_i_error);
+ TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+ TEST(callstate(e->data->e_tags), nua_callstate_terminating);
+ TEST_1(e = e->next); 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);
+ 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;
+
+ TEST_1(test_nat_remove_filter(ctx->nat, f) == 0);
+
+ if (print_headings)
+ printf("TEST NUA-4.7.4: PASSED\n");
+
+ nua_set_params(b->nua, NTATAG_SIP_T1X64(2000), TAG_END());
+ run_b_until(ctx, nua_r_set_params, until_final_response);
+
+ completed_4_7_4:
+
/* XXX - PRACK timeout, PRACK failing, media failing, re-INVITEs */
if (print_headings)
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 6e69fa465c..107247c8cc 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
@@ -109,6 +109,24 @@ int cancel_when_calling(CONDITION_PARAMS)
}
}
+int bye_when_calling(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_calling:
+ BYE(ep, call, nh, TAG_END());
+ return 0;
+ case nua_callstate_terminated:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
int cancel_when_ringing(CONDITION_PARAMS)
{
@@ -163,7 +181,7 @@ int test_call_cancel(struct context *ctx)
b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
if (print_headings)
- printf("TEST NUA-5.1: cancel call\n");
+ printf("TEST NUA-5.1.1: cancel call\n");
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
@@ -212,10 +230,65 @@ int test_call_cancel(struct context *ctx)
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
if (print_headings)
- printf("TEST NUA-5.1: PASSED\n");
+ printf("TEST NUA-5.1.1: PASSED\n");
/* ------------------------------------------------------------------------ */
+ if (print_headings)
+ printf("TEST NUA-5.1.2: cancel call (with nua_bye())\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)),
+ SIPTAG_SUBJECT_STR("TEST NUA-5.1.2"),
+ SOATAG_USER_SDP_STR(a_call->sdp),
+ TAG_END());
+
+ run_ab_until(ctx, -1, bye_when_calling, -1, until_terminated);
+
+ /* Client transitions:
+ INIT -(C1)-> CALLING: nua_invite(), nua_i_state, nua_cancel()
+ CALLING -(C6a)-> 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); /* CALLING */
+ TEST_1(is_offer_sent(e->data->e_tags));
+ TEST_1(e = e->next); 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_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); /* TERMINATED */
+ TEST_1(!e->next);
+
+ /*
+ Server transitions:
+ INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+ RECEIVED -(S6a)--> 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_cancel);
+ 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-5.1.2: PASSED\n");
+
+ /* ----------------------------------------------------------------------- */
+
if (print_headings)
printf("TEST NUA-5.2.1: cancel call when ringing\n");
@@ -679,20 +752,24 @@ int test_call_destroy_4(struct context *ctx)
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); if (e->data->e_event == nua_r_invite) {
+ 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);
+ }
+ if (e->data->e_event == nua_r_invite) {
+ 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_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
- TEST_1(!e->next);
- TEST_1(!e->next);
free_events_in_list(ctx, a->events);
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
@@ -1445,8 +1522,6 @@ int test_bye_to_invalid_contact(struct context *ctx)
TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
- TEST_1(!e->next);
- free_events_in_list(ctx, b->events);
TEST_1(!nua_handle_has_active_call(b_call->nh));
TEST_1(nua_handle_has_active_call(a_call->nh));
@@ -1457,19 +1532,21 @@ int test_bye_to_invalid_contact(struct context *ctx)
if (print_headings)
printf("TEST NUA-6.4.3: PASSED\n");
- if (!ctx->p)
+ if (!ctx->p) {
+ free_events_in_list(ctx, b->events);
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);
+ if (!e->next || (!e->next->next || !e->next->data->e_status != 200))
+ /* 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) {
+ for (e = e->next; e; e = e->next) {
TEST_E(e->data->e_event, nua_r_register);
TEST_1(sip = sip_object(e->data->e_msg));
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 269275d9f1..e8f67d94c3 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c
@@ -36,6 +36,7 @@
#include "test_nua.h"
#include
+#include
#if HAVE_FUNC
#elif HAVE_FUNCTION
@@ -107,17 +108,52 @@ int test_nua_init(struct context *ctx,
TEST_1(close(temp) == 0);
ctx->p = test_proxy_create(ctx->root,
- AUTHTAG_METHOD("Digest"),
- AUTHTAG_REALM("test-proxy"),
- AUTHTAG_OPAQUE("kuik"),
- AUTHTAG_DB(passwd_name),
- AUTHTAG_QOP("auth-int"),
- AUTHTAG_ALGORITHM("md5"),
- AUTHTAG_NEXT_EXPIRES(60),
TAG_IF(ctx->proxy_logging, TPTAG_LOG(1)),
TAG_END());
- ctx->proxy_tests = ctx->p != NULL;
+ if (ctx->p) {
+ ctx->a.domain =
+ test_proxy_add_domain(ctx->p,
+ URL_STRING_MAKE("sip:example.com")->us_url,
+ AUTHTAG_METHOD("Digest"),
+ AUTHTAG_REALM("test-proxy"),
+ AUTHTAG_OPAQUE("kuik"),
+ AUTHTAG_DB(passwd_name),
+ AUTHTAG_QOP("auth-int"),
+ AUTHTAG_ALGORITHM("md5"),
+ AUTHTAG_NEXT_EXPIRES(60),
+ TAG_END());
+
+ ctx->b.domain =
+ test_proxy_add_domain(ctx->p,
+ URL_STRING_MAKE("sip:example.org")->us_url,
+ AUTHTAG_METHOD("Digest"),
+ AUTHTAG_REALM("test-proxy"),
+ AUTHTAG_OPAQUE("kuik"),
+ AUTHTAG_DB(passwd_name),
+ AUTHTAG_QOP("auth-int"),
+ AUTHTAG_ALGORITHM("md5"),
+ AUTHTAG_NEXT_EXPIRES(60),
+ TAG_END());
+
+ test_proxy_domain_set_outbound(ctx->b.domain, 1);
+
+ ctx->c.domain =
+ test_proxy_add_domain(ctx->p,
+ URL_STRING_MAKE("sip:example.net")->us_url,
+ AUTHTAG_METHOD("Digest"),
+ AUTHTAG_REALM("test-proxy"),
+ AUTHTAG_OPAQUE("kuik"),
+ AUTHTAG_DB(passwd_name),
+ AUTHTAG_QOP("auth-int"),
+ AUTHTAG_ALGORITHM("md5"),
+ AUTHTAG_NEXT_EXPIRES(60),
+ AUTHTAG_MAX_NCOUNT(1),
+ TAG_END());
+
+ ctx->proxy_tests = 1;
+ }
+
if (print_headings)
printf("TEST NUA-2.1.1: PASSED\n");
@@ -276,6 +312,7 @@ int test_nua_init(struct context *ctx,
NUTAG_INSTANCE(ctx->b.instance),
/* Quicker timeout */
NTATAG_SIP_T1X64(2000),
+ TPTAG_KEEPALIVE(100),
TAG_IF(ctx->b.logging, TPTAG_LOG(1)),
TAG_END());
TEST_1(ctx->b.nua);
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 5798748dab..cc41308888 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.c
@@ -65,6 +65,8 @@ int tstflags = 0;
static RETSIGTYPE sig_alarm(int s)
{
fprintf(stderr, "%s: FAIL! test timeout!\n", name);
+ if (tstflags & tst_abort)
+ abort();
exit(1);
}
#endif
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 5947f04f72..a77c7f54ee 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h
@@ -139,6 +139,7 @@ struct context
int running;
+ struct domain *domain;
condition_function *next_condition;
nua_event_t next_event, last_event;
nua_t *nua;
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 1e89252126..355d289adb 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c
@@ -146,47 +146,59 @@ void print_event(nua_event_t event,
tagi_t tags[])
{
tagi_t const *t;
+ static su_nanotime_t started = 0;
+ su_nanotime_t now;
+ char timestamp[32];
+
+ su_nanotime(&now);
+
+ if (started == 0) started = now;
+
+ now -= started; now /= 1000000;
+
+ snprintf(timestamp, sizeof timestamp, "%03u.%03u",
+ (unsigned)(now / 1000), (unsigned)(now % 1000));
if (event == nua_i_state) {
- fprintf(stderr, "%s.nua(%p): event %s %s\n",
+ fprintf(stderr, "%s %s.nua(%p): event %s %s\n", timestamp,
ep->name, (void *)nh, nua_event_name(event),
nua_callstate_name(callstate(tags)));
}
else if ((int)event >= nua_r_set_params) {
t = tl_find(tags, nutag_substate);
if (t) {
- fprintf(stderr, "%s.nua(%p): event %s status %u %s (%s)\n",
+ fprintf(stderr, "%s %s.nua(%p): event %s status %u %s (%s)\n", timestamp,
ep->name, (void*)nh, nua_event_name(event), status, phrase,
nua_substate_name(t->t_value));
}
else {
- fprintf(stderr, "%s.nua(%p): event %s status %u %s\n",
+ fprintf(stderr, "%s %s.nua(%p): event %s status %u %s\n", timestamp,
ep->name, (void *)nh, nua_event_name(event), status, phrase);
}
}
else if (event == nua_i_notify) {
t = tl_find(tags, nutag_substate);
- fprintf(stderr, "%s.nua(%p): event %s %s (%s)\n",
+ fprintf(stderr, "%s %s.nua(%p): event %s %s (%s)\n", timestamp,
ep->name, (void *)nh, nua_event_name(event), phrase,
nua_substate_name(t ? t->t_value : 0));
}
else if ((int)event >= 0) {
- fprintf(stderr, "%s.nua(%p): event %s %s\n",
+ fprintf(stderr, "%s %s.nua(%p): event %s %s\n", timestamp,
ep->name, (void *)nh, nua_event_name(event), phrase);
}
else if (status > 0) {
- fprintf(stderr, "%s.nua(%p): call %s() with status %u %s\n",
+ fprintf(stderr, "%s %s.nua(%p): call %s() with status %u %s\n", timestamp,
ep->name, (void *)nh, operation, status, phrase);
}
else {
t = tl_find(tags, siptag_subject_str);
if (t && t->t_value) {
char const *subject = (char const *)t->t_value;
- fprintf(stderr, "%s.nua(%p): call %s() \"%s\"\n",
+ fprintf(stderr, "%s %s.nua(%p): call %s() \"%s\"\n", timestamp,
ep->name, (void *)nh, operation, subject);
}
else
- fprintf(stderr, "%s.nua(%p): call %s()\n",
+ fprintf(stderr, "%s %s.nua(%p): call %s()\n", timestamp,
ep->name, (void *)nh, operation);
}
@@ -292,7 +304,7 @@ void run_abc_until(struct context *ctx,
memset(&c->flags, 0, sizeof c->flags);
for (; a->running || b->running || c->running;) {
- su_root_step(ctx->root, 1000);
+ su_root_step(ctx->root, 100);
}
}
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 bcdb160bb3..e2b169a66b 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c
@@ -35,14 +35,17 @@
#include
struct proxy;
-struct proxy_transaction;
+struct domain;
+union proxy_or_domain;
+struct proxy_tr;
+struct client_tr;
struct registration_entry;
struct binding;
#define SU_ROOT_MAGIC_T struct proxy
-#define NTA_LEG_MAGIC_T struct proxy
-#define NTA_OUTGOING_MAGIC_T struct proxy_transaction
-#define NTA_INCOMING_MAGIC_T struct proxy_transaction
+#define NTA_LEG_MAGIC_T union proxy_or_domain
+#define NTA_OUTGOING_MAGIC_T struct client_tr
+#define NTA_INCOMING_MAGIC_T struct proxy_tr
#include
#include
@@ -83,50 +86,74 @@ STORAGE void PREFIX ##_remove(T *node) \
} \
extern int LIST_DUMMY_VARIABLE
-#include "test_proxy.h"
+#include
+#include
struct proxy {
su_home_t home[1];
+ void *magic;
su_root_t *parent;
su_clone_r clone;
tagi_t *tags;
su_root_t *root;
- auth_mod_t *auth;
nta_agent_t *agent;
url_t const *uri;
-
- nta_leg_t *defleg;
+ url_t const *rr_uri;
- nta_leg_t *example_net;
- nta_leg_t *example_org;
- nta_leg_t *example_com;
+ nta_leg_t *defleg;
sip_contact_t *transport_contacts;
- struct proxy_transaction *stateless;
- struct proxy_transaction *transactions;
+ struct proxy_tr *stateless;
+ struct proxy_tr *transactions;
+
+ struct domain *domains;
+
+ struct {
+ sip_time_t session_expires, min_se;
+ } prefs;
+};
+
+struct domain {
+ su_home_t home[1];
+ void *magic;
+ struct proxy *proxy;
+ struct domain *next, **prev;
+
+ url_t *uri;
+
+ nta_leg_t *rleg, *uleg;
+
+ auth_mod_t *auth;
struct registration_entry *entries;
struct {
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;
-};
+ int authorize;
+ } prefs;
+
+ tagi_t *tags;
+};
+
+LIST_PROTOS(static, domain, struct domain);
+static int _domain_init(void *_d);
+static int domain_init(struct domain *domain);
+static void domain_destroy(struct domain *domain);
+
+LIST_BODIES(static, domain, struct domain, next, prev);
LIST_PROTOS(static, registration_entry, struct registration_entry);
-static struct registration_entry *registration_entry_new(struct proxy *,
+static struct registration_entry *registration_entry_new(struct domain *,
url_t const *);
static void registration_entry_destroy(struct registration_entry *e);
struct registration_entry
{
struct registration_entry *next, **prev;
- struct proxy *proxy; /* backpointer */
+ struct domain *domain; /* backpointer */
url_t *aor; /* address-of-record */
struct binding *bindings; /* list of bindings */
sip_contact_t *contacts;
@@ -145,7 +172,7 @@ struct binding
static struct binding *binding_new(su_home_t *home,
sip_contact_t *contact,
tport_t *tport,
- sip_call_id_t *call_id,
+ sip_call_id_t const *call_id,
uint32_t cseq,
sip_time_t registered,
sip_time_t expires);
@@ -157,51 +184,75 @@ static int binding_is_active(struct binding const *b)
(b->tport == NULL || tport_is_clear_to_send(b->tport));
}
-LIST_PROTOS(static, proxy_transaction, struct proxy_transaction);
-struct proxy_transaction *proxy_transaction_new(struct proxy *);
-static void proxy_transaction_destroy(struct proxy_transaction *t);
+LIST_PROTOS(static, proxy_tr, struct proxy_tr);
+struct proxy_tr *proxy_tr_new(struct proxy *);
+static void proxy_tr_destroy(struct proxy_tr *t);
-struct proxy_transaction
+struct proxy_tr
{
- struct proxy_transaction *next, **prev;
+ struct proxy_tr *next, **prev;
struct proxy *proxy; /* backpointer */
- sip_request_t *rq; /* request line */
+
+ struct domain *origin; /* originating domain */
+ struct domain *domain; /* destination domain */
+
+ sip_time_t now; /* when received */
+
nta_incoming_t *server; /* server transaction */
- nta_outgoing_t *client; /* client transaction */
+ msg_t *msg; /* request message */
+ sip_t *sip; /* request headers */
+
+ sip_method_t method; /* request method */
+ int status; /* best status */
+ url_t *target; /* request-URI */
+
+ struct client_tr *clients; /* client transactions */
+
+ struct registration_entry *entry;
+ /* Registration entry */
+
+ auth_mod_t *am; /* Authentication module */
+ auth_status_t *as; /* Authentication status */
+ unsigned use_auth; /* Authentication method (401/407) to use */
+
+ unsigned rr:1;
};
+LIST_PROTOS(static, client_tr, struct client_tr);
+
+struct client_tr
+{
+ struct client_tr *next, **prev;
+ struct proxy_tr *t;
+
+ int status; /* response status */
+ sip_request_t *rq; /* request line */
+ msg_t *msg; /* request message */
+ sip_t *sip; /* request headers */
+ nta_outgoing_t *client; /* transaction */
+};
+
+LIST_BODIES(static, client_tr, struct client_tr, next, prev);
+
static sip_contact_t *create_transport_contacts(struct proxy *p);
-static int proxy_request(struct proxy *proxy,
+union proxy_or_domain { struct proxy proxy[1]; struct domain domain[1]; };
+
+static int proxy_request(union proxy_or_domain *proxy,
nta_leg_t *leg,
nta_incoming_t *irq,
sip_t const *sip);
-static int proxy_ack_cancel(struct proxy_transaction *t,
- nta_incoming_t *irq,
- sip_t const *sip);
-
-static int proxy_response(struct proxy_transaction *t,
- nta_outgoing_t *client,
- sip_t const *sip);
-
-static int process_register(struct proxy *proxy,
- nta_incoming_t *irq,
- sip_t const *sip);
-
-static int domain_request(struct proxy *proxy,
+static int domain_request(union proxy_or_domain *domain,
nta_leg_t *leg,
nta_incoming_t *irq,
sip_t const *sip);
-static int process_options(struct proxy *proxy,
- nta_incoming_t *irq,
+static int proxy_response(struct client_tr *client,
+ nta_outgoing_t *orq,
sip_t const *sip);
-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];
@@ -211,7 +262,8 @@ static auth_challenger_t proxy_challenger[1];
static int
test_proxy_init(su_root_t *root, struct proxy *proxy)
{
- struct proxy_transaction *t;
+ struct proxy_tr *t;
+ struct client_tr *c;
auth_challenger_t _proxy_challenger[1] =
{{
@@ -232,8 +284,6 @@ test_proxy_init(su_root_t *root, struct proxy *proxy)
proxy->root = root;
- proxy->auth = auth_mod_create(root, TAG_NEXT(proxy->tags));
-
proxy->agent = nta_agent_create(root,
URL_STRING_MAKE("sip:0.0.0.0:*"),
NULL, NULL,
@@ -247,53 +297,32 @@ test_proxy_init(su_root_t *root, struct proxy *proxy)
proxy->defleg = nta_leg_tcreate(proxy->agent,
proxy_request,
- proxy,
+ (union proxy_or_domain *)proxy,
NTATAG_NO_DIALOG(1),
TAG_END());
- proxy->example_net = nta_leg_tcreate(proxy->agent,
- domain_request,
- proxy,
- NTATAG_NO_DIALOG(1),
- URLTAG_URL("sip:example.net"),
- TAG_END());
- proxy->example_org = nta_leg_tcreate(proxy->agent,
- domain_request,
- proxy,
- NTATAG_NO_DIALOG(1),
- URLTAG_URL("sip:example.org"),
- TAG_END());
- proxy->example_com = nta_leg_tcreate(proxy->agent,
- domain_request,
- proxy,
- NTATAG_NO_DIALOG(1),
- URLTAG_URL("sip:example.com"),
- TAG_END());
-
- proxy->prefs.min_expires = 30;
- proxy->prefs.expires = 3600;
- proxy->prefs.max_expires = 3600;
-
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)
+ if (!proxy->defleg)
return -1;
+ /* if (!proxy->example_net || !proxy->example_org || !proxy->example_com)
+ return -1; */
- t = su_zalloc(proxy->home, sizeof *t);
+ /* Create stateless client */
+ t = su_zalloc(proxy->home, sizeof *t);
+ c = su_zalloc(proxy->home, sizeof *c);
- if (!t)
+ if (!t || !c)
return -1;
proxy->stateless = t;
t->proxy = proxy;
+ c->t = t, client_tr_insert(&t->clients, c);
t->server = nta_incoming_default(proxy->agent);
- t->client = nta_outgoing_default(proxy->agent, proxy_response, t);
+ c->client = nta_outgoing_default(proxy->agent, proxy_response, c);
- if (!t->client || !t->server)
+ if (!c->client || !t->server)
return -1;
proxy->uri = nta_agent_contact(proxy->agent)->m_url;
@@ -304,17 +333,15 @@ test_proxy_init(su_root_t *root, struct proxy *proxy)
static void
test_proxy_deinit(su_root_t *root, struct proxy *proxy)
{
- struct proxy_transaction *t;
-
- auth_mod_destroy(proxy->auth);
+ struct proxy_tr *t;
if ((t = proxy->stateless)) {
- nta_incoming_destroy(t->server), t->server = NULL;
- nta_outgoing_destroy(t->client), t->client = NULL;
+ proxy->stateless = NULL;
+ proxy_tr_destroy(t);
}
- while (proxy->entries)
- registration_entry_destroy(proxy->entries);
+ while (proxy->domains)
+ domain_destroy(proxy->domains);
nta_agent_destroy(proxy->agent);
@@ -330,6 +357,8 @@ struct proxy *test_proxy_create(su_root_t *root,
if (p) {
ta_list ta;
+ p->magic = test_proxy_create;
+
p->parent = root;
ta_start(ta, tag, value);
@@ -362,27 +391,27 @@ url_t const *test_proxy_uri(struct proxy const *p)
return p ? p->uri : NULL;
}
-void test_proxy_set_expiration(struct proxy *p,
- sip_time_t min_expires,
- sip_time_t expires,
- sip_time_t max_expires)
+void test_proxy_domain_set_expiration(struct domain *d,
+ sip_time_t min_expires,
+ sip_time_t expires,
+ sip_time_t max_expires)
{
- if (p) {
- p->prefs.min_expires = min_expires;
- p->prefs.expires = expires;
- p->prefs.max_expires = max_expires;
+ if (d) {
+ d->prefs.min_expires = min_expires;
+ d->prefs.expires = expires;
+ d->prefs.max_expires = max_expires;
}
}
-void test_proxy_get_expiration(struct proxy *p,
- sip_time_t *return_min_expires,
- sip_time_t *return_expires,
- sip_time_t *return_max_expires)
+void test_proxy_domain_get_expiration(struct domain *d,
+ sip_time_t *return_min_expires,
+ sip_time_t *return_expires,
+ sip_time_t *return_max_expires)
{
- if (p) {
- if (return_min_expires) *return_min_expires = p->prefs.min_expires;
- if (return_expires) *return_expires = p->prefs.expires;
- if (return_max_expires) *return_max_expires = p->prefs.max_expires;
+ if (d) {
+ if (return_min_expires) *return_min_expires = d->prefs.min_expires;
+ if (return_expires) *return_expires = d->prefs.expires;
+ if (return_max_expires) *return_max_expires = d->prefs.max_expires;
}
}
@@ -407,20 +436,36 @@ void test_proxy_get_session_timer(struct proxy *p,
}
}
-void test_proxy_set_outbound(struct proxy *p,
- int use_outbound)
+void test_proxy_domain_set_outbound(struct domain *d,
+ int use_outbound)
{
- if (p) {
- p->prefs.outbound_tcp = use_outbound;
+ if (d) {
+ d->prefs.outbound_tcp = use_outbound;
}
}
-void test_proxy_get_outbound(struct proxy *p,
- int *return_use_outbound)
+void test_proxy_domain_get_outbound(struct domain *d,
+ int *return_use_outbound)
{
- if (p) {
+ if (d) {
if (return_use_outbound)
- *return_use_outbound = p->prefs.outbound_tcp;
+ *return_use_outbound = d->prefs.outbound_tcp;
+ }
+}
+
+void test_proxy_domain_set_authorize(struct domain *d, int authorize)
+{
+ if (d) {
+ d->prefs.authorize = authorize;
+ }
+}
+
+void test_proxy_domain_get_authorize(struct domain *d,
+ int *return_authorize)
+{
+ if (d) {
+ if (return_authorize)
+ *return_authorize = d->prefs.authorize;
}
}
@@ -441,6 +486,106 @@ int test_proxy_close_tports(struct proxy *p)
/* ---------------------------------------------------------------------- */
+struct domain *test_proxy_add_domain(struct proxy *p,
+ url_t const *uri,
+ tag_type_t tag, tag_value_t value, ...)
+{
+ struct domain *d;
+
+ if (p == NULL || uri == NULL)
+ return NULL;
+
+ d = su_home_clone(p->home, sizeof *d);
+
+ if (d) {
+ ta_list ta;
+ int init = 0;
+
+ ta_start(ta, tag, value);
+
+ d->magic = domain_init;
+
+ d->proxy = p;
+ d->uri = url_hdup(d->home, uri);
+ d->tags = tl_adup(d->home, ta_args(ta));
+
+ d->prefs.min_expires = 300;
+ d->prefs.expires = 3600;
+ d->prefs.max_expires = 36000;
+ d->prefs.outbound_tcp = 0;
+ d->prefs.authorize = 0;
+
+ if (d->uri && d->tags &&
+ !su_task_execute(su_clone_task(p->clone), _domain_init, d, &init)) {
+ if (init == 0)
+ /* OK */;
+ else
+ d = NULL;
+ }
+ else
+ su_home_unref(d->home);
+ }
+
+ return d;
+}
+
+static int _domain_init(void *_d)
+{
+ return domain_init(_d);
+}
+
+static int domain_init(struct domain *d)
+{
+ struct proxy *p = d->proxy;
+ url_t uri[1];
+
+ *uri = *d->uri;
+
+ d->auth = auth_mod_create(p->root, TAG_NEXT(d->tags));
+
+ /* Leg for URIs without userpart */
+ d->rleg = nta_leg_tcreate(d->proxy->agent,
+ domain_request,
+ (union proxy_or_domain *)d,
+ NTATAG_NO_DIALOG(1),
+ URLTAG_URL(uri),
+ TAG_END());
+
+ /* Leg for URIs with wildcard userpart */
+ uri->url_user = "%";
+ d->uleg = nta_leg_tcreate(d->proxy->agent,
+ domain_request,
+ (union proxy_or_domain *)d,
+ NTATAG_NO_DIALOG(1),
+ URLTAG_URL(uri),
+ TAG_END());
+
+ if (d->auth && d->rleg && d->uleg) {
+ domain_insert(&p->domains, d);
+ return 0;
+ }
+
+ domain_destroy(d);
+
+ return -1;
+}
+
+static void domain_destroy(struct domain *d)
+{
+ while (d->entries)
+ registration_entry_destroy(d->entries);
+
+ nta_leg_destroy(d->rleg), d->rleg = NULL;
+ nta_leg_destroy(d->uleg), d->uleg = NULL;
+ auth_mod_destroy(d->auth), d->auth = NULL;
+
+ domain_remove(d);
+
+ su_home_unref(d->home);
+}
+
+/* ---------------------------------------------------------------------- */
+
static sip_contact_t *create_transport_contacts(struct proxy *p)
{
su_home_t *home = p->home;
@@ -474,166 +619,332 @@ static sip_contact_t *create_transport_contacts(struct proxy *p)
/* ---------------------------------------------------------------------- */
-static int challenge_request(struct proxy *, nta_incoming_t *, sip_t const *);
+static int proxy_tr_with(struct proxy *proxy,
+ struct domain *domain,
+ nta_incoming_t *irq,
+ sip_t const *sip,
+ int (*process)(struct proxy_tr *));
+static int proxy_transaction(struct proxy_tr *t);
+static int respond_transaction(struct proxy_tr *t,
+ int status, char const *phrase,
+ tag_type_t tag, tag_value_t value,
+ ...);
+static int validate_transaction(struct proxy_tr *t);
+static int originating_transaction(struct proxy_tr *t);
+static int challenge_transaction(struct proxy_tr *t);
+static int session_timers(struct proxy_tr *t);
+static int incoming_transaction(struct proxy_tr *t);
+static int target_transaction(struct proxy_tr *t,
+ url_t const *target,
+ tport_t *tport);
+static int process_register(struct proxy_tr *t);
+static int process_options(struct proxy_tr *t);
-/** Forward request */
-static
-int proxy_request(struct proxy *proxy,
- nta_leg_t *leg,
- nta_incoming_t *irq,
- sip_t const *sip)
+static int proxy_ack_cancel(struct proxy_tr *t,
+ nta_incoming_t *irq,
+ sip_t const *sip);
+
+static struct registration_entry *
+registration_entry_find(struct domain const *domain, url_t const *uri);
+
+static int proxy_request(union proxy_or_domain *pod,
+ nta_leg_t *leg,
+ nta_incoming_t *irq,
+ sip_t const *sip)
{
- url_t const *request_uri, *target;
- struct proxy_transaction *t = NULL;
- sip_request_t *rq = NULL;
- sip_max_forwards_t *mf;
+ assert(pod->proxy->magic = test_proxy_init);
+
+ return proxy_tr_with(pod->proxy, NULL, irq, sip, proxy_transaction);
+}
+
+static int domain_request(union proxy_or_domain *pod,
+ nta_leg_t *leg,
+ nta_incoming_t *irq,
+ sip_t const *sip)
+{
+ int (*process)(struct proxy_tr *) = NULL;
sip_method_t method = sip->sip_request->rq_method;
- 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;
+ assert(pod->domain->magic = domain_init);
- mf = sip->sip_max_forwards;
+ if (leg == pod->domain->uleg)
+ process = proxy_transaction;
+ else if (method == sip_method_register)
+ process = process_register;
+ else if (method == sip_method_options)
+ process = process_options;
- if (mf && mf->mf_count <= 1) {
- if (sip->sip_request->rq_method == sip_method_options) {
- return process_options(proxy, irq, sip);
- }
- nta_incoming_treply(irq, SIP_483_TOO_MANY_HOPS, TAG_END());
- return 483;
- }
+ if (process == NULL)
+ return 501; /* Not implemented */
- if (method != sip_method_ack && method != sip_method_cancel &&
- str0casecmp(sip->sip_from->a_url->url_host, "example.net") == 0) {
- /* Challenge everything but CANCEL and ACK coming from Mr. C */
- int status = challenge_request(proxy, irq, sip);
- if (status)
- return status;
- }
+ return proxy_tr_with(pod->domain->proxy, pod->domain, irq, sip, process);
+}
- if (method == sip_method_invite) {
- if (proxy->prefs.min_se) {
- if (!sip->sip_min_se ||
- sip->sip_min_se->min_delta < proxy->prefs.min_se) {
- min_se = sip_min_se_init(min_se0);
- min_se->min_delta = proxy->prefs.min_se;
- }
+static int proxy_tr_with(struct proxy *proxy,
+ struct domain *domain,
+ nta_incoming_t *irq,
+ sip_t const *sip,
+ int (*process)(struct proxy_tr *))
+{
+ struct proxy_tr *t = NULL;
+ int status = 500;
- if (sip->sip_session_expires
- && sip->sip_session_expires->x_delta < proxy->prefs.min_se
- && sip_has_supported(sip->sip_supported, "timer")) {
- if (min_se == NULL)
- min_se = sip->sip_min_se; assert(min_se);
- nta_incoming_treply(irq, SIP_422_SESSION_TIMER_TOO_SMALL,
- SIPTAG_MIN_SE(min_se),
- TAG_END());
- return 422;
- }
- }
+ assert(proxy->magic = test_proxy_init);
- if (proxy->prefs.session_expires) {
- if (!sip->sip_session_expires ||
- sip->sip_session_expires->x_delta > proxy->prefs.session_expires) {
- x = sip_session_expires_init(x0);
- x->x_delta = proxy->prefs.session_expires;
- if (!sip_has_supported(sip->sip_supported, "timer"))
- require = "timer";
- }
- }
- }
+ t = proxy_tr_new(proxy);
+ if (t) {
+ t->proxy = proxy, t->domain = domain, t->server = irq;
+ t->msg = nta_incoming_getrequest(irq);
+ t->sip = sip_object(t->msg);
- /* We don't do any route processing */
- request_uri = sip->sip_request->rq_url;
+ t->method = sip->sip_request->rq_method;
+ t->target = sip->sip_request->rq_url;
+ t->now = nta_incoming_received(irq, NULL);
- if (!request_uri->url_host ||
- (strcasecmp(request_uri->url_host, "example.org") &&
- strcasecmp(request_uri->url_host, "example.net") &&
- strcasecmp(request_uri->url_host, "example.com"))) {
- target = request_uri;
+ if (t->method != sip_method_ack && t->method != sip_method_cancel)
+ nta_incoming_bind(irq, proxy_ack_cancel, t);
+
+ if (process(t) < 200)
+ return 0;
+
+ proxy_tr_destroy(t);
}
else {
- struct registration_entry *e;
- struct binding *b;
-
- if (sip->sip_request->rq_method == sip_method_register)
- return process_register(proxy, irq, sip);
-
- e = registration_entry_find(proxy, request_uri);
- if (e == NULL) {
- nta_incoming_treply(irq, SIP_404_NOT_FOUND, TAG_END());
- return 404;
- }
-
- for (b = e->bindings; b; b = b->next)
- if (binding_is_active(b))
- break;
-
- if (b == NULL) {
- 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);
- if (t == NULL) {
nta_incoming_treply(irq, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
- return 500;
}
- nta_incoming_bind(t->server = irq, proxy_ack_cancel, t);
+
+ return status;
+}
+
+/** Forward request */
+static int proxy_transaction(struct proxy_tr *t)
+{
+ if (originating_transaction(t))
+ return t->status;
+
+ if (validate_transaction(t))
+ return t->status;
+
+ if (session_timers(t))
+ return t->status;
+
+ if (t->domain)
+ return incoming_transaction(t);
+
+ return target_transaction(t, t->target, NULL);
+}
- rq = sip_request_create(proxy->home,
- sip->sip_request->rq_method,
- sip->sip_request->rq_method_name,
- (url_string_t *)target,
- NULL);
- if (rq == NULL) {
- nta_incoming_treply(irq, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
- proxy_transaction_destroy(t);
- return 500;
- }
- t->rq = rq;
+static int respond_transaction(struct proxy_tr *t,
+ int status, char const *phrase,
+ tag_type_t tag, tag_value_t value,
+ ...)
+{
+ ta_list ta;
+ void *info = NULL, *response = NULL;
- /* Forward request */
- t->client = nta_outgoing_mcreate(proxy->agent, proxy_response, t, NULL,
- nta_incoming_getrequest(irq),
- /* rewrite request */
- SIPTAG_REQUEST(rq),
- 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);
- nta_incoming_treply(irq, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
- return 500;
+ ta_start(ta, tag, value);
+
+ if (t->as)
+ info = t->as->as_info, response = t->as->as_response;
+
+ if (nta_incoming_treply(t->server, t->status = status, phrase,
+ SIPTAG_HEADER(info),
+ SIPTAG_HEADER(response),
+ ta_tags(ta)) < 0)
+ t->status = status = 500;
+
+ ta_end(ta);
+
+ return status;
+}
+
+static int originating_transaction(struct proxy_tr *t)
+{
+ struct domain *o;
+ char const *host;
+
+ host = t->sip->sip_from->a_url->url_host;
+ if (!host)
+ return 0;
+
+ for (o = t->proxy->domains; o; o = o->next)
+ if (strcasecmp(host, o->uri->url_host) == 0)
+ break;
+
+ t->origin = o;
+
+ if (o && o->auth && o->prefs.authorize) {
+ t->am = o->auth;
+ t->use_auth = 407;
}
- else if (sip->sip_request->rq_method == sip_method_ack)
- proxy_transaction_destroy(t);
return 0;
}
-static
-int challenge_request(struct proxy *p,
- nta_incoming_t *irq,
- sip_t const *sip)
+static int validate_transaction(struct proxy_tr *t)
{
- int status;
- auth_status_t *as;
- msg_t *msg;
+ sip_max_forwards_t *mf;
- as = auth_status_new(p->home);
- if (!as)
+ mf = t->sip->sip_max_forwards;
+
+ if (mf && mf->mf_count <= 1) {
+ if (t->method == sip_method_options)
+ return process_options(t);
+
+ return respond_transaction(t, SIP_483_TOO_MANY_HOPS, TAG_END());
+ }
+
+ /* Remove our routes */
+ while (t->sip->sip_route &&
+ url_has_param(t->sip->sip_route->r_url, "lr") &&
+ url_cmp(t->proxy->rr_uri, t->sip->sip_route->r_url) == 0) {
+ sip_route_remove(t->msg, t->sip);
+ /* add record-route also to the forwarded request */
+ t->rr = 1;
+ }
+
+ if (t->use_auth)
+ return challenge_transaction(t);
+
+ return 0;
+}
+
+static int session_timers(struct proxy_tr *t)
+{
+ sip_t *sip = t->sip;
+ sip_session_expires_t *x = NULL, x0[1];
+ sip_min_se_t *min_se = NULL, min_se0[1];
+ char const *require = NULL;
+
+ if (t->method == sip_method_invite) {
+ if (t->proxy->prefs.min_se) {
+ if (!sip->sip_min_se ||
+ sip->sip_min_se->min_delta < t->proxy->prefs.min_se) {
+ min_se = sip_min_se_init(min_se0);
+ min_se->min_delta = t->proxy->prefs.min_se;
+ }
+
+ if (sip->sip_session_expires
+ && sip->sip_session_expires->x_delta < t->proxy->prefs.min_se
+ && sip_has_supported(sip->sip_supported, "timer")) {
+ if (min_se == NULL)
+ min_se = sip->sip_min_se; assert(min_se);
+ return respond_transaction(t, SIP_422_SESSION_TIMER_TOO_SMALL,
+ SIPTAG_MIN_SE(min_se),
+ TAG_END());
+ }
+ }
+
+ if (t->proxy->prefs.session_expires) {
+ if (!sip->sip_session_expires ||
+ sip->sip_session_expires->x_delta > t->proxy->prefs.session_expires) {
+ x = sip_session_expires_init(x0);
+ x->x_delta = t->proxy->prefs.session_expires;
+ if (!sip_has_supported(sip->sip_supported, "timer"))
+ require = "timer";
+ }
+ }
+
+ if (x || min_se || require)
+ sip_add_tl(t->msg, t->sip,
+ SIPTAG_REQUIRE_STR(require),
+ SIPTAG_MIN_SE(min_se),
+ SIPTAG_SESSION_EXPIRES(x),
+ TAG_END());
+ }
+
+ return 0;
+}
+
+static int incoming_transaction(struct proxy_tr *t)
+{
+ struct registration_entry *e;
+ struct binding *b;
+
+#if 0
+ if (sip->sip_request->rq_method == sip_method_register)
+ return process_register(proxy, irq, sip);
+#endif
+
+ t->entry = e = registration_entry_find(t->domain, t->target);
+ if (e == NULL)
+ return respond_transaction(t, SIP_404_NOT_FOUND, TAG_END());
+
+ for (b = e->bindings; b; b = b->next) {
+ if (binding_is_active(b))
+ target_transaction(t, b->contact->m_url, b->tport);
+
+ if (t->clients) /* XXX - enable forking */
+ break;
+ }
+
+ if (t->clients != NULL)
+ return 0;
+
+ return respond_transaction(t, SIP_480_TEMPORARILY_UNAVAILABLE, TAG_END());
+}
+
+static int target_transaction(struct proxy_tr *t,
+ url_t const *target,
+ tport_t *tport)
+{
+ struct client_tr *c = su_zalloc(t->proxy->home, sizeof *c);
+
+ if (c == NULL)
return 500;
+ c->t = t;
+ c->msg = msg_copy(t->msg);
+ c->sip = sip_object(c->msg);
+
+ if (c->msg)
+ c->rq = sip_request_create(msg_home(c->msg),
+ c->sip->sip_request->rq_method,
+ c->sip->sip_request->rq_method_name,
+ (url_string_t *)target,
+ NULL);
+
+ msg_header_insert(c->msg, (msg_pub_t *)c->sip, (msg_header_t *)c->rq);
+
+ if (t->rr && 0) {
+ sip_record_route_t rr[1];
+
+ *sip_record_route_init(rr)->r_url = *t->proxy->rr_uri;
+
+ msg_header_add_dup(c->msg, (msg_pub_t *)c->sip, (msg_header_t *)rr);
+ }
+
+ if (c->rq)
+ /* Forward request */
+ c->client = nta_outgoing_mcreate(t->proxy->agent,
+ proxy_response, c,
+ NULL,
+ msg_ref_create(c->msg),
+ NTATAG_TPORT(tport),
+ TAG_END());
+
+ if (c->client)
+ return client_tr_insert(&t->clients, c), 0;
+
+ msg_destroy(c->msg);
+ su_free(t->proxy->home, c);
+
+ return 500;
+}
+
+static int challenge_transaction(struct proxy_tr *t)
+{
+ auth_status_t *as;
+ sip_t *sip = t->sip;
+
+ assert(t->am);
+
+ t->as = as = auth_status_new(t->proxy->home);
+ if (!as)
+ return respond_transaction(t, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
+
as->as_method = sip->sip_request->rq_method_name;
- msg = nta_incoming_getrequest(irq);
- as->as_source = msg_addrinfo(msg);
+ as->as_source = msg_addrinfo(t->msg);
as->as_user_uri = sip->sip_from->a_url;
as->as_display = sip->sip_from->a_display;
@@ -642,271 +953,196 @@ int challenge_request(struct proxy *p,
as->as_body = sip->sip_payload->pl_data,
as->as_bodylen = sip->sip_payload->pl_len;
- auth_mod_check_client(p->auth, as, sip->sip_proxy_authorization,
- proxy_challenger);
+ if (t->use_auth == 401)
+ auth_mod_check_client(t->am, as, sip->sip_authorization,
+ registrar_challenger);
+ else
+ auth_mod_check_client(t->am, as, sip->sip_proxy_authorization,
+ proxy_challenger);
- if ((status = as->as_status)) {
- nta_incoming_treply(irq,
- as->as_status, as->as_phrase,
- SIPTAG_HEADER((void *)as->as_info),
- SIPTAG_HEADER((void *)as->as_response),
- TAG_END());
- }
- else if (as->as_match) {
- msg_header_remove(msg, NULL, as->as_match);
- }
+ if (as->as_status)
+ return respond_transaction(t, as->as_status, as->as_phrase, TAG_END());
- msg_destroy(msg);
- su_home_unref(as->as_home);
+ if (as->as_match)
+ msg_header_remove(t->msg, (msg_pub_t *)sip, as->as_match);
- return status;
+ return 0;
}
-int proxy_ack_cancel(struct proxy_transaction *t,
+int proxy_ack_cancel(struct proxy_tr *t,
nta_incoming_t *irq,
sip_t const *sip)
{
- if (sip == NULL) {
- proxy_transaction_destroy(t);
+ struct client_tr *c;
+ int status;
+
+ if (sip == NULL) { /* timeout */
+ proxy_tr_destroy(t);
return 0;
}
- if (sip->sip_request->rq_method == sip_method_cancel) {
- /* We don't care about response to CANCEL (or ACK)
- * so we give NULL as callback pointer (and nta immediately
- * destroys transaction object or marks it disposable)
- */
- if (nta_outgoing_tcancel(t->client, NULL, NULL, TAG_END()))
- return 200;
- else
- return 500;
- }
- else {
+ if (sip->sip_request->rq_method != sip_method_cancel)
return 500;
+
+ status = 200;
+
+ for (c = t->clients; c; c = c->next) {
+ if (c->client && c->status < 200)
+ /*
+ * We don't care about response to CANCEL (or ACK)
+ * so we give NULL as callback pointer (and nta immediately
+ * destroys transaction object or marks it disposable)
+ */
+ if (nta_outgoing_tcancel(c->client, NULL, NULL, TAG_END()) == NULL)
+ status = 500;
}
+
+ return status;
}
-int proxy_response(struct proxy_transaction *t,
+int proxy_response(struct client_tr *c,
nta_outgoing_t *client,
sip_t const *sip)
{
int final;
+ assert(c->t);
+
if (sip) {
msg_t *response = nta_outgoing_getresponse(client);
final = sip->sip_status->st_status >= 200;
sip_via_remove(response, sip_object(response));
- nta_incoming_mreply(t->server, response);
+ nta_incoming_mreply(c->t->server, response);
}
else {
final = 1;
- nta_incoming_treply(t->server, SIP_408_REQUEST_TIMEOUT, TAG_END());
+ respond_transaction(c->t, SIP_408_REQUEST_TIMEOUT, TAG_END());
}
if (final)
- proxy_transaction_destroy(t);
+ proxy_tr_destroy(c->t);
return 0;
}
-struct proxy_transaction *
-proxy_transaction_new(struct proxy *proxy)
+struct proxy_tr *
+proxy_tr_new(struct proxy *proxy)
{
- struct proxy_transaction *t;
+ struct proxy_tr *t;
t = su_zalloc(proxy->home, sizeof *t);
if (t) {
t->proxy = proxy;
- proxy_transaction_insert(&proxy->transactions, t);
+ proxy_tr_insert(&proxy->transactions, t);
}
return t;
}
static
-void proxy_transaction_destroy(struct proxy_transaction *t)
+void proxy_tr_destroy(struct proxy_tr *t)
{
+ struct client_tr *c;
+
if (t == t->proxy->stateless)
return;
- proxy_transaction_remove(t);
+
+ proxy_tr_remove(t);
+
+ if (t->as)
+ su_home_unref(t->as->as_home), t->as = NULL;
+
+ while (t->clients) {
+ client_tr_remove(c = t->clients);
+ nta_outgoing_destroy(c->client), c->client = NULL;
+ msg_destroy(c->msg), c->msg = NULL;
+ su_free(t->proxy->home, c);
+ }
+
nta_incoming_destroy(t->server);
- nta_outgoing_destroy(t->client);
- su_free(t->proxy->home, t->rq);
+
su_free(t->proxy->home, t);
}
-LIST_BODIES(static, proxy_transaction, struct proxy_transaction, next, prev);
+LIST_BODIES(static, proxy_tr, struct proxy_tr, next, prev);
/* ---------------------------------------------------------------------- */
-
-static
-int domain_request(struct proxy *proxy,
- nta_leg_t *leg,
- nta_incoming_t *irq,
- sip_t const *sip)
+static int process_options(struct proxy_tr *t)
{
- sip_method_t method = sip->sip_request->rq_method;
-
- if (method == sip_method_register)
- return process_register(proxy, irq, sip);
-
- if (method == sip_method_options)
- return process_options(proxy, irq, sip);
-
- return 501;
-}
-
-static
-int process_options(struct proxy *proxy,
- nta_incoming_t *irq,
- sip_t const *sip)
-{
- nta_incoming_treply(irq, SIP_200_OK,
- SIPTAG_CONTACT(proxy->transport_contacts),
- TAG_END());
- return 200;
+ return respond_transaction(t, SIP_200_OK,
+ SIPTAG_CONTACT(t->proxy->transport_contacts),
+ TAG_END());
}
/* ---------------------------------------------------------------------- */
+static int check_received_contact(struct proxy_tr *t);
+static int validate_contacts(struct proxy_tr *t);
+static int check_out_of_order(struct proxy_tr *t);
+static int update_bindings(struct proxy_tr *t);
-static int process_register2(struct proxy *p, auth_status_t *as,
- nta_incoming_t *irq, sip_t const *sip);
-
-static int set_status(auth_status_t *as, int status, char const *phrase);
-
-static int validate_contacts(struct proxy *p, auth_status_t *as,
- sip_t const *sip);
-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,
- nta_incoming_t *irq,
- sip_t const *sip);
-
-sip_contact_t *binding_contacts(su_home_t *home, struct binding *bindings);
-
-int process_register(struct proxy *proxy,
- nta_incoming_t *irq,
- sip_t const *sip)
+int process_register(struct proxy_tr *t)
{
- auth_status_t *as;
- msg_t *msg;
- int status;
+ /* This is before authentication because we want to be bug-compatible */
+ if (check_received_contact(t))
+ return t->status;
- as = auth_status_new(proxy->home);
- if (!as)
- return 500;
+ if (t->domain->auth) {
+ t->am = t->domain->auth, t->use_auth = 401;
+ if (challenge_transaction(t))
+ return t->status;
+ }
- as->as_method = sip->sip_request->rq_method_name;
- msg = nta_incoming_getrequest(irq);
- as->as_source = msg_addrinfo(msg);
- msg_destroy(msg);
+ if (validate_contacts(t))
+ return t->status;
- as->as_user_uri = sip->sip_from->a_url;
- as->as_display = sip->sip_from->a_display;
+ t->entry = registration_entry_find(t->domain, t->sip->sip_to->a_url);
- if (sip->sip_payload)
- as->as_body = sip->sip_payload->pl_data,
- as->as_bodylen = sip->sip_payload->pl_len;
-
- process_register2(proxy, as, irq, sip);
- 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());
-
- status = as->as_status;
-
- su_home_unref(as->as_home);
-
- return status;
+ if (check_out_of_order(t))
+ return t->status;
+
+ return update_bindings(t);
}
-static int process_register2(struct proxy *p,
- auth_status_t *as,
- nta_incoming_t *irq,
- sip_t const *sip)
+static int check_received_contact(struct proxy_tr *t)
{
- struct registration_entry *e = NULL;
+ sip_t *sip = t->sip;
sip_contact_t *m = sip->sip_contact;
sip_via_t *v = sip->sip_via;
if (m && v && v->v_received && m->m_url->url_host
&& strcasecmp(v->v_received, m->m_url->url_host)
&& host_is_ip_address(m->m_url->url_host))
- return set_status(as, 406, "Unacceptable Contact");
+ return respond_transaction(t, 406, "Unacceptable Contact", TAG_END());
- auth_mod_check_client(p->auth, as, sip->sip_authorization,
- registrar_challenger);
- if (as->as_status)
- return as->as_status;
- assert(as->as_response == NULL);
+ return 0;
+}
- if (validate_contacts(p, as, sip))
- return as->as_status;
+/* Validate expiration times */
+static int validate_contacts(struct proxy_tr *t)
+{
+ sip_contact_t const *m = t->sip->sip_contact;
+ sip_expires_t const *ex = t->sip->sip_expires;
+ sip_date_t const *date = t->sip->sip_date;
+ sip_time_t expires;
- e = registration_entry_find(p, sip->sip_to->a_url);
- if (!sip->sip_contact) {
- as->as_response = (msg_header_t *)e->contacts;
- return set_status(as, SIP_200_OK);
+ if (m && m->m_url->url_type == url_any) {
+ if (!ex || ex->ex_delta || ex->ex_time || m->m_next)
+ return respond_transaction(t, SIP_400_BAD_REQUEST, TAG_END());
+ return 0;
}
- if (e && check_out_of_order(p, as, e, sip))
- return as->as_status;
-
- if (!e)
- e = registration_entry_new(p, sip->sip_to->a_url);
- if (!e)
- return set_status(as, SIP_500_INTERNAL_SERVER_ERROR);
-
- if (binding_update(p, as, e, irq, sip))
- return as->as_status;
-
- msg_header_free(p->home, (void *)e->contacts);
- e->contacts = binding_contacts(p->home, e->bindings);
-
- as->as_response = (msg_header_t *)e->contacts;
-
- return set_status(as, SIP_200_OK);
-}
-
-static int set_status(auth_status_t *as, int status, char const *phrase)
-{
- return as->as_phrase = phrase, as->as_status = status;
-}
-
-static int validate_contacts(struct proxy *p,
- auth_status_t *as,
- sip_t const *sip)
-{
- sip_contact_t const *m;
- sip_time_t expires;
- sip_time_t now = sip_now();
-
- for (m = sip->sip_contact; m; m = m->m_next) {
- if (m->m_url->url_type == url_any) {
- if (!sip->sip_expires ||
- sip->sip_expires->ex_delta ||
- sip->sip_expires->ex_time ||
- sip->sip_contact->m_next)
- return set_status(as, SIP_400_BAD_REQUEST);
- else
- return 0;
- }
-
- expires = sip_contact_expires(m, sip->sip_expires, sip->sip_date,
- p->prefs.expires, now);
+ for (; m; m = m->m_next) {
+ expires = sip_contact_expires(m, ex, date, t->domain->prefs.expires, t->now);
- if (expires > 0 && expires < p->prefs.min_expires) {
- as->as_response = (msg_header_t *)
- sip_min_expires_format(as->as_home, "%u",
- (unsigned)p->prefs.min_expires);
- return set_status(as, SIP_423_INTERVAL_TOO_BRIEF);
+ if (expires > 0 && expires < t->domain->prefs.min_expires) {
+ sip_min_expires_t me[1];
+
+ sip_min_expires_init(me)->me_delta = t->domain->prefs.min_expires;
+
+ return respond_transaction(t, SIP_423_INTERVAL_TOO_BRIEF,
+ SIPTAG_MIN_EXPIRES(me),
+ TAG_END());
}
}
@@ -914,31 +1150,27 @@ static int validate_contacts(struct proxy *p,
}
/** Check for out-of-order register request */
-static
-int check_out_of_order(struct proxy *p,
- auth_status_t *as,
- struct registration_entry *e,
- sip_t const *sip)
+static int check_out_of_order(struct proxy_tr *t)
{
struct binding const *b;
- sip_call_id_t const *id;
+ sip_call_id_t const *id = t->sip->sip_call_id;
+ uint32_t cseq = t->sip->sip_cseq->cs_seq;
sip_contact_t *m;
- if (e == NULL || !sip->sip_contact)
+ if (t->entry == NULL || !t->sip->sip_contact)
return 0;
- id = sip->sip_call_id;
-
/* RFC 3261 subsection 10.3 step 6 and step 7 (p. 66): */
/* Check for reordered register requests */
- for (b = e->bindings; b; b = b->next) {
+ for (b = t->entry->bindings; b; b = b->next) {
if (binding_is_active(b) &&
- strcmp(sip->sip_call_id->i_id, b->call_id->i_id) == 0 &&
- sip->sip_cseq->cs_seq <= b->cseq) {
- for (m = sip->sip_contact; m; m = m->m_next) {
+ strcmp(id->i_id, b->call_id->i_id) == 0 &&
+ cseq <= b->cseq) {
+ for (m = t->sip->sip_contact; m; m = m->m_next) {
if (m->m_url->url_type == url_any ||
url_cmp_all(m->m_url, b->contact->m_url) == 0)
- return set_status(as, SIP_500_INTERNAL_SERVER_ERROR);
+ return respond_transaction(t, SIP_500_INTERNAL_SERVER_ERROR,
+ TAG_END());
}
}
}
@@ -946,37 +1178,40 @@ int check_out_of_order(struct proxy *p,
return 0;
}
-
static struct registration_entry *
-registration_entry_find(struct proxy const *proxy, url_t const *uri)
+registration_entry_find(struct domain const *d, url_t const *uri)
{
struct registration_entry *e;
/* Our routing table */
- for (e = proxy->entries; e; e = e->next) {
+ for (e = d->entries; e; e = e->next) {
if (url_cmp(uri, e->aor) == 0)
return e;
}
+
return NULL;
}
static struct registration_entry *
-registration_entry_new(struct proxy *proxy, url_t const *aor)
+registration_entry_new(struct domain *d, url_t const *aor)
{
struct registration_entry *e;
- e = su_zalloc(proxy->home, sizeof *e);
+ if (d == NULL)
+ return NULL;
+
+ e = su_zalloc(d->home, sizeof *e);
if (!e)
return NULL;
- e->proxy = proxy;
- e->aor = url_hdup(proxy->home, aor);
+ e->domain = d;
+ e->aor = url_hdup(d->home, aor);
if (!e->aor) {
- su_free(proxy->home, e);
+ su_free(d->home, e);
return NULL;
}
- registration_entry_insert(&proxy->entries, e);
+ registration_entry_insert(&d->entries, e);
return e;
}
@@ -986,14 +1221,19 @@ registration_entry_destroy(struct registration_entry *e)
{
if (e) {
registration_entry_remove(e);
- su_free(e->proxy->home, e->aor);
+ su_free(e->domain->home, e->aor);
while (e->bindings)
- binding_destroy(e->proxy->home, e->bindings);
- msg_header_free(e->proxy->home, (void *)e->contacts);
- su_free(e->proxy->home, e);
+ binding_destroy(e->domain->home, e->bindings);
+ msg_header_free(e->domain->home, (void *)e->contacts);
+ su_free(e->domain->home, e);
}
}
+sip_contact_t *entry_contacts(struct registration_entry *entry)
+{
+ return entry ? entry->contacts : NULL;
+}
+
LIST_BODIES(static, registration_entry, struct registration_entry, next, prev);
/* ---------------------------------------------------------------------- */
@@ -1003,7 +1243,7 @@ static
struct binding *binding_new(su_home_t *home,
sip_contact_t *contact,
tport_t *tport,
- sip_call_id_t *call_id,
+ sip_call_id_t const *call_id,
uint32_t cseq,
sip_time_t registered,
sip_time_t expires)
@@ -1046,40 +1286,48 @@ void binding_destroy(su_home_t *home, struct binding *b)
su_free(home, b);
}
-static
-int binding_update(struct proxy *p,
- auth_status_t *as,
- struct registration_entry *e,
- nta_incoming_t *irq,
- sip_t const *sip)
+static int update_bindings(struct proxy_tr *t)
{
+ struct domain *d = t->domain;
struct binding *b, *old, *next, *last, *bindings = NULL, **bb = &bindings;
sip_contact_t *m;
+ sip_call_id_t const *id = t->sip->sip_call_id;
+ uint32_t cseq = t->sip->sip_cseq->cs_seq;
+ sip_expires_t *ex = t->sip->sip_expires;
+ sip_date_t *date = t->sip->sip_date;
sip_time_t expires;
- sip_time_t now = sip_now();
tport_t *tport = NULL;
+ sip_contact_t *contacts = NULL, **mm = &contacts;
+ void *tbf;
- assert(sip->sip_contact);
+ if (t->sip->sip_contact == NULL) {
+ if (t->entry)
+ contacts = t->entry->contacts;
+ goto ok200;
+ }
- if (p->prefs.outbound_tcp &&
- str0casecmp(sip->sip_via->v_protocol, sip_transport_tcp) == 0)
- tport = nta_incoming_transport(p->agent, irq, NULL);
+ if (t->entry == NULL)
+ t->entry = registration_entry_new(d, t->sip->sip_to->a_url);
+ if (t->entry == NULL)
+ return respond_transaction(t, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
+
+ if (d->prefs.outbound_tcp &&
+ str0casecmp(t->sip->sip_via->v_protocol, sip_transport_tcp) == 0)
+ tport = nta_incoming_transport(t->proxy->agent, t->server, NULL);
/* Create new bindings */
- for (m = sip->sip_contact; m; m = m->m_next) {
+ for (m = t->sip->sip_contact; m; m = m->m_next) {
if (m->m_url->url_type == url_any)
break;
- expires = sip_contact_expires(m, sip->sip_expires, sip->sip_date,
- p->prefs.expires, now);
+ expires = sip_contact_expires(m, ex, date, d->prefs.expires, t->now);
- if (expires > p->prefs.max_expires)
- expires = p->prefs.max_expires;
+ if (expires > d->prefs.max_expires)
+ expires = d->prefs.max_expires;
msg_header_remove_param(m->m_common, "expires");
- b = binding_new(p->home, m, tport, sip->sip_call_id, sip->sip_cseq->cs_seq,
- now, now + expires);
+ b = binding_new(d->home, m, tport, id, cseq, t->now, t->now + expires);
if (!b)
break;
@@ -1092,7 +1340,7 @@ int binding_update(struct proxy *p,
if (m == NULL) {
/* Merge new bindings with old ones */
- for (old = e->bindings; old; old = next) {
+ for (old = t->entry->bindings; old; old = next) {
next = old->next;
for (b = bindings; b != last; b = b->next) {
@@ -1102,12 +1350,12 @@ int binding_update(struct proxy *p,
if (strcmp(old->call_id->i_id, b->call_id->i_id) == 0) {
b->registered = old->registered;
}
- binding_destroy(p->home, old);
+ binding_destroy(d->home, old);
break;
}
}
- for (bb = &e->bindings; *bb; bb = &(*bb)->next)
+ for (bb = &t->entry->bindings; *bb; bb = &(*bb)->next)
;
if ((*bb = bindings))
@@ -1115,8 +1363,8 @@ int binding_update(struct proxy *p,
}
else if (m->m_url->url_type == url_any) {
/* Unregister all */
- for (b = e->bindings; b; b = b->next) {
- b->expires = now;
+ for (b = t->entry->bindings; b; b = b->next) {
+ b->expires = t->now;
}
}
else {
@@ -1124,34 +1372,35 @@ int binding_update(struct proxy *p,
for (old = bindings; old; old = next) {
next = old->next;
- binding_destroy(p->home, old);
+ binding_destroy(d->home, old);
}
- return set_status(as, SIP_500_INTERNAL_SERVER_ERROR);
+ return respond_transaction(t, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
}
- return 0;
-}
-
-sip_contact_t *binding_contacts(su_home_t *home, struct binding *bindings)
-{
- sip_contact_t *retval = NULL, **mm = &retval;
- struct binding *b;
- sip_time_t now = sip_now();
-
- for (b = bindings; b; b = b->next) {
+ for (b = t->entry->bindings; b; b = b->next) {
char const *expires;
- if (b->expires <= now)
+
+ if (b->expires <= t->now)
continue;
- *mm = sip_contact_copy(home, b->contact);
+
+ *mm = sip_contact_copy(d->home, b->contact);
if (*mm) {
- expires = su_sprintf(home, "expires=%u", (unsigned)(b->expires - now));
- msg_header_add_param(home, (*mm)->m_common, expires);
+ expires = su_sprintf(d->home, "expires=%u",
+ (unsigned)(b->expires - t->now));
+ msg_header_add_param(d->home, (*mm)->m_common, expires);
mm = &(*mm)->m_next;
}
}
- return retval;
+ tbf = t->entry->contacts;
+ t->entry->contacts = contacts;
+ msg_header_free(d->home, tbf);
+
+ ok200:
+ return respond_transaction(t, SIP_200_OK,
+ SIPTAG_CONTACT(contacts),
+ TAG_END());
}
/* ---------------------------------------------------------------------- */
@@ -1159,16 +1408,19 @@ sip_contact_t *binding_contacts(su_home_t *home, struct binding *bindings)
static int close_tports(void *_proxy)
{
struct proxy *p = _proxy;
+ struct domain *d;
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;
+ for (d = p->domains; d; d = d->next) {
+ for (e = d->entries; e; e = e->next) {
+ for (b = e->bindings; b; b = b->next) {
+ if (b->tport) {
+ tport_shutdown(b->tport, 1);
+ tport_unref(b->tport);
+ b->tport = NULL;
+ }
}
}
}
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 9f74a533a8..fa433eca2a 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.h
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.h
@@ -31,6 +31,7 @@
SOFIA_BEGIN_DECLS
struct proxy;
+struct domain;
struct proxy *test_proxy_create(su_root_t *, tag_type_t, tag_value_t, ...);
@@ -38,15 +39,19 @@ void test_proxy_destroy(struct proxy *);
url_t const *test_proxy_uri(struct proxy const *);
-void test_proxy_set_expiration(struct proxy *,
- sip_time_t min_expires,
- sip_time_t expires,
- sip_time_t max_expires);
+struct domain *test_proxy_add_domain(struct proxy *,
+ url_t const *domain,
+ tag_type_t, tag_value_t, ...);
-void test_proxy_get_expiration(struct proxy *,
- sip_time_t *return_min_expires,
- sip_time_t *return_expires,
- sip_time_t *return_max_expires);
+void test_proxy_domain_set_expiration(struct domain *,
+ sip_time_t min_expires,
+ sip_time_t expires,
+ sip_time_t max_expires);
+
+void test_proxy_domain_get_expiration(struct domain *,
+ sip_time_t *return_min_expires,
+ sip_time_t *return_expires,
+ sip_time_t *return_max_expires);
void test_proxy_set_session_timer(struct proxy *p,
sip_time_t session_expires,
@@ -56,6 +61,14 @@ void test_proxy_get_session_timer(struct proxy *p,
sip_time_t *return_session_expires,
sip_time_t *return_min_se);
+void test_proxy_domain_set_authorize(struct domain *d, int authorize);
+void test_proxy_domain_get_authorize(struct domain *d, int *return_authorize);
+
+void test_proxy_domain_set_outbound(struct domain *d,
+ int use_outbound);
+void test_proxy_domain_get_outbound(struct domain *d,
+ int *return_use_outbound);
+
int test_proxy_close_tports(struct proxy *p);
SOFIA_END_DECLS
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 ed1ab1520d..aafdb961db 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/test_register.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_register.c
@@ -57,9 +57,6 @@ int test_register_to_proxy(struct context *ctx)
sip_cseq_t cseq[1];
int seen_401;
- if (ctx->p)
- test_proxy_set_expiration(ctx->p, 5, 5, 10);
-
if (print_headings)
printf("TEST NUA-2.3.0.1: un-REGISTER a\n");
@@ -108,7 +105,6 @@ int test_register_to_proxy(struct context *ctx)
if (print_headings)
printf("TEST NUA-2.3.0.3: PASSED\n");
-
/* REGISTER test
A B
@@ -123,6 +119,8 @@ int test_register_to_proxy(struct context *ctx)
if (print_headings)
printf("TEST NUA-2.3.1: REGISTER a\n");
+ test_proxy_domain_set_expiration(ctx->a.domain, 5, 5, 10);
+
TEST_1(a_reg->nh = nua_handle(a->nua, a_reg, TAG_END()));
sip_cseq_init(cseq)->cs_seq = 12;
@@ -142,7 +140,7 @@ int test_register_to_proxy(struct context *ctx)
TEST_1(e = a->events->head);
TEST_1(sip = sip_object(e->data->e_msg));
- if (ctx->nat) {
+ if (ctx->nat && e->data->e_status == 100) {
TEST_E(e->data->e_event, nua_r_register);
TEST(e->data->e_status, 100);
TEST(sip->sip_status->st_status, 406);
@@ -184,12 +182,16 @@ int test_register_to_proxy(struct context *ctx)
TEST_1(e = a->specials->head);
}
+ test_proxy_domain_set_expiration(ctx->a.domain, 600, 3600, 36000);
+
if (print_headings)
printf("TEST NUA-2.3.1: PASSED\n");
if (print_headings)
printf("TEST NUA-2.3.2: REGISTER b\n");
+ test_proxy_domain_set_expiration(ctx->b.domain, 5, 5, 10);
+
TEST_1(b_reg->nh = nua_handle(b->nua, b_reg, TAG_END()));
/* Test application-supplied contact */
@@ -237,14 +239,14 @@ int test_register_to_proxy(struct context *ctx)
if (print_headings)
printf("TEST NUA-2.3.2: PASSED\n");
- if (ctx->p) {
- test_proxy_close_tports(ctx->p);
- test_proxy_set_expiration(ctx->p, 600, 3600, 36000);
- }
+ test_proxy_domain_set_expiration(ctx->b.domain, 600, 3600, 36000);
if (print_headings)
printf("TEST NUA-2.3.3: REGISTER c\n");
+ test_proxy_domain_set_expiration(ctx->c.domain, 600, 3600, 36000);
+ test_proxy_domain_set_authorize(ctx->c.domain, 2);
+
TEST_1(c_reg->nh = nua_handle(c->nua, c_reg, TAG_END()));
REGISTER(c, c_reg, c_reg->nh, SIPTAG_TO(c->to),
@@ -279,7 +281,12 @@ int test_register_to_proxy(struct context *ctx)
TEST_1(sip = sip_object(e->data->e_msg));
TEST(sip->sip_status->st_status, 423);
TEST_1(e = e->next);
- TEST(e->data->e_status, 200);
+ if (e->data->e_status == 100 && e->data->e_event == nua_r_register) {
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST(sip->sip_status->st_status, 401);
+ TEST_1(e = e->next);
+ }
+ TEST(e->data->e_status, 200); TEST_E(e->data->e_event, nua_r_register);
TEST_1(sip = sip_object(e->data->e_msg));
TEST_1(sip->sip_contact);
TEST_S(sip->sip_contact->m_display, "C");
@@ -366,6 +373,30 @@ int test_register_to_proxy(struct context *ctx)
if (print_headings)
printf("TEST NUA-2.3.4: PASSED\n");
+ if (!ctx->p)
+ return 0;
+
+ if (print_headings)
+ printf("TEST NUA-2.3.5: re-REGISTER when TCP connection is closed\n");
+
+ test_proxy_close_tports(ctx->p);
+
+ run_b_until(ctx, -1, save_until_final_response);
+
+ TEST_1(e = b->events->head);
+ TEST_E(e->data->e_event, nua_r_register);
+ if (e->data->e_status == 100)
+ TEST_1(e = e->next);
+ TEST_1(sip = sip_object(e->data->e_msg));
+ TEST_1(sip->sip_contact);
+ 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-2.3.5: PASSED\n");
+
END();
}
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 ead1658f96..020b1f2c4e 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c
@@ -814,6 +814,7 @@ static
size_t change_status_to_483(void *a, void *message, size_t len);
int save_until_notified_and_responded_twice(CONDITION_PARAMS);
int save_until_notify_responded_twice(CONDITION_PARAMS);
+int accept_subscription_until_terminated(CONDITION_PARAMS);
int test_subscribe_notify_graceful(struct context *ctx)
{
@@ -902,7 +903,7 @@ int test_subscribe_notify_graceful(struct context *ctx)
SUBSCRIBE(a, a_call, a_call->nh, TAG_END());
run_ab_until(ctx, -1, save_until_notified_and_responded_twice,
- -1, save_until_notify_responded_twice);
+ -1, accept_subscription_until_terminated);
#if 0
/* Client events:
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 6c005c1fcc..e441ccb5fb 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
@@ -64,9 +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.bit0 = 1;
if (event == nua_r_subscribe || event == nua_r_unsubscribe) {
- if (status >= 300)
+ if (status == 407) {
+ AUTHENTICATE(ep, call, nh,
+ NUTAG_AUTH("Digest:\"test-proxy\":charlie:secret"),
+ TAG_END());
+ }
+ else if (status >= 300)
return 1;
else if (status >= 200)
ep->flags.bit1 = 1;
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 c3d539b25b..4054f18920 100644
--- a/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c
+++ b/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c
@@ -1264,6 +1264,67 @@ int test_media_replace(struct context *ctx)
}
+int test_media_reject(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 *b_sdp;
+
+ 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 97\r\n"
+ "a=rtpmap:97 GSM/8000\n"
+ ;
+
+ char const b_caps[] =
+ "m=audio 0 RTP/AVP 96 97\n"
+ "a=rtpmap:96 G7231/8000\n"
+ "a=rtpmap:97 G729/8000\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_REJECTED);
+ TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_REJECTED);
+
+ 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)
{
BEGIN();
@@ -1356,6 +1417,11 @@ int test_asynch_offer_answer(struct context *ctx)
int test_deinit(struct context *ctx)
{
BEGIN();
+
+ su_root_destroy(ctx->root), ctx->root = NULL;
+ soa_destroy(ctx->a);
+ soa_destroy(ctx->b);
+
END();
}
@@ -1459,6 +1525,7 @@ int main(int argc, char *argv[])
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_media_reject(ctx); SINGLE_FAILURE_CHECK();
retval |= test_asynch_offer_answer(ctx); SINGLE_FAILURE_CHECK();
}
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 500a898319..64ee4ee916 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
@@ -225,6 +225,66 @@ TPORT_DLL extern tag_typedef_t tptag_timeout;
TPORT_DLL extern tag_typedef_t tptag_timeout_ref;
#define TPTAG_TIMEOUT_REF(x) tptag_timeout_ref, tag_uint_vr(&(x))
+TPORT_DLL extern tag_typedef_t tptag_keepalive;
+/**Keepalive interval in milliseconds.
+ *
+ * If 0 or UINT_MAX, do not use keepalives. Default value is 0.
+ *
+ * On TCP, the keepalive if a CR-LF-CR-LF sequence.
+ *
+ * Use with tport_tcreate(), tport_tbind(), tport_set_params(), nua_create(),
+ * nta_agent_create(), nta_agent_add_tport(), nth_engine_create(), or
+ * initial nth_site_create().
+ *
+ * @sa TPTAG_PINGPONG(), TPTAG_PONG2PING(), TPTAG_TIMEOUT(), TPTAG_IDLE()
+ */
+#define TPTAG_KEEPALIVE(x) tptag_keepalive, tag_uint_v((x))
+
+TPORT_DLL extern tag_typedef_t tptag_keepalive_ref;
+#define TPTAG_KEEPALIVE_REF(x) tptag_keepalive_ref, tag_uint_vr(&(x))
+
+TPORT_DLL extern tag_typedef_t tptag_pingpong;
+/**Ping-pong interval in milliseconds.
+ *
+ * If 0 or UINT_MAX, do not check for PONGs. Default value is 0.
+ *
+ * If set, the ping-pong protocol is used on TCP connections. If pinger
+ * sends a ping and receives no data in the specified ping-pong interval, it
+ * considers the connection failed and closes it. The value recommended in
+ * draft-ietf-sip-outbound-10 is 10 seconds (10000 milliseconds).
+ *
+ * Use with tport_tcreate(), tport_tbind(), tport_set_params(), nua_create(),
+ * nta_agent_create(), nta_agent_add_tport(), nth_engine_create(), or
+ * initial nth_site_create().
+ *
+ * @sa TPTAG_PONG2PING(), TPTAG_KEEPALIVE(), TPTAG_TIMEOUT(), TPTAG_IDLE(),
+ * draft-ietf-sip-outbound-10.txt
+ */
+#define TPTAG_PINGPONG(x) tptag_pingpong, tag_uint_v((x))
+
+TPORT_DLL extern tag_typedef_t tptag_pingpong_ref;
+#define TPTAG_PINGPONG_REF(x) tptag_pingpong_ref, tag_uint_vr(&(x))
+
+TPORT_DLL extern tag_typedef_t tptag_pong2ping;
+/**Respond PING with PONG.
+ *
+ * If true, respond with PONG to PING. Default value is 0 (false).
+ *
+ * If set, the ping-pong protocol is used on TCP connections. If a ping (at
+ * least 4 whitespace characters) is received within messages, a pong
+ * (CR-LF) is sent in response.
+ *
+ * Use with tport_tcreate(), tport_tbind(), tport_set_params(), nua_create(),
+ * nta_agent_create(), nta_agent_add_tport(), nth_engine_create(), or
+ * initial nth_site_create().
+ *
+ * @sa TPTAG_PINGPONG(), TPTAG_KEEPALIVE(), TPTAG_TIMEOUT(), TPTAG_IDLE()
+ */
+#define TPTAG_PONG2PING(x) tptag_pong2ping, tag_bool_v((x))
+
+TPORT_DLL extern tag_typedef_t tptag_pong2ping_ref;
+#define TPTAG_PONG2PING_REF(x) tptag_pong2ping_ref, tag_bool_vr(&(x))
+
TPORT_DLL extern tag_typedef_t tptag_sigcomp_lifetime;
/**Default SigComp lifetime in seconds.
*
diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/test_tport.c b/libs/sofia-sip/libsofia-sip-ua/tport/test_tport.c
index e7355051a2..a6cf2a3ec3 100644
--- a/libs/sofia-sip/libsofia-sip-ua/tport/test_tport.c
+++ b/libs/sofia-sip/libsofia-sip-ua/tport/test_tport.c
@@ -33,6 +33,9 @@
* @date Created: Wed Apr 3 11:25:13 2002 ppessi
*/
+/* always assert()s */
+#undef NDEBUG
+
#include "config.h"
#include
@@ -49,6 +52,8 @@ typedef struct tp_test_s tp_test_t;
#include
#include
+#include "tport_internal.h" /* Get SU_DEBUG_*() */
+
#include "test_class.h"
#include "test_protos.h"
#include "sofia-sip/msg.h"
@@ -101,6 +106,9 @@ struct tp_test_s {
int tt_received;
msg_t *tt_rmsg;
uint8_t tt_digest[SU_MD5_DIGEST_SIZE];
+
+ su_addrinfo_t const *tt_tcp_addr;
+ tport_t *tt_tcp;
};
int tstflags;
@@ -359,7 +367,11 @@ static void tp_test_recv(tp_test_t *tt,
tt->tt_status = 1;
tt->tt_received++;
- if (test_msg_md5(tt, msg))
+ if (msg_has_error(msg)) {
+ tt->tt_status = -1;
+ tt->tt_rtport = tp;
+ }
+ else if (test_msg_md5(tt, msg))
msg_destroy(msg);
else if (tt->tt_rmsg)
msg_destroy(msg);
@@ -451,7 +463,7 @@ tp_stack_class_t const tp_test_class[1] =
static int init_test(tp_test_t *tt)
{
tp_name_t myname[1] = {{ "*", "*", "*", "*", "sigcomp" }};
-#if HAVE_NETINET_SCTP_H
+#if HAVE_SCTP
char const * transports[] = { "udp", "tcp", "sctp", NULL };
#else
char const * transports[] = { "udp", "tcp", NULL };
@@ -635,7 +647,7 @@ static int init_test(tp_test_t *tt)
for (tp = tport_primaries(tt->tt_srv_tports); tp; tp = tport_next(tp)) {
TEST_1(tpn = tport_name(tp));
- if (1 || tt->tt_flags & tst_verbatim) {
+ if (tt->tt_flags & tst_verbatim) {
char const *host = tpn->tpn_host != tpn->tpn_canon ? tpn->tpn_host : "";
printf("bound transport to %s/%s:%s%s%s%s%s\n",
tpn->tpn_proto, tpn->tpn_canon, tpn->tpn_port,
@@ -661,6 +673,11 @@ static int init_test(tp_test_t *tt)
tt->tt_tcp_name->tpn_ident = NULL;
*tt->tt_tcp_comp = *tpn;
tt->tt_tcp_comp->tpn_ident = NULL;
+
+ if (tt->tt_tcp_addr == NULL) {
+ tt->tt_tcp_addr = tport_get_address(tp);
+ tt->tt_tcp = tp;
+ }
}
else if (strcmp(tpn->tpn_proto, "sctp") == 0) {
*tt->tt_sctp_name = *tpn;
@@ -738,7 +755,7 @@ static int udp_test(tp_test_t *tt)
TEST_1(md5 = msg_content_md5_make(home, "R6nitdrtJFpxYzrPaSXfrA=="));
TEST(msg_header_insert(msg, (void *)tst, (msg_header_t *)md5), 0);
-
+
TEST_1(sep = msg_separator_create(home));
TEST(msg_header_insert(msg, (void *)tst, (msg_header_t *)sep), 0);
@@ -778,23 +795,72 @@ static int udp_test(tp_test_t *tt)
END();
}
+int pending_server_close, pending_client_close;
+
+void server_closed_callback(tp_stack_t *tt, tp_client_t *client,
+ tport_t *tp, msg_t *msg, int error)
+{
+ assert(msg == NULL);
+ assert(client == NULL);
+ if (msg == NULL) {
+ tport_release(tp, pending_server_close, NULL, NULL, client, 0);
+ pending_server_close = 0;
+ }
+}
+
+void client_closed_callback(tp_stack_t *tt, tp_client_t *client,
+ tport_t *tp, msg_t *msg, int error)
+{
+ assert(msg == NULL);
+ assert(client == NULL);
+ if (msg == NULL) {
+ tport_release(tp, pending_client_close, NULL, NULL, client, 0);
+ pending_client_close = 0;
+ }
+}
+
static int tcp_test(tp_test_t *tt)
{
BEGIN();
-#ifndef WIN32 /* Windows seems to be buffering too much */
-
msg_t *msg = NULL;
int i;
tport_t *tp, *tp0;
char ident[16];
+ su_time_t started;
+
+ /* Send a single message */
+ TEST_1(!new_test_msg(tt, &msg, "tcp-first", 1, 1024));
+ TEST_1(tp = tport_tsend(tt->tt_tports, msg, tt->tt_tcp_name, TAG_END()));
+ TEST_S(tport_name(tp)->tpn_ident, "client");
+ tp0 = tport_incref(tp);
+ msg_destroy(msg);
+
+ tport_set_params(tp,
+ TPTAG_KEEPALIVE(100),
+ TPTAG_PINGPONG(500),
+ TPTAG_IDLE(500),
+ TAG_END());
+
+ TEST(tport_test_run(tt, 5), 1);
+ TEST_1(!check_msg(tt, tt->tt_rmsg, "tcp-first"));
+ msg_destroy(tt->tt_rmsg), tt->tt_rmsg = NULL;
+
+ /* Ask for notification upon close */
+ pending_client_close = tport_pend(tp0, NULL, client_closed_callback, NULL);
+ TEST_1(pending_client_close > 0);
+ tp = tt->tt_rtport;
+ pending_server_close = tport_pend(tp, NULL, server_closed_callback, NULL);
+ TEST_1(pending_server_close > 0);
+
+#ifndef WIN32 /* Windows seems to be buffering too much */
/* Create a large message, just to force queueing in sending end */
TEST(new_test_msg(tt, &msg, "tcp-0", 1, 16 * 64 * 1024), 0);
test_create_md5(tt, msg);
TEST_1(tp = tport_tsend(tt->tt_tports, msg, tt->tt_tcp_name, TAG_END()));
TEST_S(tport_name(tp)->tpn_ident, "client");
- tp0 = tport_incref(tp);
+ TEST_P(tport_incref(tp), tp0); tport_decref(&tp);
msg_destroy(msg);
/* Fill up the queue */
@@ -827,10 +893,12 @@ static int tcp_test(tp_test_t *tt)
msg_destroy(tt->tt_rmsg), tt->tt_rmsg = NULL;
}
+#endif
+
/* This uses a new connection */
TEST_1(!new_test_msg(tt, &msg, "tcp-no-reuse", 1, 1024));
TEST_1(tp = tport_tsend(tt->tt_tports, msg, tt->tt_tcp_name,
- TPTAG_REUSE(0), TAG_END()));
+ TPTAG_REUSE(0), TAG_END()));
TEST_S(tport_name(tp)->tpn_ident, "client");
TEST_1(tport_incref(tp) != tp0); tport_decref(&tp);
msg_destroy(msg);
@@ -838,7 +906,7 @@ static int tcp_test(tp_test_t *tt)
/* This uses the old connection */
TEST_1(!new_test_msg(tt, &msg, "tcp-reuse", 1, 1024));
TEST_1(tp = tport_tsend(tt->tt_tports, msg, tt->tt_tcp_name,
- TPTAG_REUSE(1), TAG_END()));
+ TPTAG_REUSE(1), TAG_END()));
TEST_S(tport_name(tp)->tpn_ident, "client");
TEST_1(tport_incref(tp) == tp0); tport_decref(&tp);
msg_destroy(msg);
@@ -863,9 +931,99 @@ static int tcp_test(tp_test_t *tt)
TEST_1(!check_msg(tt, tt->tt_rmsg, "tcp-last"));
msg_destroy(tt->tt_rmsg), tt->tt_rmsg = NULL;
+ TEST_1(pending_server_close && pending_client_close);
+ SU_DEBUG_3(("tport_test(%p): waiting for PONG timeout\n", (void *)tp0));
+
+ /* Wait until notifications -
+ client closes when no pong is received and notifys pending,
+ then server closes and notifys pending */
+ while (pending_server_close || pending_client_close)
+ su_root_step(tt->tt_root, 50);
+
tport_decref(&tp0);
-#endif
+ /* Again a single message */
+ TEST_1(!new_test_msg(tt, &msg, "tcp-pingpong", 1, 512));
+ TEST_1(tp = tport_tsend(tt->tt_tports, msg, tt->tt_tcp_name, TAG_END()));
+ TEST_S(tport_name(tp)->tpn_ident, "client");
+ tp0 = tport_incref(tp);
+ msg_destroy(msg);
+
+ tport_set_params(tp0,
+ TPTAG_KEEPALIVE(250),
+ TPTAG_PINGPONG(200),
+ TAG_END());
+
+ TEST(tport_test_run(tt, 5), 1);
+ TEST_1(!check_msg(tt, tt->tt_rmsg, "tcp-pingpong"));
+ msg_destroy(tt->tt_rmsg), tt->tt_rmsg = NULL;
+
+ /* Ask for notifications upon close */
+ pending_client_close = tport_pend(tp0, NULL, client_closed_callback, NULL);
+ TEST_1(pending_client_close > 0);
+
+ tp = tt->tt_rtport;
+ pending_server_close = tport_pend(tp, NULL, server_closed_callback, NULL);
+ TEST_1(pending_server_close > 0);
+
+ /* Now server responds with pong ... */
+ TEST(tport_set_params(tp, TPTAG_PONG2PING(1), TAG_END()), 1);
+
+ started = su_now();
+
+ while (pending_server_close && pending_client_close) {
+ su_root_step(tt->tt_root, 50);
+ if (su_duration(su_now(), started) > 1000)
+ break;
+ }
+
+ /* ... and we are still pending after a second */
+ TEST_1(pending_client_close && pending_server_close);
+ TEST_1(su_duration(su_now(), started) > 1000);
+
+ tport_shutdown(tp0, 2);
+ tport_unref(tp0);
+
+ while (pending_server_close || pending_client_close)
+ su_root_step(tt->tt_root, 50);
+
+ END();
+}
+
+static int test_incomplete(tp_test_t *tt)
+{
+ BEGIN();
+
+ su_addrinfo_t const *ai = tt->tt_tcp_addr;
+ su_socket_t s;
+ int connected;
+
+ TEST_1(ai != NULL);
+
+ TEST(tport_set_params(tt->tt_tcp, TPTAG_TIMEOUT(500), TAG_END()), 1);
+
+ s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ TEST_1(s != SOCKET_ERROR);
+
+ connected = connect(s, ai->ai_addr, ai->ai_addrlen);
+
+ su_root_step(tt->tt_root, 50);
+
+ TEST(send(s, "F", 1, 0), 1);
+ su_root_step(tt->tt_root, 50);
+ TEST(send(s, "O", 1, 0), 1);
+ su_root_step(tt->tt_root, 50);
+ TEST(send(s, "O", 1, 0), 1);
+ su_root_step(tt->tt_root, 50);
+ TEST(send(s, " ", 1, 0), 1);
+ su_root_step(tt->tt_root, 50);
+
+ tt->tt_received = 0;
+ TEST(tport_test_run(tt, 5), -1);
+ TEST(tt->tt_received, 1);
+ TEST_P(tt->tt_rmsg, NULL);
+
+ su_close(s);
END();
}
@@ -967,34 +1125,48 @@ static int sctp_test(tp_test_t *tt)
msg_t *msg = NULL;
int i, n;
- tport_t *tp;
+ tport_t *tp, *tp0;
char buffer[32];
if (!tt->tt_sctp_name->tpn_proto)
return 0;
/* Just a small and nice message first */
- TEST_1(!new_test_msg(tt, &msg, "sctp-small", 1, 1024));
+ TEST_1(!new_test_msg(tt, &msg, "cid:sctp-first", 1, 1024));
test_create_md5(tt, msg);
TEST_1(tp = tport_tsend(tt->tt_tports, msg, tt->tt_sctp_name, TAG_END()));
TEST_S(tport_name(tp)->tpn_ident, "client");
msg_destroy(msg);
-
+
+ tport_set_params(tp, TPTAG_KEEPALIVE(100), TPTAG_IDLE(500), TAG_END());
+
TEST(tport_test_run(tt, 5), 1);
TEST_1(!check_msg(tt, tt->tt_rmsg, NULL));
test_check_md5(tt, tt->tt_rmsg);
msg_destroy(tt->tt_rmsg), tt->tt_rmsg = NULL;
- if (1)
- return 0; /* SCTP does not work reliably. Really. */
+ tp0 = tport_ref(tp);
+
+ pending_server_close = pending_client_close = 0;
+
+ /* Ask for notification upon close */
+ pending_client_close = tport_pend(tp, NULL, client_closed_callback, NULL);
+ TEST_1(pending_client_close > 0);
+ tp = tt->tt_rtport;
+ pending_server_close = tport_pend(tp, NULL, server_closed_callback, NULL);
+ TEST_1(pending_server_close > 0);
+
+ if (0) { /* SCTP does not work reliably. Really. */
+
+ tt->tt_received = 0;
/* Create large messages, just to force queueing in sending end */
for (n = 0; !tport_queuelen(tp); n++) {
snprintf(buffer, sizeof buffer, "cid:sctp-%u", n);
TEST_1(!new_test_msg(tt, &msg, buffer, 1, 32000));
test_create_md5(tt, msg);
- TEST_1(tport_tsend(tp, msg, tt->tt_sctp_name, TAG_END()));
+ TEST_1(tp = tport_tsend(tp0, msg, tt->tt_sctp_name, TAG_END()));
TEST_S(tport_name(tp)->tpn_ident, "client");
msg_destroy(msg);
}
@@ -1003,11 +1175,11 @@ static int sctp_test(tp_test_t *tt)
for (i = 1; i < TPORT_QUEUESIZE; i++) {
snprintf(buffer, sizeof buffer, "cid:sctp-%u", n + i);
TEST_1(!new_test_msg(tt, &msg, buffer, 1, 1024));
- TEST_1(tport_tsend(tp, msg, tt->tt_sctp_name, TAG_END()));
+ TEST_1(tp = tport_tsend(tp0, msg, tt->tt_sctp_name, TAG_END()));
msg_destroy(msg);
}
- /* This overflows the queue */
+ /* Try to overflow the queue */
snprintf(buffer, sizeof buffer, "cid:sctp-%u", n + i);
TEST_1(!new_test_msg(tt, &msg, buffer, 1, 1024));
TEST_1(!tport_tsend(tt->tt_tports, msg, tt->tt_sctp_name, TAG_END()));
@@ -1020,13 +1192,13 @@ static int sctp_test(tp_test_t *tt)
msg_destroy(tt->tt_rmsg), tt->tt_rmsg = NULL;
/* This uses a new connection */
- TEST_1(!new_test_msg(tt, &msg, "cid-sctp-new", 1, 1024));
+ TEST_1(!new_test_msg(tt, &msg, "cid:sctp-new", 1, 1024));
TEST_1(tport_tsend(tt->tt_tports, msg, tt->tt_sctp_name,
TPTAG_REUSE(0), TAG_END()));
msg_destroy(msg);
/* Receive every message from queue */
- for (tt->tt_received = 0; tt->tt_received < TPORT_QUEUESIZE + n;) {
+ for (; tt->tt_received < n + TPORT_QUEUESIZE - 1;) {
TEST(tport_test_run(tt, 10), 1);
/* Validate message */
TEST_1(!check_msg(tt, tt->tt_rmsg, NULL));
@@ -1034,7 +1206,7 @@ static int sctp_test(tp_test_t *tt)
}
/* Try to send a single message */
- TEST_1(!new_test_msg(tt, &msg, "cid:sctp-final", 1, 1024));
+ TEST_1(!new_test_msg(tt, &msg, "cid:sctp-final", 1, 512));
TEST_1(tport_tsend(tt->tt_tports, msg, tt->tt_sctp_name, TAG_END()));
msg_destroy(msg);
@@ -1042,6 +1214,15 @@ static int sctp_test(tp_test_t *tt)
TEST_1(!check_msg(tt, tt->tt_rmsg, NULL));
msg_destroy(tt->tt_rmsg), tt->tt_rmsg = NULL;
+ }
+
+ tport_unref(tp0);
+
+ /* Wait until notifications -
+ client closes when idle and notifys pending,
+ then server closes and notifys pending */
+ while (pending_server_close || pending_client_close)
+ su_root_step(tt->tt_root, 50);
END();
}
@@ -1055,15 +1236,35 @@ static int tls_test(tp_test_t *tt)
msg_t *msg = NULL;
int i;
char ident[16];
+ tport_t *tp, *tp0;
TEST_S(dst->tpn_proto, "tls");
- tt->tt_received = 0;
+ /* Send a single message */
+ TEST_1(!new_test_msg(tt, &msg, "tls-first", 1, 1024));
+ TEST_1(tp = tport_tsend(tt->tt_tports, msg, dst, TAG_END()));
+ TEST_1(tp0 = tport_ref(tp));
+ msg_destroy(msg);
- /* Create a large message, just to force queueing in sending end */
- TEST_1(!new_test_msg(tt, &msg, "tls-0", 16, 64 * 1024));
+ TEST(tport_test_run(tt, 5), 1);
+
+ TEST_1(!check_msg(tt, tt->tt_rmsg, "tls-first"));
+ msg_destroy(tt->tt_rmsg), tt->tt_rmsg = NULL;
+
+ tport_set_params(tp, TPTAG_KEEPALIVE(100), TPTAG_IDLE(500), TAG_END());
+
+ /* Ask for notification upon close */
+ pending_client_close = tport_pend(tp0, NULL, client_closed_callback, NULL);
+ TEST_1(pending_client_close > 0);
+ tp = tt->tt_rtport;
+ pending_server_close = tport_pend(tp, NULL, server_closed_callback, NULL);
+ TEST_1(pending_server_close > 0);
+
+ /* Send a largish message */
+ TEST_1(!new_test_msg(tt, &msg, "tls-0", 16, 16 * 1024));
test_create_md5(tt, msg);
- TEST_1(tport_tsend(tt->tt_tports, msg, dst, TAG_END()));
+ TEST_1(tp = tport_tsend(tt->tt_tports, msg, dst, TAG_END()));
+ TEST_1(tp == tp0);
msg_destroy(msg);
/* Fill up the queue */
@@ -1071,25 +1272,20 @@ static int tls_test(tp_test_t *tt)
snprintf(ident, sizeof ident, "tls-%u", i);
TEST_1(!new_test_msg(tt, &msg, ident, 2, 512));
- TEST_1(tport_tsend(tt->tt_tports, msg, dst, TAG_END()));
+ TEST_1(tp = tport_tsend(tt->tt_tports, msg, dst, TAG_END()));
+ TEST_1(tp == tp0);
msg_destroy(msg);
}
- /* This overflows the queue */
- TEST_1(!new_test_msg(tt, &msg, "tls-overflow", 1, 1024));
- TEST_1(!tport_tsend(tt->tt_tports, msg, dst, TAG_END()));
- msg_destroy(msg);
-
- TEST(tport_test_run(tt, 60), 1);
-
- msg_destroy(tt->tt_rmsg), tt->tt_rmsg = NULL;
-
/* This uses a new connection */
TEST_1(!new_test_msg(tt, &msg, "tls-no-reuse", 1, 1024));
- TEST_1(tport_tsend(tt->tt_tports, msg, dst,
+ TEST_1(tp = tport_tsend(tt->tt_tports, msg, dst,
TPTAG_REUSE(0), TAG_END()));
+ TEST_1(tp != tp0);
msg_destroy(msg);
+ tt->tt_received = 0;
+
/* Receive every message from queue */
while (tt->tt_received < TPORT_QUEUESIZE + 1) {
TEST(tport_test_run(tt, 5), 1);
@@ -1107,6 +1303,14 @@ static int tls_test(tp_test_t *tt)
TEST_1(!check_msg(tt, tt->tt_rmsg, "tls-last"));
msg_destroy(tt->tt_rmsg), tt->tt_rmsg = NULL;
+ tport_decref(&tp0);
+
+ /* Wait until notifications -
+ client closes when idle and notifys pending,
+ then server closes and notifys pending */
+ while (pending_server_close || pending_client_close)
+ su_root_step(tt->tt_root, 50);
+
#endif
END();
@@ -1363,9 +1567,24 @@ static int filter_test(tp_test_t *tt)
END();
}
+#if HAVE_ALARM
+#include
+
+static RETSIGTYPE sig_alarm(int s)
+{
+ fprintf(stderr, "%s: FAIL! test timeout!\n", name);
+ exit(1);
+}
+
+char const alarm_option[] = " [--no-alarm]";
+
+#else
+char const alarm_option[] = "";
+#endif
+
void usage(int exitcode)
{
- fprintf(stderr, "usage: %s [-v] [-a]\n", name);
+ fprintf(stderr, "usage: %s [-v] [-a]%s\n", name, alarm_option);
exit(exitcode);
}
@@ -1373,7 +1592,9 @@ int main(int argc, char *argv[])
{
int flags = 0; /* XXX */
int retval = 0;
+ int no_alarm = 0;
int i;
+
tp_test_t tt[1] = {{{ SU_HOME_INIT(tt) }}};
for (i = 1; argv[i]; i++) {
@@ -1381,6 +1602,8 @@ int main(int argc, char *argv[])
tstflags |= tst_verbatim;
else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--abort") == 0)
tstflags |= tst_abort;
+ else if (strcmp(argv[i], "--no-alarm") == 0)
+ no_alarm = 1;
else
usage(1);
}
@@ -1389,6 +1612,13 @@ int main(int argc, char *argv[])
tstflags |= tst_verbatim;
#endif
+#if HAVE_ALARM
+ if (!no_alarm) {
+ signal(SIGALRM, sig_alarm);
+ alarm(120);
+ }
+#endif
+
/* Use log */
if (flags & tst_verbatim)
tport_log->log_default = 9;
@@ -1406,6 +1636,7 @@ int main(int argc, char *argv[])
retval |= sctp_test(tt); fflush(stdout);
retval |= udp_test(tt); fflush(stdout);
retval |= tcp_test(tt); fflush(stdout);
+ retval |= test_incomplete(tt); fflush(stdout);
retval |= reuse_test(tt); fflush(stdout);
retval |= tls_test(tt); fflush(stdout);
if (0) /* Not yet working... */
diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport.c
index 66d31569d0..846ae10210 100644
--- a/libs/sofia-sip/libsofia-sip-ua/tport/tport.c
+++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport.c
@@ -47,7 +47,7 @@
typedef struct tport_nat_s tport_nat_t;
#define SU_WAKEUP_ARG_T struct tport_s
-#define SU_TIMER_ARG_T struct tport_master
+#define SU_TIMER_ARG_T struct tport_s
#define SU_MSG_ARG_T union tport_su_msg_arg
#include
@@ -127,6 +127,33 @@ RBTREE_BODIES(su_inline, tprb, tport_t,
TP_IS_RED, TP_SET_RED, TP_IS_BLACK, TP_SET_BLACK, TP_COPY_COLOR,
tp_cmp, TP_INSERT, TP_REMOVE);
+static void tplist_insert(tport_t **list, tport_t *tp)
+{
+ if (*list == NULL)
+ *list = tp;
+ else
+ tp->tp_right = *list, (*list)->tp_left = tp, *list = tp;
+
+ for (tp = *list; tp; tp = tp->tp_right) {
+ assert(tp->tp_left == NULL || tp == tp->tp_left->tp_right);
+ assert(tp->tp_right == NULL || tp == tp->tp_right->tp_left);
+ }
+}
+
+static void tplist_remove(tport_t **list, tport_t *tp)
+{
+ if (*list == tp) {
+ *list = tp->tp_right; assert(tp->tp_left == NULL);
+ }
+ else if (tp->tp_left) {
+ tp->tp_left->tp_right = tp->tp_right;
+ }
+ if (tp->tp_right) {
+ tp->tp_right->tp_left = tp->tp_left;
+ }
+ TP_REMOVE(tp);
+}
+
enum {
/** Default per-thread read queue length */
THRP_PENDING = 8
@@ -304,6 +331,12 @@ int tport_is_clear_to_send(tport_t const *self)
!self->tp_send_close);
}
+/** Return true if transport has message in send queue. @NEW_1_12_7 */
+int tport_has_queued(tport_t const *self)
+{
+ return self && self->tp_queue && self->tp_queue[self->tp_qhead];
+}
+
/** MTU for transport */
su_inline unsigned tport_mtu(tport_t const *self)
{
@@ -387,9 +420,6 @@ tport_t *tport_by_addrinfo(tport_primary_t const *pri,
tp_name_t const *tpn);
void tport_peer_address(tport_t *self, msg_t *msg);
-static unsigned long tport_now(void);
-
-static void tport_tick(su_root_magic_t *, su_timer_t *, tport_master_t *mr);
static void tport_parse(tport_t *self, int complete, su_time_t now);
@@ -410,7 +440,6 @@ static void tport_zap_primary(tport_primary_t *);
static char *localipname(int pf, char *buf, size_t bufsiz);
static int getprotohints(su_addrinfo_t *hints,
char const *proto, int flags);
-static void tport_send_queue(tport_t *self);
/* Stack class used when transports are being destroyed */
@@ -454,7 +483,6 @@ tport_t *tport_tcreate(tp_stack_t *stack,
tport_master_t *mr;
tp_name_t *tpn;
tport_params_t *tpp;
- unsigned tick;
ta_list ta;
if (!stack || !tpac || !root) {
@@ -483,6 +511,9 @@ tport_t *tport_tcreate(tp_stack_t *stack,
tpp->tpp_idle = UINT_MAX;
tpp->tpp_timeout = UINT_MAX;
tpp->tpp_sigcomp_lifetime = UINT_MAX;
+ tpp->tpp_keepalive = 0;
+ tpp->tpp_pingpong = 0;
+ tpp->tpp_pong2ping = 0;
tpp->tpp_stun_server = 1;
tpp->tpp_tos = -1; /* set invalid, valid values are 0-255 */
@@ -497,21 +528,10 @@ tport_t *tport_tcreate(tp_stack_t *stack,
tport_set_params(mr->mr_master, ta_tags(ta));
tport_open_log(mr, ta_args(ta));
- tick = 5000; /* For testing, usually 30000 is enough */
- if (tpp->tpp_idle < 4 * tick)
- tick = tpp->tpp_idle / 4;
- if (tpp->tpp_timeout < 4 * tick)
- tick = tpp->tpp_timeout / 4;
- if (tick < 200)
- tick = 200;
-
#if HAVE_SOFIA_STUN
tport_init_stun_server(mr, ta_args(ta));
#endif
- mr->mr_timer = su_timer_create(su_root_task(root), tick);
- su_timer_set(mr->mr_timer, tport_tick, mr);
-
ta_end(ta);
return mr->mr_master;
@@ -640,8 +660,10 @@ void tport_zap_primary(tport_primary_t *pri)
if (pri->pri_vtable->vtp_deinit_primary)
pri->pri_vtable->vtp_deinit_primary(pri);
- while (pri->pri_secondary)
- tport_zap_secondary(pri->pri_secondary);
+ while (pri->pri_open)
+ tport_zap_secondary(pri->pri_open);
+ while (pri->pri_closed)
+ tport_zap_secondary(pri->pri_closed);
/* We have just a single-linked list for primary transports */
for (prip = &pri->pri_master->mr_primaries;
@@ -651,7 +673,7 @@ void tport_zap_primary(tport_primary_t *pri)
*prip = pri->pri_next;
- tport_zap_secondary(pri->pri_primary);
+ tport_zap_secondary((tport_t *)pri);
}
/**Create a primary transport object with socket.
@@ -724,7 +746,7 @@ tport_primary_t *tport_listen(tport_master_t *mr,
pri->pri_primary->tp_has_connection = 0;
SU_DEBUG_5(("%s(%p): %s " TPN_FORMAT "\n",
- __func__, (void *)pri, "listening at",
+ __func__, (void *)pri, "listening at",
TPN_ARGS(pri->pri_primary->tp_name)));
return pri;
@@ -812,9 +834,8 @@ int tport_set_events(tport_t *self, int set, int clear)
/**Allocate a secondary transport. @internal
*
- * The function tport_alloc_secondary() creates a secondary transport
- * object. The new transport initally shares parameters structure with the
- * original transport.
+ * Create a secondary transport object. The new transport initally shares
+ * parameters structure with the original transport.
*
* @param pri primary transport
* @param socket socket for transport
@@ -836,7 +857,8 @@ tport_t *tport_alloc_secondary(tport_primary_t *pri,
self = su_home_clone(mr->mr_home, pri->pri_vtable->vtp_secondary_size);
if (self) {
- SU_DEBUG_7(("%s(%p): new secondary tport %p\n", __func__, (void *)pri, (void *)self));
+ SU_DEBUG_7(("%s(%p): new secondary tport %p\n",
+ __func__, (void *)pri, (void *)self));
self->tp_refs = -1; /* Freshly allocated */
self->tp_master = mr;
@@ -850,7 +872,10 @@ tport_t *tport_alloc_secondary(tport_primary_t *pri,
self->tp_addrinfo->ai_addr = (void *)self->tp_addr;
self->tp_socket = socket;
-
+
+ self->tp_timer = su_timer_create(su_root_task(mr->mr_root), 0);
+ self->tp_stime = self->tp_ktime = self->tp_rtime = su_now();
+
if (pri->pri_vtable->vtp_init_secondary &&
pri->pri_vtable->vtp_init_secondary(self, socket, accepted,
return_reason) < 0) {
@@ -889,13 +914,18 @@ tport_t *tport_connect(tport_primary_t *pri,
su_addrinfo_t *ai,
tp_name_t const *tpn)
{
+ tport_t *tp;
+
if (ai == NULL || ai->ai_addrlen > sizeof (pri->pri_primary->tp_addr))
return NULL;
if (pri->pri_vtable->vtp_connect)
return pri->pri_vtable->vtp_connect(pri, ai, tpn);
- else
- return tport_base_connect(pri, ai, ai, tpn);
+
+ tp = tport_base_connect(pri, ai, ai, tpn);
+ if (tp)
+ tport_set_secondary_timer(tp);
+ return tp;
}
/**Create a connected transport object with socket.
@@ -961,14 +991,14 @@ tport_t *tport_base_connect(tport_primary_t *pri,
/* Bind this socket to same IP address as the primary server socket */
if (getsockname(server_socket, &susa.su_sa, &susalen) < 0) {
- SU_DEBUG_3(("tport_connect: getsockname(): %s\n",
- su_strerror(su_errno())));
+ SU_DEBUG_3(("%s(%p): getsockname(): %s\n",
+ __func__, (void *)self, su_strerror(su_errno())));
}
else {
susa.su_port = 0;
if (bind(s, &susa.su_sa, susalen) < 0) {
- SU_DEBUG_3(("tport_connect: bind(local-ip): %s\n",
- su_strerror(su_errno())));
+ SU_DEBUG_3(("%s(%p): bind(local-ip): %s\n",
+ __func__, (void *)self, su_strerror(su_errno())));
}
}
}
@@ -1005,16 +1035,16 @@ tport_t *tport_base_connect(tport_primary_t *pri,
if (ai == real_ai) {
SU_DEBUG_5(("%s(%p): %s to " TPN_FORMAT "\n",
- __func__, (void *)self, what, TPN_ARGS(self->tp_name)));
+ __func__, (void *)self, what, TPN_ARGS(self->tp_name)));
}
else {
SU_DEBUG_5(("%s(%p): %s via %s to " TPN_FORMAT "\n",
- __func__, (void *)self, what,
+ __func__, (void *)self, what,
tport_hostport(buf, sizeof(buf), (void *)ai->ai_addr, 2),
TPN_ARGS(self->tp_name)));
}
-
- tprb_append(&pri->pri_secondary, self);
+
+ tprb_append(&pri->pri_open, self);
return self;
}
@@ -1028,7 +1058,13 @@ void tport_zap_secondary(tport_t *self)
return;
/* Remove from rbtree */
- tprb_remove(&self->tp_pri->pri_secondary, self);
+ if (!tport_is_closed(self))
+ tprb_remove(&self->tp_pri->pri_open, self);
+ else
+ tplist_remove(&self->tp_pri->pri_closed, self);
+
+ if (self->tp_timer)
+ su_timer_destroy(self->tp_timer), self->tp_timer = NULL;
/* Do not deinit primary as secondary! */
if (tport_is_secondary(self) &&
@@ -1038,21 +1074,22 @@ void tport_zap_secondary(tport_t *self)
if (self->tp_msg) {
msg_destroy(self->tp_msg), self->tp_msg = NULL;
SU_DEBUG_3(("%s(%p): zapped partially received message\n",
- __func__, (void *)self));
+ __func__, (void *)self));
}
- if (self->tp_queue && self->tp_queue[self->tp_qhead]) {
+ if (tport_has_queued(self)) {
size_t n = 0, i, N = self->tp_params->tpp_qsize;
for (i = self->tp_qhead; self->tp_queue[i]; i = (i + 1) % N) {
msg_destroy(self->tp_queue[i]), self->tp_queue[i] = NULL;
n++;
}
SU_DEBUG_3(("%s(%p): zapped %lu queued messages\n",
- __func__, (void *)self, (LU)n));
+ __func__, (void *)self, (LU)n));
}
if (self->tp_pused) {
- SU_DEBUG_3(("%s(%p): zapped with pending requests\n", __func__, (void *)self));
+ SU_DEBUG_3(("%s(%p): zapped while pending\n",
+ __func__, (void *)self));
}
mr = self->tp_master;
@@ -1086,10 +1123,18 @@ tport_t *tport_ref(tport_t *tp)
/** Destroy reference to a transport object. */
void tport_unref(tport_t *tp)
{
- if (tp && tp->tp_refs > 0)
- if (--tp->tp_refs == 0 && tp->tp_params->tpp_idle == 0)
- if (!tport_is_closed(tp))
- tport_close(tp);
+ if (tp == NULL || tp->tp_refs <= 0)
+ return;
+ if (--tp->tp_refs > 0)
+ return;
+
+ if (!tport_is_secondary(tp))
+ return;
+
+ if (tp->tp_params->tpp_idle == 0)
+ tport_close(tp);
+
+ tport_set_secondary_timer(tp);
}
/** Create a new reference to transport object. */
@@ -1113,6 +1158,14 @@ void tport_decref(tport_t **ttp)
*
* @param self pointer to a transport object
* @param tag,value,... list of tags
+ *
+ * @TAGS
+ * TPTAG_MTU_REF(), TPTAG_QUEUESIZE_REF(), TPTAG_IDLE_REF(),
+ * TPTAG_TIMEOUT_REF(), TPTAG_KEEPALIVE_REF(), TPTAG_PINGPONG_REF(),
+ * TPTAG_PONG2PING_REF(), TPTAG_DEBUG_DROP_REF(), TPTAG_THRPSIZE_REF(),
+ * TPTAG_THRPRQSIZE_REF(), TPTAG_SIGCOMP_LIFETIME_REF(),
+ * TPTAG_CONNECT_REF(), TPTAG_SDWN_ERROR_REF(), TPTAG_REUSE_REF(),
+ * TPTAG_STUN_SERVER_REF(), TPTAG_PUBLIC_REF() and TPTAG_TOS_REF().
*/
int tport_get_params(tport_t const *self,
tag_type_t tag, tag_value_t value, ...)
@@ -1140,10 +1193,15 @@ int tport_get_params(tport_t const *self,
TPTAG_QUEUESIZE(tpp->tpp_qsize),
TPTAG_IDLE(tpp->tpp_idle),
TPTAG_TIMEOUT(tpp->tpp_timeout),
+ TPTAG_KEEPALIVE(tpp->tpp_keepalive),
+ TPTAG_PINGPONG(tpp->tpp_pingpong),
+ TPTAG_PONG2PING(tpp->tpp_pong2ping),
TPTAG_SDWN_ERROR(tpp->tpp_sdwn_error),
TPTAG_DEBUG_DROP(tpp->tpp_drop),
TPTAG_THRPSIZE(tpp->tpp_thrpsize),
TPTAG_THRPRQSIZE(tpp->tpp_thrprqsize),
+ TPTAG_SIGCOMP_LIFETIME(tpp->tpp_sigcomp_lifetime),
+ TPTAG_STUN_SERVER(tpp->tpp_stun_server),
TAG_IF(pri, TPTAG_PUBLIC(pri ? pri->pri_public : 0)),
TPTAG_TOS(tpp->tpp_tos),
TAG_END());
@@ -1160,6 +1218,7 @@ int tport_get_params(tport_t const *self,
*
* @TAGS
* TPTAG_MTU(), TPTAG_QUEUESIZE(), TPTAG_IDLE(), TPTAG_TIMEOUT(),
+ * TPTAG_KEEPALIVE(), TPTAG_PINGPONG(), TPTAG_PONG2PING(),
* TPTAG_DEBUG_DROP(), TPTAG_THRPSIZE(), TPTAG_THRPRQSIZE(),
* TPTAG_SIGCOMP_LIFETIME(), TPTAG_CONNECT(), TPTAG_SDWN_ERROR(),
* TPTAG_REUSE(), TPTAG_STUN_SERVER(), and TPTAG_TOS().
@@ -1172,7 +1231,7 @@ int tport_set_params(tport_t *self,
tport_params_t tpp[1], *tpp0;
usize_t mtu;
- int connect, sdwn_error, reusable, stun_server;
+ int connect, sdwn_error, reusable, stun_server, pong2ping;
if (self == NULL)
return su_seterrno(EINVAL);
@@ -1184,6 +1243,7 @@ int tport_set_params(tport_t *self,
sdwn_error = tpp->tpp_sdwn_error;
reusable = self->tp_reusable;
stun_server = tpp->tpp_stun_server;
+ pong2ping = tpp->tpp_pong2ping;
ta_start(ta, tag, value);
@@ -1192,6 +1252,9 @@ int tport_set_params(tport_t *self,
TAG_IF(!self->tp_queue, TPTAG_QUEUESIZE_REF(tpp->tpp_qsize)),
TPTAG_IDLE_REF(tpp->tpp_idle),
TPTAG_TIMEOUT_REF(tpp->tpp_timeout),
+ TPTAG_KEEPALIVE_REF(tpp->tpp_keepalive),
+ TPTAG_PINGPONG_REF(tpp->tpp_pingpong),
+ TPTAG_PONG2PING_REF(pong2ping),
TPTAG_DEBUG_DROP_REF(tpp->tpp_drop),
TPTAG_THRPSIZE_REF(tpp->tpp_thrpsize),
TPTAG_THRPRQSIZE_REF(tpp->tpp_thrprqsize),
@@ -1208,10 +1271,10 @@ int tport_set_params(tport_t *self,
if (n == 0)
return 0;
- if (tpp->tpp_idle > 0 && tpp->tpp_idle < 2000)
- tpp->tpp_idle = 2000;
- if (tpp->tpp_timeout < 1000)
- tpp->tpp_timeout = 1000;
+ if (tpp->tpp_idle > 0 && tpp->tpp_idle < 100)
+ tpp->tpp_idle = 100;
+ if (tpp->tpp_timeout < 100)
+ tpp->tpp_timeout = 100;
if (tpp->tpp_drop > 1000)
tpp->tpp_drop = 1000;
if (tpp->tpp_thrprqsize > 0)
@@ -1229,6 +1292,10 @@ int tport_set_params(tport_t *self,
tpp->tpp_sdwn_error = sdwn_error;
self->tp_reusable = reusable;
tpp->tpp_stun_server = stun_server;
+ tpp->tpp_pong2ping = pong2ping;
+
+ if (memcmp(tpp0, tpp, sizeof tpp) == 0)
+ return n;
if (tport_is_secondary(self) &&
self->tp_params == self->tp_pri->pri_primary->tp_params) {
@@ -1238,6 +1305,9 @@ int tport_set_params(tport_t *self,
memcpy(tpp0, tpp, sizeof tpp);
+ if (tport_is_secondary(self))
+ tport_set_secondary_timer(self);
+
return n;
}
@@ -1441,7 +1511,8 @@ int tport_bind_client(tport_master_t *mr,
if (public == tport_type_local)
public = tport_type_client;
- SU_DEBUG_5(("%s(%p) to " TPN_FORMAT "\n", __func__, (void *)mr, TPN_ARGS(tpn)));
+ SU_DEBUG_5(("%s(%p) to " TPN_FORMAT "\n",
+ __func__, (void *)mr, TPN_ARGS(tpn)));
memset(tpn0, 0, sizeof(tpn0));
@@ -1503,7 +1574,8 @@ int tport_bind_server(tport_master_t *mr,
(void)hostname;
- SU_DEBUG_5(("%s(%p) to " TPN_FORMAT "\n", __func__, (void *)mr, TPN_ARGS(tpn)));
+ SU_DEBUG_5(("%s(%p) to " TPN_FORMAT "\n",
+ __func__, (void *)mr, TPN_ARGS(tpn)));
if (tpn->tpn_host == NULL || strcmp(tpn->tpn_host, tpn_any) == 0) {
/* Use a local IP address */
@@ -1618,7 +1690,7 @@ int tport_bind_server(tport_master_t *mr,
break;
SU_DEBUG_3(("%s(%p): cannot bind all transports to port %u, trying %u\n",
- __func__, (void *)mr, old, port));
+ __func__, (void *)mr, old, port));
}
tport_freeaddrinfo(res);
@@ -1723,7 +1795,8 @@ int tport_server_addrinfo(tport_master_t *mr,
int error = tport_getaddrinfo(host, service, hints, return_addrinfo);
if (error || !*return_addrinfo) {
SU_DEBUG_3(("%s(%p): su_getaddrinfo(%s, %s) for %s: %s\n",
- __func__, (void *)mr, host ? host : "\"\"", service, protocol,
+ __func__, (void *)mr,
+ host ? host : "\"\"", service, protocol,
su_gai_strerror(error)));
return su_seterrno(error != EAI_MEMORY ? ENOENT : ENOMEM);
}
@@ -1774,13 +1847,13 @@ tport_get_local_addrinfo(tport_master_t *mr,
if (error) {
#if SU_HAVE_IN6
SU_DEBUG_3(("%s(%p): su_getlocalinfo() for %s address: %s\n",
- __func__, (void *)mr,
+ __func__, (void *)mr,
family == AF_INET6 ? "ip6"
: family == AF_INET ? "ip4" : "ip",
su_gli_strerror(error)));
#else
SU_DEBUG_3(("%s(%p): su_getlocalinfo() for %s address: %s\n",
- __func__, (void *)mr,
+ __func__, (void *)mr,
family == AF_INET ? "ip4" : "ip",
su_gli_strerror(error)));
#endif
@@ -1972,13 +2045,19 @@ int tport_addrinfo_copy(su_addrinfo_t *dst, void *addr, socklen_t addrlen,
/** Close a transport.
*
- * The function tport_close() closes a socket associated with a transport
- * object.
+ * Close the socket associated with a transport object. Report an error to
+ * all pending clients, if required. Set/reset timer, too.
*/
void tport_close(tport_t *self)
{
- SU_DEBUG_5(("%s(%p): " TPN_FORMAT "\n", "tport_close", (void *)self,
- TPN_ARGS(self->tp_name)));
+ SU_DEBUG_5(("%s(%p): " TPN_FORMAT "\n",
+ __func__, (void *)self, TPN_ARGS(self->tp_name)));
+
+ if (self->tp_closed || !tport_is_secondary(self))
+ return;
+
+ tprb_remove(&self->tp_pri->pri_open, self);
+ tplist_insert(&self->tp_pri->pri_closed, self);
self->tp_closed = 1;
self->tp_send_close = 3;
@@ -2009,7 +2088,7 @@ void tport_close(tport_t *self)
msg_ref_destroy(self->tp_queue[i]), self->tp_queue[i] = NULL;
}
}
-
+
self->tp_index = 0;
self->tp_events = 0;
}
@@ -2024,16 +2103,23 @@ void tport_close(tport_t *self)
*/
int tport_shutdown(tport_t *self, int how)
{
+ int retval;
if (!tport_is_secondary(self))
return -1;
-
- SU_DEBUG_7(("%s(%p, %d)\n", "tport_shutdown", (void *)self, how));
+ retval = tport_shutdown0(self, how);
+ tport_set_secondary_timer(self);
+ return retval;
+}
+
+/** Internal shutdown function */
+int tport_shutdown0(tport_t *self, int how)
+{
+ SU_DEBUG_7(("%s(%p, %d)\n", __func__, (void *)self, how));
if (!tport_is_tcp(self) ||
- how < 0 ||
+ how < 0 || how >= 2 ||
(how == 0 && self->tp_send_close) ||
- (how == 1 && self->tp_recv_close > 1) ||
- how >= 2) {
+ (how == 1 && self->tp_recv_close > 1)) {
tport_close(self);
return 1;
}
@@ -2052,7 +2138,7 @@ int tport_shutdown(tport_t *self, int how)
else if (how == 1) {
self->tp_send_close = 2;
tport_set_events(self, 0, SU_WAIT_OUT);
- if (self->tp_queue && self->tp_queue[self->tp_qhead]) {
+ if (tport_has_queued(self)) {
unsigned short i, N = self->tp_params->tpp_qsize;
for (i = 0; i < N; i++) {
if (self->tp_queue[i]) {
@@ -2063,107 +2149,143 @@ int tport_shutdown(tport_t *self, int how)
}
}
+ return 0;
+}
+
+static void tport_secondary_timer(su_root_magic_t *magic,
+ su_timer_t *t,
+ tport_t *self)
+{
+ su_time_t now;
+
+ if (tport_is_closed(self)) {
+ if (self->tp_refs == 0)
+ tport_zap_secondary(self);
+ return;
+ }
+
+ now = /* su_timer_expired(t); */ su_now();
+
+ if (self->tp_pri->pri_vtable->vtp_secondary_timer)
+ self->tp_pri->pri_vtable->vtp_secondary_timer(self, now);
+ else
+ tport_base_timer(self, now);
+}
+
+/** Base timer for secondary transports.
+ *
+ * Closes and zaps unused transports. Sets the timer again.
+ */
+void tport_base_timer(tport_t *self, su_time_t now)
+{
+ unsigned timeout = self->tp_params->tpp_idle;
+
+ if (timeout != UINT_MAX) {
+ if (self->tp_refs == 0 &&
+ self->tp_msg == NULL &&
+ !tport_has_queued(self) &&
+ su_time_cmp(su_time_add(self->tp_rtime, timeout), now) < 0 &&
+ su_time_cmp(su_time_add(self->tp_stime, timeout), now) < 0) {
+ SU_DEBUG_7(("%s(%p): unused for %d ms,%s zapping\n",
+ __func__, (void *)self,
+ timeout, tport_is_closed(self) ? "" : " closing and"));
+ if (!tport_is_closed(self))
+ tport_close(self);
+ tport_zap_secondary(self);
+ return;
+ }
+ }
+
+ tport_set_secondary_timer(self);
+}
+
+/** Set timer for a secondary transport.
+ *
+ * This function should be called after any network activity:
+ * tport_base_connect(), tport_send_msg(), tport_send_queue(),
+ * tport_recv_data(), tport_shutdown0(), tport_close(),
+ *
+ * @retval 0 always
+ */
+int tport_set_secondary_timer(tport_t *self)
+{
+ su_time_t const infinity = { ULONG_MAX, 999999 };
+ su_time_t target = infinity;
+ char const *why = "not specified";
+ su_timer_f timer = tport_secondary_timer;
+
+ if (!tport_is_secondary(self))
+ return 0;
+
+ if (tport_is_closed(self)) {
+ if (self->tp_refs == 0) {
+ SU_DEBUG_7(("tport(%p): set timer at %u ms because %s\n",
+ self, 0, "zap"));
+ su_timer_set_interval(self->tp_timer, timer, self, 0);
+ }
+ else
+ su_timer_reset(self->tp_timer);
+
+ return 0;
+ }
+
+ if (self->tp_params->tpp_idle != UINT_MAX) {
+ if (self->tp_refs == 0 &&
+ self->tp_msg == NULL && !tport_has_queued(self)) {
+ if (su_time_cmp(self->tp_stime, self->tp_rtime) < 0) {
+ target = su_time_add(self->tp_rtime, self->tp_params->tpp_idle);
+ why = "idle since recv";
+ }
+ else {
+ target = su_time_add(self->tp_stime, self->tp_params->tpp_idle);
+ why = "idle since send";
+ }
+ }
+ }
+
+ if (self->tp_pri->pri_vtable->vtp_next_secondary_timer)
+ self->tp_pri->pri_vtable->
+ vtp_next_secondary_timer(self, &target, &why);
+
+ if (su_time_cmp(target, infinity)) {
+ SU_DEBUG_7(("tport(%p): set timer at %ld ms because %s\n",
+ (void *)self, su_duration(target, su_now()), why));
+ su_timer_set_at(self->tp_timer, timer, self, target);
+ }
+ else {
+ SU_DEBUG_9(("tport(%p): reset timer\n", (void *)self));
+ su_timer_reset(self->tp_timer);
+ }
return 0;
}
-su_inline
-unsigned long tport_now(void)
-{
- return su_now().tv_sec;
-}
-
-/** Transport timer function. */
-static
-void tport_tick(su_root_magic_t *magic, su_timer_t *t, tport_master_t *mr)
-{
- tport_primary_t *dad;
- tport_t *tp, *tp_next;
- su_time_t now = su_now();
- int ts = (int)su_time_ms(now);
-
- /* Go through all primary transports */
- for (dad = mr->mr_primaries; dad; dad = dad->pri_next) {
- if (dad->pri_primary->tp_addrinfo->ai_protocol == IPPROTO_SCTP) {
- /* Go through all SCTP connections */
-
- tp = dad->pri_secondary;
-
- for (tp = tprb_first(tp); tp; tp = tp_next) {
- tp_next = tprb_succ(tp);
- if (tp->tp_queue && tp->tp_queue[tp->tp_qhead]) {
- SU_DEBUG_9(("tport_tick(%p) - trying to send to %s/%s:%s\n",
- (void *)tp, tp->tp_protoname, tp->tp_host, tp->tp_port));
- tport_send_queue(tp);
- }
- }
- }
-
- /* Go through all secondary transports with incomplete messages */
- for (tp = tprb_first(dad->pri_secondary); tp; tp = tp_next) {
- msg_t *msg = tp->tp_msg;
- int closed;
-
- if (msg &&
- tp->tp_params->tpp_timeout < INT_MAX &&
- (int)tp->tp_params->tpp_timeout < ts - (int)tp->tp_time &&
- !msg_is_streaming(msg)) {
- SU_DEBUG_5(("tport_tick(%p): incomplete message idle for %d ms\n",
- (void *)tp, ts - (int)tp->tp_time));
- msg_set_streaming(msg, 0);
- msg_set_flags(msg, MSG_FLG_ERROR | MSG_FLG_TRUNC | MSG_FLG_TIMEOUT);
- tport_deliver(tp, msg, NULL, NULL, now);
- tp->tp_msg = NULL;
- }
-
- tp_next = tprb_succ(tp);
-
- if (tp->tp_refs)
- continue;
-
- closed = tport_is_closed(tp);
-
- if (!closed &&
- !(tp->tp_params->tpp_idle > 0
- && (int)tp->tp_params->tpp_idle < ts - (int)tp->tp_time)) {
- continue;
- }
-
- if (closed) {
- SU_DEBUG_5(("tport_tick(%p): closed, zapping\n", (void *)tp));
- } else {
- SU_DEBUG_5(("tport_tick(%p): unused for %d ms, closing and zapping\n",
- (void *)tp, ts - (int)tp->tp_time));
- if (!tport_is_closed(tp))
- tport_close(tp);
- }
-
- tport_zap_secondary(tp);
- }
- }
-
- su_timer_set(t, tport_tick, mr);
-}
/** Flush idle connections. */
int tport_flush(tport_t *tp)
{
tport_t *tp_next;
+ tport_primary_t *pri;
if (tp == NULL)
return -1;
+ pri = tp->tp_pri;
+
+ while (pri->pri_closed)
+ tport_zap_secondary(pri->pri_closed);
+
/* Go through all secondary transports, zap idle ones */
- for (tp = tprb_first(tp->tp_pri->pri_secondary); tp; tp = tp_next) {
+ for (tp = tprb_first(tp->tp_pri->pri_open); tp; tp = tp_next) {
tp_next = tprb_succ(tp);
if (tp->tp_refs != 0)
continue;
SU_DEBUG_1(("tport_flush(%p): %szapping\n",
- (void *)tp, tport_is_closed(tp) ? "" : "closing and "));
- if (!tport_is_closed(tp))
- tport_close(tp);
+ (void *)tp, tport_is_closed(tp) ? "" : "closing and "));
+
+ tport_close(tp);
tport_zap_secondary(tp);
}
@@ -2401,10 +2523,10 @@ void tport_error_report(tport_t *self, int errcode,
}
else {
if (tport_is_primary(self))
- SU_DEBUG_3(("%s(%p): %s (with %s)\n", __func__, (void *)self,
+ SU_DEBUG_3(("%s(%p): %s (with %s)\n", __func__, (void *)self,
errmsg, self->tp_protoname));
else
- SU_DEBUG_3(("%s(%p): %s (with %s/%s:%s)\n", __func__, (void *)self,
+ SU_DEBUG_3(("%s(%p): %s (with %s/%s:%s)\n", __func__, (void *)self,
errmsg, self->tp_protoname, self->tp_host, self->tp_port));
}
@@ -2470,9 +2592,9 @@ int tport_accept(tport_primary_t *pri, int events)
if (tport_setname(self, pri->pri_protoname, ai, NULL) != -1) {
SU_DEBUG_5(("%s(%p): new connection from " TPN_FORMAT "\n",
- __func__, (void *)self, TPN_ARGS(self->tp_name)));
+ __func__, (void *)self, TPN_ARGS(self->tp_name)));
- tprb_append(&pri->pri_secondary, self);
+ tprb_append(&pri->pri_open, self);
/* Return succesfully */
return 0;
@@ -2550,15 +2672,20 @@ static int tport_connected(su_root_magic_t *magic, su_wait_t *w, tport_t *self)
su_root_deregister(mr->mr_root, self->tp_index);
self->tp_index = -1;
self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP;
+
if (su_wait_create(wait, self->tp_socket, self->tp_events) == -1 ||
(self->tp_index = su_root_register(mr->mr_root,
wait, tport_wakeup, self, 0))
== -1) {
tport_close(self);
+ tport_set_secondary_timer(self);
+ return 0;
}
- else if (self->tp_queue && self->tp_queue[self->tp_qhead]) {
+
+ if (tport_has_queued(self))
tport_send_event(self);
- }
+ else
+ tport_set_secondary_timer(self);
return 0;
}
@@ -2574,7 +2701,7 @@ static int tport_wakeup_pri(su_root_magic_t *m, su_wait_t *w, tport_t *self)
#endif
SU_DEBUG_7(("%s(%p): events%s%s%s%s%s%s\n",
- "tport_wakeup_pri", (void *)self,
+ "tport_wakeup_pri", (void *)self,
events & SU_WAIT_IN ? " IN" : "",
SU_WAIT_ACCEPT != SU_WAIT_IN &&
(events & SU_WAIT_ACCEPT) ? " ACCEPT" : "",
@@ -2600,7 +2727,7 @@ static int tport_wakeup(su_root_magic_t *magic, su_wait_t *w, tport_t *self)
#endif
SU_DEBUG_7(("%s(%p): events%s%s%s%s%s\n",
- "tport_wakeup", (void *)self,
+ "tport_wakeup", (void *)self,
events & SU_WAIT_IN ? " IN" : "",
events & SU_WAIT_OUT ? " OUT" : "",
events & SU_WAIT_HUP ? " HUP" : "",
@@ -2629,8 +2756,12 @@ static int tport_base_wakeup(tport_t *self, int events)
if ((events & SU_WAIT_HUP) && !self->tp_closed)
tport_hup_event(self);
- if (error)
+ if (error) {
+ if (self->tp_closed && error == EPIPE)
+ return 0;
+
tport_error_report(self, error, NULL);
+ }
return 0;
}
@@ -2654,7 +2785,7 @@ int tport_continue(tport_t *self)
*/
void tport_hup_event(tport_t *self)
{
- SU_DEBUG_7(("%s(%p)\n", __func__, (void *)self));
+ SU_DEBUG_7(("%s(%p)\n", __func__, (void *)self));
if (self->tp_msg) {
su_time_t now = su_now();
@@ -2662,8 +2793,12 @@ void tport_hup_event(tport_t *self)
tport_parse(self, 1, now);
}
+ if (!tport_is_secondary(self))
+ return;
+
/* End of stream */
- tport_shutdown(self, 0);
+ tport_shutdown0(self, 0);
+ tport_set_secondary_timer(self);
}
/** Receive data available on the socket.
@@ -2685,18 +2820,15 @@ int tport_recv_data(tport_t *self)
*/
void tport_recv_event(tport_t *self)
{
- su_time_t now;
int again;
SU_DEBUG_7(("%s(%p)\n", "tport_recv_event", (void *)self));
do {
- now = su_now();
-
/* Receive data from socket */
again = tport_recv_data(self);
- self->tp_time = su_time_ms(now);
+ su_time(&self->tp_rtime);
#if HAVE_SOFIA_STUN
if (again == 3) /* STUN keepalive */
@@ -2708,9 +2840,6 @@ void tport_recv_event(tport_t *self)
if (!su_is_blocking(error)) {
tport_error_report(self, error, NULL);
- /* Failure: shutdown socket */
- if (tport_has_connection(self))
- tport_close(self);
return;
}
else {
@@ -2720,17 +2849,22 @@ void tport_recv_event(tport_t *self)
}
if (again >= 0)
- tport_parse(self, !again, now);
+ tport_parse(self, !again, self->tp_rtime);
}
while (again > 1);
+ if (!tport_is_secondary(self))
+ return;
+
if (again == 0 && !tport_is_dgram(self)) {
/* End of stream */
if (!self->tp_closed) {
/* Don't shutdown completely if there are queued messages */
- tport_shutdown(self, self->tp_queue && self->tp_queue[self->tp_qhead] ? 0 : 2);
+ tport_shutdown0(self, tport_has_queued(self) ? 0 : 2);
}
}
+
+ tport_set_secondary_timer(self);
}
/*
@@ -2757,7 +2891,7 @@ static void tport_parse(tport_t *self, int complete, su_time_t now)
if (msg_get_flags(msg, MSG_FLG_TOOLARGE))
SU_DEBUG_3(("%s(%p): too large message from " TPN_FORMAT "\n",
- __func__, (void *)self, TPN_ARGS(self->tp_name)));
+ __func__, (void *)self, TPN_ARGS(self->tp_name)));
/* Do not try to read anymore from this connection? */
if (tport_is_stream(self) &&
@@ -2934,7 +3068,7 @@ ssize_t tport_recv_iovec(tport_t const *self,
if (!(*in_out_msg = msg = tport_msg_alloc(self, N))) {
SU_DEBUG_7(("%s(%p): cannot allocate msg for "MOD_ZU" bytes "
"from (%s/%s:%s)\n",
- __func__, (void *)self, N,
+ __func__, (void *)self, N,
self->tp_protoname, self->tp_host, self->tp_port));
return -1;
}
@@ -2953,7 +3087,7 @@ ssize_t tport_recv_iovec(tport_t const *self,
int err = su_errno();
SU_DEBUG_7(("%s(%p): cannot get msg %p buffer for "MOD_ZU" bytes "
"from (%s/%s:%s): %s\n",
- __func__, (void *)self, (void *)msg, N,
+ __func__, (void *)self, (void *)msg, N,
self->tp_protoname, self->tp_host, self->tp_port,
su_strerror(err)));
su_seterrno(err);
@@ -2965,7 +3099,7 @@ ssize_t tport_recv_iovec(tport_t const *self,
SU_DEBUG_7(("%s(%p) msg %p from (%s/%s:%s) has "MOD_ZU" bytes, "
"veclen = "MOD_ZD"\n",
__func__, (void *)self,
- (void *)msg, self->tp_protoname, self->tp_host, self->tp_port,
+ (void *)msg, self->tp_protoname, self->tp_host, self->tp_port,
N, veclen));
for (i = 0; veclen > 1 && i < veclen; i++) {
@@ -3070,7 +3204,7 @@ tport_t *tport_tsend(tport_t *self,
if (tpn->tpn_comp) {
ai->ai_flags |= TP_AI_COMPRESSED;
SU_DEBUG_9(("%s: compressed msg(%p) with %s\n",
- __func__, (void *)msg, tpn->tpn_comp));
+ __func__, (void *)msg, tpn->tpn_comp));
}
if (!tpn->tpn_comp || cc == NONE)
@@ -3168,6 +3302,8 @@ int tport_prepare_and_send(tport_t *self, msg_t *msg,
struct sigcomp_compartment *cc,
unsigned mtu)
{
+ int retval;
+
/* Prepare message for sending - i.e., encode it */
if (msg_prepare(msg) < 0) {
msg_set_errno(msg, errno);
@@ -3193,7 +3329,11 @@ int tport_prepare_and_send(tport_t *self, msg_t *msg,
return 0;
}
- return tport_send_msg(self, msg, tpn, cc);
+ retval = tport_send_msg(self, msg, tpn, cc);
+
+ tport_set_secondary_timer(self);
+
+ return retval;
}
@@ -3243,7 +3383,7 @@ int tport_send_msg(tport_t *self, msg_t *msg,
assert(iovused > 0);
- self->tp_time = su_time_ms(now = su_now());
+ self->tp_stime = self->tp_ktime = now = su_now();
nerror = tport_vsend(self, msg, tpn, iov, iovused, cc);
SU_DEBUG_9(("tport_vsend returned "MOD_ZD"\n", nerror));
@@ -3272,7 +3412,7 @@ int tport_send_msg(tport_t *self, msg_t *msg,
char const *comp = tpn->tpn_comp;
SU_DEBUG_1(("tport(%p): send truncated for %s/%s:%s%s%s\n",
- (void *)self, tpn->tpn_proto, tpn->tpn_host, tpn->tpn_port,
+ (void *)self, tpn->tpn_proto, tpn->tpn_host, tpn->tpn_port,
comp ? ";comp=" : "", comp ? comp : ""));
su_seterrno(EIO);
@@ -3288,14 +3428,17 @@ int tport_send_msg(tport_t *self, msg_t *msg,
self->tp_slogged = NULL;
self->tp_stats.sent_msgs ++;
+ if (!tport_is_secondary(self))
+ return 0;
+
ai = msg_addrinfo(msg); assert(ai);
close_after = (ai->ai_flags & TP_AI_CLOSE) == TP_AI_CLOSE;
sdwn_after = (ai->ai_flags & TP_AI_SHUTDOWN) == TP_AI_SHUTDOWN ||
self->tp_send_close;
if (close_after || sdwn_after)
- tport_shutdown(self, close_after ? 2 : 1);
-
+ tport_shutdown0(self, close_after ? 2 : 1);
+
return 0;
}
@@ -3339,7 +3482,7 @@ ssize_t tport_vsend(tport_t *self,
tpn = self->tp_name;
SU_DEBUG_7(("tport_vsend(%p): "MOD_ZU" bytes of "MOD_ZU" to %s/%s:%s%s\n",
- (void *)self, n, m, tpn->tpn_proto, tpn->tpn_host,
+ (void *)self, n, m, tpn->tpn_proto, tpn->tpn_host,
tpn->tpn_port,
(ai->ai_flags & TP_AI_COMPRESSED) ? ";comp=sigcomp" : ""));
}
@@ -3370,7 +3513,7 @@ int tport_send_error(tport_t *self, msg_t *msg,
if (self->tp_addrinfo->ai_family == AF_INET) {
SU_DEBUG_3(("tport_vsend(%p): %s with (s=%d %s/%s:%s%s)\n",
- (void *)self, su_strerror(error), (int)self->tp_socket,
+ (void *)self, su_strerror(error), (int)self->tp_socket,
tpn->tpn_proto, tpn->tpn_host, tpn->tpn_port, comp));
}
#if SU_HAVE_IN6
@@ -3378,7 +3521,7 @@ int tport_send_error(tport_t *self, msg_t *msg,
su_sockaddr_t const *su = (su_sockaddr_t const *)ai->ai_addr;
SU_DEBUG_3(("tport_vsend(%p): %s with "
"(s=%d, IP6=%s/%s:%s%s (scope=%i) addrlen=%u)\n",
- (void *)self, su_strerror(error), (int)self->tp_socket,
+ (void *)self, su_strerror(error), (int)self->tp_socket,
tpn->tpn_proto, tpn->tpn_host, tpn->tpn_port, comp,
su->su_scope_id, (unsigned)ai->ai_addrlen));
}
@@ -3386,7 +3529,7 @@ int tport_send_error(tport_t *self, msg_t *msg,
else {
SU_DEBUG_3(("\ttport_vsend(%p): %s with "
"(s=%d, AF=%u addrlen=%u)%s\n",
- (void *)self, su_strerror(error),
+ (void *)self, su_strerror(error),
(int)self->tp_socket, ai->ai_family, (unsigned)ai->ai_addrlen, comp));
}
@@ -3517,7 +3660,8 @@ int tport_queue(tport_t *self, msg_t *msg)
unsigned short N = self->tp_params->tpp_qsize;
SU_DEBUG_7(("tport_queue(%p): queueing %p for %s/%s:%s\n",
- (void *)self, (void *)msg, self->tp_protoname, self->tp_host, self->tp_port));
+ (void *)self, (void *)msg,
+ self->tp_protoname, self->tp_host, self->tp_port));
if (self->tp_queue == NULL) {
assert(N > 0);
@@ -3600,8 +3744,10 @@ int tport_tqsend(tport_t *self, msg_t *msg, msg_t *next,
if (close_after)
ai->ai_flags |= TP_AI_CLOSE;
- if (self->tp_queue[qhead] == msg)
+ if (self->tp_queue[qhead] == msg) {
tport_send_queue(self);
+ tport_set_secondary_timer(self);
+ }
return 0;
}
@@ -3615,6 +3761,7 @@ int tport_tqsend(tport_t *self, msg_t *msg, msg_t *next,
if (self->tp_queue[qhead] == msg) {
/* XXX - what about errors? */
tport_send_msg(self, msg, self->tp_name, NULL);
+ tport_set_secondary_timer(self);
if (!self->tp_unsent) {
msg_destroy(self->tp_queue[qhead]);
if ((self->tp_queue[qhead] = msg_ref_create(next)))
@@ -3655,24 +3802,21 @@ void tport_send_event(tport_t *self)
assert(tport_is_connection_oriented(self));
SU_DEBUG_7(("tport_send_event(%p) - ready to send to (%s/%s:%s)\n",
- (void *)self, self->tp_protoname, self->tp_host, self->tp_port));
+ (void *)self, self->tp_protoname, self->tp_host, self->tp_port));
tport_send_queue(self);
+ tport_set_secondary_timer(self);
}
/** Send queued messages */
-static
void tport_send_queue(tport_t *self)
{
msg_t *msg;
msg_iovec_t *iov;
size_t i, iovused, n, total;
unsigned short qhead = self->tp_qhead, N = self->tp_params->tpp_qsize;
- su_time_t now;
assert(self->tp_queue && self->tp_queue[qhead]);
- self->tp_time = su_time_ms(now = su_now());
-
msg = self->tp_queue[qhead];
iov = self->tp_unsent, self->tp_unsent = NULL;
@@ -3680,6 +3824,9 @@ void tport_send_queue(tport_t *self)
if (iov && iovused) {
ssize_t e;
+
+ self->tp_stime = self->tp_ktime = su_now();
+
e = tport_vsend(self, msg, self->tp_name, iov, iovused, NULL);
if (e == -1) /* XXX */
@@ -3688,7 +3835,7 @@ void tport_send_queue(tport_t *self)
n = (size_t)e;
if (n > 0 && self->tp_master->mr_log && self->tp_slogged != msg) {
- tport_log_msg(self, msg, "send", "to", now);
+ tport_log_msg(self, msg, "send", "to", self->tp_stime);
self->tp_slogged = msg;
}
@@ -3719,7 +3866,7 @@ void tport_send_queue(tport_t *self)
while (msg_is_prepared(msg = self->tp_queue[self->tp_qhead = qhead])) {
/* XXX - what about errors? */
tport_send_msg(self, msg, self->tp_name, NULL);
- if (self->tp_unsent)
+ if (self->tp_unsent)
return;
msg = self->tp_queue[qhead]; /* tport_send_msg() may flush queue! */
@@ -3892,14 +4039,14 @@ int tport_pend(tport_t *self,
{
tport_pending_t *pending;
- if (self == NULL || callback == NULL || client == NULL)
+ if (self == NULL || callback == 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,
+ (void *)self, (void *)msg,
self->tp_protoname, self->tp_host, self->tp_port,
self->tp_pused));
@@ -3954,13 +4101,15 @@ int tport_release(tport_t *self,
if (pending->p_client != client ||
pending->p_msg != msg) {
- SU_DEBUG_1(("tport_release(%p): %u %p by %p not pending\n", (void *)self,
- pendd, (void *)msg, (void *)client));
+ SU_DEBUG_1(("%s(%p): %u %p by %p not pending\n",
+ __func__, (void *)self,
+ pendd, (void *)msg, (void *)client));
return su_seterrno(EINVAL), -1;
}
- SU_DEBUG_7(("tport_release(%p): %p by %p with %p%s\n",
- (void *)self, (void *)msg, (void *)client, (void *)reply,
+ SU_DEBUG_7(("%s(%p): %p by %p with %p%s\n",
+ __func__, (void *)self,
+ (void *)msg, (void *)client, (void *)reply,
still_pending ? " (preliminary)" : ""));
/* sigcomp can here associate request (msg) with response (reply) */
@@ -4104,7 +4253,7 @@ tport_t *tport_next(tport_t const *self)
tport_t *tport_secondary(tport_t const *self)
{
if (tport_is_primary(self))
- return self->tp_pri->pri_secondary;
+ return self->tp_pri->pri_open;
else
return NULL;
}
@@ -4240,7 +4389,7 @@ tport_t *tport_by_name(tport_t const *self, tp_name_t const *tpn)
socklen_t sulen;
su_sockaddr_t su[1];
- sub = self->tp_pri->pri_secondary;
+ sub = self->tp_pri->pri_open;
memset(su, 0, sizeof su);
@@ -4302,7 +4451,7 @@ tport_t *tport_by_name(tport_t const *self, tp_name_t const *tpn)
}
else {
SU_DEBUG_7(("tport(%p): EXPENSIVE unresolved " TPN_FORMAT "\n",
- (void *)self, TPN_ARGS(tpn)));
+ (void *)self, TPN_ARGS(tpn)));
sub = tprb_first(sub);
}
@@ -4322,11 +4471,11 @@ tport_t *tport_by_name(tport_t const *self, tp_name_t const *tpn)
if ((socklen_t)sub->tp_addrlen != sulen ||
memcmp(sub->tp_addr, su, sulen)) {
SU_DEBUG_7(("tport(%p): not found by name " TPN_FORMAT "\n",
- (void *)self, TPN_ARGS(tpn)));
+ (void *)self, TPN_ARGS(tpn)));
break;
}
SU_DEBUG_7(("tport(%p): found %p by name " TPN_FORMAT "\n",
- (void *)self, (void *)sub, TPN_ARGS(tpn)));
+ (void *)self, (void *)sub, TPN_ARGS(tpn)));
}
else if ((strcasecmp(canon, sub->tp_canon) &&
strcasecmp(host, sub->tp_host)) ||
@@ -4354,7 +4503,7 @@ tport_t *tport_by_addrinfo(tport_primary_t const *pri,
sa = ai->ai_addr;
- sub = pri->pri_secondary, maybe = NULL;
+ sub = pri->pri_open, maybe = NULL;
comp = tport_canonize_comp(tpn->tpn_comp);
@@ -4407,10 +4556,10 @@ tport_t *tport_by_addrinfo(tport_primary_t const *pri,
if (sub)
SU_DEBUG_7(("%s(%p): found %p by name " TPN_FORMAT "\n",
- __func__, (void *)pri, (void *)sub, TPN_ARGS(tpn)));
+ __func__, (void *)pri, (void *)sub, TPN_ARGS(tpn)));
else
SU_DEBUG_7(("%s(%p): not found by name " TPN_FORMAT "\n",
- __func__, (void *)pri, TPN_ARGS(tpn)));
+ __func__, (void *)pri, TPN_ARGS(tpn)));
return (tport_t *)sub;
}
diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h b/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h
index db95fdd751..b330b143b2 100644
--- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h
+++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h
@@ -94,6 +94,9 @@ typedef struct {
unsigned tpp_mtu; /**< Maximum packet size */
unsigned tpp_idle; /**< Allowed connection idle time. */
unsigned tpp_timeout; /**< Allowed idle time for message. */
+ unsigned tpp_keepalive; /**< Keepalive PING interval */
+ unsigned tpp_pingpong; /**< PONG-to-PING interval */
+
unsigned tpp_sigcomp_lifetime; /**< SigComp compartment lifetime */
unsigned tpp_thrpsize; /**< Size of thread pool */
@@ -106,6 +109,7 @@ typedef struct {
unsigned tpp_conn_orient:1; /**< Connection-orienteded */
unsigned tpp_sdwn_error:1; /**< If true, shutdown is error. */
unsigned tpp_stun_server:1; /**< If true, use stun server */
+ unsigned tpp_pong2ping:1; /**< If true, respond with pong to ping */
unsigned :0;
@@ -122,7 +126,7 @@ typedef struct {
struct tport_s {
su_home_t tp_home[1]; /**< Memory home */
- int tp_refs; /**< Number of references to tport */
+ ssize_t tp_refs; /**< Number of references to tport */
unsigned tp_black:1; /**< Used by red-black-tree */
@@ -130,8 +134,13 @@ struct tport_s {
unsigned tp_conn_orient:1; /**< Is connection-oriented */
unsigned tp_has_connection:1; /**< Has real connection */
unsigned tp_reusable:1; /**< Can this connection be reused */
- unsigned tp_closed : 1; /**< This transport is closed */
- /**< Remote end has sent FIN (2) or we should not just read */
+ unsigned tp_closed : 1;
+ /**< This transport is closed.
+ *
+ * A closed transport is inserted into pri_closed list.
+ */
+
+ /** Remote end has sent FIN (2) or we should not just read */
unsigned tp_recv_close:2;
/** We will send FIN (1) or have sent FIN (2) */
unsigned tp_send_close:2;
@@ -150,10 +159,10 @@ struct tport_s {
tp_magic_t *tp_magic; /**< Context provided by consumer */
- msg_t const *tp_rlogged; /**< Last logged when receiving */
- msg_t const *tp_slogged; /**< Last logged when sending */
+ su_timer_t *tp_timer; /**< Timer object */
- unsigned tp_time; /**< When this transport was last used */
+ su_time_t tp_ktime; /**< Keepalive timer updated */
+ su_time_t tp_ptime; /**< Ping sent */
tp_name_t tp_name[1]; /**< Transport name.
*
@@ -178,16 +187,18 @@ struct tport_s {
/* ==== Receive queue ================================================== */
msg_t *tp_msg; /**< Message being received */
+ msg_t const *tp_rlogged; /**< Last logged when receiving */
+ su_time_t tp_rtime; /**< Last time received data */
+ unsigned short tp_ping; /**< Whitespace ping being received */
/* ==== Pending messages =============================================== */
- tport_pending_t *tp_pending; /**< Pending requests */
- tport_pending_t *tp_released; /**< Released pends */
+ unsigned short tp_reported; /**< Report counter */
unsigned tp_plen; /**< Size of tp_pending */
unsigned tp_pused; /**< Used pends */
- unsigned short tp_reported; /**< Report counter */
- unsigned short tp_pad;
-
+ tport_pending_t *tp_pending; /**< Pending requests */
+ tport_pending_t *tp_released; /**< Released pends */
+
/* ==== Send queue ===================================================== */
msg_t **tp_queue; /**< Messages being sent */
@@ -199,6 +210,9 @@ struct tport_s {
msg_iovec_t *tp_iov; /**< Iovecs allocated for sending */
size_t tp_iovlen; /**< Number of allocated iovecs */
+ msg_t const *tp_slogged; /**< Last logged when sending */
+ su_time_t tp_stime; /**< Last time sent message */
+
/* ==== Extensions ===================================================== */
tport_compressor_t *tp_comp;
@@ -228,10 +242,10 @@ struct tport_primary {
* tport_type_stun, etc.
*/
- char pri_ident[16];
tport_primary_t *pri_next; /**< Next primary tport */
- tport_t *pri_secondary; /**< Secondary tports */
+ tport_t *pri_open; /**< Open secondary tports */
+ tport_t *pri_closed; /**< Closed secondary tports */
unsigned pri_updating:1; /**< Currently updating address */
unsigned pri_natted:1; /**< Using natted address */
@@ -241,7 +255,6 @@ struct tport_primary {
void *pri_stun_handle;
tport_params_t pri_params[1]; /**< Transport parameters */
-
};
/** Master structure */
@@ -307,7 +320,7 @@ struct tport_master {
#endif
};
-/** Virtual funtion table for transports */
+/** Virtual function table for transports */
struct tport_vtable
{
char const *vtp_name;
@@ -344,6 +357,9 @@ struct tport_vtable
int (*vtp_stun_response)(tport_t const *self,
void *msg, size_t msglen,
void *addr, socklen_t addrlen);
+ int (*vtp_next_secondary_timer)(tport_t *self, su_time_t *,
+ char const **return_why);
+ void (*vtp_secondary_timer)(tport_t *self, su_time_t);
};
int tport_register_type(tport_vtable_t const *vtp);
@@ -388,11 +404,16 @@ tport_t *tport_alloc_secondary(tport_primary_t *pri,
int tport_accept(tport_primary_t *pri, int events);
void tport_zap_secondary(tport_t *self);
+int tport_set_secondary_timer(tport_t *self);
+void tport_base_timer(tport_t *self, su_time_t now);
+
int tport_bind_socket(int socket,
su_addrinfo_t *ai,
char const **return_culprit);
void tport_close(tport_t *self);
+int tport_shutdown0(tport_t *self, int how);
+int tport_has_queued(tport_t const *self);
int tport_error_event(tport_t *self);
void tport_recv_event(tport_t *self);
@@ -414,6 +435,8 @@ int tport_send_msg(tport_t *self, msg_t *msg,
tp_name_t const *tpn,
struct sigcomp_compartment *cc);
+void tport_send_queue(tport_t *self);
+
void tport_deliver(tport_t *self, msg_t *msg, msg_t *next,
tport_compressor_t *comp,
su_time_t now);
@@ -430,6 +453,9 @@ void tport_dump_iovec(tport_t const *self, msg_t *msg,
size_t n, su_iovec_t const iov[], size_t iovused,
char const *what, char const *how);
+int tport_tcp_ping(tport_t *self, su_time_t now);
+int tport_tcp_pong(tport_t *self);
+
extern tport_vtable_t const tport_udp_vtable;
extern tport_vtable_t const tport_udp_client_vtable;
@@ -461,6 +487,15 @@ int tport_recv_stream(tport_t *self);
ssize_t tport_send_stream(tport_t const *self, msg_t *msg,
msg_iovec_t iov[], size_t iovused);
+int tport_tcp_next_timer(tport_t *self, su_time_t *, char const **);
+void tport_tcp_timer(tport_t *self, su_time_t);
+
+int tport_next_recv_timeout(tport_t *, su_time_t *, char const **);
+void tport_recv_timeout_timer(tport_t *self, su_time_t now);
+
+int tport_next_keepalive(tport_t *self, su_time_t *, char const **);
+void tport_keepalive_timer(tport_t *self, su_time_t now);
+
extern tport_vtable_t const tport_sctp_vtable;
extern tport_vtable_t const tport_sctp_client_vtable;
extern tport_vtable_t const tport_tls_vtable;
diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tag.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tag.c
index e775869395..b577d93c09 100644
--- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tag.c
+++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tag.c
@@ -64,6 +64,9 @@ tag_typedef_t tptag_sdwn_after = BOOLTAG_TYPEDEF(sdwn_after);
tag_typedef_t tptag_close_after = BOOLTAG_TYPEDEF(sdwn_after);
tag_typedef_t tptag_idle = UINTTAG_TYPEDEF(idle);
tag_typedef_t tptag_timeout = UINTTAG_TYPEDEF(timeout);
+tag_typedef_t tptag_keepalive = UINTTAG_TYPEDEF(keepalive);
+tag_typedef_t tptag_pingpong = UINTTAG_TYPEDEF(pingpong);
+tag_typedef_t tptag_pong2ping = BOOLTAG_TYPEDEF(pong2ping);
tag_typedef_t tptag_sigcomp_lifetime = UINTTAG_TYPEDEF(sigcomp_lifetime);
tag_typedef_t tptag_certificate = STRTAG_TYPEDEF(certificate);
tag_typedef_t tptag_compartment = PTRTAG_TYPEDEF(compartment);
diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c
index 0a242cc47e..73ab7324c7 100644
--- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c
+++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c
@@ -587,6 +587,10 @@ int tls_error(tls_t *tls, int ret, char const *who, char const *operation,
return 0;
case SSL_ERROR_SYSCALL:
+ if (SSL_get_shutdown(tls->con) & SSL_RECEIVED_SHUTDOWN)
+ return 0; /* EOS */
+ if (errno == 0)
+ return 0; /* EOS */
return -1;
default:
@@ -665,10 +669,12 @@ int tls_want_read(tls_t *tls, int events)
if (tls && (events & tls->read_events)) {
int ret = tls_read(tls);
- if (ret >= 0)
+ if (ret > 0)
return 1;
- else if (errno == EAGAIN)
+ else if (ret == 0)
return 0;
+ else if (errno == EAGAIN)
+ return 2;
else
return -1;
}
diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_connect.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_connect.c
index dc57ec21f6..93affa56a6 100644
--- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_connect.c
+++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_connect.c
@@ -201,6 +201,8 @@ static tport_t *tport_http_connect(tport_primary_t *pri, su_addrinfo_t *ai,
return NULL;
}
+ tport_set_secondary_timer(tport);
+
return tport;
}
diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_sctp.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_sctp.c
index df97398c84..8c06dee251 100644
--- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_sctp.c
+++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_sctp.c
@@ -37,12 +37,12 @@
#include "config.h"
+#if HAVE_SCTP
+
#include "tport_internal.h"
#if HAVE_NETINET_SCTP_H
#include
-#undef HAVE_SCTP
-#define HAVE_SCTP 1
#endif
#include
@@ -63,7 +63,6 @@
#define SOL_SCTP IPPROTO_SCTP
#endif
-
enum { MAX_STREAMS = 1 };
typedef struct tport_sctp_t
{
@@ -81,8 +80,6 @@ typedef struct tport_sctp_t
#define TP_SCTP_MSG_MAX (65536)
-#if HAVE_SCTP
-
static int tport_sctp_init_primary(tport_primary_t *,
tp_name_t tpn[1],
su_addrinfo_t *, tagi_t const *,
@@ -100,6 +97,9 @@ static int tport_recv_sctp(tport_t *self);
static ssize_t tport_send_sctp(tport_t const *self, msg_t *msg,
msg_iovec_t iov[], size_t iovused);
+static int tport_sctp_next_timer(tport_t *self, su_time_t *, char const **);
+static void tport_sctp_timer(tport_t *self, su_time_t);
+
tport_vtable_t const tport_sctp_client_vtable =
{
"sctp", tport_type_client,
@@ -116,11 +116,14 @@ tport_vtable_t const tport_sctp_client_vtable =
NULL,
tport_recv_sctp,
tport_send_sctp,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ tport_sctp_next_timer,
+ tport_sctp_timer,
};
-#undef NEXT_VTABLE
-#define NEXT_VTABLE &tport_sctp_client_vtable
-
tport_vtable_t const tport_sctp_vtable =
{
"sctp", tport_type_local,
@@ -137,11 +140,14 @@ tport_vtable_t const tport_sctp_vtable =
NULL,
tport_recv_sctp,
tport_send_sctp,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ tport_sctp_next_timer,
+ tport_sctp_timer,
};
-#undef NEXT_VTABLE
-#define NEXT_VTABLE &tport_sctp_vtable
-
static int tport_sctp_init_primary(tport_primary_t *pri,
tp_name_t tpn[1],
su_addrinfo_t *ai,
@@ -263,4 +269,56 @@ static ssize_t tport_send_sctp(tport_t const *self, msg_t *msg,
return su_vsend(self->tp_socket, iov, iovused, MSG_NOSIGNAL, NULL, 0);
}
+/** Calculate tick timer if send is pending. */
+int tport_next_sctp_send_tick(tport_t *self,
+ su_time_t *return_target,
+ char const **return_why)
+{
+ unsigned timeout = 100; /* Retry 10 times a second... */
+
+ if (tport_has_queued(self)) {
+ su_time_t ntime = su_time_add(self->tp_ktime, timeout);
+ if (su_time_cmp(ntime, *return_target) < 0)
+ *return_target = ntime, *return_why = "send tick";
+ }
+
+ return 0;
+}
+
+/** Tick timer if send is pending */
+void tport_sctp_send_tick_timer(tport_t *self, su_time_t now)
+{
+ unsigned timeout = 100;
+
+ /* Send timeout */
+ if (tport_has_queued(self) &&
+ su_time_cmp(su_time_add(self->tp_ktime, timeout), now) < 0) {
+ uint64_t bytes = self->tp_stats.sent_bytes;
+ su_time_t stime = self->tp_stime;
+
+ tport_send_queue(self);
+
+ if (self->tp_stats.sent_bytes == bytes)
+ self->tp_stime = stime; /* Restore send timestamp */
+ }
+}
+
+/** Calculate next timer for SCTP. */
+int tport_sctp_next_timer(tport_t *self,
+ su_time_t *return_target,
+ char const **return_why)
+{
+ return
+ tport_next_recv_timeout(self, return_target, return_why) |
+ tport_next_sctp_send_tick(self, return_target, return_why);
+}
+
+/** SCTP timer. */
+void tport_sctp_timer(tport_t *self, su_time_t now)
+{
+ tport_sctp_send_tick_timer(self, now);
+ tport_recv_timeout_timer(self, now);
+ tport_base_timer(self, now);
+}
+
#endif
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 2916076841..f2a98a237f 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
@@ -45,6 +45,7 @@
#endif
#include
+#include
#include
#include
#include
@@ -76,6 +77,12 @@ tport_vtable_t const tport_tcp_vtable =
NULL,
tport_recv_stream,
tport_send_stream,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ tport_tcp_next_timer,
+ tport_tcp_timer,
};
tport_vtable_t const tport_tcp_client_vtable =
@@ -84,7 +91,7 @@ tport_vtable_t const tport_tcp_client_vtable =
sizeof (tport_primary_t),
tport_tcp_init_client,
NULL,
- tport_accept,
+ NULL,
NULL,
sizeof (tport_t),
tport_tcp_init_secondary,
@@ -94,6 +101,12 @@ tport_vtable_t const tport_tcp_client_vtable =
NULL,
tport_recv_stream,
tport_send_stream,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ tport_tcp_next_timer,
+ tport_tcp_timer,
};
static int tport_tcp_setsndbuf(int socket, int atleast);
@@ -205,6 +218,20 @@ static int tport_tcp_setsndbuf(int socket, int atleast)
#endif
}
+/** Return span of whitespace from buffer */
+static inline size_t ws_span(void *buffer, size_t len)
+{
+ size_t i;
+ char const *b = buffer;
+
+ for (i = 0; i < len; i++) {
+ if (b[i] != '\r' && b[i] != '\n' && b[i] != ' ' && b[i] != '\t')
+ break;
+ }
+
+ return i;
+}
+
/** Receive from stream.
*
* @retval -1 error
@@ -217,7 +244,7 @@ int tport_recv_stream(tport_t *self)
{
msg_t *msg;
ssize_t n, N, veclen;
- int err;
+ int err, initial;
msg_iovec_t iovec[msg_n_fragments] = {{ 0 }};
N = su_getmsgsize(self->tp_socket);
@@ -233,6 +260,36 @@ int tport_recv_stream(tport_t *self)
return -1;
}
+ initial = self->tp_msg == NULL;
+ memset(&self->tp_ptime, 0, sizeof self->tp_ptime);
+
+ while (initial && N <= 8) { /* Check for whitespace */
+ char crlf[9];
+ size_t i;
+
+ n = su_recv(self->tp_socket, crlf, N, MSG_PEEK);
+
+ i = ws_span(crlf, n);
+ if (i == 0)
+ break;
+
+ n = su_recv(self->tp_socket, crlf, i, 0);
+ if (n <= 0)
+ return (int)n;
+
+ SU_DEBUG_7(("%s(%p): received keepalive\n", __func__, (void *)self));
+
+ N -= n, self->tp_ping += n;
+
+ if (N == 0) {
+ /* outbound-10 section 3.5.1 - send pong */
+ if (self->tp_ping >= 4)
+ tport_tcp_pong(self);
+
+ return 1;
+ }
+ }
+
veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 0);
if (veclen == -1)
return -1;
@@ -242,11 +299,30 @@ int tport_recv_stream(tport_t *self)
msg_set_address(msg, self->tp_addr, (socklen_t)(self->tp_addrlen));
n = su_vrecv(self->tp_socket, iovec, veclen, 0, NULL, NULL);
+
if (n == SOCKET_ERROR)
return tport_recv_error_report(self);
assert(n <= N);
+ /* Check if message contains only whitespace */
+ /* This can happen if multiple PINGs are received at once */
+ if (initial) {
+ size_t i = ws_span(iovec->siv_base, iovec->siv_len);
+
+ if (i + self->tp_ping >= 4)
+ tport_tcp_pong(self);
+ else
+ self->tp_ping += i;
+
+ if (i == iovec->siv_len && veclen == 1) {
+ SU_DEBUG_7(("%s(%p): received %u bytes of keepalive\n",
+ __func__, (void *)self, (unsigned)i));
+ msg_destroy(self->tp_msg), self->tp_msg = NULL;
+ return 1;
+ }
+ }
+
/* Write the received data to the message dump file */
if (self->tp_master->mr_dump_file)
tport_dump_iovec(self, msg, n, iovec, veclen, "recv", "from");
@@ -254,9 +330,13 @@ int tport_recv_stream(tport_t *self)
/* Mark buffer as used */
msg_recv_commit(msg, n, n == 0);
+ if (n > 0)
+ self->tp_ping = 0;
+
return n != 0;
}
+/** Send to stream */
ssize_t tport_send_stream(tport_t const *self, msg_t *msg,
msg_iovec_t iov[],
size_t iovused)
@@ -267,3 +347,184 @@ ssize_t tport_send_stream(tport_t const *self, msg_t *msg,
#endif
return su_vsend(self->tp_socket, iov, iovused, MSG_NOSIGNAL, NULL, 0);
}
+
+/** Calculate timeout if receive is incomplete. */
+int tport_next_recv_timeout(tport_t *self,
+ su_time_t *return_target,
+ char const **return_why)
+{
+ unsigned timeout = self->tp_params->tpp_timeout;
+
+ if (timeout < INT_MAX) {
+ /* Recv timeout */
+ if (self->tp_msg) {
+ su_time_t ntime = su_time_add(self->tp_rtime, timeout);
+ if (su_time_cmp(ntime, *return_target) < 0)
+ *return_target = ntime, *return_why = "recv timeout";
+ }
+
+#if 0
+ /* Send timeout */
+ if (tport_has_queued(self)) {
+ su_time_t ntime = su_time_add(self->tp_stime, timeout);
+ if (su_time_cmp(ntime, *return_target) < 0)
+ *return_target = ntime, *return_why = "send timeout";
+ }
+#endif
+ }
+
+ return 0;
+}
+
+/** Timeout timer if receive is incomplete */
+void tport_recv_timeout_timer(tport_t *self, su_time_t now)
+{
+ unsigned timeout = self->tp_params->tpp_timeout;
+
+ if (timeout < INT_MAX) {
+ if (self->tp_msg &&
+ su_time_cmp(su_time_add(self->tp_rtime, timeout), now) < 0) {
+ msg_t *msg = self->tp_msg;
+ msg_set_streaming(msg, 0);
+ msg_set_flags(msg, MSG_FLG_ERROR | MSG_FLG_TRUNC | MSG_FLG_TIMEOUT);
+ tport_deliver(self, msg, NULL, NULL, now);
+ self->tp_msg = NULL;
+ }
+
+#if 0
+ /* Send timeout */
+ if (tport_has_queued(self) &&
+ su_time_cmp(su_time_add(self->tp_stime, timeout), now) < 0) {
+ stime = su_time_add(self->tp_stime, self->tp_params->tpp_timeout);
+ if (su_time_cmp(stime, target) < 0)
+ target = stime;
+ }
+#endif
+ }
+}
+
+/** Calculate next timeout for keepalive */
+int tport_next_keepalive(tport_t *self,
+ su_time_t *return_target,
+ char const **return_why)
+{
+ /* Keepalive timer */
+ unsigned timeout = self->tp_params->tpp_keepalive;
+
+ if (timeout != 0 && timeout != UINT_MAX) {
+ if (!tport_has_queued(self)) {
+ su_time_t ntime = su_time_add(self->tp_ktime, timeout);
+ if (su_time_cmp(ntime, *return_target) < 0)
+ *return_target = ntime, *return_why = "keepalive";
+ }
+ }
+
+ timeout = self->tp_params->tpp_pingpong;
+ if (timeout != 0) {
+ if (self->tp_ptime.tv_sec && !self->tp_recv_close) {
+ su_time_t ntime = su_time_add(self->tp_ptime, timeout);
+ if (su_time_cmp(ntime, *return_target) < 0)
+ *return_target = ntime, *return_why = "waiting for pong";
+ }
+ }
+
+ return 0;
+}
+
+
+/** Keepalive timer. */
+void tport_keepalive_timer(tport_t *self, su_time_t now)
+{
+ unsigned timeout = self->tp_params->tpp_pingpong;
+
+ if (timeout != 0) {
+ if (self->tp_ptime.tv_sec && !self->tp_recv_close &&
+ su_time_cmp(su_time_add(self->tp_ptime, timeout), now) < 0) {
+ SU_DEBUG_3(("%s(%p): %s to " TPN_FORMAT "%s\n",
+ __func__, (void *)self,
+ "closing connection", TPN_ARGS(self->tp_name),
+ " because of PONG timeout"));
+ tport_close(self);
+ return;
+ }
+ }
+
+ timeout = self->tp_params->tpp_keepalive;
+
+ if (timeout != 0 && timeout != UINT_MAX) {
+ if (su_time_cmp(su_time_add(self->tp_ktime, timeout), now) < 0) {
+ tport_tcp_ping(self, now);
+ }
+ }
+}
+
+/** Send PING */
+int tport_tcp_ping(tport_t *self, su_time_t now)
+{
+ ssize_t n;
+ char *why = "";
+
+ if (tport_has_queued(self))
+ return 0;
+
+ n = send(self->tp_socket, "\r\n\r\n", 4, 0);
+
+ if (n > 0)
+ self->tp_ktime = now;
+
+ if (n == 4) {
+ if (self->tp_ptime.tv_sec == 0)
+ self->tp_ptime = now;
+ }
+ else if (n == -1) {
+ int error = su_errno();
+
+ why = " failed";
+
+ if (!su_is_blocking(error))
+ tport_error_report(self, error, NULL);
+ else
+ why = " blocking";
+
+ return -1;
+ }
+
+ SU_DEBUG_7(("%s(%p): %s to " TPN_FORMAT "%s\n",
+ __func__, (void *)self,
+ "sending PING", TPN_ARGS(self->tp_name), why));
+
+ return n == -1 ? -1 : 0;
+}
+
+/** Send pong */
+int tport_tcp_pong(tport_t *self)
+{
+ self->tp_ping = 0;
+
+ if (tport_has_queued(self) || !self->tp_params->tpp_pong2ping)
+ return 0;
+
+ SU_DEBUG_7(("%s(%p): %s to " TPN_FORMAT "%s\n",
+ __func__, (void *)self,
+ "sending PONG", TPN_ARGS(self->tp_name), ""));
+
+ return send(self->tp_socket, "\r\n", 2, 0);
+}
+
+/** Calculate next timer for TCP. */
+int tport_tcp_next_timer(tport_t *self,
+ su_time_t *return_target,
+ char const **return_why)
+{
+ return
+ tport_next_recv_timeout(self, return_target, return_why) |
+ tport_next_keepalive(self, return_target, return_why);
+}
+
+/** TCP timer. */
+void tport_tcp_timer(tport_t *self, su_time_t now)
+{
+ tport_recv_timeout_timer(self, now);
+ tport_keepalive_timer(self, now);
+ tport_base_timer(self, now);
+}
diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c
index eec8279b10..d25bfe34f0 100644
--- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c
+++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c
@@ -307,6 +307,8 @@ int tport_tls_events(tport_t *self, int events)
ret = tls_want_read(tlstp->tlstp_context, events);
if (ret > 0)
tport_recv_event(self);
+ else if (ret == 0) /* End-of-stream */
+ tport_shutdown0(self, 2);
else if (ret < 0)
tport_error_report(self, errno, NULL);
}
diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_udp.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_udp.c
index b7c1ba51c3..db5288369f 100644
--- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_udp.c
+++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_udp.c
@@ -198,6 +198,9 @@ static void tport_check_trunc(tport_t *tp, su_addrinfo_t *ai)
"TEST", 4, 0,
(void *)ai->ai_addr, ai->ai_addrlen);
+ if (n != 4)
+ return;
+
for (;;) {
n = su_recvfrom(tp->tp_socket, buffer, sizeof buffer, MSG_TRUNC,
(void *)&su, &sulen);
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 b26f751bdc..9a05516127 100644
--- a/libs/sofia-sip/libsofia-sip-ua/url/torture_url.c
+++ b/libs/sofia-sip/libsofia-sip-ua/url/torture_url.c
@@ -120,11 +120,12 @@ int test_quote(void)
d = url_as_string(home, u); TEST_1(d);
TEST_S(d, c);
- d = "sip:&=+$,;?/:&=+$,@[::1]:56001;param=+$,/:@&;another=@"
+ d = "sip:&=+$,;?/:&=+$,@[::1]:56001;param=+$,/:@&;another=@%40%2F"
"?header=" RESERVED "&%3b%2f%3f%3a%40%26%3d%2b%24%2c";
u = url_hdup(home, (url_t *)d); TEST_1(u);
TEST_S(u->url_user, "&=+$,;?/");
TEST_S(u->url_host, "[::1]");
+ TEST_S(u->url_params, "param=+$,/:@&;another=@%40/");
TEST_S(u->url_headers, "header=" RESERVED "&%3B%2F%3F%3A%40%26%3D%2B%24%2C");
url_digest(hash1, sizeof(hash1), u, NULL);
url_digest(hash2, sizeof(hash2), (url_t const *)d, NULL);
@@ -134,10 +135,12 @@ int test_quote(void)
d = url_as_string(home, u); TEST_1(d);
TEST_S(d, c);
- d = "http://&=+$,;:&=+$,;@host:8080/foo;param=+$,/:@&;another=@"
+ d = "http://&=+$,;:&=+$,;@host:8080/foo%2F%3B%3D"
+ ";param=+$,%2f%3b%3d/bar;param=:@&;another=@"
"?query=" RESERVED;
u = url_hdup(home, (url_t *)d); TEST_1(u);
TEST_S(u->url_user, "&=+$,;"); TEST_S(u->url_password, "&=+$,;");
+ TEST_S(u->url_path, "foo%2F%3B%3D;param=+$,%2F%3B%3D/bar;param=:@&;another=@");
url_digest(hash1, sizeof(hash1), u, NULL);
url_digest(hash2, sizeof(hash2), (url_t const *)d, NULL);
TEST(memcmp(hash1, hash2, sizeof(hash1)), 0);
@@ -242,7 +245,7 @@ int test_sip(void)
"sip:user:pass@host:32;param=1"
"?From=foo@bar&To=bar@baz#unf";
char sip2url[] =
- "sip:user/path;tel-param:pass@host:32;param=1"
+ "sip:user/path;tel-param:pass@host:32;param=1%3d%3d1"
"?From=foo@bar&To=bar@baz#unf";
char sip2[sizeof(sipurl) + 32];
char sipsurl[] =
@@ -305,6 +308,7 @@ int test_sip(void)
TEST_1(tst = su_strdup(home, sip2url));
TEST_1(url_d(url, tst) == 0);
TEST_S(url->url_user, "user/path;tel-param");
+ TEST_S(url->url_params, "param=1%3D%3D1");
TEST_S(url_query_as_header_string(home, url->url_headers),
"From:foo@bar\nTo:bar@baz");
diff --git a/libs/sofia-sip/libsofia-sip-ua/url/url.c b/libs/sofia-sip/libsofia-sip-ua/url/url.c
index 051f81d214..312e5340fe 100644
--- a/libs/sofia-sip/libsofia-sip-ua/url/url.c
+++ b/libs/sofia-sip/libsofia-sip-ua/url/url.c
@@ -129,6 +129,8 @@
else if (a < 128) \
mask96 &= ~(1U << (127 - a))
+#define NUL '\0'
+#define NULNULNUL '\0', '\0', '\0'
#define RMASK1 0xbe19003f
#define RMASK2 0x8000001e
@@ -141,8 +143,10 @@
/* Internal prototypes */
static char *url_canonize(char *d, char const *s, size_t n,
+ unsigned syn33,
char const allowed[]);
static char *url_canonize2(char *d, char const *s, size_t n,
+ unsigned syn33,
unsigned m32, unsigned m64, unsigned m96);
static int url_tel_cmp_numbers(char const *A, char const *B);
@@ -322,18 +326,24 @@ char *url_unescape(char *d, char const *s)
/** Canonize a URL component */
static
-char *url_canonize(char *d, char const *s, size_t n, char const allowed[])
+char *url_canonize(char *d, char const *s, size_t n,
+ unsigned syn33,
+ char const allowed[])
{
unsigned mask32 = 0xbe19003f, mask64 = 0x8000001e, mask96 = 0x8000001d;
MASKS_WITH_ALLOWED(allowed, mask32, mask64, mask96);
- return url_canonize2(d, s, n, mask32, mask64, mask96);
+ return url_canonize2(d, s, n, syn33, mask32, mask64, mask96);
}
+#define SYN33(c) (1 << (c - 33))
+#define IS_SYN33(syn33, c) ((syn33 & (1 << (c - 33))) != 0)
+
/** Canonize a URL component (with precomputed mask) */
static
-char *url_canonize2(char *d, char const * const s, size_t n,
+char *url_canonize2(char *d, char const * const s, size_t n,
+ unsigned syn33,
unsigned m32, unsigned m64, unsigned m96)
{
size_t i = 0;
@@ -347,7 +357,7 @@ char *url_canonize2(char *d, char const * const s, size_t n,
unsigned char c = s[i], h1, h2;
if (c != '%') {
- if (IS_EXCLUDED(c, m32, m64, m96))
+ if (!IS_SYN33(syn33, c) && IS_EXCLUDED(c, m32, m64, m96))
return NULL;
*d = c;
continue;
@@ -393,7 +403,7 @@ char *url_canonize2(char *d, char const * const s, size_t n,
* be escaped.
*/
static
-char *url_canonize3(char *d, char const * const s, size_t n,
+char *url_canonize3(char *d, char const * const s, size_t n,
unsigned m32, unsigned m64, unsigned m96)
{
size_t i = 0;
@@ -575,7 +585,7 @@ int _url_d(url_t *url, char *s)
char *scheme;
url->url_scheme = scheme = s; s[n] = '\0'; s = s + n + 1;
- if (!(scheme = url_canonize(scheme, scheme, SIZE_MAX, "+")))
+ if (!(scheme = url_canonize(scheme, scheme, SIZE_MAX, 0, "+")))
return -1;
n = scheme - url->url_scheme;
@@ -694,7 +704,7 @@ int _url_d(url_t *url, char *s)
case url_file:
case url_rtsp:
case url_rtspu:
- if (!url_canonize2(port, port, SIZE_MAX, RESERVED_MASK))
+ if (!url_canonize2(port, port, SIZE_MAX, 0, RESERVED_MASK))
return -1;
/* Check that port is really numeric or wildcard */
@@ -758,14 +768,14 @@ int url_d(url_t *url, char *s)
# define SIP_USER_UNRESERVED "&=+$,;?/"
s = (char *)url->url_user;
- if (s && !url_canonize(s, s, SIZE_MAX, SIP_USER_UNRESERVED))
+ if (s && !url_canonize(s, s, SIZE_MAX, 0, SIP_USER_UNRESERVED))
return -1;
/* Having different charset in user and password does not make sense */
/* but that is how it is defined in RFC 3261 */
# define SIP_PASS_UNRESERVED "&=+$,"
s = (char *)url->url_password;
- if (s && !url_canonize(s, s, SIZE_MAX, SIP_PASS_UNRESERVED))
+ if (s && !url_canonize(s, s, SIZE_MAX, 0, SIP_PASS_UNRESERVED))
return -1;
}
@@ -773,31 +783,37 @@ int url_d(url_t *url, char *s)
# define USER_UNRESERVED "&=+$,;"
s = (char *)url->url_user;
- if (s && !url_canonize(s, s, SIZE_MAX, USER_UNRESERVED))
+ if (s && !url_canonize(s, s, SIZE_MAX, 0, USER_UNRESERVED))
return -1;
# define PASS_UNRESERVED "&=+$,;:"
s = (char *)url->url_password;
- if (s && !url_canonize(s, s, SIZE_MAX, PASS_UNRESERVED))
+ if (s && !url_canonize(s, s, SIZE_MAX, 0, PASS_UNRESERVED))
return -1;
}
s = (char *)url->url_host;
- if (s && !url_canonize2(s, s, SIZE_MAX, RESERVED_MASK))
+ if (s && !url_canonize2(s, s, SIZE_MAX, 0, RESERVED_MASK))
return -1;
/* port is canonized by _url_d() */
-
- /* Allow all URI characters but ? and ; */
-# define PATH_UNRESERVED "/:@&=+$,"
s = (char *)url->url_path;
- if (s && !url_canonize(s, s, SIZE_MAX, PATH_UNRESERVED))
+ if (s && !url_canonize(s, s, SIZE_MAX,
+ /* Allow all URI characters but ? */
+ /* Allow unescaped /;?@, - but do not convert */
+ SYN33('/') | SYN33(';') | SYN33('=') | SYN33('@') |
+ SYN33(','),
+ /* Convert escaped :&+$ to unescaped */
+ ":&+$"))
return -1;
- /* Allow all URI characters but ? */
-# define PARAMS_UNRESERVED ";" PATH_UNRESERVED
s = (char *)url->url_params;
- if (s && !url_canonize(s, s, SIZE_MAX, PARAMS_UNRESERVED))
+ if (s && !url_canonize(s, s, SIZE_MAX,
+ /* Allow all URI characters but ? */
+ /* Allow unescaped ;=@, - but do not convert */
+ SYN33(';') | SYN33('=') | SYN33('@') | SYN33(','),
+ /* Convert escaped /:&+$ to unescaped */
+ "/:&+$"))
return -1;
/* Unhex alphanumeric and unreserved URI characters */
@@ -807,7 +823,7 @@ int url_d(url_t *url, char *s)
/* Allow all URI characters (including reserved ones) */
s = (char *)url->url_fragment;
- if (s && !url_canonize2(s, s, SIZE_MAX, URIC_MASK))
+ if (s && !url_canonize2(s, s, SIZE_MAX, 0, URIC_MASK))
return -1;
return 0;
@@ -1927,7 +1943,7 @@ void url_string_update(su_md5_t *md5, char const *s)
su_md5_update(md5, ":", 1);
}
else if (n && s[n] == ':' ) {
- at = url_canonize(schema, s, n, "+");
+ at = url_canonize(schema, s, n, 0, "+");
type = url_get_type(schema, at - schema);
su_md5_iupdate(md5, schema, at - schema);
|