Merge up to the most recent sofia-sip darcs tree. Includes the following patches from darcs:

Tue Aug 21 09:38:59 EDT 2007  Pekka.Pessi@nokia.com
  * tport_type_udp.c: checking error while checking that MSG_TRUNC works.
Shall I pull this patch? (1/43)  [ynWvpxqadjk], or ? for help: y

Tue Aug 21 10:49:33 EDT 2007  Pekka.Pessi@nokia.com
  * nua_params.c: NUTAG_SIPS_URL() now sets the handle target, too.

  Problem reported by Jari Tenhunen.
Shall I pull this patch? (2/43)  [ynWvpxqadjk], or ? for help: y

Thu Aug 23 11:22:42 EDT 2007  Pekka.Pessi@nokia.com
  * nta.c: do not destroy INVITE transaction if it has been CANCELed

  Handle gracefully cases where the INVITE transaction is destroyed
  immediately after canceling it. The old behaviour was to left it up to the
  application to ACK the final response returned to INVITE.

  Thanks for Fabio Margarido for reporting this problem.
Shall I pull this patch? (3/43)  [ynWvpxqadjk], or ? for help: y

Thu Aug 23 13:02:01 EDT 2007  Pekka.Pessi@nokia.com
  * test_soa.c: added test with user SDP containing already rejected media
Shall I pull this patch? (4/43)  [ynWvpxqadjk], or ? for help: y

Fri Aug 24 09:41:20 EDT 2007  Pekka.Pessi@nokia.com
  * nta: added option for processing orphan responses matching with a dialog

  The orphan responses matching with the dialog can now be processed by the
  response callback.The dialog leg can be created with
  NTATAG_RESPONSE_CALLBACK() or a response callback can be later bound to the
  leg with nta_leg_bind_response().

  This is practically useful only with 200 OK responses to the INVITE that are
  retransmitted by the UAS. By default, the retransmission are catched by the
  ACK transaction (which then retransmits the ACK request message). However,
  after ACK transaction times out, the retransmitted 200 OK indicates most
  probably that the ACK request messages do not reach UAS.

  Partially fixes the sf.net bug #1750691 reported by Mikhail Zabaluev.
Shall I pull this patch? (5/43)  [ynWvpxqadjk], or ? for help: y

Fri Aug 24 09:41:20 EDT 2007  Pekka.Pessi@nokia.com
  UNDO: nta: added option for processing orphan responses matching with a dialog

  The orphan responses matching with the dialog can now be processed by the
  response callback.The dialog leg can be created with
  NTATAG_RESPONSE_CALLBACK() or a response callback can be later bound to the
  leg with nta_leg_bind_response().

  This is practically useful only with 200 OK responses to the INVITE that are
  retransmitted by the UAS. By default, the retransmission are catched by the
  ACK transaction (which then retransmits the ACK request message). However,
  after ACK transaction times out, the retransmitted 200 OK indicates most
  probably that the ACK request messages do not reach UAS.

  Partially fixes the sf.net bug #1750691 reported by Mikhail Zabaluev.
Shall I pull this patch? (6/43)  [ynWvpxqadjk], or ? for help: y

Thu Aug 30 07:00:10 EDT 2007  Pekka.Pessi@nokia.com
  * nta.c: disabled nta_msg_ackbye(). Fix for sf.net bug #1750691

  Thanks for Mikhail Zabaluev for reporting this bug.
Shall I pull this patch? (7/43)  [ynWvpxqadjk], or ? for help: y

Thu Aug 30 06:54:38 EDT 2007  Pekka.Pessi@nokia.com
  * test_nua: added test for sf.net bug #1750691
Shall I pull this patch? (8/43)  [ynWvpxqadjk], or ? for help: y

Thu Aug 30 07:03:45 EDT 2007  Pekka.Pessi@nokia.com
  * test_nua: added test for nua_bye() sending CANCEL
Shall I pull this patch? (9/43)  [ynWvpxqadjk], or ? for help: y

Fri Aug 31 12:08:09 EDT 2007  Pekka.Pessi@nokia.com
  * url.c: fixed escaping of '/' %2F, ';' %3B and '=' %3D in URL path/params

  Thanks for Fabio Margarido for reporting this bug.
Shall I pull this patch? (10/43)  [ynWvpxqadjk], or ? for help: y

