1 /* $Id$ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <pjmedia/sdp.h>
21 #include <pjmedia/errno.h>
22 #include <pjlib-util/scanner.h>
23 #include <pj/array.h>
24 #include <pj/except.h>
25 #include <pj/log.h>
26 #include <pj/os.h>
27 #include <pj/string.h>
28 #include <pj/pool.h>
29 #include <pj/assert.h>
30 #include <pj/ctype.h>
31 
32 
33 enum {
34     SKIP_WS = 0,
35     SYNTAX_ERROR = 1,
36 };
37 // New token definition from RFC 4566 (SDP)
38 #define TOKEN		"!#$%&'*+-.^_`{|}~"
39 //#define TOKEN		"-.!%*_=`'~"
40 //#define TOKEN		"'`-./:?\"#$&*;=@[]^_`{|}+~!"
41 #define NTP_OFFSET	((pj_uint32_t)2208988800)
42 #define THIS_FILE	"sdp.c"
43 
44 typedef struct parse_context
45 {
46     pj_status_t last_error;
47 } parse_context;
48 
49 
50 /*
51  * Prototypes for line parser.
52  */
53 static void parse_version(pj_scanner *scanner, parse_context *ctx);
54 static void parse_origin(pj_scanner *scanner, pjmedia_sdp_session *ses,
55 			 parse_context *ctx);
56 static void parse_time(pj_scanner *scanner, pjmedia_sdp_session *ses,
57 		       parse_context *ctx);
58 static void parse_generic_line(pj_scanner *scanner, pj_str_t *str,
59 			       parse_context *ctx);
60 static void parse_connection_info(pj_scanner *scanner, pjmedia_sdp_conn *conn,
61 				  parse_context *ctx);
62 static void parse_bandwidth_info(pj_scanner *scanner, pjmedia_sdp_bandw *bandw,
63 				  parse_context *ctx);
64 static pjmedia_sdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner,
65 				    parse_context *ctx);
66 static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
67 			parse_context *ctx);
68 static void on_scanner_error(pj_scanner *scanner);
69 
70 /*
71  * Scanner character specification.
72  */
73 static int is_initialized;
74 static pj_cis_buf_t cis_buf;
75 static pj_cis_t cs_digit, cs_token;
76 
init_sdp_parser(void)77 static void init_sdp_parser(void)
78 {
79     if (is_initialized != 0)
80 	return;
81 
82     pj_enter_critical_section();
83 
84     if (is_initialized != 0) {
85 	pj_leave_critical_section();
86 	return;
87     }
88 
89     pj_cis_buf_init(&cis_buf);
90 
91     pj_cis_init(&cis_buf, &cs_token);
92     pj_cis_add_alpha(&cs_token);
93     pj_cis_add_num(&cs_token);
94     pj_cis_add_str(&cs_token, TOKEN);
95 
96     pj_cis_init(&cis_buf, &cs_digit);
97     pj_cis_add_num(&cs_digit);
98 
99     is_initialized = 1;
100     pj_leave_critical_section();
101 }
102 
pjmedia_sdp_attr_create(pj_pool_t * pool,const char * name,const pj_str_t * value)103 PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create( pj_pool_t *pool,
104 						   const char *name,
105 						   const pj_str_t *value)
106 {
107     pjmedia_sdp_attr *attr;
108 
109     PJ_ASSERT_RETURN(pool && name, NULL);
110 
111     attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
112     pj_strdup2(pool, &attr->name, name);
113 
114     if (value)
115 	pj_strdup_with_null(pool, &attr->value, value);
116     else {
117 	attr->value.ptr = NULL;
118 	attr->value.slen = 0;
119     }
120 
121     return attr;
122 }
123 
pjmedia_sdp_attr_clone(pj_pool_t * pool,const pjmedia_sdp_attr * rhs)124 PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_clone(pj_pool_t *pool,
125 						 const pjmedia_sdp_attr *rhs)
126 {
127     pjmedia_sdp_attr *attr;
128 
129     PJ_ASSERT_RETURN(pool && rhs, NULL);
130 
131     attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
132 
133     pj_strdup(pool, &attr->name, &rhs->name);
134     pj_strdup_with_null(pool, &attr->value, &rhs->value);
135 
136     return attr;
137 }
138 
pjmedia_sdp_attr_find(unsigned count,pjmedia_sdp_attr * const attr_array[],const pj_str_t * name,const pj_str_t * c_fmt)139 PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_find (unsigned count,
140 				       pjmedia_sdp_attr *const attr_array[],
141 				       const pj_str_t *name,
142 				       const pj_str_t *c_fmt)
143 {
144     unsigned i;
145     unsigned c_pt = 0xFFFF;
146 
147     PJ_ASSERT_RETURN(count <= PJMEDIA_MAX_SDP_ATTR, NULL);
148 
149     if (c_fmt)
150 	c_pt = pj_strtoul(c_fmt);
151 
152     for (i=0; i<count; ++i) {
153 	if (pj_strcmp(&attr_array[i]->name, name) == 0) {
154 	    const pjmedia_sdp_attr *a = attr_array[i];
155 	    if (c_fmt) {
156 		unsigned pt = (unsigned) pj_strtoul2(&a->value, NULL, 10);
157 		if (pt == c_pt) {
158 		    return (pjmedia_sdp_attr*)a;
159 		}
160 	    } else
161 		return (pjmedia_sdp_attr*)a;
162 	}
163     }
164     return NULL;
165 }
166 
pjmedia_sdp_attr_find2(unsigned count,pjmedia_sdp_attr * const attr_array[],const char * c_name,const pj_str_t * c_fmt)167 PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_find2(unsigned count,
168 				       pjmedia_sdp_attr *const attr_array[],
169 				       const char *c_name,
170 				       const pj_str_t *c_fmt)
171 {
172     pj_str_t name;
173 
174     name.ptr = (char*)c_name;
175     name.slen = pj_ansi_strlen(c_name);
176 
177     return pjmedia_sdp_attr_find(count, attr_array, &name, c_fmt);
178 }
179 
180 
181 
pjmedia_sdp_attr_add(unsigned * count,pjmedia_sdp_attr * attr_array[],pjmedia_sdp_attr * attr)182 PJ_DEF(pj_status_t) pjmedia_sdp_attr_add(unsigned *count,
183 					 pjmedia_sdp_attr *attr_array[],
184 					 pjmedia_sdp_attr *attr)
185 {
186     PJ_ASSERT_RETURN(count && attr_array && attr, PJ_EINVAL);
187     PJ_ASSERT_RETURN(*count < PJMEDIA_MAX_SDP_ATTR, PJ_ETOOMANY);
188 
189     attr_array[*count] = attr;
190     (*count)++;
191 
192     return PJ_SUCCESS;
193 }
194 
195 
pjmedia_sdp_attr_remove_all(unsigned * count,pjmedia_sdp_attr * attr_array[],const char * name)196 PJ_DEF(unsigned) pjmedia_sdp_attr_remove_all(unsigned *count,
197 					     pjmedia_sdp_attr *attr_array[],
198 					     const char *name)
199 {
200     unsigned i, removed = 0;
201     pj_str_t attr_name;
202 
203     PJ_ASSERT_RETURN(count && attr_array && name, PJ_EINVAL);
204     PJ_ASSERT_RETURN(*count <= PJMEDIA_MAX_SDP_ATTR, PJ_ETOOMANY);
205 
206     attr_name.ptr = (char*)name;
207     attr_name.slen = pj_ansi_strlen(name);
208 
209     for (i=0; i<*count; ) {
210 	if (pj_strcmp(&attr_array[i]->name, &attr_name)==0) {
211 	    pj_array_erase(attr_array, sizeof(pjmedia_sdp_attr*),
212 			   *count, i);
213 	    --(*count);
214 	    ++removed;
215 	} else {
216 	    ++i;
217 	}
218     }
219 
220     return removed;
221 }
222 
223 
pjmedia_sdp_attr_remove(unsigned * count,pjmedia_sdp_attr * attr_array[],pjmedia_sdp_attr * attr)224 PJ_DEF(pj_status_t) pjmedia_sdp_attr_remove( unsigned *count,
225 					     pjmedia_sdp_attr *attr_array[],
226 					     pjmedia_sdp_attr *attr )
227 {
228     unsigned i, removed=0;
229 
230     PJ_ASSERT_RETURN(count && attr_array && attr, PJ_EINVAL);
231     PJ_ASSERT_RETURN(*count <= PJMEDIA_MAX_SDP_ATTR, PJ_ETOOMANY);
232 
233     for (i=0; i<*count; ) {
234 	if (attr_array[i] == attr) {
235 	    pj_array_erase(attr_array, sizeof(pjmedia_sdp_attr*),
236 			   *count, i);
237 	    --(*count);
238 	    ++removed;
239 	} else {
240 	    ++i;
241 	}
242     }
243 
244     return removed ? PJ_SUCCESS : PJ_ENOTFOUND;
245 }
246 
247 
pjmedia_sdp_attr_get_rtpmap(const pjmedia_sdp_attr * attr,pjmedia_sdp_rtpmap * rtpmap)248 PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtpmap( const pjmedia_sdp_attr *attr,
249 						 pjmedia_sdp_rtpmap *rtpmap)
250 {
251     pj_scanner scanner;
252     pj_str_t token;
253     pj_status_t status = -1;
254     char term = 0;
255     PJ_USE_EXCEPTION;
256 
257     PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtpmap")==0, PJ_EINVALIDOP);
258 
259     if (attr->value.slen == 0)
260         return PJMEDIA_SDP_EINATTR;
261 
262     init_sdp_parser();
263 
264     /* Check if input is null terminated, and null terminate if
265      * necessary. Unfortunately this may crash the application if
266      * attribute was allocated from a read-only memory location.
267      * But this shouldn't happen as attribute's value normally is
268      * null terminated.
269      */
270     if (attr->value.ptr[attr->value.slen] != 0 &&
271 	attr->value.ptr[attr->value.slen] != '\r' &&
272 	attr->value.ptr[attr->value.slen] != '\n')
273     {
274 	pj_assert(!"Shouldn't happen");
275 	term = attr->value.ptr[attr->value.slen];
276 	attr->value.ptr[attr->value.slen] = '\0';
277     }
278 
279     /* The buffer passed to the scanner is not guaranteed to be NULL
280      * terminated, but should be safe. See ticket #2063.
281      */
282     pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen,
283 		 PJ_SCAN_AUTOSKIP_WS, &on_scanner_error);
284 
285     /* rtpmap sample:
286      *	a=rtpmap:98 L16/16000/2.
287      */
288 
289     /* Init */
290     rtpmap->pt.slen = rtpmap->param.slen = rtpmap->enc_name.slen = 0;
291     rtpmap->clock_rate = 0;
292 
293     /* Parse */
294     PJ_TRY {
295 
296 	/* Get payload type. */
297 	pj_scan_get(&scanner, &cs_token, &rtpmap->pt);
298 
299 
300 	/* Get encoding name. */
301 	pj_scan_get(&scanner, &cs_token, &rtpmap->enc_name);
302 
303 	/* Expecting '/' after encoding name. */
304 	if (pj_scan_get_char(&scanner) != '/') {
305 	    status = PJMEDIA_SDP_EINRTPMAP;
306 	    goto on_return;
307 	}
308 
309 
310 	/* Get the clock rate. */
311 	pj_scan_get(&scanner, &cs_digit, &token);
312 	rtpmap->clock_rate = pj_strtoul(&token);
313 
314 	/* Expecting either '/' or EOF */
315 	if (*scanner.curptr == '/') {
316 	    pj_scan_get_char(&scanner);
317 	    rtpmap->param.ptr = scanner.curptr;
318 	    rtpmap->param.slen = scanner.end - scanner.curptr;
319 	} else {
320 	    rtpmap->param.slen = 0;
321 	}
322 
323 	status = PJ_SUCCESS;
324     }
325     PJ_CATCH_ANY {
326 	status = PJMEDIA_SDP_EINRTPMAP;
327     }
328     PJ_END;
329 
330 
331 on_return:
332     pj_scan_fini(&scanner);
333     if (term) {
334 	attr->value.ptr[attr->value.slen] = term;
335     }
336     return status;
337 }
338 
pjmedia_sdp_attr_get_fmtp(const pjmedia_sdp_attr * attr,pjmedia_sdp_fmtp * fmtp)339 PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_fmtp( const pjmedia_sdp_attr *attr,
340 					       pjmedia_sdp_fmtp *fmtp)
341 {
342     const char *p = attr->value.ptr;
343     const char *end = attr->value.ptr + attr->value.slen;
344     pj_str_t token;
345 
346     PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "fmtp")==0, PJ_EINVALIDOP);
347 
348     if (attr->value.slen == 0)
349         return PJMEDIA_SDP_EINATTR;
350 
351     /* fmtp BNF:
352      *	a=fmtp:<format> <format specific parameter>
353      */
354 
355     /* Get format. */
356     token.ptr = (char*)p;
357     while (pj_isdigit(*p) && p!=end)
358 	++p;
359     token.slen = p - token.ptr;
360     if (token.slen == 0)
361 	return PJMEDIA_SDP_EINFMTP;
362 
363     fmtp->fmt = token;
364 
365     /* Expecting space after format. */
366     if (*p != ' ') return PJMEDIA_SDP_EINFMTP;
367 
368     /* Get space. */
369     ++p;
370 
371     /* Set the remaining string as fmtp format parameter. */
372     fmtp->fmt_param.ptr = (char*)p;
373     fmtp->fmt_param.slen = end - p;
374 
375     return PJ_SUCCESS;
376 }
377 
378 
pjmedia_sdp_attr_get_rtcp(const pjmedia_sdp_attr * attr,pjmedia_sdp_rtcp_attr * rtcp)379 PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtcp(const pjmedia_sdp_attr *attr,
380 					      pjmedia_sdp_rtcp_attr *rtcp)
381 {
382     pj_scanner scanner;
383     pj_str_t token;
384     pj_status_t status = -1;
385     PJ_USE_EXCEPTION;
386 
387     PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtcp")==0, PJ_EINVALIDOP);
388 
389     if (attr->value.slen == 0)
390         return PJMEDIA_SDP_EINATTR;
391 
392     init_sdp_parser();
393 
394     /* fmtp BNF:
395      *	a=rtcp:<port> [nettype addrtype address]
396      */
397 
398     /* The buffer passed to the scanner is not guaranteed to be NULL
399      * terminated, but should be safe. See ticket #2063.
400      */
401     pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen,
402 		 PJ_SCAN_AUTOSKIP_WS, &on_scanner_error);
403 
404     /* Init */
405     rtcp->net_type.slen = rtcp->addr_type.slen = rtcp->addr.slen = 0;
406 
407     /* Parse */
408     PJ_TRY {
409 
410 	/* Get the port */
411 	pj_scan_get(&scanner, &cs_token, &token);
412 	rtcp->port = pj_strtoul(&token);
413 
414 	/* Have address? */
415 	if (!pj_scan_is_eof(&scanner)) {
416 
417 	    /* Get network type */
418 	    pj_scan_get(&scanner, &cs_token, &rtcp->net_type);
419 
420 	    /* Get address type */
421 	    pj_scan_get(&scanner, &cs_token, &rtcp->addr_type);
422 
423 	    /* Get the address */
424 	    //pj_scan_get(&scanner, &cs_token, &rtcp->addr);
425 	    pj_scan_get_until_chr(&scanner, "/ \t\r\n", &rtcp->addr);
426 
427 	}
428 
429 	status = PJ_SUCCESS;
430 
431     }
432     PJ_CATCH_ANY {
433 	status = PJMEDIA_SDP_EINRTCP;
434     }
435     PJ_END;
436 
437     pj_scan_fini(&scanner);
438     return status;
439 }
440 
441 
pjmedia_sdp_attr_create_rtcp(pj_pool_t * pool,const pj_sockaddr * a)442 PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create_rtcp(pj_pool_t *pool,
443 						       const pj_sockaddr *a)
444 {
445     enum {
446 	ATTR_LEN = PJ_INET6_ADDRSTRLEN+16
447     };
448     char tmp_addr[PJ_INET6_ADDRSTRLEN];
449     pjmedia_sdp_attr *attr;
450 
451     attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
452     attr->name = pj_str("rtcp");
453     attr->value.ptr = (char*) pj_pool_alloc(pool, ATTR_LEN);
454     if (a->addr.sa_family == pj_AF_INET()) {
455 	attr->value.slen =
456 	    pj_ansi_snprintf(attr->value.ptr, ATTR_LEN,
457 			    "%u IN IP4 %s",
458 			    pj_sockaddr_get_port(a),
459 			    pj_sockaddr_print(a, tmp_addr,
460 					      sizeof(tmp_addr), 0));
461     } else if (a->addr.sa_family == pj_AF_INET6()) {
462 	attr->value.slen =
463 	    pj_ansi_snprintf(attr->value.ptr, ATTR_LEN,
464 			    "%u IN IP6 %s",
465 			    pj_sockaddr_get_port(a),
466 			    pj_sockaddr_print(a, tmp_addr,
467 					      sizeof(tmp_addr), 0));
468 
469     } else {
470 	pj_assert(!"Unsupported address family");
471 	return NULL;
472     }
473 
474     return attr;
475 }
476 
477 
pjmedia_sdp_attr_get_ssrc(const pjmedia_sdp_attr * attr,pjmedia_sdp_ssrc_attr * ssrc)478 PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_ssrc(const pjmedia_sdp_attr *attr,
479 					      pjmedia_sdp_ssrc_attr *ssrc)
480 {
481     pj_scanner scanner;
482     pj_str_t token;
483     pj_status_t status = -1;
484     PJ_USE_EXCEPTION;
485 
486     PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "ssrc")==0, PJ_EINVALIDOP);
487 
488     if (attr->value.slen == 0)
489         return PJMEDIA_SDP_EINATTR;
490 
491     init_sdp_parser();
492 
493     /* ssrc BNF:
494      *  a=ssrc:<ssrc-id> <attribute>
495      *	a=ssrc:<ssrc-id> <attribute>:<value>
496      */
497 
498     /* The buffer passed to the scanner is not guaranteed to be NULL
499      * terminated, but should be safe. See ticket #2063.
500      */
501     pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen,
502 		 PJ_SCAN_AUTOSKIP_WS, &on_scanner_error);
503 
504     /* Init */
505     pj_bzero(ssrc, sizeof(*ssrc));
506 
507     /* Parse */
508     PJ_TRY {
509         pj_str_t scan_attr;
510 
511 	/* Get the ssrc */
512 	pj_scan_get(&scanner, &cs_digit, &token);
513 	ssrc->ssrc = pj_strtoul(&token);
514 
515     	pj_scan_get_char(&scanner);
516 	pj_scan_get(&scanner, &cs_token, &scan_attr);
517 
518 	/* Get cname attribute, if any */
519 	if (!pj_scan_is_eof(&scanner) &&
520 	    pj_scan_get_char(&scanner) == ':' &&
521 	    pj_strcmp2(&scan_attr, "cname"))
522 	{
523 	    pj_scan_get(&scanner, &cs_token, &ssrc->cname);
524 	}
525 
526 	status = PJ_SUCCESS;
527 
528     }
529     PJ_CATCH_ANY {
530 	status = PJMEDIA_SDP_EINSSRC;
531     }
532     PJ_END;
533 
534     pj_scan_fini(&scanner);
535     return status;
536 }
537 
538 
pjmedia_sdp_attr_create_ssrc(pj_pool_t * pool,pj_uint32_t ssrc,const pj_str_t * cname)539 PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create_ssrc( pj_pool_t *pool,
540 							pj_uint32_t ssrc,
541 							const pj_str_t *cname)
542 {
543     pjmedia_sdp_attr *attr;
544 
545     if (cname->slen == 0)
546         return NULL;
547 
548     attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
549     attr->name = pj_str("ssrc");
550     attr->value.ptr = (char*) pj_pool_alloc(pool, cname->slen+7 /* " cname:"*/
551     						  + 10 /* 32-bit integer */
552     						  + 1 /* NULL */);
553     attr->value.slen = pj_ansi_snprintf(attr->value.ptr, cname->slen+18,
554     					"%d cname:%.*s", ssrc,
555 			   	   	(int)cname->slen, cname->ptr);
556 
557     return attr;
558 }
559 
560 
pjmedia_sdp_attr_to_rtpmap(pj_pool_t * pool,const pjmedia_sdp_attr * attr,pjmedia_sdp_rtpmap ** p_rtpmap)561 PJ_DEF(pj_status_t) pjmedia_sdp_attr_to_rtpmap(pj_pool_t *pool,
562 					       const pjmedia_sdp_attr *attr,
563 					       pjmedia_sdp_rtpmap **p_rtpmap)
564 {
565     PJ_ASSERT_RETURN(pool && attr && p_rtpmap, PJ_EINVAL);
566 
567     *p_rtpmap = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_rtpmap);
568     PJ_ASSERT_RETURN(*p_rtpmap, PJ_ENOMEM);
569 
570     return pjmedia_sdp_attr_get_rtpmap(attr, *p_rtpmap);
571 }
572 
573 
pjmedia_sdp_rtpmap_to_attr(pj_pool_t * pool,const pjmedia_sdp_rtpmap * rtpmap,pjmedia_sdp_attr ** p_attr)574 PJ_DEF(pj_status_t) pjmedia_sdp_rtpmap_to_attr(pj_pool_t *pool,
575 					       const pjmedia_sdp_rtpmap *rtpmap,
576 					       pjmedia_sdp_attr **p_attr)
577 {
578     pjmedia_sdp_attr *attr;
579     char tempbuf[128];
580     int len;
581 
582     /* Check arguments. */
583     PJ_ASSERT_RETURN(pool && rtpmap && p_attr, PJ_EINVAL);
584 
585     /* Check that mandatory attributes are specified. */
586     PJ_ASSERT_RETURN(rtpmap->enc_name.slen && rtpmap->clock_rate,
587 		     PJMEDIA_SDP_EINRTPMAP);
588 
589 
590     attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
591     PJ_ASSERT_RETURN(attr != NULL, PJ_ENOMEM);
592 
593     attr->name.ptr = "rtpmap";
594     attr->name.slen = 6;
595 
596     /* Format: ":pt enc_name/clock_rate[/param]" */
597     len = pj_ansi_snprintf(tempbuf, sizeof(tempbuf),
598 			   "%.*s %.*s/%u%s%.*s",
599 			   (int)rtpmap->pt.slen,
600 			   rtpmap->pt.ptr,
601 			   (int)rtpmap->enc_name.slen,
602 			   rtpmap->enc_name.ptr,
603 			   rtpmap->clock_rate,
604 			   (rtpmap->param.slen ? "/" : ""),
605 			   (int)rtpmap->param.slen,
606 			   rtpmap->param.ptr);
607 
608     if (len < 1 || len >= (int)sizeof(tempbuf))
609 	return PJMEDIA_SDP_ERTPMAPTOOLONG;
610 
611     attr->value.slen = len;
612     attr->value.ptr = (char*) pj_pool_alloc(pool, attr->value.slen+1);
613     pj_memcpy(attr->value.ptr, tempbuf, attr->value.slen+1);
614 
615     *p_attr = attr;
616     return PJ_SUCCESS;
617 }
618 
619 
print_connection_info(pjmedia_sdp_conn * c,char * buf,int len)620 static int print_connection_info( pjmedia_sdp_conn *c, char *buf, int len)
621 {
622     int printed;
623 
624     printed = pj_ansi_snprintf(buf, len, "c=%.*s %.*s %.*s\r\n",
625 			       (int)c->net_type.slen,
626 			       c->net_type.ptr,
627 			       (int)c->addr_type.slen,
628 			       c->addr_type.ptr,
629 			       (int)c->addr.slen,
630 			       c->addr.ptr);
631     if (printed < 1 || printed >= len)
632 	return -1;
633 
634     return printed;
635 }
636 
637 
pjmedia_sdp_conn_clone(pj_pool_t * pool,const pjmedia_sdp_conn * rhs)638 PJ_DEF(pjmedia_sdp_conn*) pjmedia_sdp_conn_clone (pj_pool_t *pool,
639 						  const pjmedia_sdp_conn *rhs)
640 {
641     pjmedia_sdp_conn *c = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_conn);
642     if (!c) return NULL;
643 
644     if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL;
645     if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL;
646     if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL;
647 
648     return c;
649 }
650 
651 PJ_DEF(pjmedia_sdp_bandw*)
pjmedia_sdp_bandw_clone(pj_pool_t * pool,const pjmedia_sdp_bandw * rhs)652 pjmedia_sdp_bandw_clone (pj_pool_t *pool,
653 			 const pjmedia_sdp_bandw *rhs)
654 {
655     pjmedia_sdp_bandw *b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw);
656     if (!b) return NULL;
657 
658     if (!pj_strdup (pool, &b->modifier, &rhs->modifier)) return NULL;
659     b->value = rhs->value;
660 
661     return b;
662 }
663 
print_bandw(const pjmedia_sdp_bandw * bandw,char * buf,pj_size_t len)664 static pj_ssize_t print_bandw(const pjmedia_sdp_bandw *bandw,
665 			      char *buf, pj_size_t len)
666 {
667     char *p = buf;
668 
669     if ((int)len < bandw->modifier.slen + 10 + 5)
670 	return -1;
671 
672     *p++ = 'b';
673     *p++ = '=';
674     pj_memcpy(p, bandw->modifier.ptr, bandw->modifier.slen);
675     p += bandw->modifier.slen;
676     *p++ = ':';
677     p += pj_utoa(bandw->value, p);
678 
679     *p++ = '\r';
680     *p++ = '\n';
681     return p-buf;
682 }
683 
print_attr(const pjmedia_sdp_attr * attr,char * buf,pj_size_t len)684 static pj_ssize_t print_attr(const pjmedia_sdp_attr *attr,
685 			     char *buf, pj_size_t len)
686 {
687     char *p = buf;
688 
689     if ((int)len < attr->name.slen + attr->value.slen + 10)
690 	return -1;
691 
692     *p++ = 'a';
693     *p++ = '=';
694     pj_memcpy(p, attr->name.ptr, attr->name.slen);
695     p += attr->name.slen;
696 
697 
698     if (attr->value.slen) {
699 	*p++ = ':';
700 	pj_memcpy(p, attr->value.ptr, attr->value.slen);
701 	p += attr->value.slen;
702     }
703 
704     *p++ = '\r';
705     *p++ = '\n';
706     return p-buf;
707 }
708 
print_media_desc(pjmedia_sdp_media * m,char * buf,int len)709 static int print_media_desc( pjmedia_sdp_media *m, char *buf, int len)
710 {
711     char *p = buf;
712     char *end = buf+len;
713     unsigned i;
714     int printed;
715 
716     /* check length for the "m=" line. */
717     if (len < m->desc.media.slen+m->desc.transport.slen+12+24) {
718 	return -1;
719     }
720     *p++ = 'm';	    /* m= */
721     *p++ = '=';
722     pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen);
723     p += m->desc.media.slen;
724     *p++ = ' ';
725     printed = pj_utoa(m->desc.port, p);
726     p += printed;
727     if (m->desc.port_count > 1) {
728 	*p++ = '/';
729 	printed = pj_utoa(m->desc.port_count, p);
730 	p += printed;
731     }
732     *p++ = ' ';
733     pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen);
734     p += m->desc.transport.slen;
735     for (i=0; i<m->desc.fmt_count; ++i) {
736 	*p++ = ' ';
737 	pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen);
738 	p += m->desc.fmt[i].slen;
739     }
740     *p++ = '\r';
741     *p++ = '\n';
742 
743     /* print connection info, if present. */
744     if (m->conn) {
745 	printed = print_connection_info(m->conn, p, (int)(end-p));
746 	if (printed < 0) {
747 	    return -1;
748 	}
749 	p += printed;
750     }
751 
752     /* print optional bandwidth info. */
753     for (i=0; i<m->bandw_count; ++i) {
754 	printed = (int)print_bandw(m->bandw[i], p, end-p);
755 	if (printed < 0) {
756 	    return -1;
757 	}
758 	p += printed;
759     }
760 
761     /* print attributes. */
762     for (i=0; i<m->attr_count; ++i) {
763 	printed = (int)print_attr(m->attr[i], p, end-p);
764 	if (printed < 0) {
765 	    return -1;
766 	}
767 	p += printed;
768     }
769 
770     return (int)(p-buf);
771 }
772 
pjmedia_sdp_media_clone(pj_pool_t * pool,const pjmedia_sdp_media * rhs)773 PJ_DEF(pjmedia_sdp_media*) pjmedia_sdp_media_clone(
774 						 pj_pool_t *pool,
775 						 const pjmedia_sdp_media *rhs)
776 {
777     unsigned int i;
778     pjmedia_sdp_media *m = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_media);
779     PJ_ASSERT_RETURN(m != NULL, NULL);
780 
781     pj_strdup (pool, &m->desc.media, &rhs->desc.media);
782     m->desc.port = rhs->desc.port;
783     m->desc.port_count = rhs->desc.port_count;
784     pj_strdup (pool, &m->desc.transport, &rhs->desc.transport);
785     m->desc.fmt_count = rhs->desc.fmt_count;
786     for (i=0; i<rhs->desc.fmt_count; ++i)
787 	pj_strdup(pool, &m->desc.fmt[i], &rhs->desc.fmt[i]);
788 
789     if (rhs->conn) {
790 	m->conn = pjmedia_sdp_conn_clone (pool, rhs->conn);
791 	PJ_ASSERT_RETURN(m->conn != NULL, NULL);
792     } else {
793 	m->conn = NULL;
794     }
795 
796     m->bandw_count = rhs->bandw_count;
797     for (i=0; i < rhs->bandw_count; ++i) {
798 	m->bandw[i] = pjmedia_sdp_bandw_clone (pool, rhs->bandw[i]);
799 	PJ_ASSERT_RETURN(m->bandw[i] != NULL, NULL);
800     }
801 
802     m->attr_count = rhs->attr_count;
803     for (i=0; i < rhs->attr_count; ++i) {
804 	m->attr[i] = pjmedia_sdp_attr_clone (pool, rhs->attr[i]);
805 	PJ_ASSERT_RETURN(m->attr[i] != NULL, NULL);
806     }
807 
808     return m;
809 }
810 
pjmedia_sdp_media_find_attr(const pjmedia_sdp_media * m,const pj_str_t * name,const pj_str_t * fmt)811 PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_media_find_attr(
812 				const pjmedia_sdp_media *m,
813 				const pj_str_t *name, const pj_str_t *fmt)
814 {
815     PJ_ASSERT_RETURN(m && name, NULL);
816     return pjmedia_sdp_attr_find(m->attr_count, m->attr, name, fmt);
817 }
818 
819 
820 
pjmedia_sdp_media_find_attr2(const pjmedia_sdp_media * m,const char * name,const pj_str_t * fmt)821 PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_media_find_attr2(
822 				const pjmedia_sdp_media *m,
823 				const char *name, const pj_str_t *fmt)
824 {
825     PJ_ASSERT_RETURN(m && name, NULL);
826     return pjmedia_sdp_attr_find2(m->attr_count, m->attr, name, fmt);
827 }
828 
829 
pjmedia_sdp_media_add_attr(pjmedia_sdp_media * m,pjmedia_sdp_attr * attr)830 PJ_DEF(pj_status_t) pjmedia_sdp_media_add_attr( pjmedia_sdp_media *m,
831 						pjmedia_sdp_attr *attr)
832 {
833     return pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
834 }
835 
pjmedia_sdp_session_add_attr(pjmedia_sdp_session * s,pjmedia_sdp_attr * attr)836 PJ_DEF(pj_status_t) pjmedia_sdp_session_add_attr(pjmedia_sdp_session *s,
837 						 pjmedia_sdp_attr *attr)
838 {
839     return pjmedia_sdp_attr_add(&s->attr_count, s->attr, attr);
840 }
841 
pjmedia_sdp_media_remove_all_attr(pjmedia_sdp_media * m,const char * name)842 PJ_DEF(unsigned) pjmedia_sdp_media_remove_all_attr(pjmedia_sdp_media *m,
843 						   const char *name)
844 {
845     return pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, name);
846 }
847 
pjmedia_sdp_media_remove_attr(pjmedia_sdp_media * m,pjmedia_sdp_attr * attr)848 PJ_DEF(pj_status_t) pjmedia_sdp_media_remove_attr(pjmedia_sdp_media *m,
849 			      			  pjmedia_sdp_attr *attr)
850 {
851     return pjmedia_sdp_attr_remove(&m->attr_count, m->attr, attr);
852 }
853 
print_session(const pjmedia_sdp_session * ses,char * buf,pj_ssize_t len)854 static int print_session(const pjmedia_sdp_session *ses,
855 			 char *buf, pj_ssize_t len)
856 {
857     char *p = buf;
858     char *end = buf+len;
859     unsigned i;
860     int printed;
861 
862     /* Check length for v= and o= lines. */
863     if (len < 5+
864 	      2+ses->origin.user.slen+18+
865 	      ses->origin.net_type.slen+ses->origin.addr.slen + 2)
866     {
867 	return -1;
868     }
869 
870     /* SDP version (v= line) */
871     pj_memcpy(p, "v=0\r\n", 5);
872     p += 5;
873 
874     /* Owner (o=) line. */
875     *p++ = 'o';
876     *p++ = '=';
877     pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen);
878     p += ses->origin.user.slen;
879     *p++ = ' ';
880     printed = pj_utoa(ses->origin.id, p);
881     p += printed;
882     *p++ = ' ';
883     printed = pj_utoa(ses->origin.version, p);
884     p += printed;
885     *p++ = ' ';
886     pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen);
887     p += ses->origin.net_type.slen;
888     *p++ = ' ';
889     pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen);
890     p += ses->origin.addr_type.slen;
891     *p++ = ' ';
892     pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen);
893     p += ses->origin.addr.slen;
894     *p++ = '\r';
895     *p++ = '\n';
896 
897     /* Session name (s=) line. */
898     if ((end-p)  < 8+ses->name.slen) {
899 	return -1;
900     }
901     *p++ = 's';
902     *p++ = '=';
903     pj_memcpy(p, ses->name.ptr, ses->name.slen);
904     p += ses->name.slen;
905     *p++ = '\r';
906     *p++ = '\n';
907 
908     /* Connection line (c=) if exist. */
909     if (ses->conn) {
910 	printed = print_connection_info(ses->conn, p, (int)(end-p));
911 	if (printed < 1) {
912 	    return -1;
913 	}
914 	p += printed;
915     }
916 
917     /* print optional bandwidth info. */
918     for (i=0; i<ses->bandw_count; ++i) {
919 	printed = (int)print_bandw(ses->bandw[i], p, end-p);
920 	if (printed < 1) {
921 	    return -1;
922 	}
923 	p += printed;
924     }
925 
926     /* Time */
927     if ((end-p) < 24) {
928 	return -1;
929     }
930     *p++ = 't';
931     *p++ = '=';
932     printed = pj_utoa(ses->time.start, p);
933     p += printed;
934     *p++ = ' ';
935     printed = pj_utoa(ses->time.stop, p);
936     p += printed;
937     *p++ = '\r';
938     *p++ = '\n';
939 
940     /* Print all attribute (a=) lines. */
941     for (i=0; i<ses->attr_count; ++i) {
942 	printed = (int)print_attr(ses->attr[i], p, end-p);
943 	if (printed < 0) {
944 	    return -1;
945 	}
946 	p += printed;
947     }
948 
949     /* Print media (m=) lines. */
950     for (i=0; i<ses->media_count; ++i) {
951 	printed = print_media_desc(ses->media[i], p, (int)(end-p));
952 	if (printed < 0) {
953 	    return -1;
954 	}
955 	p += printed;
956     }
957 
958     return (int)(p-buf);
959 }
960 
961 /******************************************************************************
962  * PARSERS
963  */
964 
parse_version(pj_scanner * scanner,parse_context * ctx)965 static void parse_version(pj_scanner *scanner, parse_context *ctx)
966 {
967     ctx->last_error = PJMEDIA_SDP_EINVER;
968 
969     /* check equal sign */
970     if (*(scanner->curptr+1) != '=') {
971 	on_scanner_error(scanner);
972 	return;
973     }
974 
975     /* check version is 0 */
976     if (*(scanner->curptr+2) != '0') {
977 	on_scanner_error(scanner);
978 	return;
979     }
980 
981     /* We've got what we're looking for, skip anything until newline */
982     pj_scan_skip_line(scanner);
983 }
984 
parse_origin(pj_scanner * scanner,pjmedia_sdp_session * ses,parse_context * ctx)985 static void parse_origin(pj_scanner *scanner, pjmedia_sdp_session *ses,
986 			 parse_context *ctx)
987 {
988     pj_str_t str;
989 
990     ctx->last_error = PJMEDIA_SDP_EINORIGIN;
991 
992     /* check equal sign */
993     if (*(scanner->curptr+1) != '=') {
994 	on_scanner_error(scanner);
995 	return;
996     }
997 
998     /* o= */
999     pj_scan_advance_n(scanner, 2, SKIP_WS);
1000 
1001     /* username. */
1002     pj_scan_get_until_ch(scanner, ' ', &ses->origin.user);
1003     pj_scan_get_char(scanner);
1004 
1005     /* id */
1006     pj_scan_get_until_ch(scanner, ' ', &str);
1007     ses->origin.id = pj_strtoul(&str);
1008     pj_scan_get_char(scanner);
1009 
1010     /* version */
1011     pj_scan_get_until_ch(scanner, ' ', &str);
1012     ses->origin.version = pj_strtoul(&str);
1013     pj_scan_get_char(scanner);
1014 
1015     /* network-type */
1016     pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type);
1017     pj_scan_get_char(scanner);
1018 
1019     /* addr-type */
1020     pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type);
1021     pj_scan_get_char(scanner);
1022 
1023     /* address */
1024     pj_scan_get_until_chr(scanner, " \t\r\n", &ses->origin.addr);
1025 
1026     /* We've got what we're looking for, skip anything until newline */
1027     pj_scan_skip_line(scanner);
1028 
1029 }
1030 
parse_time(pj_scanner * scanner,pjmedia_sdp_session * ses,parse_context * ctx)1031 static void parse_time(pj_scanner *scanner, pjmedia_sdp_session *ses,
1032 		       parse_context *ctx)
1033 {
1034     pj_str_t str;
1035 
1036     ctx->last_error = PJMEDIA_SDP_EINTIME;
1037 
1038     /* check equal sign */
1039     if (*(scanner->curptr+1) != '=') {
1040 	on_scanner_error(scanner);
1041 	return;
1042     }
1043 
1044     /* t= */
1045     pj_scan_advance_n(scanner, 2, SKIP_WS);
1046 
1047     /* start time */
1048     pj_scan_get_until_ch(scanner, ' ', &str);
1049     ses->time.start = pj_strtoul(&str);
1050 
1051     pj_scan_get_char(scanner);
1052 
1053     /* stop time */
1054     pj_scan_get_until_chr(scanner, " \t\r\n", &str);
1055     ses->time.stop = pj_strtoul(&str);
1056 
1057     /* We've got what we're looking for, skip anything until newline */
1058     pj_scan_skip_line(scanner);
1059 }
1060 
parse_generic_line(pj_scanner * scanner,pj_str_t * str,parse_context * ctx)1061 static void parse_generic_line(pj_scanner *scanner, pj_str_t *str,
1062 			       parse_context *ctx)
1063 {
1064     ctx->last_error = PJMEDIA_SDP_EINSDP;
1065 
1066     /* check equal sign */
1067     if (*(scanner->curptr+1) != '=') {
1068 	on_scanner_error(scanner);
1069 	return;
1070     }
1071 
1072     /* x= */
1073     pj_scan_advance_n(scanner, 2, SKIP_WS);
1074 
1075     /* get anything until newline (including whitespaces). */
1076     pj_scan_get_until_chr(scanner, "\r\n", str);
1077 
1078     /* newline. */
1079     pj_scan_get_newline(scanner);
1080 }
1081 
parse_connection_info(pj_scanner * scanner,pjmedia_sdp_conn * conn,parse_context * ctx)1082 static void parse_connection_info(pj_scanner *scanner, pjmedia_sdp_conn *conn,
1083 				  parse_context *ctx)
1084 {
1085     ctx->last_error = PJMEDIA_SDP_EINCONN;
1086 
1087     /* c= */
1088     pj_scan_advance_n(scanner, 2, SKIP_WS);
1089 
1090     /* network-type */
1091     pj_scan_get_until_ch(scanner, ' ', &conn->net_type);
1092     pj_scan_get_char(scanner);
1093 
1094     /* addr-type */
1095     pj_scan_get_until_ch(scanner, ' ', &conn->addr_type);
1096     pj_scan_get_char(scanner);
1097 
1098     /* address. */
1099     pj_scan_get_until_chr(scanner, "/ \t\r\n", &conn->addr);
1100     PJ_TODO(PARSE_SDP_CONN_ADDRESS_SUBFIELDS);
1101 
1102     /* We've got what we're looking for, skip anything until newline */
1103     pj_scan_skip_line(scanner);
1104 }
1105 
parse_bandwidth_info(pj_scanner * scanner,pjmedia_sdp_bandw * bandw,parse_context * ctx)1106 static void parse_bandwidth_info(pj_scanner *scanner, pjmedia_sdp_bandw *bandw,
1107 				  parse_context *ctx)
1108 {
1109     pj_str_t str;
1110 
1111     ctx->last_error = PJMEDIA_SDP_EINBANDW;
1112 
1113     /* b= */
1114     pj_scan_advance_n(scanner, 2, SKIP_WS);
1115 
1116     /* modifier */
1117     pj_scan_get_until_ch(scanner, ':', &bandw->modifier);
1118     pj_scan_get_char(scanner);
1119 
1120     /* value */
1121     pj_scan_get_until_chr(scanner, " \t\r\n", &str);
1122     bandw->value = pj_strtoul(&str);
1123 
1124     /* We've got what we're looking for, skip anything until newline */
1125     pj_scan_skip_line(scanner);
1126 }
1127 
parse_media(pj_scanner * scanner,pjmedia_sdp_media * med,parse_context * ctx)1128 static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
1129 			parse_context *ctx)
1130 {
1131     pj_str_t str;
1132 
1133     ctx->last_error = PJMEDIA_SDP_EINMEDIA;
1134 
1135     /* check the equal sign */
1136     if (*(scanner->curptr+1) != '=') {
1137 	on_scanner_error(scanner);
1138 	return;
1139     }
1140 
1141     /* m= */
1142     pj_scan_advance_n(scanner, 2, SKIP_WS);
1143 
1144     /* type */
1145     pj_scan_get_until_ch(scanner, ' ', &med->desc.media);
1146     pj_scan_get_char(scanner);
1147 
1148     /* port */
1149     pj_scan_get(scanner, &cs_token, &str);
1150     med->desc.port = (unsigned short)pj_strtoul(&str);
1151     if (*scanner->curptr == '/') {
1152 	/* port count */
1153 	pj_scan_get_char(scanner);
1154 	pj_scan_get(scanner, &cs_token, &str);
1155 	med->desc.port_count = pj_strtoul(&str);
1156 
1157     } else {
1158 	med->desc.port_count = 0;
1159     }
1160 
1161     if (pj_scan_get_char(scanner) != ' ') {
1162 	PJ_THROW(SYNTAX_ERROR);
1163     }
1164 
1165     /* transport */
1166     pj_scan_get_until_chr(scanner, " \t\r\n", &med->desc.transport);
1167 
1168     /* format list */
1169     med->desc.fmt_count = 0;
1170     while (*scanner->curptr == ' ') {
1171 	pj_str_t fmt;
1172 
1173 	pj_scan_get_char(scanner);
1174 
1175 	/* Check again for the end of the line */
1176 	if ((*scanner->curptr == '\r') || (*scanner->curptr == '\n'))
1177 		break;
1178 
1179 	pj_scan_get(scanner, &cs_token, &fmt);
1180 	if (med->desc.fmt_count < PJMEDIA_MAX_SDP_FMT)
1181 	    med->desc.fmt[med->desc.fmt_count++] = fmt;
1182 	else
1183 	    PJ_PERROR(2,(THIS_FILE, PJ_ETOOMANY,
1184 		         "Error adding SDP media format %.*s, "
1185 			 "format is ignored",
1186 			 (int)fmt.slen, fmt.ptr));
1187     }
1188 
1189     /* We've got what we're looking for, skip anything until newline */
1190     pj_scan_skip_line(scanner);
1191 }
1192 
on_scanner_error(pj_scanner * scanner)1193 static void on_scanner_error(pj_scanner *scanner)
1194 {
1195     PJ_UNUSED_ARG(scanner);
1196 
1197     PJ_THROW(SYNTAX_ERROR);
1198 }
1199 
parse_attr(pj_pool_t * pool,pj_scanner * scanner,parse_context * ctx)1200 static pjmedia_sdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner,
1201 				    parse_context *ctx)
1202 {
1203     pjmedia_sdp_attr *attr;
1204 
1205     ctx->last_error = PJMEDIA_SDP_EINATTR;
1206 
1207     attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
1208 
1209     /* check equal sign */
1210     if (*(scanner->curptr+1) != '=') {
1211 	on_scanner_error(scanner);
1212 	return NULL;
1213     }
1214 
1215     /* skip a= */
1216     pj_scan_advance_n(scanner, 2, SKIP_WS);
1217 
1218     /* get attr name. */
1219     pj_scan_get(scanner, &cs_token, &attr->name);
1220 
1221     if (*scanner->curptr && *scanner->curptr != '\r' &&
1222 	*scanner->curptr != '\n')
1223     {
1224 	/* skip ':' if present. */
1225 	if (*scanner->curptr == ':')
1226 	    pj_scan_get_char(scanner);
1227 
1228 	/* get value */
1229 	if (*scanner->curptr != '\r' && *scanner->curptr != '\n') {
1230 	    pj_scan_get_until_chr(scanner, "\r\n", &attr->value);
1231 	} else {
1232 	    attr->value.ptr = NULL;
1233 	    attr->value.slen = 0;
1234 	}
1235 
1236     } else {
1237 	attr->value.ptr = NULL;
1238 	attr->value.slen = 0;
1239     }
1240 
1241     /* We've got what we're looking for, skip anything until newline */
1242     pj_scan_skip_line(scanner);
1243 
1244     return attr;
1245 }
1246 
1247 
1248 /*
1249  * Apply direction attribute in session to all media.
1250  */
apply_media_direction(pjmedia_sdp_session * sdp)1251 static void apply_media_direction(pjmedia_sdp_session *sdp)
1252 {
1253     pjmedia_sdp_attr *dir_attr = NULL;
1254     unsigned i;
1255 
1256     const pj_str_t inactive = { "inactive", 8 };
1257     const pj_str_t sendonly = { "sendonly", 8 };
1258     const pj_str_t recvonly = { "recvonly", 8 };
1259     const pj_str_t sendrecv = { "sendrecv", 8 };
1260 
1261     /* Find direction attribute in session, don't need to find default
1262      * direction "sendrecv".
1263      */
1264     for (i = 0; i < sdp->attr_count && !dir_attr; ++i) {
1265 	if (!pj_strcmp(&sdp->attr[i]->name, &sendonly) ||
1266 	    !pj_strcmp(&sdp->attr[i]->name, &recvonly) ||
1267 	    !pj_strcmp(&sdp->attr[i]->name, &inactive))
1268 	{
1269 	    dir_attr = sdp->attr[i];
1270 	}
1271     }
1272 
1273     /* Found the direction attribute */
1274     if (dir_attr) {
1275 	/* Remove the direction attribute in session */
1276 	pjmedia_sdp_attr_remove(&sdp->attr_count, sdp->attr, dir_attr);
1277 
1278 	/* Apply the direction attribute to all media, but not overriding it
1279 	 * if media already has direction attribute.
1280 	 */
1281 	for (i = 0; i < sdp->media_count; ++i) {
1282 	    pjmedia_sdp_media *m;
1283 	    unsigned j;
1284 
1285 	    /* Find direction attribute in this media */
1286 	    m = sdp->media[i];
1287 	    for (j = 0; j < m->attr_count; ++j) {
1288 		if (!pj_strcmp(&m->attr[j]->name, &sendrecv) ||
1289 		    !pj_strcmp(&m->attr[j]->name, &sendonly) ||
1290 		    !pj_strcmp(&m->attr[j]->name, &recvonly) ||
1291 		    !pj_strcmp(&m->attr[j]->name, &inactive))
1292 		{
1293 		    break;
1294 		}
1295 	    }
1296 
1297 	    /* Not found, apply direction attribute from session */
1298 	    if (j == m->attr_count)
1299 		pjmedia_sdp_media_add_attr(m, dir_attr);
1300 	}
1301     }
1302 }
1303 
1304 
1305 /*
1306  * Parse SDP message.
1307  */
pjmedia_sdp_parse(pj_pool_t * pool,char * buf,pj_size_t len,pjmedia_sdp_session ** p_sdp)1308 PJ_DEF(pj_status_t) pjmedia_sdp_parse( pj_pool_t *pool,
1309 				       char *buf, pj_size_t len,
1310 				       pjmedia_sdp_session **p_sdp)
1311 {
1312     pj_scanner scanner;
1313     pjmedia_sdp_session *session;
1314     pjmedia_sdp_media *media = NULL;
1315     pjmedia_sdp_attr *attr;
1316     pjmedia_sdp_conn *conn;
1317     pjmedia_sdp_bandw *bandw;
1318     pj_str_t dummy;
1319     int cur_name = 254;
1320     parse_context ctx;
1321     PJ_USE_EXCEPTION;
1322 
1323     ctx.last_error = PJ_SUCCESS;
1324 
1325     init_sdp_parser();
1326 
1327     pj_scan_init(&scanner, buf, len, 0, &on_scanner_error);
1328     session = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
1329     PJ_ASSERT_RETURN(session != NULL, PJ_ENOMEM);
1330 
1331     /* Ignore leading newlines */
1332     while (*scanner.curptr=='\r' || *scanner.curptr=='\n')
1333 	pj_scan_get_char(&scanner);
1334 
1335     PJ_TRY {
1336 	while (!pj_scan_is_eof(&scanner)) {
1337 		cur_name = *scanner.curptr;
1338 		switch (cur_name) {
1339 		case 'a':
1340 		    attr = parse_attr(pool, &scanner, &ctx);
1341 		    if (attr) {
1342 			if (media) {
1343 			    if (media->attr_count < PJMEDIA_MAX_SDP_ATTR)
1344 				pjmedia_sdp_media_add_attr(media, attr);
1345 			    else
1346 				PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY,
1347 					      "Error adding media attribute, "
1348 					      "attribute is ignored"));
1349 			} else {
1350 			    if (session->attr_count < PJMEDIA_MAX_SDP_ATTR)
1351 				pjmedia_sdp_session_add_attr(session, attr);
1352 			    else
1353 				PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY,
1354 					      "Error adding session attribute"
1355 					      ", attribute is ignored"));
1356 			}
1357 		    }
1358 		    break;
1359 		case 'o':
1360 		    parse_origin(&scanner, session, &ctx);
1361 		    break;
1362 		case 's':
1363 		    parse_generic_line(&scanner, &session->name, &ctx);
1364 		    break;
1365 		case 'c':
1366 		    conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
1367 		    parse_connection_info(&scanner, conn, &ctx);
1368 		    if (media) {
1369 			media->conn = conn;
1370 		    } else {
1371 			session->conn = conn;
1372 		    }
1373 		    break;
1374 		case 't':
1375 		    parse_time(&scanner, session, &ctx);
1376 		    break;
1377 		case 'm':
1378 		    media = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
1379 		    parse_media(&scanner, media, &ctx);
1380 		    if (session->media_count < PJMEDIA_MAX_SDP_MEDIA)
1381 			session->media[ session->media_count++ ] = media;
1382 		    else
1383 			PJ_PERROR(2,(THIS_FILE, PJ_ETOOMANY,
1384 				     "Error adding media, media is ignored"));
1385 		    break;
1386 		case 'v':
1387 		    parse_version(&scanner, &ctx);
1388 		    break;
1389 		case 13:
1390 		case 10:
1391 		    pj_scan_get_char(&scanner);
1392 		    /* Allow empty newlines at the end of the message */
1393 		    while (!pj_scan_is_eof(&scanner)) {
1394 			if (*scanner.curptr != 13 && *scanner.curptr != 10) {
1395 			    ctx.last_error = PJMEDIA_SDP_EINSDP;
1396 			    on_scanner_error(&scanner);
1397 			}
1398 			pj_scan_get_char(&scanner);
1399 		    }
1400 		    break;
1401 		case 'b':
1402 		    bandw = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_bandw);
1403 		    parse_bandwidth_info(&scanner, bandw, &ctx);
1404 		    if (media) {
1405 			if (media->bandw_count < PJMEDIA_MAX_SDP_BANDW)
1406 			    media->bandw[media->bandw_count++] = bandw;
1407 			else
1408 			    PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY,
1409 					  "Error adding media bandwidth "
1410 					  "info, info is ignored"));
1411 		    } else {
1412 			if (session->bandw_count < PJMEDIA_MAX_SDP_BANDW)
1413 			    session->bandw[session->bandw_count++] = bandw;
1414 			else
1415 			    PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY,
1416 					  "Error adding session bandwidth "
1417 					  "info, info is ignored"));
1418 		    }
1419 		    break;
1420 		default:
1421 		    if (cur_name >= 'a' && cur_name <= 'z')
1422 			parse_generic_line(&scanner, &dummy, &ctx);
1423 		    else  {
1424 			ctx.last_error = PJMEDIA_SDP_EINSDP;
1425 			on_scanner_error(&scanner);
1426 		    }
1427 		    break;
1428 		}
1429 	}
1430 
1431 	ctx.last_error = PJ_SUCCESS;
1432 
1433     }
1434     PJ_CATCH_ANY {
1435 
1436 	PJ_PERROR(4, (THIS_FILE, ctx.last_error,
1437 		      "Error parsing SDP in line %d col %d",
1438 		      scanner.line, pj_scan_get_col(&scanner)));
1439 
1440 	session = NULL;
1441 
1442 	pj_assert(ctx.last_error != PJ_SUCCESS);
1443     }
1444     PJ_END;
1445 
1446     pj_scan_fini(&scanner);
1447 
1448     if (session)
1449 	apply_media_direction(session);
1450 
1451     *p_sdp = session;
1452     return ctx.last_error;
1453 }
1454 
1455 /*
1456  * Print SDP description.
1457  */
pjmedia_sdp_print(const pjmedia_sdp_session * desc,char * buf,pj_size_t size)1458 PJ_DEF(int) pjmedia_sdp_print( const pjmedia_sdp_session *desc,
1459 			       char *buf, pj_size_t size)
1460 {
1461     return print_session(desc, buf, size);
1462 }
1463 
1464 
1465 /*
1466  * Clone session
1467  */
pjmedia_sdp_session_clone(pj_pool_t * pool,const pjmedia_sdp_session * rhs)1468 PJ_DEF(pjmedia_sdp_session*) pjmedia_sdp_session_clone( pj_pool_t *pool,
1469 			   			const pjmedia_sdp_session *rhs)
1470 {
1471     pjmedia_sdp_session *sess;
1472     unsigned i;
1473 
1474     PJ_ASSERT_RETURN(pool && rhs, NULL);
1475 
1476     sess = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
1477     PJ_ASSERT_RETURN(sess != NULL, NULL);
1478 
1479     /* Clone origin line. */
1480     pj_strdup(pool, &sess->origin.user, &rhs->origin.user);
1481     sess->origin.id = rhs->origin.id;
1482     sess->origin.version = rhs->origin.version;
1483     pj_strdup(pool, &sess->origin.net_type, &rhs->origin.net_type);
1484     pj_strdup(pool, &sess->origin.addr_type, &rhs->origin.addr_type);
1485     pj_strdup(pool, &sess->origin.addr, &rhs->origin.addr);
1486 
1487     /* Clone subject line. */
1488     pj_strdup(pool, &sess->name, &rhs->name);
1489 
1490     /* Clone connection line */
1491     if (rhs->conn) {
1492 	sess->conn = pjmedia_sdp_conn_clone(pool, rhs->conn);
1493 	PJ_ASSERT_RETURN(sess->conn != NULL, NULL);
1494     }
1495 
1496     /* Duplicate bandwidth info */
1497     sess->bandw_count = rhs->bandw_count;
1498     for (i=0; i<rhs->bandw_count; ++i) {
1499 	sess->bandw[i] = pjmedia_sdp_bandw_clone(pool, rhs->bandw[i]);
1500     }
1501 
1502     /* Clone time line. */
1503     sess->time.start = rhs->time.start;
1504     sess->time.stop = rhs->time.stop;
1505 
1506     /* Duplicate session attributes. */
1507     sess->attr_count = rhs->attr_count;
1508     for (i=0; i<rhs->attr_count; ++i) {
1509 	sess->attr[i] = pjmedia_sdp_attr_clone(pool, rhs->attr[i]);
1510     }
1511 
1512     /* Duplicate media descriptors. */
1513     sess->media_count = rhs->media_count;
1514     for (i=0; i<rhs->media_count; ++i) {
1515 	sess->media[i] = pjmedia_sdp_media_clone(pool, rhs->media[i]);
1516     }
1517 
1518     return sess;
1519 }
1520 
1521 
1522 #define CHECK(exp,ret)	do {			\
1523 			    /*pj_assert(exp);*/	\
1524 			    if (!(exp))		\
1525 				return ret;	\
1526 			} while (0)
1527 
1528 /* Validate SDP connetion info. */
validate_sdp_conn(const pjmedia_sdp_conn * c)1529 static pj_status_t validate_sdp_conn(const pjmedia_sdp_conn *c)
1530 {
1531     CHECK( c, PJ_EINVAL);
1532     CHECK( pj_strcmp2(&c->net_type, "IN")==0, PJMEDIA_SDP_EINCONN);
1533     CHECK( pj_strcmp2(&c->addr_type, "IP4")==0 ||
1534 	   pj_strcmp2(&c->addr_type, "IP6")==0,
1535 	   PJMEDIA_SDP_EINCONN);
1536     CHECK( c->addr.slen != 0, PJMEDIA_SDP_EINCONN);
1537 
1538     return PJ_SUCCESS;
1539 }
1540 
1541 
1542 /* Validate SDP session descriptor. */
pjmedia_sdp_validate(const pjmedia_sdp_session * sdp)1543 PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp)
1544 {
1545     return pjmedia_sdp_validate2(sdp, PJ_TRUE);
1546 }
1547 
1548 
1549 /* Validate SDP session descriptor. */
pjmedia_sdp_validate2(const pjmedia_sdp_session * sdp,pj_bool_t strict)1550 PJ_DEF(pj_status_t) pjmedia_sdp_validate2(const pjmedia_sdp_session *sdp,
1551 					  pj_bool_t strict)
1552 {
1553     unsigned i;
1554     const pj_str_t STR_RTPMAP = { "rtpmap", 6 };
1555 
1556     CHECK( sdp != NULL, PJ_EINVAL);
1557 
1558     /* Validate origin line. */
1559     CHECK( sdp->origin.user.slen != 0, PJMEDIA_SDP_EINORIGIN);
1560     CHECK( pj_strcmp2(&sdp->origin.net_type, "IN")==0,
1561 	   PJMEDIA_SDP_EINORIGIN);
1562     CHECK( pj_strcmp2(&sdp->origin.addr_type, "IP4")==0 ||
1563 	   pj_strcmp2(&sdp->origin.addr_type, "IP6")==0,
1564 	   PJMEDIA_SDP_EINORIGIN);
1565     CHECK( sdp->origin.addr.slen != 0, PJMEDIA_SDP_EINORIGIN);
1566 
1567     /* Validate subject line. */
1568     CHECK( sdp->name.slen != 0, PJMEDIA_SDP_EINNAME);
1569 
1570     /* Ignore start and stop time. */
1571 
1572     /* If session level connection info is present, validate it. */
1573     if (sdp->conn) {
1574 	pj_status_t status = validate_sdp_conn(sdp->conn);
1575 	if (status != PJ_SUCCESS)
1576 	    return status;
1577     }
1578 
1579     /* Validate each media. */
1580     for (i=0; i<sdp->media_count; ++i) {
1581 	const pjmedia_sdp_media *m = sdp->media[i];
1582 	unsigned j;
1583 
1584 	/* Validate the m= line. */
1585 	CHECK( m->desc.media.slen != 0, PJMEDIA_SDP_EINMEDIA);
1586 	CHECK( m->desc.transport.slen != 0, PJMEDIA_SDP_EINMEDIA);
1587 	CHECK( m->desc.fmt_count != 0 || m->desc.port==0, PJMEDIA_SDP_ENOFMT);
1588 
1589 	/* If media level connection info is present, validate it. */
1590 	if (m->conn) {
1591 	    pj_status_t status = validate_sdp_conn(m->conn);
1592 	    if (status != PJ_SUCCESS)
1593 		return status;
1594 	}
1595 
1596 	/* If media doesn't have connection info, then connection info
1597 	 * must be present in the session.
1598 	 */
1599 	if (m->conn == NULL) {
1600 	    if (sdp->conn == NULL)
1601 		if (strict || m->desc.port != 0)
1602 		    return PJMEDIA_SDP_EMISSINGCONN;
1603 	}
1604 
1605 	/* Verify payload type. */
1606 	for (j=0; j<m->desc.fmt_count; ++j) {
1607 
1608 	    /* Arrgh noo!! Payload type can be non-numeric!!
1609 	     * RTC based programs sends "null" for instant messaging!
1610 	     */
1611 	    if (pj_isdigit(*m->desc.fmt[j].ptr)) {
1612 		unsigned long pt;
1613 		pj_status_t status = pj_strtoul3(&m->desc.fmt[j], &pt, 10);
1614 
1615 		/* Payload type is between 0 and 127.
1616 		 */
1617 		CHECK( status == PJ_SUCCESS && pt <= 127, PJMEDIA_SDP_EINPT);
1618 
1619 		/* If port is not zero, then for each dynamic payload type, an
1620 		 * rtpmap attribute must be specified.
1621 		 */
1622 		if (m->desc.port != 0 && pt >= 96) {
1623 		    const pjmedia_sdp_attr *a;
1624 
1625 		    a = pjmedia_sdp_media_find_attr(m, &STR_RTPMAP,
1626 						    &m->desc.fmt[j]);
1627 		    CHECK( a != NULL, PJMEDIA_SDP_EMISSINGRTPMAP);
1628 		}
1629 	    }
1630 	}
1631     }
1632 
1633     /* Looks good. */
1634     return PJ_SUCCESS;
1635 }
1636 
1637 
pjmedia_sdp_transport_cmp(const pj_str_t * t1,const pj_str_t * t2)1638 PJ_DEF(pj_status_t) pjmedia_sdp_transport_cmp( const pj_str_t *t1,
1639 					       const pj_str_t *t2)
1640 {
1641     pj_uint32_t t1_proto, t2_proto;
1642 
1643     /* Exactly equal? */
1644     if (pj_stricmp(t1, t2) == 0)
1645 	return PJ_SUCCESS;
1646 
1647     /* Check if boths are RTP/AVP based */
1648     t1_proto = pjmedia_sdp_transport_get_proto(t1);
1649     t2_proto = pjmedia_sdp_transport_get_proto(t2);
1650     if (PJMEDIA_TP_PROTO_HAS_FLAG(t1_proto, PJMEDIA_TP_PROTO_RTP_AVP) &&
1651 	PJMEDIA_TP_PROTO_HAS_FLAG(t2_proto, PJMEDIA_TP_PROTO_RTP_AVP))
1652     {
1653 	return PJ_SUCCESS;
1654     }
1655 
1656     /* Compatible? */
1657     //{
1658     //	static const pj_str_t ID_RTP_AVP  = { "RTP/AVP", 7 };
1659     //	static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
1660     //	if ((!pj_stricmp(t1, &ID_RTP_AVP) || !pj_stricmp(t1, &ID_RTP_SAVP)) &&
1661     //      (!pj_stricmp(t2, &ID_RTP_AVP) || !pj_stricmp(t2, &ID_RTP_SAVP)))
1662     //	    return PJ_SUCCESS;
1663     //}
1664 
1665     return PJMEDIA_SDP_ETPORTNOTEQUAL;
1666 }
1667 
1668 
1669 /*
1670  * Get media transport info, e.g: protocol and profile.
1671  */
pjmedia_sdp_transport_get_proto(const pj_str_t * tp)1672 PJ_DEF(pj_uint32_t) pjmedia_sdp_transport_get_proto(const pj_str_t *tp)
1673 {
1674     pj_str_t token, rest = {0};
1675     pj_ssize_t idx;
1676 
1677     PJ_ASSERT_RETURN(tp, PJMEDIA_TP_PROTO_NONE);
1678 
1679     idx = pj_strtok2(tp, "/", &token, 0);
1680     if (idx != tp->slen)
1681 	pj_strset(&rest, tp->ptr + token.slen + 1, tp->slen - token.slen - 1);
1682 
1683     if (pj_stricmp2(&token, "RTP") == 0) {
1684 	/* Starts with "RTP" */
1685 
1686 	/* RTP/AVP */
1687 	if (pj_stricmp2(&rest, "AVP") == 0)
1688 	    return PJMEDIA_TP_PROTO_RTP_AVP;
1689 
1690 	/* RTP/SAVP */
1691 	if (pj_stricmp2(&rest, "SAVP") == 0)
1692 	    return PJMEDIA_TP_PROTO_RTP_SAVP;
1693 
1694 	/* RTP/AVPF */
1695 	if (pj_stricmp2(&rest, "AVPF") == 0)
1696 	    return PJMEDIA_TP_PROTO_RTP_AVPF;
1697 
1698 	/* RTP/SAVPF */
1699 	if (pj_stricmp2(&rest, "SAVPF") == 0)
1700 	    return PJMEDIA_TP_PROTO_RTP_SAVPF;
1701 
1702     } else if (pj_stricmp2(&token, "UDP") == 0) {
1703 	/* Starts with "UDP" */
1704 
1705 	/* Plain UDP */
1706 	if (rest.slen == 0)
1707 	    return PJMEDIA_TP_PROTO_UDP;
1708 
1709 	/* DTLS-SRTP */
1710 	if (pj_stricmp2(&rest, "TLS/RTP/SAVP") == 0)
1711 	    return PJMEDIA_TP_PROTO_DTLS_SRTP;
1712 
1713 	/* DTLS-SRTP with RTCP-FB */
1714 	if (pj_stricmp2(&rest, "TLS/RTP/SAVPF") == 0)
1715 	    return PJMEDIA_TP_PROTO_DTLS_SRTPF;
1716     }
1717 
1718     /* Unknown transport */
1719     return PJMEDIA_TP_PROTO_UNKNOWN;
1720 }
1721 
1722 
pjmedia_sdp_media_deactivate(pj_pool_t * pool,pjmedia_sdp_media * m)1723 PJ_DEF(pj_status_t) pjmedia_sdp_media_deactivate(pj_pool_t *pool,
1724 						 pjmedia_sdp_media *m)
1725 {
1726     PJ_ASSERT_RETURN(m, PJ_EINVAL);
1727     PJ_UNUSED_ARG(pool);
1728 
1729     /* Set port to zero */
1730     m->desc.port = 0;
1731 
1732     /* And remove attributes */
1733     m->attr_count = 0;
1734 
1735     return PJ_SUCCESS;
1736 }
1737 
1738 
pjmedia_sdp_media_clone_deactivate(pj_pool_t * pool,const pjmedia_sdp_media * rhs)1739 PJ_DEF(pjmedia_sdp_media*) pjmedia_sdp_media_clone_deactivate(
1740 						pj_pool_t *pool,
1741 						const pjmedia_sdp_media *rhs)
1742 {
1743     unsigned int i;
1744     pjmedia_sdp_media *m;
1745 
1746     PJ_ASSERT_RETURN(pool && rhs, NULL);
1747 
1748     m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
1749     pj_memcpy(m, rhs, sizeof(*m));
1750 
1751     /* Clone the media line only */
1752     pj_strdup (pool, &m->desc.media, &rhs->desc.media);
1753     pj_strdup (pool, &m->desc.transport, &rhs->desc.transport);
1754     for (i=0; i<rhs->desc.fmt_count; ++i)
1755 	pj_strdup(pool, &m->desc.fmt[i], &rhs->desc.fmt[i]);
1756 
1757     if (rhs->conn) {
1758 	m->conn = pjmedia_sdp_conn_clone (pool, rhs->conn);
1759 	PJ_ASSERT_RETURN(m->conn != NULL, NULL);
1760     }
1761 
1762     m->bandw_count = rhs->bandw_count;
1763     for (i=0; i < rhs->bandw_count; ++i) {
1764 	m->bandw[i] = pjmedia_sdp_bandw_clone (pool, rhs->bandw[i]);
1765 	PJ_ASSERT_RETURN(m->bandw[i] != NULL, NULL);
1766     }
1767 
1768     /* And deactivate it */
1769     pjmedia_sdp_media_deactivate(pool, m);
1770 
1771     return m;
1772 }
1773