1 /*
2 * This file is part of the Sofia-SIP package
3 *
4 * Copyright (C) 2006 Nokia Corporation.
5 *
6 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25 /**@CFILE nua_client.c
26 * @brief Client transaction handling
27 *
28 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
29 *
30 * @date Created: Tue Feb 3 16:10:45 EET 2009
31 */
32
33 #include "config.h"
34
35 #include <stddef.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <limits.h>
39
40 #include <assert.h>
41
42 #include <sofia-sip/su_string.h>
43 #include <sofia-sip/su_tagarg.h>
44 #include <sofia-sip/su_tag_inline.h>
45
46 #include <sofia-sip/sip_util.h>
47 #include <sofia-sip/sip_protos.h>
48 #include <sofia-sip/sip_status.h>
49
50 #define SU_MSG_ARG_T struct nua_ee_data
51 #define SU_TIMER_ARG_T struct nua_client_request
52
53 #define NUA_SAVED_EVENT_T su_msg_t *
54 #define NUA_SAVED_SIGNAL_T su_msg_t *
55
56 #define NTA_AGENT_MAGIC_T struct nua_s
57 #define NTA_LEG_MAGIC_T struct nua_handle_s
58 #define NTA_OUTGOING_MAGIC_T struct nua_client_request
59
60 #include "nua_stack.h"
61 #include "nua_dialog.h"
62 #include "nua_client.h"
63
64 #include <sofia-sip/su_wait.h>
65
66 #if 0
67 su_inline int can_redirect(sip_contact_t const *m, sip_method_t method);
68 #endif
69
70 /**@internal
71 *
72 * @class nua_client_request
73 *
74 * Each handle has a queue of client-side requests; if a request is pending,
75 * a new request from API is added to the queue. After the request is
76 * complete, it is removed from the queue and destroyed by the default. The
77 * exception is the client requests bound to a dialog usage: they are saved
78 * and re-used when the dialog usage is refreshed (and sometimes when the
79 * usage is terminated).
80 *
81 * The client request is subclassed and its behaviour modified using virtual
82 * function table in #nua_client_methods_t.
83 *
84 * The first three methods (crm_template(), crm_init(), crm_send()) are
85 * called when the request is sent first time.
86 *
87 * The crm_template() is called if a template request message is needed (for
88 * example, in case of unregister, unsubscribe and unpublish, the template
89 * message is taken from the request establishing the usage).
90 *
91 * The crm_init() is called when the template message and dialog leg has
92 * been created and populated by the tags procided by the application. Its
93 * parameters msg and sip are pointer to the template request message that
94 * is saved in the nua_client_request::cr_msg field.
95 *
96 * The crm_send() is called with a copy of the template message that has
97 * been populated with all the fields included in the request, including
98 * @CSeq and @MaxForwards. The crm_send() function, such as
99 * nua_publish_client_request(), usually calls nua_base_client_trequest() that
100 * then creates the nta-level transaction.
101 *
102 * The response to the request is processed by crm_check_restart(), which
103 * modifies and restarts the request when needed (e.g., when negotiating
104 * expiration time). After the request has been suitably modified, e.g., the
105 * expiration time has been increased, the restart function calls
106 * nua_client_restart(), which restarts the request and relays the
107 * intermediate response to the application with nua_client_restart() and
108 * crm_report().
109 *
110 * The final responses are processed by crm_recv() and and preliminary ones
111 * by crm_preliminary(). All virtual functions should call
112 * nua_base_client_response() beside method-specific processing.
113 *
114 * The nua_base_client_response() relays the response to the application with
115 * nua_client_restart() and crm_report().
116 *
117 * @par Terminating Dialog Usages and Dialogs
118 *
119 * The response is marked as terminating with nua_client_set_terminating().
120 * When a terminating request completes the dialog usage is removed and the
121 * dialog is destroyed (unless there is an another active usage).
122 */
123 static void nua_client_request_destroy(nua_client_request_t *cr);
124 static int nua_client_init_request0(nua_client_request_t *cr);
125 static int nua_client_request_try(nua_client_request_t *cr);
126 static int nua_client_request_sendmsg(nua_client_request_t *cr);
127 static void nua_client_restart_after(su_root_magic_t *magic,
128 su_timer_t *timer,
129 nua_client_request_t *cr);
130
131 /**Create a client request.
132 *
133 * @retval 0 if request is pending
134 * @retval > 0 if error event has been sent
135 * @retval < 0 upon an error
136 */
nua_client_create(nua_handle_t * nh,int event,nua_client_methods_t const * methods,tagi_t const * const tags)137 int nua_client_create(nua_handle_t *nh,
138 int event,
139 nua_client_methods_t const *methods,
140 tagi_t const * const tags)
141 {
142 su_home_t *home = nh->nh_home;
143 nua_client_request_t *cr;
144 sip_method_t method;
145 char const *name;
146
147 method = methods->crm_method, name = methods->crm_method_name;
148 if (!name) {
149 tagi_t const *t = tl_find_last(tags, nutag_method);
150 if (t)
151 name = (char const *)t->t_value;
152 }
153
154 cr = su_zalloc(home, sizeof *cr + methods->crm_extra);
155 if (!cr) {
156 return nua_stack_event(nh->nh_nua, nh,
157 NULL,
158 (enum nua_event_e)event,
159 NUA_ERROR_AT(__FILE__, __LINE__),
160 NULL);
161 }
162
163 cr->cr_methods = methods;
164 cr->cr_event = event;
165 cr->cr_method = method;
166 cr->cr_method_name = name;
167 cr->cr_contactize = methods->crm_flags.target_refresh;
168 cr->cr_dialog = methods->crm_flags.create_dialog;
169 cr->cr_auto = 1;
170
171 if (su_msg_is_non_null(nh->nh_nua->nua_signal)) {
172 nua_event_data_t *e = su_msg_data(nh->nh_nua->nua_signal)->ee_data;
173
174 if (tags == e->e_tags && event == e->e_event) {
175 cr->cr_auto = 0;
176
177 if (tags) {
178 nua_move_signal(cr->cr_signal, nh->nh_nua->nua_signal);
179 if (cr->cr_signal[0]) {
180 /* Steal reference from signal */
181 cr->cr_owner = e->e_nh, e->e_nh = NULL;
182 cr->cr_tags = tags;
183 }
184 }
185 }
186 }
187
188 if (cr->cr_owner == NULL)
189 cr->cr_owner = nua_handle_ref(nh);
190
191 if (tags && cr->cr_tags == NULL)
192 cr->cr_tags = tl_tlist(nh->nh_home, TAG_NEXT(tags));
193
194 #if HAVE_MEMLEAK_LOG
195 SU_DEBUG_0(("%p %s() for %s\n", cr, __func__, cr->cr_methods->crm_method_name));
196 #endif
197
198 if (nua_client_request_queue(cr))
199 return 0;
200
201 return nua_client_init_request(cr);
202 }
203
nua_client_tcreate(nua_handle_t * nh,int event,nua_client_methods_t const * methods,tag_type_t tag,tag_value_t value,...)204 int nua_client_tcreate(nua_handle_t *nh,
205 int event,
206 nua_client_methods_t const *methods,
207 tag_type_t tag, tag_value_t value, ...)
208 {
209 int retval;
210 ta_list ta;
211 ta_start(ta, tag, value);
212 retval = nua_client_create(nh, event, methods, ta_args(ta));
213 ta_end(ta);
214 return retval;
215 }
216
217 #if HAVE_MEMLEAK_LOG
218 nua_client_request_t *
nua_client_request_ref_by(nua_client_request_t * cr,char const * where,unsigned line,char const * who)219 nua_client_request_ref_by(nua_client_request_t *cr,
220 char const *where, unsigned line, char const *who)
221 {
222 SU_DEBUG_0(("%p ref %s to %u by %s:%u: %s()\n",
223 cr, cr->cr_methods->crm_method_name,
224 ++(cr->cr_refs), where, line, who));
225 return cr;
226 }
227
nua_client_request_unref_by(nua_client_request_t * cr,char const * where,unsigned line,char const * who)228 int nua_client_request_unref_by(nua_client_request_t *cr,
229 char const *where, unsigned line, char const *who)
230 {
231 SU_DEBUG_0(("%p unref %s to %u by %s:%u: %s()\n",
232 cr, cr->cr_methods->crm_method_name,
233 cr->cr_refs - 1, where, line, who));
234
235 if (cr->cr_refs > 1) {
236 cr->cr_refs--;
237 return 0;
238 }
239 else {
240 cr->cr_refs = 0;
241 nua_client_request_destroy(cr);
242 return 1;
243 }
244 }
245 #else
nua_client_request_ref(nua_client_request_t * cr)246 nua_client_request_t *nua_client_request_ref(nua_client_request_t *cr)
247 {
248 cr->cr_refs++;
249 return cr;
250 }
251
nua_client_request_unref(nua_client_request_t * cr)252 int nua_client_request_unref(nua_client_request_t *cr)
253 {
254 if (cr->cr_refs > 1) {
255 cr->cr_refs--;
256 return 0;
257 }
258 else {
259 cr->cr_refs = 0;
260 nua_client_request_destroy(cr);
261 return 1;
262 }
263 }
264 #endif
265
nua_client_request_queue(nua_client_request_t * cr)266 int nua_client_request_queue(nua_client_request_t *cr)
267 {
268 int queued = 0;
269 nua_client_request_t **queue = &cr->cr_owner->nh_ds->ds_cr;
270
271 assert(cr->cr_prev == NULL && cr->cr_next == NULL);
272
273 cr->cr_status = 0;
274
275 nua_client_request_ref(cr);
276
277 if (cr->cr_method != sip_method_invite &&
278 cr->cr_method != sip_method_cancel) {
279 while (*queue) {
280 if ((*queue)->cr_method == sip_method_invite ||
281 (*queue)->cr_method == sip_method_cancel)
282 break;
283 queue = &(*queue)->cr_next;
284 queued = 1;
285 }
286 }
287 else {
288 while (*queue) {
289 queue = &(*queue)->cr_next;
290 if (cr->cr_method == sip_method_invite)
291 queued = 1;
292 }
293 }
294
295 if ((cr->cr_next = *queue))
296 cr->cr_next->cr_prev = &cr->cr_next;
297
298 cr->cr_prev = queue, *queue = cr;
299
300 return queued;
301 }
302
303 int
nua_client_request_remove(nua_client_request_t * cr)304 nua_client_request_remove(nua_client_request_t *cr)
305 {
306 int retval = 0;
307 int in_queue = cr->cr_prev != NULL;
308
309 if (in_queue) {
310 if ((*cr->cr_prev = cr->cr_next))
311 cr->cr_next->cr_prev = cr->cr_prev;
312 }
313 cr->cr_prev = NULL, cr->cr_next = NULL;
314
315 if (cr->cr_timer) {
316 su_timer_destroy(cr->cr_timer), cr->cr_timer = NULL;
317 retval = nua_client_request_unref(cr);
318 }
319
320 if (!in_queue)
321 return retval;
322
323 return nua_client_request_unref(cr);
324 }
325
326 int
nua_client_request_clean(nua_client_request_t * cr)327 nua_client_request_clean(nua_client_request_t *cr)
328 {
329 if (cr->cr_orq) {
330 nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL, cr->cr_acked = 0;
331 return nua_client_request_unref(cr);
332 }
333 return 0;
334 }
335
336 int
nua_client_request_complete(nua_client_request_t * cr)337 nua_client_request_complete(nua_client_request_t *cr)
338 {
339 if (cr->cr_orq) {
340 nua_client_request_ref(cr);
341 if (cr->cr_methods->crm_complete)
342 /* Calls nua_invite_client_complete() */
343 cr->cr_methods->crm_complete(cr);
344 nua_client_request_clean(cr);
345 if (nua_client_request_unref(cr))
346 return 1;
347 }
348
349 return nua_client_request_remove(cr);
350 }
351
352 static void
nua_client_request_destroy(nua_client_request_t * cr)353 nua_client_request_destroy(nua_client_request_t *cr)
354 {
355 nua_handle_t *nh;
356
357 if (cr == NULL)
358 return;
359
360 /* Possible references: */
361 assert(cr->cr_prev == NULL); /* queue */
362 assert(cr->cr_orq == NULL); /* transaction callback */
363 assert(cr->cr_timer == NULL); /* timer callback */
364
365 nh = cr->cr_owner;
366
367 nua_destroy_signal(cr->cr_signal);
368
369 nua_client_bind(cr, NULL);
370
371 #if HAVE_MEMLEAK_LOG
372 SU_DEBUG_0(("%p %s for %s\n", cr, __func__, cr->cr_methods->crm_method_name));
373 #endif
374
375 if (cr->cr_msg)
376 msg_destroy(cr->cr_msg);
377 cr->cr_msg = NULL, cr->cr_sip = NULL;
378
379 if (cr->cr_orq)
380 nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
381
382 if (cr->cr_target)
383 su_free(nh->nh_home, cr->cr_target);
384
385 su_free(nh->nh_home, cr);
386
387 nua_handle_unref(nh);
388 }
389
390 /** Bind client request to a dialog usage */
nua_client_bind(nua_client_request_t * cr,nua_dialog_usage_t * du)391 int nua_client_bind(nua_client_request_t *cr, nua_dialog_usage_t *du)
392 {
393 assert(cr);
394 if (cr == NULL)
395 return -1;
396
397 if (du == NULL) {
398 du = cr->cr_usage;
399 cr->cr_usage = NULL;
400 if (du && du->du_cr == cr) {
401 du->du_cr = NULL;
402 nua_client_request_unref(cr);
403 }
404 return 0;
405 }
406
407 if (du->du_cr && cr == du->du_cr)
408 return 0;
409
410 if (du->du_cr) {
411 nua_client_bind(du->du_cr, NULL);
412 }
413
414 du->du_cr = nua_client_request_ref(cr), cr->cr_usage = du;
415
416 return 0;
417 }
418
419 /** Check if client request is in progress.
420 *
421 * A client request is in progress, if
422 * 1) it has actual transaction going on
423 * 2) it is waiting credentials from application
424 * 3) it is waiting for Retry-After timer
425 */
426 int
nua_client_request_in_progress(nua_client_request_t const * cr)427 nua_client_request_in_progress(nua_client_request_t const *cr)
428 {
429 return cr &&
430 (cr->cr_orq || cr->cr_wait_for_cred || cr->cr_timer);
431 }
432
433 /**Initialize client request for sending.
434 *
435 * This function is called when the request is taken from queue and sent.
436 *
437 * @retval 0 if request is pending
438 * @retval >=1 if error event has been sent
439 */
nua_client_init_request(nua_client_request_t * cr)440 int nua_client_init_request(nua_client_request_t *cr)
441 {
442 int retval;
443 nua_client_request_ref(cr);
444 retval = nua_client_init_request0(cr);
445 nua_client_request_unref(cr);
446 return retval;
447 }
448
449 /**Initialize client request for sending.
450 *
451 * This function is called when the request is taken from queue and sent.
452 *
453 * @retval 0 if request is pending
454 * @retval >=1 if error event has been sent
455 */
456 static
nua_client_init_request0(nua_client_request_t * cr)457 int nua_client_init_request0(nua_client_request_t *cr)
458 {
459 nua_handle_t *nh = cr->cr_owner;
460 nua_t *nua = nh->nh_nua;
461 nua_dialog_state_t *ds = nh->nh_ds;
462 msg_t *msg = NULL;
463 sip_t *sip;
464 url_string_t const *url = NULL;
465 tagi_t const *t;
466 int has_contact = 0;
467 int error = 0;
468
469 if (!cr->cr_method_name)
470 return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), NULL);
471
472 if (cr->cr_msg)
473 return nua_client_request_try(cr);
474
475 cr->cr_answer_recv = 0, cr->cr_offer_sent = 0;
476 cr->cr_offer_recv = 0, cr->cr_answer_sent = 0;
477 cr->cr_terminated = 0, cr->cr_graceful = 0;
478
479 nua_stack_init_handle(nua, nh, cr->cr_tags);
480
481 if (cr->cr_method == sip_method_cancel) {
482 if (cr->cr_methods->crm_init) {
483 error = cr->cr_methods->crm_init(cr, NULL, NULL, cr->cr_tags);
484 if (error)
485 return error;
486 }
487
488 if (cr->cr_methods->crm_send)
489 return cr->cr_methods->crm_send(cr, NULL, NULL, cr->cr_tags);
490 else
491 return nua_base_client_request(cr, NULL, NULL, cr->cr_tags);
492 }
493
494 if (!cr->cr_methods->crm_template ||
495 cr->cr_methods->crm_template(cr, &msg, cr->cr_tags) == 0)
496 msg = nua_client_request_template(cr);
497
498 sip = sip_object(msg);
499 if (!sip)
500 return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
501
502 if (nh->nh_tags) {
503 for (t = nh->nh_tags; t; t = t_next(t)) {
504 if (t->t_tag == siptag_contact ||
505 t->t_tag == siptag_contact_str)
506 has_contact = 1;
507 else if (t->t_tag == nutag_url)
508 url = (url_string_t const *)t->t_value;
509 }
510 }
511
512 /**@par Populating SIP Request Message with Tagged Arguments
513 *
514 * The tagged arguments can be used to pass values for any SIP headers
515 * to the stack. When the INVITE message (or any other SIP message) is
516 * created, the tagged values saved with nua_handle() are used first,
517 * next the tagged values given with the operation (nua_invite()) are
518 * added.
519 *
520 * When multiple tags for the same header are specified, the behaviour
521 * depends on the header type. If only a single header field can be
522 * included in a SIP message, the latest non-NULL value is used, e.g.,
523 * @Subject. However, if the SIP header can consist of multiple lines or
524 * header fields separated by comma, e.g., @Accept, all the tagged
525 * values are concatenated.
526 *
527 * However, if a tag value is #SIP_NONE (-1 casted as a void pointer),
528 * the values from previous tags are ignored.
529 */
530 for (t = cr->cr_tags; t; t = t_next(t)) {
531 if (t->t_tag == siptag_contact ||
532 t->t_tag == siptag_contact_str)
533 has_contact = 1;
534 else if (t->t_tag == nutag_url)
535 url = (url_string_t const *)t->t_value;
536 else if (t->t_tag == nutag_dialog) {
537 cr->cr_dialog = t->t_value > 1;
538 cr->cr_contactize = t->t_value >= 1;
539 }
540 else if (t->t_tag == nutag_auth && t->t_value) {
541 /* XXX ignoring errors */
542 if (nh->nh_auth)
543 auc_credentials(&nh->nh_auth, nh->nh_home, (char *)t->t_value);
544 }
545 }
546
547 if (cr->cr_method == sip_method_register && url == NULL)
548 url = (url_string_t const *)NH_PGET(nh, registrar);
549
550 if ((t = cr->cr_tags)) {
551 if (sip_add_tagis(msg, sip, &t) < 0)
552 return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
553 }
554
555 /**
556 * Now, the target URI for the request needs to be determined.
557 *
558 * For initial requests, values from tags are used. If NUTAG_URL() is
559 * given, it is used as target URI. Otherwise, if SIPTAG_TO() is given,
560 * it is used as target URI. If neither is given, the complete request
561 * line already specified using SIPTAG_REQUEST() or SIPTAG_REQUEST_STR()
562 * is used. At this point, the target URI is stored in the request line,
563 * together with method name and protocol version ("SIP/2.0"). The
564 * initial dialog information is also created: @CallID, @CSeq headers
565 * are generated, if they do not exist, and a tag is added to the @From
566 * header.
567 */
568
569 if (!ds->ds_leg) {
570 if (ds->ds_remote_tag && ds->ds_remote_tag[0] &&
571 sip_to_tag(nh->nh_home, sip->sip_to, ds->ds_remote_tag) < 0)
572 return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
573
574 if (sip->sip_from == NULL &&
575 sip_add_dup(msg, sip, (sip_header_t *)nua->nua_from) < 0)
576 return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
577
578 if (sip->sip_to == NULL && cr->cr_method == sip_method_register &&
579 sip_add_dup_as(msg, sip, sip_to_class,
580 (sip_header_t *)sip->sip_from) < 0) {
581 return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
582 }
583 }
584 else {
585 if (ds->ds_route)
586 url = NULL;
587 }
588
589 if (url && nua_client_set_target(cr, (url_t *)url) < 0)
590 return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
591
592 cr->cr_has_contact = has_contact;
593
594 if (cr->cr_methods->crm_init) {
595 error = cr->cr_methods->crm_init(cr, msg, sip, cr->cr_tags);
596 if (error < -1)
597 msg = NULL;
598 if (error < 0)
599 return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
600 if (error != 0)
601 return error;
602 }
603
604 cr->cr_msg = msg;
605 cr->cr_sip = sip;
606
607 return nua_client_request_try(cr);
608 }
609
nua_client_request_template(nua_client_request_t * cr)610 msg_t *nua_client_request_template(nua_client_request_t *cr)
611 {
612 nua_handle_t *nh = cr->cr_owner;
613 nua_t *nua = nh->nh_nua;
614 nua_dialog_state_t *ds = nh->nh_ds;
615
616 msg_t *msg = nta_msg_create(nua->nua_nta, 0);
617 sip_t *sip = sip_object(msg);
618
619 if (!sip)
620 return NULL;
621
622 if (nh->nh_tags) {
623 tagi_t const *t = nh->nh_tags;
624
625 /* Use the From header from the dialog.
626 If From is set, it is always first tag in the handle */
627 if (ds->ds_leg && t->t_tag == siptag_from)
628 t++;
629
630 /* When the INVITE message (or any other SIP message) is
631 * created, the tagged values saved with nua_handle() are used first. */
632 sip_add_tagis(msg, sip, &t);
633 }
634
635 return msg;
636 }
637
638
639 /** Restart the request message.
640 *
641 * A restarted request has not been completed successfully.
642 *
643 * @retval 0 if request is pending
644 * @retval >=1 if error event has been sent
645 */
nua_client_restart_request(nua_client_request_t * cr,int terminating,tagi_t const * tags)646 int nua_client_restart_request(nua_client_request_t *cr,
647 int terminating,
648 tagi_t const *tags)
649 {
650 if (cr) {
651 assert(nua_client_is_queued(cr));
652
653 if (tags && cr->cr_msg)
654 if (sip_add_tagis(cr->cr_msg, NULL, &tags) < 0)
655 /* XXX */;
656
657 nua_client_set_terminating(cr, terminating);
658
659 return nua_client_request_try(cr);
660 }
661
662 return 0;
663 }
664
665 /** Resend the request message.
666 *
667 * A resent request has completed once successfully - restarted has not.
668 *
669 * @retval 0 if request is pending
670 * @retval >=1 if error event has been sent
671 */
nua_client_resend_request(nua_client_request_t * cr,int terminating)672 int nua_client_resend_request(nua_client_request_t *cr,
673 int terminating)
674 {
675 if (cr) {
676 cr->cr_retry_count = 0;
677 cr->cr_challenged = 0;
678
679 if (nua_client_is_queued(cr)) {
680 if (terminating)
681 cr->cr_graceful = 1;
682 return 0;
683 }
684
685 if (terminating)
686 nua_client_set_terminating(cr, terminating);
687
688 if (nua_client_request_queue(cr))
689 return 0;
690
691 if (nua_dialog_is_reporting(cr->cr_owner->nh_ds))
692 return 0;
693
694 return nua_client_request_try(cr);
695 }
696 return 0;
697 }
698
699
700 /** Send a request message.
701 *
702 * If an error occurs, send error event to the application.
703 *
704 * @retval 0 if request is pending
705 * @retval >=1 if error event has been sent
706 */
707 static
nua_client_request_try(nua_client_request_t * cr)708 int nua_client_request_try(nua_client_request_t *cr)
709 {
710 int error = nua_client_request_sendmsg(cr);
711
712 if (error < 0)
713 error = nua_client_response(cr, NUA_ERROR_AT(__FILE__, __LINE__), NULL);
714
715 return error;
716 }
717
718 /**Send a request message.
719 *
720 * @retval 0 if request is pending
721 * @retval >=1 if error event has been sent
722 * @retval < 0 if no error event has been sent
723 */
724 static
nua_client_request_sendmsg(nua_client_request_t * cr)725 int nua_client_request_sendmsg(nua_client_request_t *cr)
726 {
727 nua_handle_t *nh = cr->cr_owner;
728 nua_dialog_state_t *ds = nh->nh_ds;
729 sip_method_t method = cr->cr_method;
730 char const *name = cr->cr_method_name;
731 url_string_t const *url = (url_string_t *)cr->cr_target;
732 nta_leg_t *leg;
733 msg_t *msg;
734 sip_t *sip;
735 int error;
736
737 assert(cr->cr_orq == NULL);
738
739 cr->cr_offer_sent = cr->cr_answer_recv = 0;
740 cr->cr_offer_recv = cr->cr_answer_sent = 0;
741
742 if (!ds->ds_leg && cr->cr_dialog) {
743 ds->ds_leg = nta_leg_tcreate(nh->nh_nua->nua_nta,
744 nua_stack_process_request, nh,
745 SIPTAG_CALL_ID(cr->cr_sip->sip_call_id),
746 SIPTAG_FROM(cr->cr_sip->sip_from),
747 SIPTAG_TO(cr->cr_sip->sip_to),
748 SIPTAG_CSEQ(cr->cr_sip->sip_cseq),
749 TAG_END());
750 if (!ds->ds_leg)
751 return -1;
752 }
753
754 if (cr->cr_sip->sip_from && ds->ds_leg) {
755 if (cr->cr_sip->sip_from->a_tag == NULL) {
756 if (sip_from_tag(msg_home(cr->cr_msg), cr->cr_sip->sip_from,
757 nta_leg_tag(ds->ds_leg, NULL)) < 0) {
758 return -1;
759 }
760 }
761 }
762
763 cr->cr_retry_count++;
764
765 if (ds->ds_leg)
766 leg = ds->ds_leg;
767 else
768 leg = nh->nh_nua->nua_dhandle->nh_ds->ds_leg; /* Default leg */
769
770 msg = msg_copy(cr->cr_msg), sip = sip_object(msg);
771
772 if (msg == NULL)
773 return -1;
774
775 if (nua_dialog_is_established(ds)) {
776 while (sip->sip_route)
777 sip_route_remove(msg, sip);
778 }
779 else if (!ds->ds_route) {
780 sip_route_t *initial_route = NH_PGET(nh, initial_route);
781
782 if (initial_route) {
783 initial_route = sip_route_dup(msg_home(msg), initial_route);
784 if (!initial_route) return -1;
785 msg_header_prepend(msg, (msg_pub_t*)sip,
786 /* This should be
787 (msg_header_t **)&sip->sip_route
788 * but directly casting pointer &sip->sip_route gives
789 * spurious type-punning warning */
790 (msg_header_t **)((char *)sip + offsetof(sip_t, sip_route)),
791 (msg_header_t *)initial_route);
792 }
793 }
794
795
796 /**
797 * For in-dialog requests, the request URI is taken from the @Contact
798 * header received from the remote party during dialog establishment,
799 * and the NUTAG_URL() is ignored.
800 *
801 * Also, the @CallID and @CSeq headers and @From and @To tags are
802 * generated based on the dialog information and added to the request.
803 * If the dialog has a route, it is added to the request, too.
804 */
805 if (nta_msg_request_complete(msg, leg, method, name, url) < 0) {
806 msg_destroy(msg);
807 return -1;
808 }
809
810 /**@MaxForwards header (with default value set by NTATAG_MAX_FORWARDS()) is
811 * also added now, if it does not exist.
812 */
813
814 if (!ds->ds_remote)
815 ds->ds_remote = sip_to_dup(nh->nh_home, sip->sip_to);
816 if (!ds->ds_local)
817 ds->ds_local = sip_from_dup(nh->nh_home, sip->sip_from);
818
819 /**
820 * Next, values previously set with nua_set_params() or nua_set_hparams()
821 * are used: @Allow, @Supported, @Organization, @UserAgent and
822 * @AllowEvents headers are added to the request if they are not already
823 * set.
824 */
825 if (!sip->sip_allow)
826 sip_add_dup(msg, sip, (sip_header_t*)NH_PGET(nh, allow));
827
828 if (!sip->sip_supported && NH_PGET(nh, supported))
829 sip_add_dup(msg, sip, (sip_header_t *)NH_PGET(nh, supported));
830
831 if (method == sip_method_register && NH_PGET(nh, path_enable) &&
832 !sip_has_feature(sip->sip_supported, "path") &&
833 !sip_has_feature(sip->sip_require, "path"))
834 sip_add_make(msg, sip, sip_supported_class, "path");
835
836 if (!sip->sip_organization && NH_PGET(nh, organization))
837 sip_add_make(msg, sip, sip_organization_class, NH_PGET(nh, organization));
838
839 if (!sip->sip_user_agent && NH_PGET(nh, user_agent))
840 sip_add_make(msg, sip, sip_user_agent_class, NH_PGET(nh, user_agent));
841
842 if (!sip->sip_via && NH_PGET(nh, via))
843 sip_add_make(msg, sip, sip_via_class, NH_PGET(nh, via));
844
845 /** Any node implementing one or more event packages SHOULD include an
846 * appropriate @AllowEvents header indicating all supported events in
847 * all methods which initiate dialogs and their responses (such as
848 * INVITE) and OPTIONS responses.
849 */
850 if (!sip->sip_allow_events &&
851 NH_PGET(nh, allow_events) &&
852 (method == sip_method_notify || /* Always in NOTIFY */
853 (!ds->ds_remote_tag && /* And in initial requests */
854 (method == sip_method_subscribe || method == sip_method_refer ||
855 method == sip_method_options ||
856 method == sip_method_invite))))
857 sip_add_dup(msg, sip, (void *)NH_PGET(nh, allow_events));
858
859 /**
860 * Next, the stack generates a @Contact header for the request (unless
861 * the application already gave a @Contact header or it does not want to
862 * use @Contact and indicates that by including SIPTAG_CONTACT(NULL) or
863 * SIPTAG_CONTACT(SIP_NONE) in the tagged parameters.) If the
864 * application has registered the URI in @From header, the @Contact
865 * header used with registration is used. Otherwise, the @Contact header
866 * is generated from the local IP address and port number.
867 */
868
869 /**For the initial requests, @ServiceRoute set that was received from the
870 * registrar is also added to the request message.
871 */
872 if (cr->cr_method != sip_method_register) {
873 if (cr->cr_contactize && cr->cr_has_contact) {
874 sip_contact_t *ltarget = sip_contact_dup(nh->nh_home, sip->sip_contact);
875 if (ds->ds_ltarget)
876 msg_header_free(nh->nh_home, (msg_header_t *)ds->ds_ltarget);
877 ds->ds_ltarget = ltarget;
878 }
879
880 if (ds->ds_ltarget && !cr->cr_has_contact)
881 sip_add_dup(msg, sip, (sip_header_t *)ds->ds_ltarget);
882
883 /*
884 FS-4102
885 It was decided to comment out this code because it does not appear to make sense
886 Dec 22, 2016
887
888 if (nua_registration_add_contact_to_request(nh, msg, sip,
889 cr->cr_contactize &&
890 !cr->cr_has_contact &&
891 !ds->ds_ltarget,
892 !ds->ds_route) < 0) {
893 msg_destroy(msg);
894 return -1;
895 }
896 */
897
898 }
899
900 cr->cr_wait_for_cred = 0;
901
902 if (cr->cr_methods->crm_send)
903 error = cr->cr_methods->crm_send(cr, msg, sip, NULL);
904 else
905 error = nua_base_client_request(cr, msg, sip, NULL);
906
907 if (error == -1)
908 msg_destroy(msg);
909
910 return error;
911 }
912
913 /**Add tags to request message and send it,
914 *
915 * @retval 0 success
916 * @retval -1 if error occurred, but event has not been sent
917 * @retval -2 if error occurred, event has not been sent,
918 * and @a msg has been destroyed
919 * @retval >=1 if error event has been sent
920 */
nua_base_client_trequest(nua_client_request_t * cr,msg_t * msg,sip_t * sip,tag_type_t tag,tag_value_t value,...)921 int nua_base_client_trequest(nua_client_request_t *cr,
922 msg_t *msg, sip_t *sip,
923 tag_type_t tag, tag_value_t value, ...)
924 {
925 int retval;
926 ta_list ta;
927 ta_start(ta, tag, value);
928 retval = nua_base_client_request(cr, msg, sip, ta_args(ta));
929 ta_end(ta);
930 return retval;
931 }
932
933 /** Send request.
934 *
935 * @retval 0 success
936 * @retval -1 if error occurred, but event has not been sent,
937 * and caller has to destroy request message @ msg
938 * @retval -2 if error occurred, event has not been sent
939 * @retval >=1 if error event has been sent
940 */
nua_base_client_request(nua_client_request_t * cr,msg_t * msg,sip_t * sip,tagi_t const * tags)941 int nua_base_client_request(nua_client_request_t *cr, msg_t *msg, sip_t *sip,
942 tagi_t const *tags)
943 {
944 nua_handle_t *nh = cr->cr_owner;
945 int proxy_is_set = NH_PISSET(nh, proxy);
946 url_string_t * proxy = NH_PGET(nh, proxy);
947
948 if (nh->nh_auth) {
949 if (cr->cr_challenged ||
950 NH_PGET(nh, auth_cache) == nua_auth_cache_dialog) {
951 if (auc_authorize(&nh->nh_auth, msg, sip) < 0)
952 return nua_client_return(cr, 900, "Cannot add credentials", msg);
953 }
954 }
955
956 cr->cr_seq = sip->sip_cseq->cs_seq; /* Save last sequence number */
957
958 assert(cr->cr_orq == NULL);
959
960 cr->cr_orq = nta_outgoing_mcreate(nh->nh_nua->nua_nta,
961 nua_client_orq_response,
962 nua_client_request_ref(cr),
963 NULL,
964 msg,
965 TAG_IF(proxy_is_set,
966 NTATAG_DEFAULT_PROXY(proxy)),
967 TAG_NEXT(tags));
968
969 if (cr->cr_orq == NULL) {
970 nua_client_request_unref(cr);
971 return -1;
972 }
973
974 return 0;
975 }
976
977 /** Callback for nta client transaction */
978 int
nua_client_orq_response(nua_client_request_t * cr,nta_outgoing_t * orq,sip_t const * sip)979 nua_client_orq_response(nua_client_request_t *cr,
980 nta_outgoing_t *orq,
981 sip_t const *sip)
982 {
983 int status;
984 char const *phrase;
985
986 if (sip && sip->sip_status) {
987 status = sip->sip_status->st_status;
988 phrase = sip->sip_status->st_phrase;
989 }
990 else {
991 status = nta_outgoing_status(orq);
992 phrase = "";
993 }
994
995 nua_client_response(cr, status, phrase, sip);
996
997 return 0;
998 }
999
1000 /**Return response to the client request.
1001 *
1002 * Return a response generated by the stack. This function is used to return
1003 * a error response within @a nua_client_methods_t#crm_init or @a
1004 * nua_client_methods_t#crm_send functions. It takes care of disposing the @a
1005 * to_be_destroyed that cannot be sent.
1006 *
1007 * @retval 0 if response event was preliminary
1008 * @retval 1 if response event was final
1009 * @retval 2 if response event destroyed the handle, too.
1010 */
nua_client_return(nua_client_request_t * cr,int status,char const * phrase,msg_t * to_be_destroyed)1011 int nua_client_return(nua_client_request_t *cr,
1012 int status,
1013 char const *phrase,
1014 msg_t *to_be_destroyed)
1015 {
1016 if (to_be_destroyed)
1017 msg_destroy(to_be_destroyed);
1018 nua_client_response(cr, status, phrase, NULL);
1019 return 1;
1020 }
1021
1022 /** Process response to the client request.
1023 *
1024 * The response can be generated by the stack (@a sip is NULL) or
1025 * returned by the remote server.
1026 *
1027 * @retval 0 if response event was preliminary
1028 * @retval 1 if response event was final
1029 * @retval 2 if response event destroyed the handle, too.
1030 */
nua_client_response(nua_client_request_t * cr,int status,char const * phrase,sip_t const * sip)1031 int nua_client_response(nua_client_request_t *cr,
1032 int status,
1033 char const *phrase,
1034 sip_t const *sip)
1035 {
1036 nua_handle_t *nh = cr->cr_owner;
1037 nua_dialog_usage_t *du = cr->cr_usage;
1038 int retval = 0;
1039
1040 if (cr->cr_restarting)
1041 return 0;
1042
1043 if (cr->cr_status == 200 && status < 200) { // ignore 183 follows 200
1044 return 0;
1045 }
1046
1047 nua_client_request_ref(cr);
1048
1049 cr->cr_status = status;
1050 cr->cr_phrase = phrase;
1051
1052 if (status < 200) {
1053 /* Xyzzy */
1054 }
1055 else if (sip && nua_client_check_restart(cr, status, phrase, sip)) {
1056 nua_client_request_unref(cr);
1057 return 0;
1058 }
1059 else if (status < 300) {
1060 if (cr->cr_terminating) {
1061 cr->cr_terminated = 1;
1062 }
1063 else {
1064 if (sip) {
1065 if (cr->cr_contactize)
1066 nua_dialog_uac_route(nh, nh->nh_ds, sip, 1, cr->cr_initial);
1067 nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
1068 }
1069
1070 if (du && du->du_cr == cr)
1071 du->du_ready = 1;
1072 }
1073 }
1074 else {
1075 sip_method_t method = cr->cr_method;
1076 int terminated, graceful = 1;
1077
1078 if (status < 700) {
1079 terminated = sip_response_terminates_dialog(status, method, &graceful);
1080 if (terminated && !cr->cr_initial) {
1081 terminated = 0, graceful = 1;
1082 }
1083 } else {
1084 /* XXX - terminate usage by all internal error responses */
1085 terminated = 0, graceful = 1;
1086 }
1087
1088 if (terminated < 0)
1089 cr->cr_terminated = terminated;
1090 else if (cr->cr_terminating || terminated)
1091 cr->cr_terminated = 1;
1092 else if (graceful)
1093 cr->cr_graceful = 1;
1094 }
1095
1096 if (status < 200) {
1097 if (cr->cr_methods->crm_preliminary)
1098 cr->cr_methods->crm_preliminary(cr, status, phrase, sip);
1099 else
1100 nua_base_client_response(cr, status, phrase, sip, NULL);
1101 cr->cr_phrase = NULL;
1102 }
1103 else {
1104 if (cr->cr_methods->crm_recv)
1105 retval = cr->cr_methods->crm_recv(cr, status, phrase, sip);
1106 else
1107 retval = nua_base_client_response(cr, status, phrase, sip, NULL);
1108 }
1109
1110 nua_client_request_unref(cr);
1111
1112 return retval;
1113 }
1114
1115 /** Check if request should be restarted.
1116 *
1117 * @retval 1 if restarted or waiting for restart
1118 * @retval 0 otherwise
1119 */
nua_client_check_restart(nua_client_request_t * cr,int status,char const * phrase,sip_t const * sip)1120 int nua_client_check_restart(nua_client_request_t *cr,
1121 int status,
1122 char const *phrase,
1123 sip_t const *sip)
1124 {
1125 nua_handle_t *nh;
1126
1127 assert(cr && status >= 200 && phrase && sip);
1128
1129 nh = cr->cr_owner;
1130
1131 if (cr->cr_retry_count > NH_PGET(nh, retry_count))
1132 return 0;
1133
1134 if (cr->cr_methods->crm_check_restart)
1135 return cr->cr_methods->crm_check_restart(cr, status, phrase, sip);
1136 else
1137 return nua_base_client_check_restart(cr, status, phrase, sip);
1138 }
1139
nua_base_client_check_restart(nua_client_request_t * cr,int status,char const * phrase,sip_t const * sip)1140 int nua_base_client_check_restart(nua_client_request_t *cr,
1141 int status,
1142 char const *phrase,
1143 sip_t const *sip)
1144 {
1145 nua_handle_t *nh = cr->cr_owner;
1146 nta_outgoing_t *orq;
1147 #if 0
1148 if (status == 302 || status == 305) {
1149 sip_route_t r[1];
1150
1151 if (!can_redirect(sip->sip_contact, cr->cr_method))
1152 return 0;
1153
1154 switch (status) {
1155 case 302:
1156 if (nua_dialog_zap(nh, nh->nh_ds) == 0 &&
1157 nua_client_set_target(cr, sip->sip_contact->m_url) >= 0)
1158 return nua_client_restart(cr, 100, "Redirected");
1159 break;
1160
1161 case 305:
1162 sip_route_init(r);
1163 *r->r_url = *sip->sip_contact->m_url;
1164 if (nua_dialog_zap(nh, nh->nh_ds) == 0 &&
1165 sip_add_dup(cr->cr_msg, cr->cr_sip, (sip_header_t *)r) >= 0)
1166 return nua_client_restart(cr, 100, "Redirected via a proxy");
1167 break;
1168
1169 default:
1170 break;
1171 }
1172 }
1173 #endif
1174
1175 if (status == 423) {
1176 unsigned my_expires = 0;
1177
1178 if (cr->cr_sip->sip_expires)
1179 my_expires = cr->cr_sip->sip_expires->ex_delta;
1180
1181 if (sip->sip_min_expires &&
1182 sip->sip_min_expires->me_delta > my_expires) {
1183 sip_expires_t ex[1];
1184 sip_expires_init(ex);
1185 ex->ex_delta = sip->sip_min_expires->me_delta;
1186
1187 if (sip_add_dup(cr->cr_msg, NULL, (sip_header_t *)ex) < 0)
1188 return 0;
1189
1190 return nua_client_restart(cr, 100, "Re-Negotiating Expiration");
1191 }
1192 }
1193
1194 if (status == 403) {
1195 if (nh->nh_auth) {
1196 /* Bad username/password */
1197 SU_DEBUG_7(("nua(%p): bad credentials, clearing them\n", (void *)nh));
1198 auc_clear_credentials(&nh->nh_auth, NULL, NULL);
1199 }
1200 }
1201
1202 if ((status == 401 && sip->sip_www_authenticate) ||
1203 (status == 407 && sip->sip_proxy_authenticate)) {
1204 int server = 0, proxy = 0;
1205
1206 if (sip->sip_www_authenticate)
1207 server = auc_challenge(&nh->nh_auth, nh->nh_home,
1208 sip->sip_www_authenticate,
1209 sip_authorization_class);
1210
1211 if (sip->sip_proxy_authenticate)
1212 proxy = auc_challenge(&nh->nh_auth, nh->nh_home,
1213 sip->sip_proxy_authenticate,
1214 sip_proxy_authorization_class);
1215
1216 if (server >= 0 && proxy >= 0) {
1217 int invalid = cr->cr_challenged && server + proxy == 0;
1218
1219 cr->cr_challenged = 1;
1220
1221 if (invalid) {
1222 /* Bad username/password */
1223 SU_DEBUG_7(("nua(%p): bad credentials, clearing them\n", (void *)nh));
1224 auc_clear_credentials(&nh->nh_auth, NULL, NULL);
1225 } else if (auc_has_authorization(&nh->nh_auth)) {
1226 return nua_client_restart(cr, 100, "Request Authorized by Cache");
1227 }
1228
1229 orq = cr->cr_orq, cr->cr_orq = NULL;
1230
1231 cr->cr_waiting = cr->cr_wait_for_cred = 1;
1232 nua_client_report(cr, status, phrase, NULL, orq, NULL);
1233 nta_outgoing_destroy(orq);
1234 cr->cr_status = 0, cr->cr_phrase = NULL;
1235 nua_client_request_unref(cr);
1236
1237 return 1;
1238 }
1239 }
1240 /* GriGiu : RFC-3261 status supported Retry-After */
1241 if ( (status == 404 || status == 413 || status == 480 || status == 486 ||
1242 status == 500 || status == 503 ||
1243 status == 600 || status == 603) &&
1244 sip->sip_retry_after &&
1245 NH_PGET(nh, retry_after_enable) &&
1246 sip->sip_retry_after->af_delta < 3200) {
1247 su_timer_t *timer;
1248 char phrase[18]; /* Retry After XXXX\0 */
1249
1250 timer = su_timer_create(su_root_task(nh->nh_nua->nua_root), 0);
1251
1252 if (su_timer_set_interval(timer, nua_client_restart_after, cr,
1253 sip->sip_retry_after->af_delta * 1000) < 0) {
1254 su_timer_destroy(timer);
1255 return 0; /* Too bad */
1256 }
1257
1258 cr->cr_timer = timer; /* This takes over cr reference from orq */
1259
1260 snprintf(phrase, sizeof phrase, "Retry After %u",
1261 (unsigned)sip->sip_retry_after->af_delta);
1262
1263 orq = cr->cr_orq, cr->cr_orq = NULL;
1264 cr->cr_waiting = 1;
1265 nua_client_report(cr, 100, phrase, NULL, orq, NULL);
1266 nta_outgoing_destroy(orq);
1267 cr->cr_status = 0, cr->cr_phrase = NULL;
1268
1269 return 1;
1270 }
1271
1272 return 0; /* This was a final response that cannot be restarted. */
1273 }
1274
1275 #if 0
1276 su_inline
1277 int can_redirect(sip_contact_t const *m, sip_method_t method)
1278 {
1279 if (m && m->m_url->url_host) {
1280 enum url_type_e type = (enum url_type_e)m->m_url->url_type;
1281 return
1282 type == url_sip ||
1283 type == url_sips ||
1284 (type == url_tel &&
1285 (method == sip_method_invite || method == sip_method_message)) ||
1286 (type == url_im && method == sip_method_message) ||
1287 (type == url_pres && method == sip_method_subscribe);
1288 }
1289 return 0;
1290 }
1291 #endif
1292
1293 /** @internal Add authorization data */
nh_authorize(nua_handle_t * nh,tag_type_t tag,tag_value_t value,...)1294 static int nh_authorize(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
1295 {
1296 int retval = 0;
1297 tagi_t const *ti;
1298 ta_list ta;
1299
1300 ta_start(ta, tag, value);
1301
1302 for (ti = ta_args(ta); ti; ti = tl_next(ti)) {
1303 if (ti->t_tag == nutag_auth && ti->t_value) {
1304 char *data = (char *)ti->t_value;
1305 int rv = auc_credentials(&nh->nh_auth, nh->nh_home, data);
1306
1307 if (rv > 0) {
1308 retval = 1;
1309 }
1310 else if (rv < 0) {
1311 retval = -1;
1312 break;
1313 }
1314 }
1315 }
1316
1317 ta_end(ta);
1318
1319 return retval;
1320 }
1321
1322 /** @NUA_EVENT nua_r_authenticate
1323 *
1324 * Response to nua_authenticate(). Under normal operation, this event is
1325 * never sent but rather the unauthenticated operation is completed.
1326 * However, if there is no operation to authentication or if there is an
1327 * authentication error the #nua_r_authenticate event is sent to the
1328 * application with the status code as follows:
1329 * - <i>202 No operation to restart</i>:\n
1330 * The authenticator associated with the handle was updated, but there was
1331 * no operation to retry with the new credentials.
1332 * - <i>900 Cannot add credentials</i>:\n
1333 * There was internal problem updating authenticator.
1334 * - <i>904 No matching challenge</i>:\n
1335 * There was no challenge matching with the credentials provided by
1336 * nua_authenticate(), e.g., their realm did not match with the one
1337 * received with the challenge.
1338 *
1339 * @param status status code from authentication
1340 * @param phrase a short textual description of @a status code
1341 * @param nh operation handle authenticated
1342 * @param hmagic application context associated with the handle
1343 * @param sip NULL
1344 * @param tags empty
1345 *
1346 * @sa nua_terminate(), nua_handle_destroy()
1347 *
1348 * @END_NUA_EVENT
1349 */
1350
1351 void
nua_stack_authenticate(nua_t * nua,nua_handle_t * nh,nua_event_t e,tagi_t const * tags)1352 nua_stack_authenticate(nua_t *nua, nua_handle_t *nh, nua_event_t e,
1353 tagi_t const *tags)
1354 {
1355 nua_client_request_t *cr = nh->nh_ds->ds_cr;
1356 int status = nh_authorize(nh, TAG_NEXT(tags));
1357
1358 if (status > 0) {
1359 if (cr && cr->cr_wait_for_cred) {
1360 cr->cr_waiting = cr->cr_wait_for_cred = 0;
1361 nua_client_restart_request(cr, cr->cr_terminating, tags);
1362 }
1363 else {
1364 nua_stack_event(nua, nh, NULL, e,
1365 202, "No operation to restart",
1366 NULL);
1367 }
1368 }
1369 else if (cr && cr->cr_wait_for_cred) {
1370 cr->cr_waiting = cr->cr_wait_for_cred = 0;
1371
1372 if (status < 0)
1373 nua_client_response(cr, 900, "Operation cannot add credentials", NULL);
1374 else
1375 nua_client_response(cr, 904, "Operation has no matching challenge ", NULL);
1376 }
1377 else if (status < 0) {
1378 nua_stack_event(nua, nh, NULL, e, 900, "Cannot add credentials", NULL);
1379 }
1380 else {
1381 nua_stack_event(nua, nh, NULL, e, 904, "No matching challenge", NULL);
1382 }
1383 }
1384
1385 /** Request restarted by timer */
1386 static void
nua_client_restart_after(su_root_magic_t * magic,su_timer_t * timer,nua_client_request_t * cr)1387 nua_client_restart_after(su_root_magic_t *magic,
1388 su_timer_t *timer,
1389 nua_client_request_t *cr)
1390 {
1391 cr->cr_waiting = 0;
1392 su_timer_destroy(cr->cr_timer), cr->cr_timer = NULL;
1393 nua_client_restart_request(cr, cr->cr_terminating, NULL);
1394 nua_client_request_unref(cr);
1395 }
1396
1397 /** Restart request.
1398 *
1399 * @retval 1 if restarted
1400 * @retval 0 otherwise
1401 */
nua_client_restart(nua_client_request_t * cr,int status,char const * phrase)1402 int nua_client_restart(nua_client_request_t *cr,
1403 int status, char const *phrase)
1404 {
1405 nua_handle_t *nh = cr->cr_owner;
1406 nta_outgoing_t *orq;
1407 int error = -1, terminated, graceful;
1408
1409 if (cr->cr_retry_count > NH_PGET(nh, retry_count))
1410 return 0;
1411
1412 orq = cr->cr_orq, cr->cr_orq = NULL; assert(orq);
1413 terminated = cr->cr_terminated, cr->cr_terminated = 0;
1414 graceful = cr->cr_graceful, cr->cr_graceful = 0;
1415
1416 cr->cr_restarting = 1;
1417 error = nua_client_request_sendmsg(cr);
1418 cr->cr_restarting = 0;
1419
1420 if (error) {
1421 cr->cr_graceful = graceful;
1422 cr->cr_terminated = terminated;
1423 assert(cr->cr_orq == NULL);
1424 cr->cr_orq = orq;
1425 return 0;
1426 }
1427
1428 nua_client_report(cr, status, phrase, NULL, orq, NULL);
1429
1430 nta_outgoing_destroy(orq);
1431 nua_client_request_unref(cr); /* ... reference used by old orq */
1432
1433 return 1;
1434 }
1435
nua_client_set_target(nua_client_request_t * cr,url_t const * target)1436 int nua_client_set_target(nua_client_request_t *cr, url_t const *target)
1437 {
1438 url_t *new_target, *old_target = cr->cr_target;
1439
1440 if (!target || target == old_target)
1441 return 0;
1442
1443 new_target = url_hdup(cr->cr_owner->nh_home, (url_t *)target);
1444 if (!new_target)
1445 return -1;
1446 cr->cr_target = new_target;
1447 if (old_target)
1448 su_free(cr->cr_owner->nh_home, old_target);
1449
1450 return 0;
1451 }
1452
1453 /**@internal
1454 * Relay response event to the application.
1455 *
1456 * @todo
1457 * If handle has already been marked as destroyed by nua_handle_destroy(),
1458 * release the handle with nh_destroy().
1459 *
1460 * @retval 0 if event was preliminary
1461 * @retval 1 if event was final
1462 * @retval 2 if event destroyed the handle, too.
1463 */
nua_base_client_tresponse(nua_client_request_t * cr,int status,char const * phrase,sip_t const * sip,tag_type_t tag,tag_value_t value,...)1464 int nua_base_client_tresponse(nua_client_request_t *cr,
1465 int status, char const *phrase,
1466 sip_t const *sip,
1467 tag_type_t tag, tag_value_t value, ...)
1468 {
1469 ta_list ta;
1470 int retval;
1471
1472 if (cr->cr_event == nua_r_destroy)
1473 return nua_base_client_response(cr, status, phrase, sip, NULL);
1474
1475 ta_start(ta, tag, value);
1476 retval = nua_base_client_response(cr, status, phrase, sip, ta_args(ta));
1477 ta_end(ta);
1478
1479 return retval;
1480 }
1481
1482 /**@internal
1483 * Relay response event to the application.
1484 *
1485 * @todo
1486 * If handle has already been marked as destroyed by nua_handle_destroy(),
1487 * release the handle with nh_destroy().
1488 *
1489 * @retval 0 if event was preliminary
1490 * @retval 1 if event was final
1491 * @retval 2 if event destroyed the handle, too.
1492 */
nua_base_client_response(nua_client_request_t * cr,int status,char const * phrase,sip_t const * sip,tagi_t const * tags)1493 int nua_base_client_response(nua_client_request_t *cr,
1494 int status, char const *phrase,
1495 sip_t const *sip,
1496 tagi_t const *tags)
1497 {
1498 nua_handle_t *nh = cr->cr_owner;
1499 sip_method_t method = cr->cr_method;
1500 nua_dialog_usage_t *du;
1501
1502 cr->cr_reporting = 1, nh->nh_ds->ds_reporting = 1;
1503
1504 if (nh->nh_auth && sip &&
1505 (sip->sip_authentication_info || sip->sip_proxy_authentication_info)) {
1506 /* Collect nextnonce */
1507 if (sip->sip_authentication_info)
1508 auc_info(&nh->nh_auth,
1509 sip->sip_authentication_info,
1510 sip_authorization_class);
1511 if (sip->sip_proxy_authentication_info)
1512 auc_info(&nh->nh_auth,
1513 sip->sip_proxy_authentication_info,
1514 sip_proxy_authorization_class);
1515 }
1516
1517 if ((method != sip_method_invite && status >= 200) || status >= 300)
1518 nua_client_request_remove(cr);
1519
1520 nua_client_report(cr, status, phrase, sip, cr->cr_orq, tags);
1521
1522 if (status < 200 ||
1523 /* Un-ACKed 2XX response to INVITE */
1524 (method == sip_method_invite && status < 300 && !cr->cr_acked)) {
1525 cr->cr_reporting = 0, nh->nh_ds->ds_reporting = 0;
1526 return 1;
1527 }
1528
1529 nua_client_request_clean(cr);
1530
1531 du = cr->cr_usage;
1532
1533 if (cr->cr_terminated < 0) {
1534 /* XXX - dialog has been terminated */;
1535 nua_dialog_deinit(nh, nh->nh_ds), cr->cr_usage = NULL;
1536 }
1537 else if (du) {
1538 if (cr->cr_terminated ||
1539 (!du->du_ready && status >= 300 && nua_client_is_bound(cr))) {
1540 /* Usage has been destroyed */
1541 nua_dialog_usage_remove(nh, nh->nh_ds, du, cr, NULL), cr->cr_usage = NULL;
1542 }
1543 else if (cr->cr_graceful) {
1544 /* Terminate usage gracefully */
1545 if (nua_dialog_usage_shutdown(nh, nh->nh_ds, du) > 0)
1546 cr->cr_usage = NULL;
1547 }
1548 }
1549 else if (cr->cr_terminated) {
1550 if (nh->nh_ds->ds_usage == NULL)
1551 nua_dialog_remove(nh, nh->nh_ds, NULL), cr->cr_usage = NULL;
1552 }
1553
1554 cr->cr_phrase = NULL;
1555 cr->cr_reporting = 0, nh->nh_ds->ds_reporting = 0;
1556
1557 if (method == sip_method_cancel)
1558 return 1;
1559
1560 return nua_client_next_request(nh->nh_ds->ds_cr, method == sip_method_invite);
1561 }
1562
1563 /** Send event, zap transaction but leave cr in list */
nua_client_report(nua_client_request_t * cr,int status,char const * phrase,sip_t const * sip,nta_outgoing_t * orq,tagi_t const * tags)1564 int nua_client_report(nua_client_request_t *cr,
1565 int status, char const *phrase,
1566 sip_t const *sip,
1567 nta_outgoing_t *orq,
1568 tagi_t const *tags)
1569 {
1570 nua_handle_t *nh;
1571
1572 if (cr->cr_event == nua_r_destroy)
1573 return 1;
1574
1575 if (cr->cr_methods->crm_report)
1576 return cr->cr_methods->crm_report(cr, status, phrase, sip, orq, tags);
1577
1578 nh = cr->cr_owner;
1579
1580 nua_stack_event(nh->nh_nua, nh,
1581 nta_outgoing_getresponse(orq),
1582 (enum nua_event_e)cr->cr_event,
1583 status, phrase,
1584 tags);
1585 return 1;
1586 }
1587
nua_client_treport(nua_client_request_t * cr,int status,char const * phrase,sip_t const * sip,nta_outgoing_t * orq,tag_type_t tag,tag_value_t value,...)1588 int nua_client_treport(nua_client_request_t *cr,
1589 int status, char const *phrase,
1590 sip_t const *sip,
1591 nta_outgoing_t *orq,
1592 tag_type_t tag, tag_value_t value, ...)
1593 {
1594 int retval;
1595 ta_list ta;
1596 ta_start(ta, tag, value);
1597 retval = nua_client_report(cr, status, phrase, sip, orq, ta_args(ta));
1598 ta_end(ta);
1599 return retval;
1600 }
1601
nua_client_next_request(nua_client_request_t * cr,int invite)1602 int nua_client_next_request(nua_client_request_t *cr, int invite)
1603 {
1604 for (; cr; cr = cr->cr_next) {
1605 if (cr->cr_method == sip_method_cancel)
1606 continue;
1607 break;
1608 }
1609
1610 if (cr && !nua_client_request_in_progress(cr)) {
1611 nua_client_init_request(cr);
1612 }
1613
1614 return 1;
1615 }
1616
1617 nua_client_request_t *
nua_client_request_pending(nua_client_request_t const * cr)1618 nua_client_request_pending(nua_client_request_t const *cr)
1619 {
1620 for (;cr;cr = cr->cr_next)
1621 if (cr->cr_orq)
1622 return (nua_client_request_t *)cr;
1623
1624 return NULL;
1625 }
1626
1627 /**@internal
1628 * Save handle parameters and initial authentication info.
1629 *
1630 * @retval -1 upon an error
1631 * @retval 0 when successful
1632 */
nua_stack_init_handle(nua_t * nua,nua_handle_t * nh,tagi_t const * tags)1633 int nua_stack_init_handle(nua_t *nua, nua_handle_t *nh, tagi_t const *tags)
1634 {
1635 int retval = 0;
1636
1637 if (nh == NULL)
1638 return -1;
1639
1640 assert(nh != nua->nua_dhandle);
1641
1642 if (nua_stack_set_params(nua, nh, nua_i_error, tags) < 0)
1643 retval = -1;
1644
1645 if (retval || nh->nh_init) /* Already initialized? */
1646 return retval;
1647
1648 if (nh->nh_tags)
1649 nh_authorize(nh, TAG_NEXT(nh->nh_tags));
1650
1651 nh->nh_init = 1;
1652
1653 return 0;
1654 }
1655