Mon Sep  3 10:14:55 EDT 2007  Pekka.Pessi@nokia.com
  * url.c: do not un-escape %40 in URI parameters.

  Do not unescape %2C, %3B, %3D, or %40 in URI parameters, nor
  %2C, %2F, %3B, %3D, or %40 in URI path.

  The @ sign can be ambiguous in the SIP URL, e.g.,

  <sip:test.info;p=value@test.com>

  can be parsed in two ways:
  1) username contains test.info;param=value and host part has test.com
  2) empty username, host part test.info, URI parameter p=value@test.com

  Previously Sofia URL parser converted escaped '@' at signs (%40) in the URI
  parameters to the unescaped form. The resulting URI could be ambiguous and
  sometimes fail the syntax check if there was no '@' sign before the
  unescaped one.

  Thanks for Jan van den Bosch and Mikhail Zabaluev for reporting this bug.
Shall I pull this patch? (11/43)  [ynWvpxqadjk], or ? for help: y

Wed Jul 25 04:59:57 EDT 2007  Pekka.Pessi@nokia.com
  * tport.c: fixed indenting, logging
Shall I pull this patch? (12/43)  [ynWvpxqadjk], or ? for help: y

Fri Jul 13 12:47:33 EDT 2007  Pekka.Pessi@nokia.com
  * nua/test_proxy.h, nua/test_proxy.c: added support for multiple domains

  Each domain has its own registrar and authentication module.
Shall I pull this patch? (13/43)  [ynWvpxqadjk], or ? for help: y

Mon Jul 23 11:19:33 EDT 2007  Pekka.Pessi@nokia.com
  * test_ops.c: added timestamp to event logging
Shall I pull this patch? (14/43)  [ynWvpxqadjk], or ? for help: y

Mon Jul 23 11:20:12 EDT 2007  Pekka.Pessi@nokia.com
  * test_nua: fixed timing problems in testing.
Shall I pull this patch? (15/43)  [ynWvpxqadjk], or ? for help: y

Mon Jul 23 11:31:04 EDT 2007  Pekka.Pessi@nokia.com
  * test_ops.c: reduce su_root_step() delay to 0.1 seconds
Shall I pull this patch? (16/43)  [ynWvpxqadjk], or ? for help: y

Mon Jul 23 11:31:22 EDT 2007  Pekka.Pessi@nokia.com
  * test_register.c: fixed timing problem
Shall I pull this patch? (17/43)  [ynWvpxqadjk], or ? for help: y

Mon Jul 23 17:03:46 EDT 2007  Pekka.Pessi@nokia.com
  * test_100rel.c: fixed timing problems resulting in events being reordered
Shall I pull this patch? (18/43)  [ynWvpxqadjk], or ? for help: y

Wed Jul 25 12:40:53 EDT 2007  Pekka.Pessi@nokia.com
  * nua (test_init.c, test_register.c): using test_proxy domains
Shall I pull this patch? (19/43)  [ynWvpxqadjk], or ? for help: y

Thu Aug 23 12:12:32 EDT 2007  Pekka.Pessi@nokia.com
  * test_soa.c: added cleanup code
Shall I pull this patch? (20/43)  [ynWvpxqadjk], or ? for help: y

Fri Aug 24 09:35:35 EDT 2007  Pekka.Pessi@nokia.com
  * nta.c: increase lifetime of ACK transaction from T4 to T1 x 64

  nta.c creates a ACK transaction in order to restransmit ACK requests when
  ever a retransmitted 2XX response to INVITE is received. The UAS retransmits
  the 2XX responses for 64 x T1 (32 second by default).

  Partially fixes the sf.net bug #1750691 reported by Mikhail Zabaluev.
Shall I pull this patch? (21/43)  [ynWvpxqadjk], or ? for help: y

Thu Sep  6 10:21:04 EDT 2007  Pekka.Pessi@nokia.com
  * Makefile.am: generating libsofia-sip-ua/docs/Doxyfile.rfc before making manpages
Shall I pull this patch? (22/43)  [ynWvpxqadjk], or ? for help: y

Wed Jul 25 12:05:33 EDT 2007  Pekka.Pessi@nokia.com
  * sofia-sip/tport_tag.h: added TPTAG_KEEPALIVE(), TPTAG_PINGPONG(), TPTAG_PONG2PING()
Shall I pull this patch? (23/43)  [ynWvpxqadjk], or ? for help: y

Wed Jul 25 12:09:06 EDT 2007  Pekka.Pessi@nokia.com
  * tport: added ping-pong keepalive on TCP. replaced single tick with connection-specific timer

  Now detecting closed connections on TLS, too.

  Added tests for idle timeout, receive timeout, ping-pong timeout.
