1 /*
2  * This file is part of the Sofia-SIP package
3  *
4  * Copyright (C) 2005 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 soa.c
26  * @brief Sofia SDP Offer/Answer Engine interface
27  *
28  * @author Pekka Pessi <Pekka.Pessi@nokia.com>
29  *
30  * @date Created: Wed Aug  3 20:27:15 EEST 2005
31  */
32 
33 #include "config.h"
34 
35 #include <stddef.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39 
40 #include <assert.h>
41 
42 #include <sofia-sip/su_tag_class.h>
43 #include <sofia-sip/su_wait.h>
44 
45 #include "sofia-sip/soa.h"
46 #include "sofia-sip/sdp.h"
47 #include "sofia-sip/soa_session.h"
48 #include "sofia-sip/soa_add.h"
49 
50 #include <sofia-sip/hostdomain.h>
51 #include <sofia-sip/bnf.h>
52 #include <sofia-sip/su_tagarg.h>
53 #include <sofia-sip/su_localinfo.h>
54 #include <sofia-sip/su_uniqueid.h>
55 
56 #include <sofia-sip/su_string.h>
57 #include <sofia-sip/su_errno.h>
58 
59 #ifndef _MSC_VER
60 #define NONE ((void *)-1)
61 #else
62 #define NONE ((void *)(INT_PTR)-1)
63 #endif
64 #define XXX assert(!"implemented")
65 
66 typedef unsigned longlong ull;
67 
68 #if HAVE_FUNC
69 #elif HAVE_FUNCTION
70 #define __func__ __FUNCTION__
71 #else
72 static char const __func__[] = "soa";
73 #endif
74 
75 /* ======================================================================== */
76 
77 /* Internal prototypes */
78 enum soa_sdp_kind {
79   soa_capability_sdp_kind,
80   soa_user_sdp_kind,
81   soa_remote_sdp_kind
82 };
83 
84 static int soa_set_sdp(soa_session_t *ss,
85 		       enum soa_sdp_kind what,
86 		       sdp_session_t const *sdp0,
87 		       char const *sdp_str, issize_t str_len);
88 
89 /* ======================================================================== */
90 
91 #define SOA_VALID_ACTIONS(a)					\
92   ((a)->sizeof_soa_session_actions >= (int)sizeof (*actions) && \
93    (a)->sizeof_soa_session >= (int)sizeof(soa_session_t) &&     \
94    (a)->soa_name != NULL &&					\
95    (a)->soa_init != NULL &&					\
96    (a)->soa_deinit != NULL &&					\
97    (a)->soa_set_params != NULL &&				\
98    (a)->soa_get_params != NULL &&				\
99    (a)->soa_get_paramlist != NULL &&				\
100    (a)->soa_media_features != NULL &&				\
101    (a)->soa_sip_require != NULL &&				\
102    (a)->soa_sip_supported != NULL &&				\
103    (a)->soa_remote_sip_features != NULL &&			\
104    (a)->soa_set_capability_sdp != NULL &&			\
105    (a)->soa_set_remote_sdp != NULL &&				\
106    (a)->soa_set_user_sdp != NULL &&				\
107    (a)->soa_generate_offer != NULL &&				\
108    (a)->soa_generate_answer != NULL &&				\
109    (a)->soa_process_answer != NULL &&				\
110    (a)->soa_process_reject != NULL &&				\
111    (a)->soa_activate_session != NULL &&				\
112    (a)->soa_deactivate_session != NULL &&			\
113    (a)->soa_terminate_session != NULL)
114 
115 /* ======================================================================== */
116 
117 /**@var SOA_DEBUG
118  *
119  * Environment variable determining the default debug log level.
120  *
121  * The SOA_DEBUG environment variable is used to determine the default
122  * debug logging level. The normal level is 3.
123  *
124  * @sa <sofia-sip/su_debug.h>, su_log_global, SOFIA_DEBUG
125  */
126 extern char const SOA_DEBUG[];
127 
128 #ifndef SU_DEBUG
129 #define SU_DEBUG 3
130 #endif
131 
132 /**Debug log for @b soa module.
133  *
134  * The soa_log is the log object used by @b soa module. The level of
135  * #soa_log is set using #SOA_DEBUG environment variable.
136  */
137 su_log_t soa_log[] = { SU_LOG_INIT("soa", "SOA_DEBUG", SU_DEBUG) };
138 
139 /* Add " around string */
140 #define NICE(s) s ? "\"" : "", s ? s : "(nil)", s ? "\"" : ""
141 
142 /* ======================================================================== */
143 
144 /* API Functions */
145 
146 struct soa_namenode
147 {
148   struct soa_namenode const *next;
149   char const *basename;
150   struct soa_session_actions const *actions;
151 };
152 
153 #define SOA_NAMELISTLEN (16)
154 
155 static struct soa_namenode const soa_default_node =
156   {
157     NULL, "default", &soa_default_actions
158   };
159 
160 static struct soa_namenode const *soa_namelist = &soa_default_node;
161 
162 /** Add a named soa backend */
soa_add(char const * name,struct soa_session_actions const * actions)163 int soa_add(char const *name,
164 	    struct soa_session_actions const *actions)
165 {
166   struct soa_namenode const *n;
167   struct soa_namenode *e;
168 
169   SU_DEBUG_9(("soa_add(%s%s%s, %p) called\n", NICE(name), (void *)actions));
170 
171   if (name == NULL || actions == NULL)
172     return su_seterrno(EFAULT);
173 
174   if (!SOA_VALID_ACTIONS(actions))
175     return su_seterrno(EINVAL);
176 
177   for (n = soa_namelist; n; n = n->next) {
178     if (su_casematch(name, n->basename))
179       return 0;
180   }
181 
182   e = malloc(sizeof *e); if (!e) return -1;
183 
184   e->next = soa_namelist;
185   e->basename = name;
186   e->actions = actions;
187 
188   soa_namelist = e;
189 
190   return 0;
191 }
192 
193 /** Search for a named backend */
soa_find(char const * name)194 struct soa_session_actions const *soa_find(char const *name)
195 {
196   SU_DEBUG_9(("soa_find(%s%s%s) called\n", NICE(name)));
197 
198   if (name) {
199     struct soa_namenode const *n;
200     size_t baselen = strcspn(name, ":/");
201 
202     for (n = soa_namelist; n; n = n->next) {
203       if (su_casenmatch(name, n->basename, baselen))
204 	break;
205     }
206 
207     if (n == NULL)
208       return (void)su_seterrno(ENOENT), NULL;
209 
210     return n->actions;
211   }
212 
213   return NULL;
214 }
215 
216 /* ======================================================================== */
217 
218 /** Create a soa session. */
soa_create(char const * name,su_root_t * root,soa_magic_t * magic)219 soa_session_t *soa_create(char const *name,
220 			  su_root_t *root,
221 			  soa_magic_t *magic)
222 {
223   struct soa_session_actions const *actions = &soa_default_actions;
224 
225   soa_session_t *ss;
226   size_t namelen;
227 
228   SU_DEBUG_9(("soa_create(\"%s\", %p, %p) called\n",
229 	      name ? name : "default", (void *)root, (void *)magic));
230 
231   if (name && name[0]) {
232     struct soa_namenode const *n;
233     size_t baselen = strcspn(name, ":/");
234 
235     for (n = soa_namelist; n; n = n->next) {
236       if (su_casenmatch(name, n->basename, baselen))
237 	break;
238     }
239     if (n == NULL)
240       return (void)su_seterrno(ENOENT), NULL;
241 
242     actions = n->actions; assert(actions);
243   }
244   else
245     name = "default";
246 
247   assert(SOA_VALID_ACTIONS(actions));
248 
249   if (root == NULL)
250     return (void)su_seterrno(EFAULT), NULL;
251 
252   namelen = strlen(name) + 1;
253 
254   ss = su_home_new(actions->sizeof_soa_session + namelen);
255   if (ss) {
256     ss->ss_root = root;
257     ss->ss_magic = magic;
258     ss->ss_actions = actions;
259     ss->ss_name = strcpy((char *)ss + actions->sizeof_soa_session, name);
260 
261     /* Calls soa_static_init by default */
262     if (ss->ss_actions->soa_init(name, ss, NULL) < 0)
263       /* Calls soa_static_deinit by default */
264       ss->ss_actions->soa_deinit(ss), ss = NULL;
265   }
266 
267   return ss;
268 }
269 
270 /** Create a copy of a @soa session object. */
soa_clone(soa_session_t * parent_ss,su_root_t * root,soa_magic_t * magic)271 soa_session_t *soa_clone(soa_session_t *parent_ss,
272 			 su_root_t *root,
273 			 soa_magic_t *magic)
274 {
275   soa_session_t *ss;
276   size_t namelen;
277 
278   SU_DEBUG_9(("soa_clone(%s::%p, %p, %p) called\n",
279 	      parent_ss ? parent_ss->ss_actions->soa_name : "",
280 	      (void *)parent_ss, (void *)root, (void *)magic));
281 
282   if (parent_ss == NULL || root == NULL)
283     return (void)su_seterrno(EFAULT), NULL;
284 
285   namelen = strlen(parent_ss->ss_name) + 1;
286 
287   ss = su_home_new(parent_ss->ss_actions->sizeof_soa_session + namelen);
288   if (ss) {
289     ss->ss_root = root;
290     ss->ss_magic = magic;
291     ss->ss_actions = parent_ss->ss_actions;
292     ss->ss_name = strcpy((char *)ss + ss->ss_actions->sizeof_soa_session,
293 			 parent_ss->ss_name);
294 
295     /* Calls soa_static_init by default */
296     if (ss->ss_actions->soa_init(NULL, ss, parent_ss) < 0)
297       /* Calls soa_static_deinit by default */
298       ss->ss_actions->soa_deinit(ss), ss = NULL;
299   }
300 
301   return ss;
302 }
303 
304 /** Increase reference count */
soa_session_ref(soa_session_t * ss)305 soa_session_t *soa_session_ref(soa_session_t *ss)
306 {
307   SU_DEBUG_9(("soa_session_ref(%s::%p) called\n",
308 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
309   return su_home_ref(ss->ss_home);
310 }
311 
312 /** Decrease reference count */
soa_session_unref(soa_session_t * ss)313 void soa_session_unref(soa_session_t *ss)
314 {
315   SU_DEBUG_9(("soa_session_unref(%s::%p) called\n",
316 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
317   su_home_unref(ss->ss_home);
318 }
319 
320 /* Initialize session */
soa_base_init(char const * name,soa_session_t * ss,soa_session_t * parent)321 int soa_base_init(char const *name,
322 		     soa_session_t *ss,
323 		     soa_session_t *parent)
324 {
325   if (parent) {
326 #define DUP(d, dup, s) if ((s) && !((d) = dup(ss->ss_home, (s)))) return -1
327     su_home_t *home = ss->ss_home;
328 
329     if (soa_description_dup(home, ss->ss_caps, parent->ss_caps) < 0)
330       return -1;
331     if (soa_description_dup(home, ss->ss_user, parent->ss_user) < 0)
332       return -1;
333     if (soa_description_dup(home, ss->ss_local, parent->ss_local) < 0)
334       return -1;
335     if (soa_description_dup(home, ss->ss_remote, parent->ss_remote) < 0)
336       return -1;
337 
338     DUP(ss->ss_address, su_strdup, parent->ss_address);
339     ss->ss_af = parent->ss_af;
340     DUP(ss->ss_hold, su_strdup, parent->ss_hold);
341 
342     DUP(ss->ss_cname, su_strdup, parent->ss_cname);
343 
344     ss->ss_srtp_enable = parent->ss_srtp_enable;
345     ss->ss_srtp_confidentiality = parent->ss_srtp_confidentiality;
346     ss->ss_srtp_integrity = parent->ss_srtp_integrity;
347   }
348 
349   return 0;
350 }
351 
352 /** Destroy a session. */
soa_destroy(soa_session_t * ss)353 void soa_destroy(soa_session_t *ss)
354 {
355   SU_DEBUG_9(("soa_destroy(%s::%p) called\n",
356 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
357 
358   if (ss) {
359     ss->ss_active = 0;
360     ss->ss_terminated++;
361     /* Calls soa_static_deinit() by default. */
362     ss->ss_actions->soa_deinit(ss);
363     su_home_unref(ss->ss_home);
364   }
365 }
366 
soa_base_deinit(soa_session_t * ss)367 void soa_base_deinit(soa_session_t *ss)
368 {
369   (void)ss;
370 }
371 
372 /** Set parameters.
373  *
374  * @param ss soa session object
375  * @param tag, value, ... tagged parameter list
376  *
377  * @return Number of parameters set, or -1 upon an error.
378  *
379  * @TAGS
380  * SOATAG_CAPS_SDP(),
381  * SOATAG_CAPS_SDP_STR(),
382  * SOATAG_USER_SDP(),
383  * SOATAG_USER_SDP_STR(),
384  * SOATAG_REMOTE_SDP(),
385  * SOATAG_REMOTE_SDP_STR(),
386  * SOATAG_AF(),
387  * SOATAG_ADDRESS(),
388  * SOATAG_AUDIO_AUX() (currently for "default" only),
389  * SOATAG_HOLD(),
390  * SOATAG_RTP_SELECT(),
391  * SOATAG_RTP_SORT(),
392  * SOATAG_RTP_MISMATCH(),
393  * SOATAG_SRTP_ENABLE(),
394  * SOATAG_SRTP_CONFIDENTIALITY(), and
395  * SOATAG_SRTP_INTEGRITY().
396  */
soa_set_params(soa_session_t * ss,tag_type_t tag,tag_value_t value,...)397 int soa_set_params(soa_session_t *ss, tag_type_t tag, tag_value_t value, ...)
398 {
399   ta_list ta;
400   int n;
401 
402   SU_DEBUG_9(("soa_set_params(%s::%p, ...) called\n",
403 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
404 
405   if (ss == NULL)
406     return su_seterrno(EFAULT), -1;
407 
408   ta_start(ta, tag, value);
409 
410   /* Calls soa_static_set_params() by default. */
411   n = ss->ss_actions->soa_set_params(ss, ta_args(ta));
412 
413   ta_end(ta);
414 
415   return n;
416 }
417 
418 /**Base method for setting parameters.
419  *
420  * @param ss   soa session object
421  * @param tags  tag item list
422  *
423  * @return Number of parameters set, or -1 upon an error.
424  *
425  * @TAGS
426  * SOATAG_CAPS_SDP(),
427  * SOATAG_CAPS_SDP_STR(),
428  * SOATAG_USER_SDP(),
429  * SOATAG_USER_SDP_STR(),
430  * SOATAG_REMOTE_SDP(),
431  * SOATAG_REMOTE_SDP_STR(),
432  * SOATAG_AF(),
433  * SOATAG_ADDRESS(),
434  * SOATAG_HOLD(),
435  * SOATAG_RTP_SELECT(),
436  * SOATAG_RTP_SORT(),
437  * SOATAG_RTP_MISMATCH(),
438  * SOATAG_SRTP_ENABLE(),
439  * SOATAG_SRTP_CONFIDENTIALITY(), and
440  * SOATAG_SRTP_INTEGRITY().
441  */
soa_base_set_params(soa_session_t * ss,tagi_t const * tags)442 int soa_base_set_params(soa_session_t *ss, tagi_t const *tags)
443 {
444   int n, change_session = 0;
445 
446   sdp_session_t const *caps_sdp, *user_sdp;
447   char const *caps_sdp_str, *user_sdp_str;
448 
449   int af;
450   char const *media_address, *hold;
451   int rtp_select, rtp_sort;
452   int rtp_mismatch;
453   int srtp_enable, srtp_confidentiality, srtp_integrity;
454 
455   af = ss->ss_af;
456 
457   hold = ss->ss_hold;
458   media_address = ss->ss_address;
459 
460   rtp_select = (int)ss->ss_rtp_select;
461   rtp_sort = (int)ss->ss_rtp_sort;
462   rtp_mismatch = ss->ss_rtp_mismatch;
463 
464   srtp_enable = ss->ss_srtp_enable;
465   srtp_confidentiality = ss->ss_srtp_confidentiality;
466   srtp_integrity = ss->ss_srtp_integrity;
467 
468   caps_sdp = user_sdp = NONE;
469   caps_sdp_str = user_sdp_str = NONE;
470 
471   n = tl_gets(tags,
472 
473 	      SOATAG_CAPS_SDP_REF(caps_sdp),
474 	      SOATAG_CAPS_SDP_STR_REF(caps_sdp_str),
475 
476 	      SOATAG_USER_SDP_REF(user_sdp),
477 	      SOATAG_USER_SDP_STR_REF(user_sdp_str),
478 
479 	      SOATAG_AF_REF(af),
480 	      SOATAG_ADDRESS_REF(media_address),
481 	      SOATAG_HOLD_REF(hold),
482 
483 	      SOATAG_RTP_SELECT_REF(rtp_select),
484 	      SOATAG_RTP_SORT_REF(rtp_sort),
485 	      SOATAG_RTP_MISMATCH_REF(rtp_mismatch),
486 
487 	      SOATAG_SRTP_ENABLE_REF(srtp_enable),
488 	      SOATAG_SRTP_CONFIDENTIALITY_REF(srtp_confidentiality),
489 	      SOATAG_SRTP_INTEGRITY_REF(srtp_integrity),
490 
491 	      TAG_END());
492 
493   if (n <= 0)
494     return n;
495 
496   if (caps_sdp != NONE || caps_sdp_str != NONE) {
497     if (caps_sdp == NONE) caps_sdp = NULL;
498     if (caps_sdp_str == NONE) caps_sdp_str = NULL;
499 
500     if (caps_sdp || caps_sdp_str) {
501       if (soa_set_capability_sdp(ss, caps_sdp, caps_sdp_str, -1) < 0) {
502 	return -1;
503       }
504     }
505     else {
506       soa_description_free(ss, ss->ss_caps);
507     }
508   }
509 
510   if (user_sdp != NONE || user_sdp_str != NONE) {
511     if (user_sdp == NONE) user_sdp = NULL;
512     if (user_sdp_str == NONE) user_sdp_str = NULL;
513 
514     if (user_sdp || user_sdp_str) {
515       if (soa_set_user_sdp(ss, user_sdp, user_sdp_str, -1) < 0) {
516 	return -1;
517       }
518       if (ss->ss_caps->ssd_str == NULL)
519 	soa_set_capability_sdp(ss, user_sdp, user_sdp_str, -1);
520     }
521     else {
522       soa_description_free(ss, ss->ss_user);
523     }
524   }
525 
526   if (af < SOA_AF_ANY || af > SOA_AF_IP6_IP4)
527     af = ss->ss_af;
528 
529   if (rtp_select < SOA_RTP_SELECT_SINGLE || rtp_select > SOA_RTP_SELECT_ALL)
530     rtp_select = (int)ss->ss_rtp_select;
531   if (rtp_sort < SOA_RTP_SORT_DEFAULT || rtp_sort > SOA_RTP_SORT_REMOTE)
532     rtp_sort = (int)ss->ss_rtp_sort;
533   rtp_mismatch = rtp_mismatch != 0;
534 
535   srtp_enable = srtp_enable != 0;
536   srtp_confidentiality = srtp_confidentiality != 0;
537   srtp_integrity = srtp_integrity != 0;
538 
539   change_session
540     =  af != (int)ss->ss_af
541     || rtp_select != (int)ss->ss_rtp_select
542     || rtp_sort != (int)ss->ss_rtp_sort
543     || rtp_mismatch != (int)ss->ss_rtp_mismatch
544     || srtp_enable != (int)ss->ss_srtp_enable
545     || srtp_confidentiality != (int)ss->ss_srtp_confidentiality
546     || srtp_integrity != (int)ss->ss_srtp_integrity
547     ;
548 
549   ss->ss_af = (enum soa_af)af;
550 
551   ss->ss_rtp_select = rtp_select;
552   ss->ss_rtp_sort = rtp_sort;
553   ss->ss_rtp_mismatch = rtp_mismatch;
554 
555   ss->ss_srtp_enable = srtp_enable;
556   ss->ss_srtp_confidentiality = srtp_confidentiality;
557   ss->ss_srtp_integrity = srtp_integrity;
558 
559   if (!su_casematch(media_address, ss->ss_address)) {
560     char const *addr = ss->ss_address;
561     ss->ss_address = su_strdup(ss->ss_home, media_address);
562     su_free(ss->ss_home, (void *)addr);
563     change_session = 1;
564   }
565 
566   if (hold == (char const *)1)
567     hold = "*";
568 
569   if (!su_casematch(hold, ss->ss_hold)) {
570     char const *h = ss->ss_hold;
571     ss->ss_hold = su_strdup(ss->ss_home, hold);
572     su_free(ss->ss_home, (void *)h);
573     change_session = 1;
574   }
575 
576   if (change_session)
577     ss->ss_user_version++;
578 
579   return n;
580 }
581 
582 /** Get tagged parameters.
583  *
584  * @param ss soa session object
585  * @param tag, value, ... tagged parameter list
586  *
587  * @return Number of parameters get, or -1 upon an error.
588  *
589  * @TAGS
590  * SOATAG_CAPS_SDP(),
591  * SOATAG_CAPS_SDP_STR(),
592  * SOATAG_USER_SDP(),
593  * SOATAG_USER_SDP_STR(),
594  * SOATAG_LOCAL_SDP(),
595  * SOATAG_LOCAL_SDP_STR(),
596  * SOATAG_REMOTE_SDP(),
597  * SOATAG_REMOTE_SDP_STR(),
598  * SOATAG_AF(),
599  * SOATAG_ADDRESS(),
600  * SOATAG_AUDIO_AUX() (currently for "default" only),
601  * SOATAG_HOLD(),
602  * SOATAG_RTP_SELECT(),
603  * SOATAG_RTP_SORT(),
604  * SOATAG_RTP_MISMATCH(),
605  * SOATAG_SRTP_ENABLE(),
606  * SOATAG_SRTP_CONFIDENTIALITY(), and
607  * SOATAG_SRTP_INTEGRITY().
608  */
soa_get_params(soa_session_t const * ss,tag_type_t tag,tag_value_t value,...)609 int soa_get_params(soa_session_t const *ss,
610 		   tag_type_t tag, tag_value_t value, ...)
611 {
612   ta_list ta;
613   int n;
614 
615   SU_DEBUG_9(("soa_get_params(%s::%p, ...) called\n",
616 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
617 
618   if (ss == NULL)
619     return su_seterrno(EFAULT), -1;
620 
621   ta_start(ta, tag, value);
622 
623   /* Calls soa_static_get_params() by default. */
624   n = ss->ss_actions->soa_get_params(ss, ta_args(ta));
625 
626   ta_end(ta);
627 
628   return n;
629 }
630 
631 /**Base method for getting tagged parameters.
632  *
633  * @param ss soa session object
634  * @param tags   tag item list
635  *
636  * @return Number of parameters get, or -1 upon an error.
637  *
638  * @TAGS
639  * SOATAG_CAPS_SDP(),
640  * SOATAG_CAPS_SDP_STR(),
641  * SOATAG_USER_SDP(),
642  * SOATAG_USER_SDP_STR(),
643  * SOATAG_LOCAL_SDP(),
644  * SOATAG_LOCAL_SDP_STR(),
645  * SOATAG_REMOTE_SDP(),
646  * SOATAG_REMOTE_SDP_STR(),
647  * SOATAG_AF(),
648  * SOATAG_ADDRESS(),
649  * SOATAG_HOLD(),
650  * SOATAG_RTP_SELECT(),
651  * SOATAG_RTP_SORT(),
652  * SOATAG_RTP_MISMATCH(),
653  * SOATAG_SRTP_ENABLE(),
654  * SOATAG_SRTP_CONFIDENTIALITY(), and
655  * SOATAG_SRTP_INTEGRITY().
656  */
soa_base_get_params(soa_session_t const * ss,tagi_t * tags)657 int soa_base_get_params(soa_session_t const *ss, tagi_t *tags)
658 {
659   int n;
660 
661   n = tl_tgets(tags,
662 	       SOATAG_CAPS_SDP(ss->ss_caps->ssd_sdp),
663 	       SOATAG_CAPS_SDP_STR(ss->ss_caps->ssd_str),
664 
665 	       SOATAG_USER_SDP(ss->ss_user->ssd_sdp),
666 	       SOATAG_USER_SDP_STR(ss->ss_user->ssd_str),
667 
668 	       SOATAG_LOCAL_SDP(ss->ss_local->ssd_sdp),
669 	       SOATAG_LOCAL_SDP_STR(ss->ss_local->ssd_str),
670 
671 	       SOATAG_REMOTE_SDP(ss->ss_remote->ssd_sdp),
672 	       SOATAG_REMOTE_SDP_STR(ss->ss_remote->ssd_unparsed),
673 
674 	       SOATAG_AF(ss->ss_af),
675 	       SOATAG_ADDRESS(ss->ss_address),
676 	       SOATAG_HOLD(ss->ss_hold),
677 
678 	       SOATAG_RTP_SELECT((int)ss->ss_rtp_select),
679 	       SOATAG_RTP_SORT((int)ss->ss_rtp_sort),
680 	       SOATAG_RTP_MISMATCH(ss->ss_rtp_mismatch),
681 
682 	       SOATAG_SRTP_ENABLE(ss->ss_srtp_enable),
683 	       SOATAG_SRTP_CONFIDENTIALITY(ss->ss_srtp_confidentiality),
684 	       SOATAG_SRTP_INTEGRITY(ss->ss_srtp_integrity),
685 
686 	       TAG_END());
687 
688   return n;
689 }
690 
691 /** Return a list of parameters. */
soa_get_paramlist(soa_session_t const * ss,tag_type_t tag,tag_value_t value,...)692 tagi_t *soa_get_paramlist(soa_session_t const *ss,
693 			  tag_type_t tag, tag_value_t value, ...)
694 {
695   ta_list ta;
696   tagi_t *params = NULL;
697 
698   SU_DEBUG_9(("soa_get_paramlist(%s::%p, ...) called\n",
699 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
700 
701   if (ss) {
702     ta_start(ta, tag, value);
703     /* Calls soa_static_get_paramlist() by default. */
704     params = ss->ss_actions->soa_get_paramlist(ss, ta_tags(ta));
705     ta_end(ta);
706   }
707 
708   return params;
709 }
710 
711 
712 /** Base bethod for getting list of parameters. */
soa_base_get_paramlist(soa_session_t const * ss,tag_type_t tag,tag_value_t value,...)713 tagi_t *soa_base_get_paramlist(soa_session_t const *ss,
714 			       tag_type_t tag, tag_value_t value,
715 			       ...)
716 {
717   ta_list ta;
718   tagi_t *params;
719 
720   if (ss == NULL)
721     return NULL;
722 
723   ta_start(ta, tag, value);
724 
725   params = tl_llist(
726 		   TAG_IF(ss->ss_caps->ssd_sdp,
727 			  SOATAG_CAPS_SDP(ss->ss_caps->ssd_sdp)),
728 		   TAG_IF(ss->ss_caps->ssd_str,
729 			  SOATAG_CAPS_SDP_STR(ss->ss_caps->ssd_str)),
730 
731 		   TAG_IF(ss->ss_user->ssd_sdp,
732 			  SOATAG_USER_SDP(ss->ss_user->ssd_sdp)),
733 		   TAG_IF(ss->ss_user->ssd_str,
734 			  SOATAG_USER_SDP_STR(ss->ss_user->ssd_str)),
735 
736 		   TAG_IF(ss->ss_local->ssd_sdp,
737 			  SOATAG_LOCAL_SDP(ss->ss_local->ssd_sdp)),
738 		   TAG_IF(ss->ss_user->ssd_str,
739 			  SOATAG_LOCAL_SDP_STR(ss->ss_local->ssd_str)),
740 
741 		   TAG_IF(ss->ss_remote->ssd_sdp,
742 			  SOATAG_REMOTE_SDP(ss->ss_remote->ssd_sdp)),
743 		   TAG_IF(ss->ss_remote->ssd_str,
744 			  SOATAG_REMOTE_SDP_STR(ss->ss_remote->ssd_unparsed)),
745 
746 		   SOATAG_AF(ss->ss_af),
747 		   TAG_IF(ss->ss_address,
748 			  SOATAG_ADDRESS(ss->ss_address)),
749 
750 		   SOATAG_SRTP_ENABLE(ss->ss_srtp_enable),
751 		   SOATAG_SRTP_CONFIDENTIALITY(ss->ss_srtp_confidentiality),
752 		   SOATAG_SRTP_INTEGRITY(ss->ss_srtp_integrity),
753 
754 		   ta_tags(ta));
755 
756   ta_end(ta);
757 
758   return params;
759 }
760 
761 #include <sofia-sip/sip_status.h>
762 
763 /** Convert @soa error to a SIP response code and phrase. */
soa_error_as_sip_response(soa_session_t * ss,char const ** return_phrase)764 int soa_error_as_sip_response(soa_session_t *ss,
765 			      char const **return_phrase)
766 {
767   SU_DEBUG_9(("soa_error_as_sip_response(%s::%p, ...) called\n",
768 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
769 
770   if (ss == NULL || ss->ss_status < 400 || ss->ss_status >= 700) {
771     if (return_phrase)
772       *return_phrase = sip_500_Internal_server_error;
773     return 500;
774   }
775 
776   if (return_phrase)
777     *return_phrase = ss->ss_phrase;
778   return ss->ss_status;
779 }
780 
781 /** Convert @soa error to a SIP @Reason header. */
soa_error_as_sip_reason(soa_session_t * ss)782 char const *soa_error_as_sip_reason(soa_session_t *ss)
783 {
784   char const *phrase;
785   int status;
786   char *reason;
787 
788   SU_DEBUG_9(("soa_error_as_sip_reason(%s::%p) called\n",
789 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
790 
791   if (ss == NULL)
792     return "SIP;cause=500;text=\"Internal Server Error\"";
793 
794   status = soa_error_as_sip_response(ss, &phrase);
795 
796   reason = su_sprintf(ss->ss_home, "SIP;cause=%u;text=\"%s\"", status, phrase);
797 
798   if (ss->ss_reason)
799     su_free(ss->ss_home, reason);
800 
801   return ss->ss_reason = reason;
802 }
803 
804 
805 /** Return SIP @Warning code and text. */
soa_get_warning(soa_session_t * ss,char const ** return_text)806 int soa_get_warning(soa_session_t *ss, char const **return_text)
807 {
808   if (!ss)
809     return 0;
810 
811   if (!ss->ss_wcode)
812     return 0;
813 
814   if (return_text)
815     *return_text = ss->ss_warning;
816 
817   return ss->ss_wcode;
818 }
819 
820 /** Return SDP description of capabilities.
821  *
822  * @param ss  pointer to @soa session
823  * @param return_sdp      return value for capability SDP structure
824  * @param return_sdp_str  return value for capability SDP string
825  * @param return_len  return value for length of capability SDP string
826  *
827  * @retval 0 if there is no description to return
828  * @retval 1 if description is returned
829  * @retval -1 upon an error
830  *
831  * @sa @RFC3261 section 11, soa_set_capability_sdp(),
832  * SOATAG_CAPS_SDP(), SOATAG_CAPS_SDP_STR(),
833  * nua_options(), #nua_i_options
834  */
soa_get_capability_sdp(soa_session_t const * ss,struct sdp_session_s const ** return_sdp,char const ** return_sdp_str,isize_t * return_len)835 int soa_get_capability_sdp(soa_session_t const *ss,
836 			   struct sdp_session_s const **return_sdp,
837 			   char const **return_sdp_str,
838 			   isize_t *return_len)
839 {
840   sdp_session_t const *sdp;
841   char const *sdp_str;
842 
843   SU_DEBUG_9(("soa_get_capability_sdp(%s::%p, [%p], [%p], [%p]) called\n",
844 	      ss ? ss->ss_actions->soa_name : "", (void *)ss,
845 	      (void *)return_sdp, (void *)return_sdp_str, (void *)return_len));
846 
847   if (ss == NULL)
848     return (void)su_seterrno(EFAULT), -1;
849 
850   sdp = ss->ss_caps->ssd_sdp;
851   sdp_str = ss->ss_caps->ssd_str;
852 
853   if (sdp == NULL)
854     return 0;
855   if (return_sdp)
856     *return_sdp = sdp;
857   if (return_sdp_str)
858     *return_sdp_str = sdp_str;
859   if (return_len)
860     *return_len = strlen(sdp_str);
861 
862   return 1;
863 }
864 
865 
866 /** Set capability SDP.
867  *
868  * Capability SDP is used instead of user SDP when generating OPTIONS
869  * responses describing media capabilities.
870  *
871  * @param ss  pointer to @soa session
872  * @param sdp pointer to SDP session structure
873  * @param str pointer to string containing SDP session description
874  * @param len lenght of string @a str
875  *
876  * @retval 1 when SDP is stored and it differs from previously stored
877  * @retval 0 when SDP is identical to previously stored one (and user version
878  *           returned by soa_get_user_version() is not incremented)
879  * @retval -1 upon an error
880  *
881  * @sa @RFC3261 section 11, soa_get_capability_sdp(),
882  * SOATAG_CAPS_SDP(), SOATAG_CAPS_SDP_STR(),
883  * nua_options(), #nua_i_options
884  */
soa_set_capability_sdp(soa_session_t * ss,struct sdp_session_s const * sdp,char const * str,issize_t len)885 int soa_set_capability_sdp(soa_session_t *ss,
886 			   struct sdp_session_s const *sdp,
887 			   char const *str, issize_t len)
888 {
889   SU_DEBUG_9(("soa_set_capability_sdp(%s::%p, %p, %p, "MOD_ZD") called\n",
890 	      ss ? ss->ss_actions->soa_name : "", (void *)ss, (void *)sdp, (void *)str, (ssize_t)len));
891 
892   return soa_set_sdp(ss, soa_capability_sdp_kind, sdp, str, len);
893 }
894 
895 /** Set capabilities */
896 int
soa_base_set_capability_sdp(soa_session_t * ss,sdp_session_t * _sdp,char const * str0,isize_t len0)897 soa_base_set_capability_sdp(soa_session_t *ss,
898 			    sdp_session_t *_sdp,
899 			    char const *str0, isize_t len0)
900 {
901   sdp_session_t sdp[1];
902   sdp_origin_t o[1] = {{ sizeof(o) }};
903   sdp_connection_t *c, c0[1] = {{ sizeof(c0) }};
904   char c_address[64];
905   sdp_time_t t[1] = {{ sizeof(t) }};
906   sdp_media_t *m;
907 
908   *sdp = *_sdp;
909 
910   if (sdp->sdp_origin)
911     *o = *sdp->sdp_origin;
912   else
913     o->o_address = c0;
914 
915   if (soa_init_sdp_origin(ss, o, c_address) < 0)
916     return -1;
917 
918   sdp->sdp_origin = o;
919 
920   if (!sdp->sdp_subject)
921     sdp->sdp_subject = "-";	/* s=- */
922 
923   sdp->sdp_time = t;		/* t=0 0 */
924 
925   /* Set port to zero - or should we check that port is already zero? */
926   for (m = sdp->sdp_media; m; m = m->m_next)
927     m->m_port = 0;
928 
929   if (sdp->sdp_connection == NULL) {
930     c = sdp->sdp_origin->o_address;
931 
932     for (m = sdp->sdp_media; m; m = m->m_next)
933       if (m->m_connections == NULL)
934 	break;
935     if (m)
936       sdp->sdp_connection = c;
937   }
938 
939   return soa_description_set(ss, ss->ss_caps, sdp, str0, len0);
940 }
941 
942 /**Return user SDP description.
943  *
944  * <i>User SDP</i> is used as basis for SDP Offer/Answer negotiation. It can
945  * be very minimal template, consisting just m= line containing media name,
946  * transport protocol, port number and list of supported codecs.
947  *
948  * The SDP used as an offer or answer (generated by soa_generate_answer() or
949  * soa_generate_offer()) is known as <i>local SDP</i> and it is available
950  * with soa_get_local_sdp() or SOATAG_LOCAL_SDP()/SOATAG_LOCAL_SDP_STR()
951  * tags.
952  *
953  * @param ss  pointer to @soa session
954  * @param return_sdp SDP  session structure return value
955  * @param return_sdp_str  return value for pointer to string
956  *                        containing the user SDP session description
957  * @param return_len  return value for user SDP session description string
958  *                    length
959  *
960  * Any of the parameters @a return_sdp, @a return_sdp_str, or @a return_len
961  * may be NULL.
962  *
963  * @retval 0 if there is no description to return
964  * @retval 1 if description is returned
965  * @retval -1 upon an error
966  *
967  * @sa soa_get_local_sdp(), soa_set_user_sdp(), soa_get_user_version(),
968  * SOATAG_USER_SDP(), SOATAG_USER_SDP_STR(), soa_get_remote_sdp(),
969  * soa_get_capability_sdp()
970  */
soa_get_user_sdp(soa_session_t const * ss,struct sdp_session_s const ** return_sdp,char const ** return_sdp_str,isize_t * return_len)971 int soa_get_user_sdp(soa_session_t const *ss,
972 		     struct sdp_session_s const **return_sdp,
973 		     char const **return_sdp_str,
974 		     isize_t *return_len)
975 {
976   sdp_session_t const *sdp;
977   char const *sdp_str;
978 
979   SU_DEBUG_9(("soa_get_user_sdp(%s::%p, [%p], [%p], [%p]) called\n",
980 	      ss ? ss->ss_actions->soa_name : "", (void *)ss,
981 			  (void *)return_sdp, (void *)return_sdp_str, (void *)return_len));
982 
983   if (ss == NULL)
984     return (void)su_seterrno(EFAULT), -1;
985 
986   sdp = ss->ss_user->ssd_sdp;
987   sdp_str = ss->ss_user->ssd_str;
988 
989   if (sdp == NULL)
990     return 0;
991   if (return_sdp)
992     *return_sdp = sdp;
993   if (return_sdp_str)
994     *return_sdp_str = sdp_str;
995   if (return_len)
996     *return_len = strlen(sdp_str);
997 
998   return 1;
999 }
1000 
1001 /**Returns the version number of user session description. The version
1002  * numbering starts from zero and it is incremented each time
1003  * soa_set_user_sdp() or soa_set_params() modifies user SDP.
1004  *
1005  * @param ss  pointer to @soa session
1006 
1007  * @return Sequence number of user SDP.
1008  *
1009  * @sa soa_set_user_sdp(), soa_get_user_sdp(), soa_set_params(),
1010  * SOATAG_USER_SDP(), SOATAG_USER_SDP_STR()
1011  */
soa_get_user_version(soa_session_t const * ss)1012 int soa_get_user_version(soa_session_t const *ss)
1013 {
1014   assert(ss != NULL);
1015   return ss ? (int)ss->ss_user_version : -1;
1016 }
1017 
1018 /**Store user SDP to soa session.
1019  *
1020  * User SDP is used as basis for SDP Offer/Answer negotiation. It can be
1021  * very minimal, consisting just m= line containing media name, transport
1022  * protocol port number and list of supported codecs.
1023  *
1024  * The SDP used as an offer or answer (generated by soa_generate_answer() or
1025  * soa_generate_offer()) is known as <i>local SDP</i> and it is available
1026  * with soa_get_local_sdp() or SOATAG_LOCAL_SDP()/SOATAG_LOCAL_SDP_STR()
1027  * tags.
1028  *
1029  * @param ss  pointer to @soa session
1030  * @param sdp pointer to SDP session structure
1031  * @param str pointer to string containing SDP session description
1032  * @param len lenght of string @a str
1033  *
1034  * Either @a sdp or @a str must be non-NULL. If @a len is -1, length of
1035  * string @a str is calculated using strlen().
1036  *
1037  * @retval 1 when SDP is stored and it differs from previously stored
1038  * @retval 0 when SDP is identical to previously stored one (and user version
1039  *           returned by soa_get_user_version() is not incremented)
1040  * @retval -1 upon an error
1041  *
1042  * @sa soa_get_user_sdp(), soa_get_user_version(), soa_set_params(),
1043  * SOATAG_USER_SDP(), SOATAG_USER_SDP_STR(), soa_generate_offer(),
1044  * soa_generate_answer(), soa_get_local_sdp(), soa_set_capability_sdp(),
1045  * soa_set_remote_sdp()
1046  */
soa_set_user_sdp(soa_session_t * ss,struct sdp_session_s const * sdp,char const * str,issize_t len)1047 int soa_set_user_sdp(soa_session_t *ss,
1048 		     struct sdp_session_s const *sdp,
1049 		     char const *str, issize_t len)
1050 {
1051   SU_DEBUG_9(("soa_set_user_sdp(%s::%p, %p, %p, "MOD_ZD") called\n",
1052 	      ss ? ss->ss_actions->soa_name : "", (void *)ss, (void *)sdp, (void *)str, (ssize_t)len));
1053 
1054   return soa_set_sdp(ss, soa_user_sdp_kind, sdp, str, len);
1055 }
1056 
1057 /** Set user SDP (base version). */
soa_base_set_user_sdp(soa_session_t * ss,sdp_session_t * sdp,char const * str0,isize_t len0)1058 int soa_base_set_user_sdp(soa_session_t *ss,
1059 			  sdp_session_t *sdp, char const *str0, isize_t len0)
1060 {
1061   ++ss->ss_user_version;
1062   return soa_description_set(ss, ss->ss_user, sdp, str0, len0);
1063 }
1064 
1065 /**Return remote SDP description of the session.
1066  *
1067  * <i>Remote SDP</i> is used, together with <i>User SDP</i> as basis for SDP
1068  * Offer/Answer negotiation.
1069  *
1070  * @param ss  pointer to @soa session
1071  * @param return_sdp SDP  session structure return value
1072  * @param return_sdp_str  return value for pointer to string
1073  *                        containing the user SDP session description
1074  * @param return_len  return value for user SDP session descrioption string
1075  *                    length
1076  *
1077  * Any of the parameters @a return_sdp, @a return_sdp_str, or @a return_len
1078  * may be NULL.
1079  *
1080  * @retval 0 if there is no description to return
1081  * @retval 1 if description is returned
1082  * @retval -1 upon an error
1083  *
1084  * @sa soa_set_remote_sdp(), soa_get_remote_version(), soa_get_params(),
1085  * soa_get_paramlist(), SOATAG_REMOTE_SDP(), SOATAG_REMOTE_SDP_STR(),
1086  * soa_get_local_sdp(), soa_get_user_sdp(), soa_get_capability_sdp().
1087  */
soa_get_remote_sdp(soa_session_t const * ss,struct sdp_session_s const ** return_sdp,char const ** return_sdp_str,isize_t * return_len)1088 int soa_get_remote_sdp(soa_session_t const *ss,
1089 		       struct sdp_session_s const **return_sdp,
1090 		       char const **return_sdp_str,
1091 		       isize_t *return_len)
1092 {
1093   sdp_session_t const *sdp;
1094   char const *sdp_str;
1095 
1096   SU_DEBUG_9(("soa_get_remote_sdp(%s::%p, [%p], [%p], [%p]) called\n",
1097 	      ss ? ss->ss_actions->soa_name : "", (void *)ss,
1098 			  (void *)return_sdp, (void *)return_sdp_str, (void *)return_len));
1099 
1100   if (ss == NULL)
1101     return (void)su_seterrno(EFAULT), -1;
1102 
1103   sdp = ss->ss_remote->ssd_sdp;
1104   sdp_str = ss->ss_remote->ssd_str;
1105 
1106   if (sdp == NULL)
1107     return 0;
1108   if (return_sdp)
1109     *return_sdp = sdp;
1110   if (return_sdp_str)
1111     *return_sdp_str = sdp_str;
1112   if (return_len)
1113     *return_len = strlen(sdp_str);
1114 
1115   return 1;
1116 }
1117 
1118 /**Returns the version number of remote session description. The version
1119  * numbering starts from zero and it is incremented each time
1120  * soa_set_remote_sdp() or soa_set_params() modifies remote SDP.
1121  *
1122  * @param ss  pointer to @soa session
1123 
1124  * @return Sequence number of remote SDP.
1125  *
1126  * @sa soa_set_remote_sdp(), soa_get_remote_sdp(), soa_set_params(),
1127  * SOATAG_REMOTE_SDP(), SOATAG_REMOTE_SDP_STR()
1128  */
soa_get_remote_version(soa_session_t const * ss)1129 int soa_get_remote_version(soa_session_t const *ss)
1130 {
1131   assert(ss != NULL);
1132   return ss->ss_remote_version;
1133 }
1134 
1135 /** Set remote SDP (offer or answer).
1136  *
1137  * <i>Remote SDP</i> is an SDP offer or answer received from the remote end.
1138  * It is used together with <i>User SDP</i> as basis for SDP Offer/Answer
1139  * negotiation in soa_generate_answer() or soa_process_answer(). Remote SDP
1140  * can be set using soa_set_params() and SOATAG_REMOTE_SDP() or
1141  * SOATAG_REMOTE_SDP_STR() tags, too.
1142  *
1143  * If the SDP Offer/Answer negotiation step cannot be completed and the
1144  * received remote offer or answer should be ignored, call
1145  * soa_clear_remote_sdp().
1146  *
1147  * @param ss  pointer to @soa session
1148  * @param sdp pointer to SDP session structure
1149  * @param str pointer to string containing SDP session description
1150  * @param len lenght of string @a str
1151  *
1152  * Either @a sdp or @a str must be non-NULL. If @a len is -1, length of
1153  * string @a str is calculated using strlen().
1154  *
1155  * @retval 1 when SDP is stored and it differs from previously stored
1156  * @retval 0 when SDP is identical to previously stored one (and remote version
1157  *           returned by soa_get_remote_version() is not incremented)
1158  * @retval -1 upon an error
1159  *
1160  * @sa soa_has_received_sdp(), soa_get_remote_sdp(),
1161  * soa_get_remote_version(), soa_set_params(), SOATAG_REMOTE_SDP(),
1162  * SOATAG_REMOTE_SDP_STR(), soa_generate_answer(), soa_process_answer(),
1163  * soa_clear_remote_sdp(), soa_init_offer_answer(), soa_get_local_sdp(),
1164  * soa_set_user_sdp(), soa_set_capability_sdp().
1165  */
soa_set_remote_sdp(soa_session_t * ss,struct sdp_session_s const * sdp,char const * str,issize_t len)1166 int soa_set_remote_sdp(soa_session_t *ss,
1167 		       struct sdp_session_s const *sdp,
1168 		       char const *str, issize_t len)
1169 {
1170   SU_DEBUG_9(("soa_set_remote_sdp(%s::%p, %p, %p, "MOD_ZD") called\n",
1171 	      ss ? ss->ss_actions->soa_name : "", (void *)ss, (void *)sdp, (void *)str, (ssize_t)len));
1172 
1173   return soa_set_sdp(ss, soa_remote_sdp_kind, sdp, str, len);
1174 }
1175 
1176 /** Base method for setting the remote SDP (offer or answer). */
soa_base_set_remote_sdp(soa_session_t * ss,int new_version,sdp_session_t * sdp,char const * str0,isize_t len0)1177 int soa_base_set_remote_sdp(soa_session_t *ss,
1178 			    int new_version,
1179 			    sdp_session_t *sdp, char const *str0, isize_t len0)
1180 {
1181   /* This is cleared in soa_generate_answer() or soa_process_answer(). */
1182   ss->ss_unprocessed_remote = 1;
1183 
1184   if (!new_version)
1185     return 0;
1186 
1187   soa_set_activity(ss, sdp->sdp_media, soa_activity_remote);
1188 
1189   ss->ss_remote_version++;
1190 
1191   return soa_description_set(ss, ss->ss_remote, sdp, str0, len0);
1192 }
1193 
1194 /** Clear remote SDP.
1195  *
1196  * Remote SDP (offer or answer) should be cleared after a it has been stored
1197  * in the SDP session object using soa_set_remote_sdp() or soa_set_params(),
1198  * but the SDP Offer/Answer negotiation step (soa_generate_answer() or
1199  * soa_process_answer()) cannot be completed.
1200  *
1201  * @param ss  pointer to @soa session
1202  *
1203  * @retval 0  when successful
1204  * @retval -1 upon an error
1205  *
1206  * @sa soa_init_offer_answer(), soa_set_remote_sdp(),
1207  * soa_has_received_sdp(), soa_set_params(), SOATAG_REMOTE_SDP(),
1208  * SOATAG_REMOTE_SDP_STR(), soa_generate_answer(), soa_process_answer(),
1209  * soa_process_reject().
1210  */
soa_clear_remote_sdp(soa_session_t * ss)1211 int soa_clear_remote_sdp(soa_session_t *ss)
1212 {
1213   SU_DEBUG_9(("soa_clear_remote_sdp(%s::%p) called\n",
1214 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
1215 
1216   if (!ss)
1217     return (void)su_seterrno(EFAULT), -1;
1218 
1219   ss->ss_unprocessed_remote = 0;
1220 
1221   return 0;
1222 }
1223 
1224 /** Check if remote SDP has been saved but it has not been processed.
1225  *
1226  * @sa soa_init_offer_answer(), soa_set_remote_sdp(), soa_generate_answer(),
1227  * soa_process_answer(), soa_clear_remote_sdp().
1228  */
soa_has_received_sdp(soa_session_t const * ss)1229 int soa_has_received_sdp(soa_session_t const *ss)
1230 {
1231   return ss && ss->ss_unprocessed_remote;
1232 }
1233 
1234 
1235 /**Get local SDP.
1236  *
1237  * The <i>local SDP</i> is used as an offer or answer and it is generated by
1238  * soa_generate_offer() or soa_generate_answer(), respectively. It can be
1239  * retrieved using soa_get_params() or soa_get_paramlist() with
1240  * SOATAG_LOCAL_SDP() or SOATAG_LOCAL_SDP_STR() tags.
1241  *
1242  * @param ss  pointer to @soa session
1243  * @param return_sdp SDP  session structure return value
1244  * @param return_sdp_str  return value for pointer to string
1245  *                        containing the user SDP session description
1246  * @param return_len  return value for user SDP session descrioption string
1247  *                    length
1248  *
1249  * Any of the parameters @a return_sdp, @a return_sdp_str, or @a return_len
1250  * may be NULL.
1251  *
1252  * @retval 0 if there is no description to return
1253  * @retval 1 if description is returned
1254  * @retval -1 upon an error
1255  *
1256  * @sa soa_generate_offer(), soa_generate_answer(), soa_get_params(),
1257  * soa_get_paramlist(), SOATAG_LOCAL_SDP(), SOATAG_LOCAL_SDP_STR(),
1258  * soa_get_user_sdp(), soa_get_remote_sdp(), soa_get_capability_sdp().
1259  */
soa_get_local_sdp(soa_session_t const * ss,struct sdp_session_s const ** return_sdp,char const ** return_sdp_str,isize_t * return_len)1260 int soa_get_local_sdp(soa_session_t const *ss,
1261 		      struct sdp_session_s const **return_sdp,
1262 		      char const **return_sdp_str,
1263 		      isize_t *return_len)
1264 {
1265   sdp_session_t const *sdp;
1266   char const *sdp_str;
1267 
1268   SU_DEBUG_9(("soa_get_local_sdp(%s::%p, [%p], [%p], [%p]) called\n",
1269 	      ss ? ss->ss_actions->soa_name : "", (void *)ss,
1270 			  (void *)return_sdp, (void *)return_sdp_str, (void *)return_len));
1271 
1272   if (ss == NULL)
1273     return (void)su_seterrno(EFAULT), -1;
1274 
1275   sdp = ss->ss_local->ssd_sdp;
1276   sdp_str = ss->ss_local->ssd_str;
1277 
1278   if (sdp == NULL)
1279     return 0;
1280   if (return_sdp)
1281     *return_sdp = sdp;
1282   if (return_sdp_str)
1283     *return_sdp_str = sdp_str;
1284   if (return_len)
1285     *return_len = strlen(sdp_str);
1286 
1287   return 1;
1288 }
1289 
1290 /**Initialize the offer/answer state machine.
1291  *
1292  * @param ss  pointer to @soa session
1293  *
1294  * @retval 0  when successful
1295  * @retval -1 upon an error
1296  */
soa_init_offer_answer(soa_session_t * ss)1297 int soa_init_offer_answer(soa_session_t *ss)
1298 {
1299   int complete;
1300 
1301   SU_DEBUG_9(("soa_init_offer_answer(%s::%p) called\n",
1302 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
1303 
1304   if (!ss)
1305     return 0;
1306 
1307   complete = ss->ss_complete;
1308 
1309   ss->ss_complete = 0;
1310   ss->ss_offer_sent = 0;
1311   ss->ss_offer_recv = 0;
1312   ss->ss_answer_sent = 0;
1313   ss->ss_answer_recv = 0;
1314 
1315   ss->ss_unprocessed_remote = 0;
1316 
1317   return complete;
1318 }
1319 
1320 /** Return list of media fetures. */
soa_media_features(soa_session_t * ss,int live,su_home_t * home)1321 char **soa_media_features(soa_session_t *ss, int live, su_home_t *home)
1322 {
1323   SU_DEBUG_9(("soa_media_features(%s::%p, %u, %p) called\n",
1324 	      ss ? ss->ss_actions->soa_name : "", (void *)ss, live, (void *)home));
1325 
1326   if (ss)
1327     /* Calls soa_base_media_features() by default. */
1328     return ss->ss_actions->soa_media_features(ss, live, home);
1329   else
1330     return (void)su_seterrno(EFAULT), NULL;
1331 }
1332 
soa_base_media_features(soa_session_t * ss,int live,su_home_t * home)1333 char **soa_base_media_features(soa_session_t *ss, int live, su_home_t *home)
1334 {
1335   return su_zalloc(home, 8 * sizeof (char **));
1336 }
1337 
soa_sip_require(soa_session_t const * ss)1338 char const * const * soa_sip_require(soa_session_t const *ss)
1339 {
1340   SU_DEBUG_9(("soa_sip_require(%s::%p) called\n",
1341 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
1342 
1343   if (ss)
1344     /* Calls soa_base_sip_require() by default */
1345     return ss->ss_actions->soa_sip_require(ss);
1346   else
1347     return (void)su_seterrno(EFAULT), NULL;
1348 }
1349 
soa_base_sip_require(soa_session_t const * ss)1350 char const * const * soa_base_sip_require(soa_session_t const *ss)
1351 {
1352   static char const *null = NULL;
1353   return &null;
1354 }
1355 
soa_sip_supported(soa_session_t const * ss)1356 char const * const * soa_sip_supported(soa_session_t const *ss)
1357 {
1358   SU_DEBUG_9(("soa_sip_supported(%s::%p) called\n",
1359 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
1360 
1361   if (ss)
1362     /* Calls soa_base_sip_supported() by default */
1363     return ss->ss_actions->soa_sip_supported(ss);
1364   else
1365     return (void)su_seterrno(EFAULT), NULL;
1366 }
1367 
soa_base_sip_supported(soa_session_t const * ss)1368 char const * const * soa_base_sip_supported(soa_session_t const *ss)
1369 {
1370   static char const *null = NULL;
1371   return &null;
1372 }
1373 
soa_remote_sip_features(soa_session_t * ss,char const * const * supported,char const * const * require)1374 int soa_remote_sip_features(soa_session_t *ss,
1375 			    char const * const * supported,
1376 			    char const * const * require)
1377 {
1378   SU_DEBUG_9(("soa_remote_sip_features(%s::%p, %p, %p) called\n",
1379 	      ss ? ss->ss_actions->soa_name : "", (void *)ss, (void *)supported, (void *)require));
1380 
1381   if (ss)
1382     /* Calls soa_base_remote_sip_features() by default */
1383     return ss->ss_actions->soa_remote_sip_features(ss, supported, require);
1384   else
1385     return (void)su_seterrno(EFAULT), -1;
1386 }
1387 
soa_base_remote_sip_features(soa_session_t * ss,char const * const * supported,char const * const * require)1388 int soa_base_remote_sip_features(soa_session_t *ss,
1389 				    char const * const * supported,
1390 				    char const * const * require)
1391 {
1392   return 0;
1393 }
1394 
1395 
1396 /**Generate offer.
1397  *
1398  * When generating the offer the user SDP is augmented with the required SDP
1399  * lines (v=, o=, t=, c=, a=rtpmap, etc.).
1400  *
1401  * The resulting SDP is also known as <i>local SDP</i>. It is available with
1402  * soa_get_local_sdp() or with soa_get_params() and soa_get_paramlist() tags
1403  * SOATAG_LOCAL_SDP() or SOATAG_LOCAL_SDP_STR().
1404  *
1405  * The user SDP has been stored to the soa session with soa_set_user_sdp()
1406  * or with soa_set_params() tags SOATAG_USER_SDP() or SOATAG_USER_SDP_STR().
1407  * There are various other parameters directing the generation of offer, set
1408  * by soa_set_params().
1409  *
1410  * @param ss pointer to session object
1411  * @param always always send offer (even if offer/answer has been completed)
1412  * @param completed pointer to callback function which is invoked when
1413  *                  operation is completed (currently not in use)
1414  *
1415  * @retval 1 when operation is successful
1416  * @retval 0 when operation was not needed
1417  * @retval -1 upon an error
1418  *
1419  * @ERRORS
1420  */
soa_generate_offer(soa_session_t * ss,int always,soa_callback_f * completed)1421 int soa_generate_offer(soa_session_t *ss,
1422 		       int always,
1423 		       soa_callback_f *completed)
1424 {
1425   SU_DEBUG_9(("soa_generate_offer(%s::%p, %u) called\n",
1426 	      ss ? ss->ss_actions->soa_name : "", (void *)ss, always));
1427 
1428   /** @ERROR EFAULT Bad address. */
1429   if (ss == NULL)
1430     return su_seterrno(EFAULT), -1;
1431 
1432   /** @ERROR EALREADY An operation is already in progress */
1433   if (ss->ss_in_progress)
1434     return su_seterrno(EALREADY), -1;
1435 
1436   /** @ERROR EPROTO We have received offer, now we should send answer */
1437   if (ss->ss_offer_recv && !ss->ss_answer_sent)
1438     return su_seterrno(EPROTO), -1;
1439 
1440   /** @ERROR EPROTO We have received SDP, but it has not been processed */
1441   if (soa_has_received_sdp(ss))
1442     return su_seterrno(EPROTO), -1;
1443 
1444   /** @ERROR EPROTO We have sent an offer, but have received no answer */
1445   if (ss->ss_offer_sent && !ss->ss_answer_recv)
1446     return su_seterrno(EPROTO), -1;
1447 
1448   /** @ERROR EPROTO We have received offer. */
1449   if (ss->ss_unprocessed_remote)
1450     return su_seterrno(EPROTO), -1;
1451 
1452   /* We should avoid actual operation unless always is true */
1453   (void)always;  /* We always regenerate offer */
1454 
1455   /* Calls soa_static_generate_offer() by default. */
1456   return ss->ss_actions->soa_generate_offer(ss, completed);
1457 
1458   /** @sa soa_init_offer_answer(), soa_set_user_sdp(), soa_get_local_sdp(),
1459    * soa_set_remote_sdp(), soa_process_answer(), soa_process_reject(),
1460    * soa_generate_answer(), soa_set_params(), soa_get_params(),
1461    * soa_get_paramlist(), SOATAG_USER_SDP(), SOATAG_USER_SDP_STR(),
1462    * SOATAG_REMOTE_SDP(), SOATAG_REMOTE_SDP_STR().
1463    */
1464 }
1465 
soa_base_generate_offer(soa_session_t * ss,soa_callback_f * completed)1466 int soa_base_generate_offer(soa_session_t *ss,
1467 			    soa_callback_f *completed)
1468 {
1469   sdp_session_t const *sdp = ss->ss_local->ssd_sdp;
1470 
1471   (void)completed;
1472 
1473   if (!sdp)
1474     return -1;
1475 
1476   soa_set_activity(ss, sdp->sdp_media, soa_activity_local); /* Wanted activity */
1477 
1478   ss->ss_offer_sent = 1;
1479   ss->ss_answer_recv = 0;
1480 
1481   return 0;
1482 }
1483 
1484 /**Process offer, generate answer.
1485  *
1486  * When generating the offer the soa session combines remote offer with
1487  * <i>user SDP</i>. There are various other parameters directing the
1488  * generation of answer, set by soa_set_params().
1489  *
1490  * Before calling soa_generate_answer() the remote SDP offer should have
1491  * been stored into the soa session @a ss with soa_set_remote_sdp() or with
1492  * the soa_set_params() tags SOATAG_REMOTE_SDP() or SOATAG_REMOTE_SDP_STR().
1493  *
1494  * Also, the <i>user SDP</i> should have been stored into @a ss with
1495  * soa_set_user_sdp() or with the soa_set_params() tags SOATAG_USER_SDP() or
1496  * SOATAG_USER_SDP_STR().
1497  *
1498  * The resulting SDP is also known as <i>local SDP</i>. It is available with
1499  * soa_get_local_sdp() or with the soa_get_params() or soa_get_paramlist()
1500  * tags SOATAG_LOCAL_SDP() and SOATAG_LOCAL_SDP_STR().
1501  *
1502  * @param ss  pointer to session object
1503  * @param completed  pointer to callback function which is invoked when
1504  *                   operation is completed (currently not in use)
1505  *
1506  * @retval 0 when operation is successful
1507  * @retval -1 upon an error
1508  *
1509  * @ERRORS
1510  */
soa_generate_answer(soa_session_t * ss,soa_callback_f * completed)1511 int soa_generate_answer(soa_session_t *ss,
1512 			soa_callback_f *completed)
1513 {
1514   SU_DEBUG_9(("soa_generate_answer(%s::%p) called\n",
1515 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
1516 
1517   /** @ERROR EFAULT Bad address as @a ss. */
1518   if (ss == NULL)
1519     return su_seterrno(EFAULT), -1;
1520 
1521   /** @ERROR EALREADY An operation is already in progress. */
1522   if (ss->ss_in_progress)
1523     return su_seterrno(EALREADY), -1;
1524 
1525   /** @ERROR EPROTO We have sent an offer, but have received no answer. */
1526   if (ss->ss_offer_sent && !ss->ss_answer_recv)
1527     return su_seterrno(EPROTO), -1;
1528 
1529   /** @ERROR EPROTO We have not received offer. */
1530   if (!ss->ss_unprocessed_remote)
1531     return su_seterrno(EPROTO), -1;
1532 
1533   /* Calls soa_static_generate_answer() by default. */
1534   return ss->ss_actions->soa_generate_answer(ss, completed);
1535 
1536   /**@sa soa_init_offer_answer(), soa_set_user_sdp(), soa_set_remote_sdp(),
1537    * soa_get_local_sdp(), soa_process_reject(), soa_generate_offer(),
1538    * soa_set_params(), soa_get_params(), soa_get_paramlist(),
1539    * SOATAG_USER_SDP(), SOATAG_USER_SDP_STR(), SOATAG_REMOTE_SDP(),
1540    * SOATAG_REMOTE_SDP_STR().
1541    */
1542 }
1543 
1544 /** Base method for processing offer, generating answer. */
soa_base_generate_answer(soa_session_t * ss,soa_callback_f * completed)1545 int soa_base_generate_answer(soa_session_t *ss,
1546 			     soa_callback_f *completed)
1547 {
1548   sdp_session_t const *l_sdp = ss->ss_local->ssd_sdp;
1549   sdp_session_t const *r_sdp = ss->ss_remote->ssd_sdp;
1550   sdp_session_t *rsession;
1551 
1552   (void)completed;
1553 
1554   if (!l_sdp || !r_sdp)
1555     return -1;
1556   rsession = sdp_session_dup(ss->ss_home, r_sdp);
1557   if (!rsession)
1558     return -1;
1559 
1560   if (ss->ss_rsession)
1561     su_free(ss->ss_home, ss->ss_rsession);
1562   ss->ss_rsession = rsession;
1563 
1564   soa_set_activity(ss, l_sdp->sdp_media, soa_activity_session);
1565 
1566   ss->ss_offer_recv = 1;
1567   ss->ss_answer_sent = 1;
1568   ss->ss_complete = 1;
1569   ss->ss_unprocessed_remote = 0;
1570 
1571   return 0;
1572 }
1573 
1574 /** Complete offer-answer after receiving answer.
1575  *
1576  * The SDP offer/answer negotiation is completed after receiving answer from
1577  * remote end. The answer is combined with the offer, and the application
1578  * should activate the media and codecs according to the negotiation result,
1579  * available as <i>local SDP</i>.
1580  *
1581  * @param ss  pointer to session object
1582  * @param completed  pointer to callback function which is invoked when
1583  *                   operation is completed (currently not in use)
1584  *
1585  * @retval 1 when operation is successful
1586  * @retval 0 when operation was not needed
1587  * @retval -1 upon an error
1588  *
1589  * @ERRORS
1590  */
soa_process_answer(soa_session_t * ss,soa_callback_f * completed)1591 int soa_process_answer(soa_session_t *ss,
1592 		       soa_callback_f *completed)
1593 {
1594   SU_DEBUG_9(("soa_process_answer(%s::%p) called\n",
1595 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
1596 
1597   /** @ERROR EFAULT Bad address as @a ss. */
1598   if (ss == NULL)
1599     return su_seterrno(EFAULT), -1;
1600 
1601   /** @ERROR EALREADY An operation is already in progress. */
1602   if (ss->ss_in_progress)
1603     return su_seterrno(EALREADY), -1;
1604 
1605   /** @ERROR EPROTO We have not sent an offer
1606       or already have received answer. */
1607   if (!ss->ss_offer_sent || ss->ss_answer_recv)
1608     return su_seterrno(EPROTO), -1;
1609 
1610   /** @ERROR EPROTO We have not received answer. */
1611   if (!ss->ss_unprocessed_remote)
1612     return su_seterrno(EPROTO), -1;
1613 
1614   /**@sa soa_init_offer_answer(), soa_set_user_sdp(), soa_set_remote_sdp(),
1615    * soa_get_local_sdp(), soa_generate_offer(), soa_generate_answer(),
1616    * soa_process_reject(), soa_set_params(), soa_get_params(),
1617    * soa_get_paramlist(), SOATAG_USER_SDP(), SOATAG_USER_SDP_STR(),
1618    * SOATAG_REMOTE_SDP(), SOATAG_REMOTE_SDP_STR().
1619    */
1620 
1621   /* Calls soa_static_process_answer() by default. */
1622   return ss->ss_actions->soa_process_answer(ss, completed);
1623 }
1624 
1625 /** Base method for completing offer-answer after receiving answer.
1626  */
soa_base_process_answer(soa_session_t * ss,soa_callback_f * completed)1627 int soa_base_process_answer(soa_session_t *ss,
1628 			    soa_callback_f *completed)
1629 {
1630   sdp_session_t const *l_sdp = ss->ss_local->ssd_sdp;
1631   sdp_session_t const *r_sdp = ss->ss_remote->ssd_sdp;
1632   sdp_session_t *rsession;
1633 
1634   (void)completed;
1635 
1636   if (!l_sdp || !r_sdp)
1637     return -1;
1638   rsession = sdp_session_dup(ss->ss_home, r_sdp);
1639   if (!rsession)
1640     return -1;
1641 
1642   if (ss->ss_rsession)
1643     su_free(ss->ss_home, ss->ss_rsession);
1644   ss->ss_rsession = rsession;
1645 
1646   soa_set_activity(ss, l_sdp->sdp_media, soa_activity_session);
1647 
1648   ss->ss_answer_recv = 1;
1649   ss->ss_complete = 1;
1650   ss->ss_unprocessed_remote = 0;
1651 
1652   return 0;
1653 }
1654 
1655 /** Process rejection of offer.
1656  *
1657  * If the SDP offer was rejected (e.g., an offer in re-INVITE asked remote
1658  * end to add video to the session but the request was rejected), the
1659  * session should be restored to the state it was before last offer-answer
1660  * negotation round with soa_process_reject().
1661  *
1662  * @param ss  pointer to session object
1663  * @param completed  pointer to callback function which is invoked when
1664  *                   operation is completed (currently not in use)
1665  *
1666  * @retval 1 when operation is successful
1667  * @retval 0 when operation was not needed
1668  * @retval -1 upon an error
1669  *
1670  * @ERRORS
1671  */
soa_process_reject(soa_session_t * ss,soa_callback_f * completed)1672 int soa_process_reject(soa_session_t *ss,
1673 		       soa_callback_f *completed)
1674 {
1675   SU_DEBUG_9(("soa_process_reject(%s::%p) called\n",
1676 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
1677 
1678   /** @ERROR EFAULT Bad address as @a ss. */
1679   if (ss == NULL)
1680     return su_seterrno(EFAULT), -1;
1681 
1682   /** @ERROR EALREADY An operation is already in progress. */
1683   if (ss->ss_in_progress)
1684     return su_seterrno(EALREADY), -1;
1685 
1686   /** @ERROR EPROTO We have not sent an offer
1687       or already have received answer. */
1688   if (!ss->ss_offer_sent || ss->ss_answer_recv)
1689     return su_seterrno(EPROTO), -1;
1690 
1691   /**@sa soa_init_offer_answer(), soa_set_user_sdp(), soa_set_remote_sdp(),
1692    * soa_get_local_sdp(), soa_generate_offer(), soa_generate_answer(),
1693    * soa_process_answer(), soa_set_params(), soa_get_params(),
1694    * soa_get_paramlist(), SOATAG_USER_SDP(), SOATAG_USER_SDP_STR(),
1695    * SOATAG_REMOTE_SDP(), SOATAG_REMOTE_SDP_STR().
1696    */
1697 
1698   /* Calls soa_static_process_reject() by default. */
1699   return ss->ss_actions->soa_process_reject(ss, completed);
1700 }
1701 
1702 /** Base method for processing rejection of offer.
1703  */
soa_base_process_reject(soa_session_t * ss,soa_callback_f * completed)1704 int soa_base_process_reject(soa_session_t *ss,
1705 			    soa_callback_f *completed)
1706 {
1707   sdp_session_t const *l_sdp = ss->ss_local->ssd_sdp;
1708 
1709   (void)completed;
1710 
1711   ss->ss_offer_sent = 0;
1712 
1713   soa_set_activity(ss, l_sdp ? l_sdp->sdp_media : NULL, soa_activity_session);
1714 
1715   return 0;
1716 }
1717 
1718 /** Activate session.
1719  *
1720  * Mark soa session as active.
1721  *
1722  * @retval 0 when operation was successful
1723  * @retval -1 upon an error
1724  *
1725  * @ERRORS
1726  */
soa_activate(soa_session_t * ss,char const * option)1727 int soa_activate(soa_session_t *ss, char const *option)
1728 {
1729   SU_DEBUG_9(("soa_activate(%s::%p, %s%s%s) called\n",
1730 	      ss ? ss->ss_actions->soa_name : "", (void *)ss, NICE(option)));
1731 
1732   /** @ERROR EFAULT Bad address as @a ss. */
1733   if (ss == NULL)
1734     return -1;
1735 
1736   ss->ss_active = 1;
1737 
1738   /* Calls soa_static_activate() by default. */
1739   return ss->ss_actions->soa_activate_session(ss, option);
1740 }
1741 
soa_base_activate(soa_session_t * ss,char const * option)1742 int soa_base_activate(soa_session_t *ss, char const *option)
1743 {
1744   (void)ss;
1745   (void)option;
1746   return 0;
1747 }
1748 
1749 /** Deactivate session.
1750  *
1751  * Mark soa session as inactive.
1752  *
1753  * @retval 0 when operation was successful
1754  * @retval -1 upon an error
1755  *
1756  * @ERRORS
1757  */
soa_deactivate(soa_session_t * ss,char const * option)1758 int soa_deactivate(soa_session_t *ss, char const *option)
1759 {
1760   SU_DEBUG_9(("soa_deactivate(%s::%p, %s%s%s) called\n",
1761 	      ss ? ss->ss_actions->soa_name : "", (void *)ss, NICE(option)));
1762 
1763   /** @ERROR EFAULT Bad address as @a ss. */
1764   if (ss == NULL)
1765     return -1;
1766 
1767   ss->ss_active = 0;
1768 
1769   /* Calls soa_static_deactivate() by default. */
1770   return ss->ss_actions->soa_deactivate_session(ss, option);
1771 }
1772 
soa_base_deactivate(soa_session_t * ss,char const * option)1773 int soa_base_deactivate(soa_session_t *ss, char const *option)
1774 {
1775   (void)ss;
1776   (void)option;
1777   return 0;
1778 }
1779 
1780 /** Terminate session. */
soa_terminate(soa_session_t * ss,char const * option)1781 void soa_terminate(soa_session_t *ss, char const *option)
1782 {
1783   SU_DEBUG_9(("soa_terminate(%s::%p) called\n",
1784 	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
1785 
1786   /** @ERROR EFAULT Bad address as @a ss. */
1787   if (ss == NULL)
1788     return;
1789 
1790   ss->ss_active = 0;
1791   ss->ss_terminated++;
1792 
1793   /* Calls soa_static_terminate() by default. */
1794   ss->ss_actions->soa_terminate_session(ss, option);
1795 }
1796 
soa_base_terminate(soa_session_t * ss,char const * option)1797 void soa_base_terminate(soa_session_t *ss, char const *option)
1798 {
1799   (void)option;
1800 
1801   soa_init_offer_answer(ss);
1802   ss->ss_oa_rounds = 0;
1803 
1804   soa_description_free(ss, ss->ss_remote);
1805   soa_set_activity(ss, NULL, soa_activity_session);
1806 }
1807 
1808 /** Return true if the SDP Offer/Answer negotation is complete.
1809  *
1810  * The soa_init_offer_answer() clears the completion flag.
1811  */
soa_is_complete(soa_session_t const * ss)1812 int soa_is_complete(soa_session_t const *ss)
1813 {
1814   return ss && ss->ss_complete;
1815 }
1816 
1817 /** Return true if audio has been activated. */
soa_is_audio_active(soa_session_t const * ss)1818 int soa_is_audio_active(soa_session_t const *ss)
1819 {
1820   int ma = ss ? ss->ss_local_activity->ma_audio : SOA_ACTIVE_DISABLED;
1821   if (ma >= 4) ma |= -8;
1822   return ma;
1823 }
1824 
1825 /** Return true if video has been activated. */
soa_is_video_active(soa_session_t const * ss)1826 int soa_is_video_active(soa_session_t const *ss)
1827 {
1828   int ma = ss ? ss->ss_local_activity->ma_video : SOA_ACTIVE_DISABLED;
1829   if (ma >= 4) ma |= -8;
1830   return ma;
1831 }
1832 
1833 /** Return true if image sharing has been activated. */
soa_is_image_active(soa_session_t const * ss)1834 int soa_is_image_active(soa_session_t const *ss)
1835 {
1836   int ma = ss ? ss->ss_local_activity->ma_image : SOA_ACTIVE_DISABLED;
1837   if (ma >= 4) ma |= -8;
1838   return ma;
1839 }
1840 
1841 /** Return true if messaging session has been activated. */
soa_is_chat_active(soa_session_t const * ss)1842 int soa_is_chat_active(soa_session_t const *ss)
1843 {
1844   int ma = ss ? ss->ss_local_activity->ma_chat : SOA_ACTIVE_DISABLED;
1845   if (ma >= 4) ma |= -8;
1846   return ma;
1847 }
1848 
1849 /** Return true if remote audio is active (not on hold). */
soa_is_remote_audio_active(soa_session_t const * ss)1850 int soa_is_remote_audio_active(soa_session_t const *ss)
1851 {
1852   int ma = ss ? ss->ss_remote_activity->ma_audio : SOA_ACTIVE_DISABLED;
1853   if (ma >= 4) ma |= -8;
1854   return ma;
1855 }
1856 
1857 /** Return true if remote video is active (not on hold). */
soa_is_remote_video_active(soa_session_t const * ss)1858 int soa_is_remote_video_active(soa_session_t const *ss)
1859 {
1860   int ma = ss ? ss->ss_remote_activity->ma_video : SOA_ACTIVE_DISABLED;
1861   if (ma >= 4) ma |= -8;
1862   return ma;
1863 }
1864 
1865 /** Return true if image sharing is active (not on hold). */
soa_is_remote_image_active(soa_session_t const * ss)1866 int soa_is_remote_image_active(soa_session_t const *ss)
1867 {
1868   int ma = ss ? ss->ss_remote_activity->ma_image : SOA_ACTIVE_DISABLED;
1869   if (ma >= 4) ma |= -8;
1870   return ma;
1871 }
1872 
1873 /** Return true if chat session is active (not on hold). */
soa_is_remote_chat_active(soa_session_t const * ss)1874 int soa_is_remote_chat_active(soa_session_t const *ss)
1875 {
1876   int ma = ss ? ss->ss_remote_activity->ma_chat : SOA_ACTIVE_DISABLED;
1877   if (ma >= 4) ma |= -8;
1878   return ma;
1879 }
1880 
1881 /* ======================================================================== */
1882 /* Methods used by soa instances */
1883 
soa_set_status(soa_session_t * ss,int status,char const * phrase)1884 int soa_set_status(soa_session_t *ss, int status, char const *phrase)
1885 {
1886   if (ss) {
1887     ss->ss_status = status, ss->ss_phrase = phrase;
1888     ss->ss_wcode = 0, ss->ss_warning = NULL;
1889   }
1890   return -1;
1891 }
1892 
soa_set_warning(soa_session_t * ss,int code,char const * text)1893 int soa_set_warning(soa_session_t *ss, int code, char const *text)
1894 {
1895   if (ss)
1896     ss->ss_wcode = code, ss->ss_warning = text;
1897   return -1;
1898 }
1899 
soa_set_activity(soa_session_t * ss,sdp_media_t const * m,enum soa_activity activity)1900 void soa_set_activity(soa_session_t *ss,
1901 		      sdp_media_t const *m,
1902 		      enum soa_activity activity)
1903 {
1904   struct soa_media_activity *ma;
1905   sdp_connection_t const *c;
1906   int mode, swap;
1907   int l_audio = SOA_ACTIVE_DISABLED, r_audio = SOA_ACTIVE_DISABLED;
1908   int l_video = SOA_ACTIVE_DISABLED, r_video = SOA_ACTIVE_DISABLED;
1909   int l_chat = SOA_ACTIVE_DISABLED,  r_chat = SOA_ACTIVE_DISABLED;
1910   int l_image = SOA_ACTIVE_DISABLED, r_image = SOA_ACTIVE_DISABLED;
1911 
1912   int *l, *r;
1913 
1914   for (; m; m = m->m_next) {
1915     if (m->m_type == sdp_media_audio)
1916       l = &l_audio, r = &r_audio;
1917     else if (m->m_type == sdp_media_video)
1918       l = &l_video, r = &r_video;
1919     else if (m->m_type == sdp_media_image)
1920       l = &l_image, r = &r_image;
1921     else if (su_casematch(m->m_type_name, "message"))
1922       l = &l_chat, r = &r_chat;
1923     else
1924       continue;
1925 
1926     if (m->m_rejected) {
1927       if (*l < 0) *l = SOA_ACTIVE_REJECTED;
1928       if (*r < 0) *r = SOA_ACTIVE_REJECTED;
1929       continue;
1930     }
1931 
1932     mode = m->m_mode, swap = ((mode << 1) & 2) | ((mode >> 1) & 1);
1933 
1934     c = sdp_media_connections((sdp_media_t *)m);
1935 
1936     switch (activity) {
1937     case soa_activity_local:
1938       *l &= SOA_ACTIVE_SENDRECV;
1939       *l |= c && c->c_mcast ? swap : mode;
1940       break;
1941     case soa_activity_remote:
1942       *r &= SOA_ACTIVE_SENDRECV;
1943       *r = c && c->c_mcast ? mode : swap;
1944       break;
1945     case soa_activity_session:
1946       *l &= SOA_ACTIVE_SENDRECV;
1947       *l |= c && c->c_mcast ? swap : mode;
1948       *r &= SOA_ACTIVE_SENDRECV;
1949       *r = c && c->c_mcast ? swap : mode;
1950       break;
1951     }
1952   }
1953 
1954   if (activity == soa_activity_local ||
1955       activity == soa_activity_session) {
1956     ma = ss->ss_local_activity;
1957     ma->ma_audio = l_audio;
1958     ma->ma_video = l_video;
1959     ma->ma_image = l_image;
1960     ma->ma_chat = l_chat;
1961   }
1962 
1963   if (activity == soa_activity_remote ||
1964       activity == soa_activity_session) {
1965     ma = ss->ss_remote_activity;
1966     ma->ma_audio = r_audio;
1967     ma->ma_video = r_video;
1968     ma->ma_image = r_image;
1969     ma->ma_chat = r_chat;
1970   }
1971 }
1972 
1973 /* ----------------------------------------------------------------------*/
1974 /* Handle SDP */
1975 
1976 
1977 /**
1978  * Parses and stores session description
1979  *
1980  * @param ss instance pointer
1981  * @param what caps, local or remote
1982  * @param sdp0 new sdp (parsed)
1983  * @param sdp_str new sdp (unparsed)
1984  * @param str_len length on unparsed data
1985  **/
1986 static
soa_set_sdp(soa_session_t * ss,enum soa_sdp_kind what,sdp_session_t const * sdp0,char const * sdp_str,issize_t str_len)1987 int soa_set_sdp(soa_session_t *ss,
1988 		enum soa_sdp_kind what,
1989 		sdp_session_t const *sdp0,
1990 		char const *sdp_str, issize_t str_len)
1991 {
1992   struct soa_description *ssd;
1993   int flags, new_version, retval;
1994   sdp_parser_t *parser = NULL;
1995   sdp_session_t sdp[1];
1996 
1997   if (ss == NULL)
1998     return -1;
1999 
2000   switch (what) {
2001   case soa_capability_sdp_kind:
2002     ssd = ss->ss_caps;
2003     flags = sdp_f_config;
2004     break;
2005   case soa_user_sdp_kind:
2006     ssd = ss->ss_user;
2007     flags = sdp_f_config;
2008     break;
2009   case soa_remote_sdp_kind:
2010     ssd = ss->ss_remote;
2011     flags = sdp_f_mode_0000;
2012     break;
2013   default:
2014     return -1;
2015   }
2016 
2017   if (sdp0) {
2018     new_version = sdp_session_cmp(sdp0, ssd->ssd_sdp) != 0;
2019     sdp_str = NULL, str_len = -1;
2020   }
2021   else if (sdp_str) {
2022     if (str_len == -1)
2023       str_len = strlen(sdp_str);
2024     new_version = !su_strnmatch(sdp_str, ssd->ssd_unparsed, str_len + 1);
2025   }
2026   else
2027     return (void)su_seterrno(EINVAL), -1;
2028 
2029   if (!new_version) {
2030     if (what == soa_remote_sdp_kind) {
2031       *sdp = *ssd->ssd_sdp;
2032       /* Calls soa_static_set_remote_sdp() by default */
2033       return ss->ss_actions->soa_set_remote_sdp(ss, new_version,
2034 						sdp, sdp_str, str_len);
2035       /* XXX - should check changes by soa_set_remote_sdp */
2036     }
2037     return 0;
2038   }
2039 
2040   if (sdp0) {
2041     /* note: case 1 - src in parsed form */
2042     *sdp = *sdp0;
2043   }
2044   else /* if (sdp_str) */ {
2045     /* note: case 2 - src in unparsed form */
2046     parser = sdp_parse(ss->ss_home, sdp_str, str_len, flags | sdp_f_anynet);
2047 
2048     if (sdp_parsing_error(parser)) {
2049       sdp_parser_free(parser);
2050       return soa_set_status(ss, 400, "Bad Session Description");
2051     }
2052 
2053     *sdp = *sdp_session(parser);
2054   }
2055 
2056   switch (what) {
2057   case soa_capability_sdp_kind:
2058     /* Calls soa_static_set_capability_sdp() by default */
2059     retval = ss->ss_actions->soa_set_capability_sdp(ss, sdp, sdp_str, str_len);
2060     break;
2061   case soa_user_sdp_kind:
2062     /* Calls soa_static_set_user_sdp() by default */
2063     retval =  ss->ss_actions->soa_set_user_sdp(ss, sdp, sdp_str, str_len);
2064     break;
2065   case soa_remote_sdp_kind:
2066     /* Calls soa_static_set_remote_sdp() by default */
2067     retval = ss->ss_actions->soa_set_remote_sdp(ss, 1, sdp, sdp_str, str_len);
2068     break;
2069   default:
2070     retval = soa_set_status(ss, 500, "Internal Error");
2071     break;
2072   }
2073 
2074   if (parser)
2075     sdp_parser_free(parser);
2076 
2077   return retval;
2078 }
2079 
2080 
2081 /** Set session descriptions. */
soa_description_set(soa_session_t * ss,struct soa_description * ssd,sdp_session_t * sdp,char const * sdp_str,isize_t str_len)2082 int soa_description_set(soa_session_t *ss,
2083 			struct soa_description *ssd,
2084 			sdp_session_t *sdp,
2085 			char const *sdp_str,
2086 			isize_t str_len)
2087 {
2088   int retval = -1;
2089 
2090   sdp_printer_t *printer = NULL;
2091   sdp_session_t *sdp_new;
2092   char *sdp_str_new;
2093   char *sdp_str0_new;
2094 
2095   void *tbf1, *tbf2, *tbf3, *tbf4;
2096 
2097   /* Store description in three forms: unparsed, parsed and reprinted */
2098 
2099   sdp_new = sdp_session_dup(ss->ss_home, sdp);
2100   printer = sdp_print(ss->ss_home, sdp, NULL, 0, 0);
2101   sdp_str_new = (char *)sdp_message(printer);
2102   if (sdp_str)
2103     sdp_str0_new = su_strndup(ss->ss_home, sdp_str, str_len);
2104   else
2105     sdp_str0_new = sdp_str_new;
2106 
2107   if (sdp_new && printer && sdp_str_new && sdp_str0_new) {
2108     tbf1 = ssd->ssd_sdp, tbf2 = ssd->ssd_printer;
2109     tbf3 = (void *)ssd->ssd_str, tbf4 = (void *)ssd->ssd_unparsed;
2110 
2111     ssd->ssd_sdp = sdp_new;
2112     ssd->ssd_printer = printer;
2113     ssd->ssd_str = sdp_str_new;
2114     ssd->ssd_unparsed = sdp_str0_new;
2115 
2116     retval = 1;
2117   }
2118   else {
2119     tbf1 = sdp_new, tbf2 = printer, tbf3 = sdp_str_new, tbf4 = sdp_str0_new;
2120   }
2121 
2122   su_free(ss->ss_home, tbf1);
2123   sdp_printer_free(tbf2);
2124   if (tbf3 != tbf4)
2125     su_free(ss->ss_home, tbf4);
2126 
2127   return retval;
2128 }
2129 
2130 
2131 /** Duplicate a session descriptions. */
soa_description_dup(su_home_t * home,struct soa_description * ssd,struct soa_description const * ssd0)2132 int soa_description_dup(su_home_t *home,
2133 			struct soa_description *ssd,
2134 			struct soa_description const *ssd0)
2135 {
2136   if (ssd0->ssd_sdp) {
2137     ssd->ssd_sdp = sdp_session_dup(home, ssd0->ssd_sdp);
2138     ssd->ssd_printer = sdp_print(home, ssd->ssd_sdp, NULL, 0, 0);
2139     ssd->ssd_str = (char *)sdp_message(ssd->ssd_printer);
2140     if (ssd0->ssd_str != ssd0->ssd_unparsed)
2141       ssd->ssd_unparsed = su_strdup(home, ssd0->ssd_unparsed);
2142     else
2143       ssd->ssd_unparsed = ssd->ssd_str;
2144   }
2145 
2146   return 0;
2147 }
2148 
2149 /** Free session descriptions. */
soa_description_free(soa_session_t * ss,struct soa_description * ssd)2150 void soa_description_free(soa_session_t *ss,
2151 			  struct soa_description *ssd)
2152 {
2153   void *tbf1, *tbf2, *tbf3, *tbf4;
2154 
2155   tbf1 = ssd->ssd_sdp, tbf2 = ssd->ssd_printer;
2156   tbf3 = (void *)ssd->ssd_str, tbf4 = (void *)ssd->ssd_unparsed;
2157 
2158   memset(ssd, 0, sizeof *ssd);
2159 
2160   su_free(ss->ss_home, tbf1);
2161   sdp_printer_free(tbf2);
2162   if (tbf3 != tbf4)
2163     su_free(ss->ss_home, tbf4);
2164 }
2165 
2166 /** Initialize SDP o= line */
2167 int
soa_init_sdp_origin(soa_session_t * ss,sdp_origin_t * o,char buffer[64])2168 soa_init_sdp_origin(soa_session_t *ss, sdp_origin_t *o, char buffer[64])
2169 {
2170   return soa_init_sdp_origin_with_session(ss, o, buffer, NULL);
2171 }
2172 
2173 
2174 /** Check if c= has valid address
2175  */
2176 int
soa_check_sdp_connection(sdp_connection_t const * c)2177 soa_check_sdp_connection(sdp_connection_t const *c)
2178 {
2179   return c != NULL &&
2180     c->c_nettype &&c->c_address &&
2181     strcmp(c->c_address, "") &&
2182     strcmp(c->c_address, "0.0.0.0") &&
2183     strcmp(c->c_address, "::");
2184 }
2185 
2186 
2187 /** Initialize SDP o= line with values from @a sdp session. */
2188 int
soa_init_sdp_origin_with_session(soa_session_t * ss,sdp_origin_t * o,char buffer[64],sdp_session_t const * sdp)2189 soa_init_sdp_origin_with_session(soa_session_t *ss,
2190 				 sdp_origin_t *o,
2191 				 char buffer[64],
2192 				 sdp_session_t const *sdp)
2193 {
2194   if (ss == NULL || o == NULL || buffer == NULL)
2195     return su_seterrno(EFAULT);
2196 
2197   assert(o->o_address);
2198 
2199   if (!o->o_username)
2200     o->o_username = "-";
2201 
2202   if (o->o_id == 0)
2203     su_randmem(&o->o_id, sizeof o->o_id);
2204   o->o_id &= ((unsigned longlong)1 << 63) - 1;
2205 
2206   if (o->o_version == 0)
2207     su_randmem(&o->o_version, sizeof o->o_version);
2208   o->o_version &= ((unsigned longlong)1 << 63) - 1;
2209 
2210   if (!soa_check_sdp_connection(o->o_address) ||
2211       host_is_local(o->o_address->c_address))
2212     return soa_init_sdp_connection_with_session(ss, o->o_address, buffer, sdp);
2213 
2214   return 0;
2215 }
2216 
2217 
2218 /** Obtain a local address for SDP connection structure */
2219 int
soa_init_sdp_connection(soa_session_t * ss,sdp_connection_t * c,char buffer[64])2220 soa_init_sdp_connection(soa_session_t *ss,
2221 			sdp_connection_t *c,
2222 			char buffer[64])
2223 {
2224   return soa_init_sdp_connection_with_session(ss, c, buffer, NULL);
2225 }
2226 
2227 static su_localinfo_t const *best_listed_address_in_localinfo(
2228   su_localinfo_t const *res, char const *address, int ip4, int ip6);
2229 static sdp_connection_t const *best_listed_address_in_session(
2230   sdp_session_t const *sdp, char const *address0, int ip4, int ip6);
2231 static su_localinfo_t const *best_listed_address(
2232   su_localinfo_t *li0, char const *address, int ip4, int ip6);
2233 
2234 
2235 /** Obtain a local address for SDP connection structure.
2236  *
2237  * Prefer an address already found in @a sdp.
2238  */
2239 int
soa_init_sdp_connection_with_session(soa_session_t * ss,sdp_connection_t * c,char buffer[64],sdp_session_t const * sdp)2240 soa_init_sdp_connection_with_session(soa_session_t *ss,
2241 				     sdp_connection_t *c,
2242 				     char buffer[64],
2243 				     sdp_session_t const *sdp)
2244 {
2245   su_localinfo_t *res, hints[1] = {{ LI_CANONNAME | LI_NUMERIC }}, li0[1];
2246   su_localinfo_t const *li, *li4, *li6;
2247   char const *address;
2248   char const *source = NULL;
2249   int ip4, ip6, error;
2250   char abuffer[64];		/* getting value from ss_address */
2251 
2252   if (ss == NULL || c == NULL || buffer == NULL)
2253     return su_seterrno(EFAULT), -1;
2254 
2255   address = ss->ss_address;
2256 
2257   if (host_is_ip_address(address)) {
2258     /* Use the application-specified address -
2259      * do not check that it is found from the local address list */
2260     c->c_nettype = sdp_net_in;
2261 
2262     if (host_is_ip4_address(address))
2263       c->c_addrtype = sdp_addr_ip4;
2264     else
2265       c->c_addrtype = sdp_addr_ip6;
2266 
2267     if (!host_is_ip6_reference(address)) {
2268       c->c_address = strcpy(buffer, address);
2269     }
2270     else {
2271       /* Remove brackets [] around the reference */
2272       size_t len = strlen(address + 1);
2273       c->c_address = memcpy(buffer, address + 1, len - 1);
2274       buffer[len - 1] = '\0';
2275     }
2276 
2277     SU_DEBUG_5(("%s: using SOATAG_ADDRESS(\"%s\")\n", __func__, c->c_address));
2278 
2279     return 0;
2280   }
2281 
2282   /* XXX - using LI_SCOPE_LINK requires some tweaking */
2283   hints->li_scope = LI_SCOPE_GLOBAL | LI_SCOPE_SITE /* | LI_SCOPE_LINK */;
2284 
2285   for (res = NULL; res == NULL;) {
2286     if ((error = su_getlocalinfo(hints, &res)) < 0
2287 	&& error != ELI_NOADDRESS) {
2288       SU_DEBUG_1(("%s: su_localinfo: %s\n", __func__,
2289 		  su_gli_strerror(error)));
2290       return -1;
2291     }
2292     if (hints->li_scope & LI_SCOPE_HOST)
2293       break;
2294     hints->li_scope |= LI_SCOPE_HOST;
2295   }
2296 
2297   if (c->c_nettype != sdp_net_in ||
2298       (c->c_addrtype != sdp_addr_ip4 && c->c_addrtype != sdp_addr_ip6)) {
2299     c->c_nettype = sdp_net_in, c->c_addrtype = (sdp_addrtype_e)0;
2300     c->c_address = strcpy(buffer, "");
2301   }
2302 
2303   switch (ss->ss_af) {
2304   case SOA_AF_IP4_ONLY:
2305     ip4 = 1, ip6 = 0;
2306     break;
2307   case SOA_AF_IP6_ONLY:
2308     ip6 = 1, ip4 = 0;
2309     break;
2310   case SOA_AF_IP4_IP6:
2311     ip4 = 2, ip6 = 1;
2312     break;
2313   case SOA_AF_IP6_IP4:
2314     ip4 = 1, ip6 = 2;
2315     break;
2316   default:
2317     ip4 = ip6 = 1;
2318   }
2319 
2320   if (ip4 && ip6) {
2321     /* Prefer address family already used in session, if any */
2322     sdp_addrtype_e addrtype = (sdp_addrtype_e)0;
2323     char const *because = "error";
2324 
2325     if (sdp && sdp->sdp_connection &&
2326 	sdp->sdp_connection->c_nettype == sdp_net_in) {
2327       addrtype = sdp->sdp_connection->c_addrtype;
2328       because = "an existing c= line";
2329     }
2330     else if (sdp) {
2331       int mip4 = 0, mip6 = 0;
2332       sdp_media_t const *m;
2333 
2334       for (m = sdp->sdp_media; m; m = m->m_next) {
2335 	sdp_connection_t const *mc;
2336 
2337 	if (m->m_rejected)
2338 	  continue;
2339 
2340 	for (mc = m->m_connections; mc; mc = mc->c_next) {
2341 	  if (mc->c_nettype == sdp_net_in) {
2342 	    if (mc->c_addrtype == sdp_addr_ip4)
2343 	      mip4++;
2344 	    else if (mc->c_addrtype == sdp_addr_ip6)
2345 	      mip6++;
2346 	  }
2347 	}
2348       }
2349 
2350       if (mip4 && mip6)
2351 	/* Mixed. */;
2352       else if (mip4)
2353 	addrtype = sdp_addr_ip4, because = "an existing c= line under m=";
2354       else if (mip6)
2355 	addrtype = sdp_addr_ip6, because = "an existing c= line under m=";
2356     }
2357 
2358     if (addrtype == 0)
2359       addrtype = c->c_addrtype, because = "the user sdp";
2360 
2361     if (addrtype == sdp_addr_ip4) {
2362       if (ip6 >= ip4)
2363 	SU_DEBUG_5(("%s: prefer %s because of %s\n", __func__, "IP4", because));
2364       ip4 = 2, ip6 = 1;
2365     }
2366     else if (addrtype == sdp_addr_ip6) {
2367       if (ip4 >= ip6)
2368 	SU_DEBUG_5(("%s: prefer %s because of %s\n", __func__, "IP4", because));
2369       ip6 = 2, ip4 = 1;
2370     }
2371   }
2372 
2373   li = NULL, li4 = NULL, li6 = NULL;
2374 
2375   if (li == NULL && ss->ss_address) {
2376     li = best_listed_address_in_localinfo(res, ss->ss_address, ip4, ip6);
2377     if (li)
2378       source = "local address from SOATAG_ADDRESS() list";
2379   }
2380 
2381   if (li == NULL && ss->ss_address && sdp) {
2382     sdp_connection_t const *c;
2383     c = best_listed_address_in_session(sdp, ss->ss_address, ip4, ip6);
2384     if (c) {
2385       li = memset(li0, 0, sizeof li0);
2386       if (c->c_addrtype == sdp_addr_ip4)
2387 	li0->li_family = AF_INET;
2388 #if SU_HAVE_IN6
2389       else
2390 	li0->li_family = AF_INET6;
2391 #endif
2392       li0->li_canonname = (char *)c->c_address;
2393       source = "address from SOATAG_ADDRESS() list already in session";
2394     }
2395   }
2396 
2397   if (li == NULL && ss->ss_address) {
2398     memset(li0, 0, sizeof li0);
2399     li0->li_canonname = abuffer;
2400     li = best_listed_address(li0, ss->ss_address, ip4, ip6);
2401     if (li)
2402       source = "address from SOATAG_ADDRESS() list";
2403   }
2404 
2405   if (li == NULL) {
2406     for (li = res; li; li = li->li_next) {
2407       if (su_casematch(li->li_canonname, c->c_address))
2408 	break;
2409     }
2410     if (li)
2411       source = "the proposed local address";
2412   }
2413 
2414   /* Check if session-level c= address is local */
2415   if (li == NULL && sdp && sdp->sdp_connection) {
2416     for (li = res; li; li = li->li_next) {
2417       if (!su_casematch(li->li_canonname, sdp->sdp_connection->c_address))
2418 	continue;
2419 #if HAVE_SIN6
2420       if (li->li_family == AF_INET6) {
2421 	if (ip6 >= ip4)
2422 	  break;
2423 	else if (!li6)
2424 	  li6 = li;		/* Best IP6 address */
2425       }
2426 #endif
2427       else if (li->li_family == AF_INET) {
2428 	if (ip4 >= ip6)
2429 	  break;
2430 	else if (!li4)
2431 	  li4 = li;		/* Best IP4 address */
2432       }
2433     }
2434 
2435     if (li == NULL && ip4)
2436       li = li4;
2437     if (li == NULL && ip6)
2438       li = li6;
2439     if (li)
2440       source = "an existing session-level c= line";
2441   }
2442 
2443   /* Check for best local media-level c= address */
2444   if (li == NULL && sdp) {
2445     sdp_media_t const *m;
2446 
2447     for (m = sdp->sdp_media; m; m = m->m_next) {
2448       sdp_connection_t const *mc;
2449 
2450       if (m->m_rejected)
2451 	continue;
2452 
2453       for (mc = m->m_connections; mc; mc = mc->c_next) {
2454 	for (li = res; li; li = li->li_next) {
2455 	  if (!su_casematch(li->li_canonname, mc->c_address))
2456 	    continue;
2457 #if HAVE_SIN6
2458 	  if (li->li_family == AF_INET6) {
2459 	    if (ip6 > ip4)
2460 	      break;
2461 	    else if (!li6)
2462 	      li6 = li;		/* Best IP6 address */
2463 	  }
2464 #endif
2465 	  else if (li->li_family == AF_INET) {
2466 	    if (ip4 > ip6)
2467 	      break;
2468 	    else if (!li4)
2469 	      li4 = li;		/* Best IP4 address */
2470 	  }
2471 	}
2472       }
2473 
2474       if (li)
2475 	break;
2476     }
2477 
2478     if (li == NULL && ip4)
2479       li = li4;
2480     if (li == NULL && ip6)
2481       li = li6;
2482     if (li)
2483       source = "an existing c= address from media descriptions";
2484   }
2485 
2486   /* Check if o= address is local */
2487   if (li == NULL && sdp && sdp->sdp_origin) {
2488     char const *address = sdp->sdp_origin->o_address->c_address;
2489 
2490     for (li = res; li; li = li->li_next) {
2491       if (!su_casematch(li->li_canonname, address))
2492 	continue;
2493 #if HAVE_SIN6
2494       if (li->li_family == AF_INET6) {
2495 	if (ip6 >= ip4)
2496 	  break;
2497 	else if (!li6)
2498 	  li6 = li;		/* Best IP6 address */
2499       }
2500 #endif
2501       else if (li->li_family == AF_INET) {
2502 	if (ip4 >= ip6)
2503 	  break;
2504 	else if (!li4)
2505 	  li4 = li;		/* Best IP4 address */
2506       }
2507     }
2508 
2509     if (li == NULL && ip4)
2510       li = li4;
2511     if (li == NULL && ip6)
2512       li = li6;
2513     if (li)
2514       source = "an existing address from o= line";
2515   }
2516 
2517   if (li == NULL) {
2518     for (li = res; li; li = li->li_next) {
2519       if (li->li_family == AF_INET) {
2520 	if (ip4 >= ip6)
2521 	  break;
2522 	else if (!li4)
2523 	  li4 = li;		/* Best IP4 address */
2524       }
2525 #if HAVE_SIN6
2526       else if (li->li_family == AF_INET6) {
2527 	if (ip6 >= ip4)
2528 	  break;
2529 	else if (!li6)
2530 	  li6 = li;		/* Best IP6 address */
2531       }
2532 #endif
2533     }
2534 
2535     if (li == NULL && ip4)
2536       li = li4;
2537     if (li == NULL && ip6)
2538       li = li6;
2539 
2540     if (li)
2541       source = "a local address";
2542   }
2543 
2544   if (li) {
2545     char const *typename;
2546 
2547     if (li->li_family == AF_INET)
2548       c->c_nettype = sdp_net_in,  c->c_addrtype = sdp_addr_ip4, typename = "IP4";
2549 #if HAVE_SIN6
2550     else if (li->li_family == AF_INET6)
2551       c->c_nettype = sdp_net_in,  c->c_addrtype = sdp_addr_ip6, typename = "IP6";
2552 #endif
2553     else
2554       typename = "???";
2555 
2556     assert(strlen(li->li_canonname) < 64);
2557     c->c_address = strcpy(buffer, li->li_canonname);
2558 
2559     SU_DEBUG_5(("%s: selected IN %s %s (%s)\n", __func__,
2560 		typename, li->li_canonname, source));
2561   }
2562 
2563   su_freelocalinfo(res);
2564 
2565   if (!li)
2566     return -1;
2567   else
2568     return 0;
2569 }
2570 
2571 /* Search for first entry from SOATAG_ADDRESS() list on localinfo list */
2572 static su_localinfo_t const *
best_listed_address_in_localinfo(su_localinfo_t const * res,char const * address,int ip4,int ip6)2573 best_listed_address_in_localinfo(su_localinfo_t const *res,
2574 				 char const *address,
2575 				 int ip4,
2576 				 int ip6)
2577 {
2578   su_localinfo_t const  *li = NULL, *best = NULL;
2579   size_t n;
2580 
2581   SU_DEBUG_3(("%s: searching for %s from list \"%s\"\n",
2582 	      __func__, ip6 && !ip4 ? "IP6 " : !ip6 && ip4 ? "IP4 " : "",
2583 	      address));
2584 
2585   for (; address[0]; address += n + strspn(address, ", ")) {
2586     n = strcspn(address, ", ");
2587     if (n == 0)
2588       continue;
2589 
2590     for (li = res; li; li = li->li_next) {
2591       if (su_casenmatch(li->li_canonname, address, n) &&
2592 	  li->li_canonname[n] == '\0')
2593 	break;
2594     }
2595 
2596     if (li == NULL)
2597       continue;
2598 #if HAVE_SIN6
2599     else if (li->li_family == AF_INET6) {
2600       if (ip6 >= ip4)
2601 	return li;
2602       else if (ip6 && !best)
2603 	best = li;		/* Best IP6 address */
2604     }
2605 #endif
2606     else if (li->li_family == AF_INET) {
2607       if (ip4 >= ip6)
2608 	return li;
2609       else if (ip4 && !best)
2610 	best = li;		/* Best IP4 address */
2611     }
2612   }
2613 
2614   return best;
2615 }
2616 
2617 /* Search for first entry from SOATAG_ADDRESS() list in session */
2618 static sdp_connection_t const *
best_listed_address_in_session(sdp_session_t const * sdp,char const * address0,int ip4,int ip6)2619 best_listed_address_in_session(sdp_session_t const *sdp,
2620 			       char const *address0,
2621 			       int ip4,
2622 			       int ip6)
2623 {
2624   sdp_connection_t *c = NULL, *best = NULL;
2625   sdp_media_t *m;
2626   char const *address;
2627   size_t n;
2628 
2629   for (address = address0; address[0]; address += n + strspn(address, ", ")) {
2630     n = strcspn(address, ", ");
2631     if (n == 0)
2632       continue;
2633 
2634     c = sdp->sdp_connection;
2635 
2636     if (c && su_casenmatch(c->c_address, address, n) && c->c_address[n] == 0)
2637       ;
2638     else
2639       for (m = sdp->sdp_media; m; m = m->m_next) {
2640 	if (m->m_connections && m->m_connections != sdp->sdp_connection) {
2641 	  c = sdp->sdp_connection;
2642 	  if (c && su_casenmatch(c->c_address, address, n) && c->c_address[n] == 0)
2643 	    break;
2644 	  c = NULL;
2645 	}
2646       }
2647 
2648     if (c == NULL || c->c_nettype != sdp_net_in)
2649       continue;
2650 #if HAVE_SIN6
2651     else if (c->c_addrtype == sdp_addr_ip6) {
2652       if (ip6 >= ip4)
2653 	return c;
2654       else if (ip6 && !best)
2655 	best = c;		/* Best IP6 address */
2656     }
2657 #endif
2658     else if (c->c_addrtype == sdp_addr_ip4) {
2659       if (ip4 >= ip6)
2660 	return c;
2661       else if (ip4 && !best)
2662 	best = c;		/* Best IP4 address */
2663     }
2664   }
2665 
2666   if (best || sdp->sdp_origin == NULL)
2667     return best;
2668 
2669   /* Check if address on list is already been used on o= line */
2670   for (address = address0; address[0]; address += n + strspn(address, ", ")) {
2671     n = strcspn(address, ", ");
2672     if (n == 0)
2673       continue;
2674     c = sdp->sdp_origin->o_address;
2675 
2676     if (su_casenmatch(c->c_address, address, n) && c->c_address[n] != 0)
2677       continue;
2678 #if HAVE_SIN6
2679     else if (c->c_addrtype == sdp_addr_ip6) {
2680       if (ip6 >= ip4)
2681 	return c;
2682       else if (ip6 && !best)
2683 	best = c;		/* Best IP6 address */
2684     }
2685 #endif
2686     else if (c->c_addrtype == sdp_addr_ip4) {
2687       if (ip4 >= ip6)
2688 	return c;
2689       else if (ip4 && !best)
2690 	best = c;		/* Best IP4 address */
2691     }
2692   }
2693 
2694   return best;
2695 }
2696 
2697 static su_localinfo_t const *
best_listed_address(su_localinfo_t * li0,char const * address,int ip4,int ip6)2698 best_listed_address(su_localinfo_t *li0,
2699 		    char const *address,
2700 		    int ip4,
2701 		    int ip6)
2702 {
2703   size_t n, best = 0;
2704   char *buffer = (char *)li0->li_canonname;
2705 
2706   for (; address[0]; address += n + strspn(address + n, " ,")) {
2707     if ((n = span_ip6_address(address))) {
2708 #if SU_HAVE_IN6
2709       if (ip6 > ip4) {
2710 	li0->li_family = AF_INET6;
2711 	strncpy(buffer, address, n)[n] = '\0';
2712 	return li0;
2713       }
2714       else if (ip6 && !best) {
2715 	li0->li_family = AF_INET6;
2716 	strncpy(buffer, address, best = n)[n] = '\0';
2717       }
2718 #endif
2719     }
2720     else if ((n = span_ip4_address(address))) {
2721       if (ip4 > ip6) {
2722 	li0->li_family = AF_INET;
2723 	strncpy(buffer, address, n)[n] = '\0';
2724 	return li0;
2725       }
2726       else if (ip4 && !best) {
2727 	li0->li_family = AF_INET;
2728 	strncpy(buffer, address, best = n)[n] = '\0';
2729       }
2730     }
2731     else {
2732       n = strcspn(address, " ,");
2733     }
2734   }
2735 
2736   if (best)
2737     return li0;
2738   else
2739     return NULL;
2740 }
2741