blob: c1a8bbce7bb9a79b69776339bd5cba40bea5f34d [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file sdp/msg.c SDP Message processing
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <re_types.h>
7#include <re_fmt.h>
8#include <re_mem.h>
9#include <re_mbuf.h>
10#include <re_list.h>
11#include <re_sa.h>
12#include <re_sdp.h>
13#include "sdp.h"
14
15
16static int attr_decode_fmtp(struct sdp_media *m, const struct pl *pl)
17{
18 struct sdp_format *fmt;
19 struct pl id, params;
20
21 if (!m)
22 return 0;
23
24 if (re_regex(pl->p, pl->l, "[^ ]+ [^]*", &id, &params))
25 return EBADMSG;
26
27 fmt = sdp_format_find(&m->rfmtl, &id);
28 if (!fmt)
29 return 0;
30
31 fmt->params = mem_deref(fmt->params);
32
33 return pl_strdup(&fmt->params, &params);
34}
35
36
37static int attr_decode_rtcp(struct sdp_media *m, const struct pl *pl)
38{
39 struct pl port, addr;
40 int err = 0;
41
42 if (!m)
43 return 0;
44
45 if (!re_regex(pl->p, pl->l, "[0-9]+ IN IP[46]1 [^ ]+",
46 &port, NULL, &addr)) {
47 (void)sa_set(&m->raddr_rtcp, &addr, pl_u32(&port));
48 }
49 else if (!re_regex(pl->p, pl->l, "[0-9]+", &port)) {
50 sa_set_port(&m->raddr_rtcp, pl_u32(&port));
51 }
52 else
53 err = EBADMSG;
54
55 return err;
56}
57
58
59static int attr_decode_rtpmap(struct sdp_media *m, const struct pl *pl)
60{
61 struct pl id, name, srate, ch;
62 struct sdp_format *fmt;
63 int err;
64
65 if (!m)
66 return 0;
67
68 if (re_regex(pl->p, pl->l, "[^ ]+ [^/]+/[0-9]+[/]*[^]*",
69 &id, &name, &srate, NULL, &ch))
70 return EBADMSG;
71
72 fmt = sdp_format_find(&m->rfmtl, &id);
73 if (!fmt)
74 return 0;
75
76 fmt->name = mem_deref(fmt->name);
77
78 err = pl_strdup(&fmt->name, &name);
79 if (err)
80 return err;
81
82 fmt->srate = pl_u32(&srate);
83 fmt->ch = ch.l ? pl_u32(&ch) : 1;
84
85 return 0;
86}
87
88
89static int attr_decode(struct sdp_session *sess, struct sdp_media *m,
90 enum sdp_dir *dir, const struct pl *pl)
91{
92 struct pl name, val;
93 int err = 0;
94
95 if (re_regex(pl->p, pl->l, "[^:]+:[^]+", &name, &val)) {
96 name = *pl;
97 val = pl_null;
98 }
99
100 if (!pl_strcmp(&name, "fmtp"))
101 err = attr_decode_fmtp(m, &val);
102
103 else if (!pl_strcmp(&name, "inactive"))
104 *dir = SDP_INACTIVE;
105
106 else if (!pl_strcmp(&name, "recvonly"))
107 *dir = SDP_SENDONLY;
108
109 else if (!pl_strcmp(&name, "rtcp"))
110 err = attr_decode_rtcp(m, &val);
111
112 else if (!pl_strcmp(&name, "rtpmap"))
113 err = attr_decode_rtpmap(m, &val);
114
115 else if (!pl_strcmp(&name, "sendonly"))
116 *dir = SDP_RECVONLY;
117
118 else if (!pl_strcmp(&name, "sendrecv"))
119 *dir = SDP_SENDRECV;
120
121 else
122 err = sdp_attr_add(m ? &m->rattrl : &sess->rattrl,
123 &name, &val);
124
125 return err;
126}
127
128
129static int bandwidth_decode(int32_t *bwv, const struct pl *pl)
130{
131 struct pl type, bw;
132
133 if (re_regex(pl->p, pl->l, "[^:]+:[0-9]+", &type, &bw))
134 return EBADMSG;
135
136 if (!pl_strcmp(&type, "CT"))
137 bwv[SDP_BANDWIDTH_CT] = pl_u32(&bw);
138
139 else if (!pl_strcmp(&type, "AS"))
140 bwv[SDP_BANDWIDTH_AS] = pl_u32(&bw);
141
142 else if (!pl_strcmp(&type, "RS"))
143 bwv[SDP_BANDWIDTH_RS] = pl_u32(&bw);
144
145 else if (!pl_strcmp(&type, "RR"))
146 bwv[SDP_BANDWIDTH_RR] = pl_u32(&bw);
147
148 else if (!pl_strcmp(&type, "TIAS"))
149 bwv[SDP_BANDWIDTH_TIAS] = pl_u32(&bw);
150
151 return 0;
152}
153
154
155static int conn_decode(struct sa *sa, const struct pl *pl)
156{
157 struct pl v;
158
159 if (re_regex(pl->p, pl->l, "IN IP[46]1 [^ ]+", NULL, &v))
160 return EBADMSG;
161
162 (void)sa_set(sa, &v, sa_port(sa));
163
164 return 0;
165}
166
167
168static int media_decode(struct sdp_media **mp, struct sdp_session *sess,
169 bool offer, const struct pl *pl)
170{
171 struct pl name, port, proto, fmtv, fmt;
172 struct sdp_media *m;
173 int err;
174
175 if (re_regex(pl->p, pl->l, "[a-z]+ [^ ]+ [^ ]+[^]*",
176 &name, &port, &proto, &fmtv))
177 return EBADMSG;
178
179 m = list_ledata(*mp ? (*mp)->le.next : sess->medial.head);
180 if (!m) {
181 if (!offer)
182 return EPROTO;
183
184 m = sdp_media_find(sess, &name, &proto, true);
185 if (!m) {
186 err = sdp_media_radd(&m, sess, &name, &proto);
187 if (err)
188 return err;
189 }
190 else {
191 list_unlink(&m->le);
192 list_append(&sess->medial, &m->le, m);
193 }
194
195 m->uproto = mem_deref(m->uproto);
196 }
197 else {
198 if (pl_strcmp(&name, m->name))
199 return offer ? ENOTSUP : EPROTO;
200
201 m->uproto = mem_deref(m->uproto);
202
203 if (!sdp_media_proto_cmp(m, &proto, offer)) {
204
205 err = pl_strdup(&m->uproto, &proto);
206 if (err)
207 return err;
208 }
209 }
210
211 while (!re_regex(fmtv.p, fmtv.l, " [^ ]+", &fmt)) {
212
213 pl_advance(&fmtv, fmt.p + fmt.l - fmtv.p);
214
215 err = sdp_format_radd(m, &fmt);
216 if (err)
217 return err;
218 }
219
220 m->raddr = sess->raddr;
221 sa_set_port(&m->raddr, m->uproto ? 0 : pl_u32(&port));
222
223 m->rdir = sess->rdir;
224
225 *mp = m;
226
227 return 0;
228}
229
230
231static int version_decode(const struct pl *pl)
232{
233 return pl_strcmp(pl, "0") ? ENOSYS : 0;
234}
235
236
237/**
238 * Decode an SDP message into an SDP Session
239 *
240 * @param sess SDP Session
241 * @param mb Memory buffer containing SDP message
242 * @param offer True if SDP offer, False if SDP answer
243 *
244 * @return 0 if success, otherwise errorcode
245 */
246int sdp_decode(struct sdp_session *sess, struct mbuf *mb, bool offer)
247{
248 struct sdp_media *m;
249 struct pl pl, val;
250 struct le *le;
251 char type = 0;
252 int err = 0;
253
254 if (!sess || !mb)
255 return EINVAL;
256
257 sdp_session_rreset(sess);
258
259 for (le=sess->medial.head; le; le=le->next) {
260
261 m = le->data;
262
263 sdp_media_rreset(m);
264 }
265
266 pl.p = (const char *)mbuf_buf(mb);
267 pl.l = mbuf_get_left(mb);
268
269 m = NULL;
270
271 for (;pl.l && !err; pl.p++, pl.l--) {
272
273 switch (*pl.p) {
274
275 case '\r':
276 case '\n':
277 if (!type)
278 break;
279
280 switch (type) {
281
282 case 'a':
283 err = attr_decode(sess, m,
284 m ? &m->rdir : &sess->rdir,
285 &val);
286 break;
287
288 case 'b':
289 err = bandwidth_decode(m? m->rbwv : sess->rbwv,
290 &val);
291 break;
292
293 case 'c':
294 err = conn_decode(m ? &m->raddr : &sess->raddr,
295 &val);
296 break;
297
298 case 'm':
299 err = media_decode(&m, sess, offer, &val);
300 break;
301
302 case 'v':
303 err = version_decode(&val);
304 break;
305 }
306
307#if 0
308 if (err)
309 re_printf("** %c='%r': %m\n", type, &val, err);
310#endif
311
312 type = 0;
313 break;
314
315 default:
316 if (type) {
317 val.l++;
318 break;
319 }
320
321 if (pl.l < 2 || *(pl.p + 1) != '=') {
322 err = EBADMSG;
323 break;
324 }
325
326 type = *pl.p;
327 val.p = pl.p + 2;
328 val.l = 0;
329
330 pl.p += 1;
331 pl.l -= 1;
332 break;
333 }
334 }
335
336 if (err)
337 return err;
338
339 if (type)
340 return EBADMSG;
341
342 for (le=sess->medial.head; le; le=le->next)
343 sdp_media_align_formats(le->data, offer);
344
345 return 0;
346}
347
348
349static int media_encode(const struct sdp_media *m, struct mbuf *mb, bool offer)
350{
351 enum sdp_bandwidth i;
352 const char *proto;
353 int err, supc = 0;
354 bool disabled;
355 struct le *le;
356 uint16_t port;
357
358 for (le=m->lfmtl.head; le; le=le->next) {
359
360 const struct sdp_format *fmt = le->data;
361
362 if (fmt->sup)
363 ++supc;
364 }
365
366 if (m->uproto && !offer) {
367 disabled = true;
368 port = 0;
369 proto = m->uproto;
370 }
371 else if (m->disabled || supc == 0 || (!offer && !sa_port(&m->raddr))) {
372 disabled = true;
373 port = 0;
374 proto = m->proto;
375 }
376 else {
377 disabled = false;
378 port = sa_port(&m->laddr);
379 proto = m->proto;
380 }
381
382 err = mbuf_printf(mb, "m=%s %u %s", m->name, port, proto);
383
384 if (disabled) {
385 err |= mbuf_write_str(mb, " 0\r\n");
386 return err;
387 }
388
389 for (le=m->lfmtl.head; le; le=le->next) {
390
391 const struct sdp_format *fmt = le->data;
392
393 if (!fmt->sup)
394 continue;
395
396 err |= mbuf_printf(mb, " %s", fmt->id);
397 }
398
399 err |= mbuf_write_str(mb, "\r\n");
400
401 if (sa_isset(&m->laddr, SA_ADDR)) {
402 const int ipver = sa_af(&m->laddr) == AF_INET ? 4 : 6;
403 err |= mbuf_printf(mb, "c=IN IP%d %j\r\n", ipver, &m->laddr);
404 }
405
406 for (i=SDP_BANDWIDTH_MIN; i<SDP_BANDWIDTH_MAX; i++) {
407
408 if (m->lbwv[i] < 0)
409 continue;
410
411 err |= mbuf_printf(mb, "b=%s:%i\r\n",
412 sdp_bandwidth_name(i), m->lbwv[i]);
413 }
414
415 for (le=m->lfmtl.head; le; le=le->next) {
416
417 const struct sdp_format *fmt = le->data;
418
419 if (!fmt->sup || !str_isset(fmt->name))
420 continue;
421
422 err |= mbuf_printf(mb, "a=rtpmap:%s %s/%u",
423 fmt->id, fmt->name, fmt->srate);
424
425 if (fmt->ch > 1)
426 err |= mbuf_printf(mb, "/%u", fmt->ch);
427
428 err |= mbuf_printf(mb, "\r\n");
429
430 if (str_isset(fmt->params))
431 err |= mbuf_printf(mb, "a=fmtp:%s %s\r\n",
432 fmt->id, fmt->params);
433 if (fmt->ench)
434 err |= fmt->ench(mb, fmt, offer, fmt->data);
435 }
436
437 if (sa_isset(&m->laddr_rtcp, SA_ALL))
438 err |= mbuf_printf(mb, "a=rtcp:%u IN IP%d %j\r\n",
439 sa_port(&m->laddr_rtcp),
440 (AF_INET == sa_af(&m->laddr_rtcp)) ? 4 : 6,
441 &m->laddr_rtcp);
442 else if (sa_isset(&m->laddr_rtcp, SA_PORT))
443 err |= mbuf_printf(mb, "a=rtcp:%u\r\n",
444 sa_port(&m->laddr_rtcp));
445
446 err |= mbuf_printf(mb, "a=%s\r\n",
447 sdp_dir_name(offer ? m->ldir : m->ldir & m->rdir));
448
449 for (le = m->lattrl.head; le; le = le->next)
450 err |= mbuf_printf(mb, "%H", sdp_attr_print, le->data);
451
452 if (m->ench)
453 err |= m->ench(mb, offer, m->arg);
454
455 return err;
456}
457
458
459/**
460 * Encode an SDP Session into a memory buffer
461 *
462 * @param mbp Pointer to allocated memory buffer
463 * @param sess SDP Session
464 * @param offer True if SDP Offer, False if SDP Answer
465 *
466 * @return 0 if success, otherwise errorcode
467 */
468int sdp_encode(struct mbuf **mbp, struct sdp_session *sess, bool offer)
469{
470 const int ipver = sa_af(&sess->laddr) == AF_INET ? 4 : 6;
471 enum sdp_bandwidth i;
472 struct mbuf *mb;
473 struct le *le;
474 int err;
475
476 if (!mbp || !sess)
477 return EINVAL;
478
479 mb = mbuf_alloc(512);
480 if (!mb)
481 return ENOMEM;
482
483 err = mbuf_printf(mb, "v=%u\r\n", SDP_VERSION);
484 err |= mbuf_printf(mb, "o=- %u %u IN IP%d %j\r\n",
485 sess->id, sess->ver++, ipver, &sess->laddr);
486 err |= mbuf_write_str(mb, "s=-\r\n");
487 err |= mbuf_printf(mb, "c=IN IP%d %j\r\n", ipver, &sess->laddr);
488
489 for (i=SDP_BANDWIDTH_MIN; i<SDP_BANDWIDTH_MAX; i++) {
490
491 if (sess->lbwv[i] < 0)
492 continue;
493
494 err |= mbuf_printf(mb, "b=%s:%i\r\n",
495 sdp_bandwidth_name(i), sess->lbwv[i]);
496 }
497
498 err |= mbuf_write_str(mb, "t=0 0\r\n");
499
500 for (le = sess->lattrl.head; le; le = le->next)
501 err |= mbuf_printf(mb, "%H", sdp_attr_print, le->data);
502
503 for (le=sess->lmedial.head; offer && le;) {
504
505 struct sdp_media *m = le->data;
506
507 le = le->next;
508
509 if (m->disabled)
510 continue;
511
512 list_unlink(&m->le);
513 list_append(&sess->medial, &m->le, m);
514 }
515
516 for (le=sess->medial.head; le; le=le->next) {
517
518 struct sdp_media *m = le->data;
519
520 err |= media_encode(m, mb, offer);
521 }
522
523 mb->pos = 0;
524
525 if (err)
526 mem_deref(mb);
527 else
528 *mbp = mb;
529
530 return err;
531}