Shall I pull this patch? (24/43)  [ynWvpxqadjk], or ? for help: y

Fri Jul  6 10:19:32 EDT 2007  Pekka.Pessi@nokia.com
  * nta.c: added nta_incoming_received()
Shall I pull this patch? (25/43)  [ynWvpxqadjk], or ? for help: y

Mon Jul 23 11:29:56 EDT 2007  Pekka.Pessi@nokia.com
  * nua_session.c: delay transition to ready when O/A is incomplete

  Delay sending ACK and subsequent transition of call to the ready state when
  the 200 OK response to the INVITE is received if the SDP Offer/Answer
  exchange using UPDATE/PRACK was still incomplete.

  Previously, if the O/A using UPDATE or PRACK was incomplete and an 200 OK
  was received, the call setup logic regarded this as a fatal error and
  terminated the call.

  Thanks for Mike Jerris for detecting and reporting this bug.
Shall I pull this patch? (26/43)  [ynWvpxqadjk], or ? for help: y

Wed Jul 25 12:22:46 EDT 2007  Pekka.Pessi@nokia.com
  * test_call_reject.c: testing Retry-After
Shall I pull this patch? (27/43)  [ynWvpxqadjk], or ? for help: y

Wed Jul 25 12:42:51 EDT 2007  Pekka.Pessi@nokia.com
  * test_nua: using rudimentary outbound support in B's proxy.
Shall I pull this patch? (28/43)  [ynWvpxqadjk], or ? for help: y

Wed Jul 25 12:48:33 EDT 2007  Pekka.Pessi@nokia.com
  * nua_register.c: added some logging to nua_register_connection_closed()
Shall I pull this patch? (29/43)  [ynWvpxqadjk], or ? for help: y

Wed Jul 25 12:43:57 EDT 2007  Pekka.Pessi@nokia.com
  * test_nua: using AUTHTAG_MAX_NCOUNT(1) for Mr. C

  C is now challenged every time.
Shall I pull this patch? (30/43)  [ynWvpxqadjk], or ? for help: y

Thu Sep  6 11:05:19 EDT 2007  Pekka.Pessi@nokia.com
  * nua/test_100rel.c: fixed timing problem re response to PRACK and ACK
Shall I pull this patch? (31/43)  [ynWvpxqadjk], or ? for help: y

Thu Sep  6 06:02:50 EDT 2007  Mikhail Zabaluev <mikhail.zabaluev@nokia.com>
  * DIST_SUBDIRS must include everything unconditionally
Shall I pull this patch? (32/43)  [ynWvpxqadjk], or ? for help: y

Thu Sep  6 13:53:04 EDT 2007  Pekka.Pessi@nokia.com
  * test_soa.c: silenced warnings
Shall I pull this patch? (33/43)  [ynWvpxqadjk], or ? for help: y

Mon Jul 23 16:59:48 EDT 2007  Pekka.Pessi@nokia.com
  * nua: refactored dialog refresh code
Shall I pull this patch? (34/43)  [ynWvpxqadjk], or ? for help: y

Mon Jul 23 16:59:48 EDT 2007  Pekka.Pessi@nokia.com
  UNDO: nua: refactored dialog refresh code
Shall I pull this patch? (35/43)  [ynWvpxqadjk], or ? for help: y

Thu Sep  6 12:01:25 EDT 2007  Pekka.Pessi@nokia.com
  * nua_dialog.[hc]: renamed functions setting refresh interval

Shall I pull this patch? (36/43)  [ynWvpxqadjk], or ? for help: y

Thu Sep  6 12:15:03 EDT 2007  Pekka.Pessi@nokia.com
  * nua_dialog.[hc], nua_stack.c: added nua_dialog_repeat_shutdown()
Shall I pull this patch? (37/43)  [ynWvpxqadjk], or ? for help: y

Thu Sep  6 12:19:20 EDT 2007  Pekka.Pessi@nokia.com
  * nua_dialog.h: renamed nua_remote_t as nua_dialog_peer_info_t
Shall I pull this patch? (38/43)  [ynWvpxqadjk], or ? for help: y

Thu Sep  6 12:23:04 EDT 2007  Pekka.Pessi@nokia.com
  * nua_stack.c: added timer to client request in order to implement Retry-After
Shall I pull this patch? (39/43)  [ynWvpxqadjk], or ? for help: y

Thu Sep  6 12:33:53 EDT 2007  Pekka.Pessi@nokia.com
  * nua: added backpointers to nua_dialog_usage_t and nua_dialog_state_t
Shall I pull this patch? (40/43)  [ynWvpxqadjk], or ? for help: y

Thu Sep  6 13:56:48 EDT 2007  Pekka.Pessi@nokia.com
  * test_nua.c: abort() in timeout alarm function if -a is given
Shall I pull this patch? (41/43)  [ynWvpxqadjk], or ? for help: y

Thu Sep  6 17:13:18 EDT 2007  Pekka.Pessi@nokia.com
  * nua_subnotref.c: include SIPTAG_EVENT() in the nua_i_notify tag list
Shall I pull this patch? (42/43)  [ynWvpxqadjk], or ? for help: y

Mon Sep 10 12:27:53 EDT 2007  Pekka.Pessi@nokia.com
  * nua: save Contact from target refresh request or response.

  Save the Contact header which the application has added to the target
  refresh requests or responses and use the saved contact in subsequent target
  refresh requests or responses.

  Previously the application had no way of specifying the Contact included in
  the automatic responses to target refresh requests.

  Thanks for Anthony Minessale for reporting this problem.
Shall I pull this patch? (43/43)  [ynWvpxqadjk], or ? for help: y




git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@5692 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Michael Jerris 2007-09-10 20:45:25 +00:00
parent 63fdd41110
commit 274a956f00
44 changed files with 3094 additions and 1120 deletions

View File

@ -42,6 +42,7 @@ $(dist_man_MANS): manpages
manpages:
-mkdir -p man man/man1 2> /dev/null
if HAVE_DOXYGEN
$(MAKE) $(AM_MAKEFLAGS) -C libsofia-sip-ua/docs built-sources
@echo 'cd utils && $(DOXYGEN)'
@cd utils && \
{ exec 3>&1 1>&2; { $(DOXYGEN) 2>&1; echo $$? >& 3 ;} | \

View File

@ -26,7 +26,8 @@ endif
# note: order does matter in the subdir list
SUBDIRS = su features bnf sresolv sdp url msg sip $(OPT_SUBDIRS_STUN) ipt soa \
tport nta nea iptsec $(OPT_SUBDIRS_NTH) nua
DIST_SUBDIRS = $(SUBDIRS) docs
DIST_SUBDIRS = su features bnf sresolv sdp url msg sip stun ipt soa \
tport nta nea iptsec nth http nua docs
DOXYGEN = doxygen

View File

@ -55,9 +55,10 @@ Or post to the Sofia-SIP mailing list:
@section subdirs Directory Structure
In libsofia-sip-ua, there are subdirectories
In libsofia-sip-ua, there are subdirectories for different modules listed
below.
Terminal and high-level libraries utilizing for both signaling and media:
Terminal and high-level libraries used for both signaling and media:
Common runtime library:
- <a href="su/index.html">"su" - sockets, memory management, threads</a>

View File

@ -2690,12 +2690,14 @@ void agent_recv_response(nta_agent_t *agent,
&& sip->sip_via && !sip->sip_via->v_next
&& agent_has_via(agent, sip->sip_via)) {
agent->sa_stats->as_trless_200++;
#if nomore /* sf.net bug #1750691. Let UAS to cope with it. */
if (agent->sa_is_a_uas) {
/* Orphan 200 Ok to INVITE. ACK and BYE it */
SU_DEBUG_5(("nta: %03d %s %s\n", status, phrase, "is ACK&BYE"));
if (nta_msg_ackbye(agent, msg) != -1)
return;
}
#endif
}
SU_DEBUG_5(("nta: %03d %s %s\n", status, phrase, "was discarded"));
@ -3067,15 +3069,19 @@ int complete_response(msg_t *response,
/** ACK and BYE an unknown 200 OK response to INVITE.
*
* A UAS may still return a 2XX series response to an INVITE request after
* the client transaction has been terminated. In that case, the UAC can not
* really accept the call, but it may send a ACK request to UAS followed
* immediately by BYE using nta_msg_ackbye(). The function does not create a
* transaction objects, but just sends the ACK and BYE request messages
* according to the @RecordRoute and @Contact headers in the @a msg.
* A UAS may still return a 2XX series response to client request after the
* client transactions has been terminated. In that case, the UAC can not
* really accept the call. This function was used to accept and immediately
* terminate such a call.
*
* @deprecated This was a bad idea: see sf.net bug #1750691. It can be used
* to amplify DoS attacks. Let UAS take care of retransmission timeout and
* let it terminate the session. As of @VERSION_1_12_7, this function just
* returns -1.
*/
int nta_msg_ackbye(nta_agent_t *agent, msg_t *msg)
{
#if nomore
sip_t *sip = sip_object(msg);
msg_t *amsg = nta_msg_create(agent, 0);
sip_t *asip = sip_object(amsg);
@ -3164,6 +3170,8 @@ int nta_msg_ackbye(nta_agent_t *agent, msg_t *msg)
err:
msg_destroy(amsg);
msg_destroy(bmsg);
#endif
(void)agent; (void)msg;
return -1;
}
@ -5121,6 +5129,21 @@ nta_incoming_magic_t *nta_incoming_magic(nta_incoming_t *irq,
return irq && irq->irq_callback == callback ? irq->irq_magic : NULL;
}
/** When received */
sip_time_t nta_incoming_received(nta_incoming_t *irq,
su_nanotime_t *return_nano)
{
su_time_t tv = { 0, 0 };
if (irq)
tv = irq->irq_received;
if (return_nano)
*return_nano = (su_nanotime_t)tv.tv_sec * 1000000000 + tv.tv_usec * 1000;
return tv.tv_sec;
}
/** Find incoming transaction. */
nta_incoming_t *nta_incoming_find(nta_agent_t const *agent,
sip_t const *sip,
@ -7007,6 +7030,7 @@ nta_outgoing_t *outgoing_create(nta_agent_t *agent,
orq->orq_via_branch = branch;
if (orq->orq_method == sip_method_ack) {
/* Find the original INVITE which we are ACKing */
if (ack_branch != NULL && ack_branch != NONE) {
if (strncasecmp(ack_branch, "branch=", 7) == 0)
orq->orq_branch = su_strdup(home, ack_branch);
@ -7313,16 +7337,11 @@ outgoing_send(nta_outgoing_t *orq, int retransmit)
if (retransmit)
return;
/* Set timers */
if (orq->orq_method == sip_method_ack) {
/* ACK */
outgoing_complete(orq); /* Timer K */
return;
}
outgoing_trying(orq); /* Timer B / F */
if (!orq->orq_reliable)
if (orq->orq_method == sip_method_ack)
;
else if (!orq->orq_reliable)
outgoing_set_timer(orq, agent->sa_t1); /* Timer A/E */
else if (orq->orq_try_tcp_instead && !tport_is_connected(tp))
outgoing_set_timer(orq, agent->sa_t4); /* Timer N3 */
@ -7771,11 +7790,11 @@ void outgoing_destroy(nta_outgoing_t *orq)
if (orq->orq_terminated || orq->orq_default) {
outgoing_free(orq);
}
/* We have to handle 200 OK statelessly =>
kill transaction immediately */
/* Application is expected to handle 200 OK statelessly
=> kill transaction immediately */
else if (orq->orq_method == sip_method_invite && !orq->orq_completed
/* (unless we have to wait to send CANCEL) */
&& !orq->orq_cancel) {
/* (unless we the transaction has been canceled) */
&& !orq->orq_canceled) {
orq->orq_destroyed = 1;
outgoing_terminate(orq);
}
@ -7927,10 +7946,14 @@ size_t outgoing_timer_bf(outgoing_queue_t *q,
timeout++;
SU_DEBUG_5(("nta: timer %s fired, %s %s (%u)\n",
timer, "timeout",
timer,
orq->orq_method != sip_method_ack ? "timeout" : "terminating",
orq->orq_method_name, orq->orq_cseq->cs_seq));
outgoing_timeout(orq, now);
if (orq->orq_method != sip_method_ack)
outgoing_timeout(orq, now);
else
outgoing_terminate(orq);
assert(q->q_head != orq || orq->orq_timeout - now > 0);
}
@ -8002,10 +8025,7 @@ int outgoing_complete(nta_outgoing_t *orq)
outgoing_reset_timer(orq); /* Timer A/E */
if (orq->orq_stateless)
return outgoing_terminate(orq);
if (orq->orq_reliable && orq->orq_method != sip_method_ack)
if (orq->orq_stateless || orq->orq_reliable)
return outgoing_terminate(orq);
if (orq->orq_method == sip_method_invite) {
@ -8607,7 +8627,7 @@ int outgoing_reply(nta_outgoing_t *orq, int status, char const *phrase,
(void *)orq, status, phrase));
orq->orq_status = status;
if (orq->orq_queue == NULL)
outgoing_complete(orq); /* Timer D/K */
outgoing_trying(orq); /* Timer F */
return 0;
}

View File

@ -284,6 +284,7 @@ SOFIAPUBFUN sip_method_t nta_incoming_method(nta_incoming_t const *irq);
SOFIAPUBFUN char const *nta_incoming_method_name(nta_incoming_t const *irq);
SOFIAPUBFUN url_t const *nta_incoming_url(nta_incoming_t const *irq);
SOFIAPUBFUN uint32_t nta_incoming_cseq(nta_incoming_t const *irq);
SOFIAPUBFUN sip_time_t nta_incoming_received(nta_incoming_t *irq, su_nanotime_t *nano);
SOFIAPUBFUN int nta_incoming_set_params(nta_incoming_t *irq,
tag_type_t tag, tag_value_t value, ...);

View File

@ -1725,15 +1725,15 @@ NTA_DLL extern tag_typedef_t ntatag_s_trless_response_ref;
/** Get number of responses without matching request.
*
* Return number of received responses for which no matching client
* transaction was found. Such responses are processed either by the default
* transaction was found. Such responses are processed either by the
* client transaction created with nta_outgoing_default(), the
* #nta_message_f message callback given to nta_agent_create(), or, missing
* both the default client transaction and message callback, they are
* silently discarded.
*
* When stack is in UA mode, the successful 2XX responses to the INVITE
* transaction are an exception: when such a response is received the stack
* tries to send an ACK and BYE requests to the originator of 2XX response.
* The NTATAG_S_TRLESS_200_REF() counter counts those successful 2XX
* responses to the INVITE without client transaction which are silently
* discarded.
*
* @sa nta_agent_get_stats(), nta_outgoing_default(), nta_agent_create(),
* <sofia-sip/nta_stateless.h>, #nta_message_f, nta_msg_ackbye(),
@ -1753,10 +1753,6 @@ NTA_DLL extern tag_typedef_t ntatag_s_trless_200_ref;
* default client transaction created with nta_outgoing_default() or
* #nta_message_f message callback given to nta_agent_create().
*
* When such a successful 2XX responses to the INVITE is received but it is
* not processed, the stack tries to send an ACK and BYE requests to the
* originator of 2XX response if stack is in UA mode (set by NTATAG_UA(1).
*
* @sa nta_agent_get_stats(), nta_outgoing_default(), nta_agent_create(),
* <sofia-sip/nta_stateless.h>, #nta_message_f, nta_msg_ackbye(),
* NTATAG_S_TRLESS_RESPONSE_REF().

View File

@ -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);

View File

@ -1174,9 +1174,9 @@ follows:
<td rowspan=2>terminating*</td>
<td rowspan=2>-</td>
<td>
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

View File

@ -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",

View File

@ -43,6 +43,7 @@
#include <sofia-sip/su_uniqueid.h>
#include <sofia-sip/sip_protos.h>
#include <sofia-sip/sip_status.h>
#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;
}

View File

@ -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 <sofia-sip/nta.h>
#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);

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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)
;
}

View File

@ -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)

View File

@ -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));

View File

@ -36,6 +36,7 @@
#include "test_nua.h"
#include <sofia-sip/tport_tag.h>
#include <sofia-sip/auth_module.h>
#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);

View File

@ -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

View File

@ -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;

View File

@ -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);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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();
}

View File

@ -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:

View File

@ -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;

View File

@ -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();
}

View File

@ -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.
*

View File

@ -33,6 +33,9 @@
* @date Created: Wed Apr 3 11:25:13 2002 ppessi
*/
/* always assert()s */
#undef NDEBUG
#include "config.h"
#include <stddef.h>
@ -49,6 +52,8 @@ typedef struct tp_test_s tp_test_t;
#include <sofia-sip/su_wait.h>
#include <sofia-sip/su_md5.h>
#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 <signal.h>
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... */

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -37,12 +37,12 @@
#include "config.h"
#if HAVE_SCTP
#include "tport_internal.h"
#if HAVE_NETINET_SCTP_H
#include <netinet/sctp.h>
#undef HAVE_SCTP
#define HAVE_SCTP 1
#endif
#include <stdlib.h>
@ -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

View File

@ -45,6 +45,7 @@
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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");

View File

@ -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);