1 /*
2 *parse or be parsed
3 */
4
5
6 #include <stdio.h>
7 #include <fcntl.h>
8 #include <assert.h>
9 #include <netinet/in.h>
10 #include <arpa/inet.h>
11
12 #include "AmConfig.h"
13 #include "AmSdp.h"
14 #include "AmUtils.h"
15 #include "AmPlugIn.h"
16 #include "AmSession.h"
17
18 #include "amci/amci.h"
19 #include "log.h"
20
21 #include <stdexcept>
22
23 #include <string>
24 #include <map>
25 using std::string;
26 using std::map;
27
28 #include <cctype>
29 #include <algorithm>
30
31 // Not on Solaris!
32 #if !defined (__SVR4) && !defined (__sun)
33 #include "strings.h"
34 #endif
35
36 #define CR '\r'
37 #define LF '\n'
38 #define CRLF "\r\n"
39
40 static void parse_session_attr(AmSdp* sdp_msg, char* s, char** next);
41 static bool parse_sdp_line_ex(AmSdp* sdp_msg, char*& s);
42 static char* parse_sdp_connection(AmSdp* sdp_msg, char* s, char t);
43 static void parse_sdp_media(AmSdp* sdp_msg, char* s);
44 static char* parse_sdp_attr(AmSdp* sdp_msg, char* s);
45 static void parse_sdp_origin(AmSdp* sdp_masg, char* s);
46
47 inline char* get_next_line(char* s);
48 inline char* skip_till_next_line(char* s, size_t& line_len);
49 static char* is_eql_next(char* s);
50 static char* parse_until(char* s, char end);
51 static char* parse_until(char* s, char* end, char c);
52 static bool contains(char* s, char* next_line, char c);
53 static bool is_wsp(char s);
54
55 static MediaType media_type(std::string media);
56 static TransProt transport_type(std::string transport);
57 static bool attr_check(std::string attr);
58
59 enum parse_st {SDP_DESCR, SDP_MEDIA};
60 enum sdp_connection_st {NET_TYPE, ADDR_TYPE, IP4, IP6};
61 enum sdp_media_st {MEDIA, PORT, PROTO, FMT};
62 enum sdp_attr_rtpmap_st {TYPE, ENC_NAME, CLK_RATE, ENC_PARAM};
63 enum sdp_attr_fmtp_st {FORMAT, FORMAT_PARAM};
64 enum sdp_origin_st {USER, ID, VERSION_ST, NETTYPE, ADDR, UNICAST_ADDR};
65
66 // inline functions
67
net_t_2_str(int nt)68 inline string net_t_2_str(int nt)
69 {
70 switch(nt){
71 case NT_IN: return "IN";
72 default: return "<unknown network type>";
73 }
74 }
75
addr_t_2_str(int at)76 inline string addr_t_2_str(int at)
77 {
78 switch(at){
79 case AT_V4: return "IP4";
80 case AT_V6: return "IP6";
81 default: return "<unknown address type>";
82 }
83 }
84
85
media_t_2_str(int mt)86 inline string media_t_2_str(int mt)
87 {
88 switch(mt){
89 case MT_AUDIO: return "audio";
90 case MT_VIDEO: return "video";
91 case MT_APPLICATION: return "application";
92 case MT_TEXT: return "text";
93 case MT_MESSAGE: return "message";
94 case MT_IMAGE: return "image";
95 default: return "<unknown media type>";
96 }
97 }
98
transport_p_2_str(int tp)99 inline string transport_p_2_str(int tp)
100 {
101 switch(tp){
102 case TP_RTPAVP: return "RTP/AVP";
103 case TP_RTPAVPF: return "RTP/AVPF";
104 case TP_UDP: return "udp";
105 case TP_RTPSAVP: return "RTP/SAVP";
106 case TP_RTPSAVPF: return "RTP/SAVPF";
107 case TP_UDPTLSRTPSAVP: return "UDP/TLS/RTP/SAVP";
108 case TP_UDPTLSRTPSAVPF: return "UDP/TLS/RTP/SAVPF";
109 case TP_UDPTL: return "udptl";
110 default: return "<unknown media type>";
111 }
112 }
113
SdpConnection()114 SdpConnection::SdpConnection()
115 : network(NT_OTHER), addrType(AT_NONE), address() {
116 memset(&ipv4, 0, sizeof(ipv4));
117 memset(&ipv6, 0, sizeof(ipv6));
118 }
119
operator ==(const SdpConnection & other) const120 bool SdpConnection::operator == (const SdpConnection& other) const
121 {
122 return network == other.network && addrType == other.addrType
123 && address == other.address;
124 }
125
operator ==(const SdpOrigin & other) const126 bool SdpOrigin::operator == (const SdpOrigin& other) const
127 {
128 return user == other.user && sessId == other.sessId
129 && sessV == other.sessV && conn == other.conn;
130 }
131
operator ==(const SdpPayload & other) const132 bool SdpPayload::operator == (const SdpPayload& other) const
133 {
134 return payload_type == other.payload_type && encoding_name == other.encoding_name
135 && clock_rate == other.clock_rate && encoding_param == other.encoding_param;
136 }
137
debugPrint() const138 string SdpConnection::debugPrint() const {
139 return addr_t_2_str(addrType) + " " + address;
140 }
141
debugPrint() const142 string SdpMedia::debugPrint() const {
143 string payload_list;
144 for(std::vector<SdpPayload>::const_iterator it=
145 payloads.begin(); it!= payloads.end(); it++) {
146 if (it != payloads.begin())
147 payload_list+=" ";
148 payload_list+=int2str(it->payload_type);
149 }
150 return "port "+int2str(port) + ", payloads: "+payload_list;
151 }
152
type2str(int type)153 string SdpMedia::type2str(int type)
154 {
155 return media_t_2_str(type);
156 }
157
operator ==(int r)158 bool SdpPayload::operator == (int r)
159 {
160 DBG("pl == r: payload_type = %i; r = %i\n", payload_type, r);
161 return payload_type == r;
162 }
163
print() const164 string SdpAttribute::print() const
165 {
166 if (value.empty())
167 return "a="+attribute+CRLF;
168 else
169 return "a="+attribute+":"+value+CRLF;
170 }
171
operator ==(const SdpAttribute & other) const172 bool SdpAttribute::operator==(const SdpAttribute& other) const
173 {
174 return attribute==other.attribute && (value.empty() || (value==other.value));
175 }
176
operator ==(const SdpMedia & other) const177 bool SdpMedia::operator == (const SdpMedia& other) const
178 {
179 if (payloads.empty()) {
180 if (!other.payloads.empty())
181 return false;
182
183 } else if (other.payloads.empty()) {
184 return false;
185
186 } else {
187 std::pair<vector<SdpPayload>::const_iterator, vector<SdpPayload>::const_iterator> pl_mismatch
188 = std::mismatch(payloads.begin(), payloads.end(), other.payloads.begin());
189
190 if (pl_mismatch.first != payloads.end() || pl_mismatch.second != other.payloads.end())
191 return false;
192 }
193
194 if (attributes.empty()) {
195 if (!other.attributes.empty()) {
196 return false;
197 }
198 } else {
199 std::pair<vector<SdpAttribute>::const_iterator, vector<SdpAttribute>::const_iterator> a_mismatch
200 = std::mismatch(attributes.begin(), attributes.end(), other.attributes.begin());
201
202 if (a_mismatch.first != attributes.end() || a_mismatch.second != other.attributes.end())
203 return false;
204 }
205
206 return type == other.type && port == other.port && nports == other.nports
207 && transport == other.transport && conn == other.conn && dir == other.dir
208 && send == other.send && recv == other.recv;
209 }
210
211 //
212 // class RtcpAddress: Methods
213 //
parse(const string & src)214 bool RtcpAddress::parse(const string &src)
215 {
216 port = 0;
217 nettype.clear();
218 addrtype.clear();
219 address.clear();
220
221 int len = src.size();
222 if (len < 1) return false;
223
224 enum { PORT, NET_TYPE, ADDR_TYPE, ADDR } s = PORT;
225
226 // parsing (somehow) according to RFC 3605
227 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
228 // connection-address] CRLF
229 // nettype, addrtype is ignored
230 for (int i = 0; i < len; ++i) {
231 switch (s) {
232
233 case (PORT):
234 if (src[i] >= '0' && src[i] <= '9') port = port * 10 + (src[i] - '0');
235 else if (src[i] == ' ') s = NET_TYPE;
236 else return false; // error
237 break;
238
239 case NET_TYPE:
240 if (src[i] == ' ') s = ADDR_TYPE;
241 else nettype += src[i];
242 break;
243
244 case ADDR_TYPE:
245 if (src[i] == ' ') s = ADDR;
246 else addrtype += src[i];
247 break;
248
249 case ADDR:
250 address += src[i];
251 break;
252 }
253 }
254 return s == PORT ||
255 (s == ADDR && !address.empty());
256 // FIXME: nettype, addrtype and addr should be verified
257 }
258
print()259 string RtcpAddress::print()
260 {
261 string s(int2str(port));
262 if (!address.empty())
263 s += " IN " + addrtype + " " + address;
264 return s;
265 }
266
RtcpAddress(const string & attr_value)267 RtcpAddress::RtcpAddress(const string &attr_value): port(0)
268 {
269 if (!parse(attr_value))
270 throw std::runtime_error("can't parse rtcp attribute value");
271 }
272
273
274 //
275 // class AmSdp: Methods
276 //
AmSdp()277 AmSdp::AmSdp()
278 : version(0),
279 origin(),
280 sessionName(),
281 conn(),
282 media()
283 {
284 origin.user = "sems";
285 origin.sessId = get_random();
286 origin.sessV = get_random();
287 }
288
AmSdp(const AmSdp & p_sdp_msg)289 AmSdp::AmSdp(const AmSdp& p_sdp_msg)
290 : version(p_sdp_msg.version),
291 origin(p_sdp_msg.origin),
292 sessionName(p_sdp_msg.sessionName),
293 conn(p_sdp_msg.conn),
294 attributes(p_sdp_msg.attributes),
295 media(p_sdp_msg.media)
296 {
297 }
298
parse(const char * _sdp_msg)299 int AmSdp::parse(const char* _sdp_msg)
300 {
301 char* s = (char*)_sdp_msg;
302 clear();
303
304 bool ret = parse_sdp_line_ex(this,s);
305
306 if(!ret && conn.address.empty()){
307 for(vector<SdpMedia>::iterator it = media.begin();
308 !ret && (it != media.end()); ++it)
309 ret = it->conn.address.empty();
310
311 if(ret){
312 ERROR("A connection field must be field must be present in every\n");
313 ERROR("media description or at the session level.\n");
314 }
315 }
316
317
318 return ret;
319 }
320
print(string & body) const321 void AmSdp::print(string& body) const
322 {
323 string out_buf = "v="+int2str(version)+"\r\n"
324 "o="+origin.user+" "+int2str(origin.sessId)+" "+
325 int2str(origin.sessV)+" IN ";
326
327 if (!origin.conn.address.empty())
328 if (origin.conn.address.find(".") != std::string::npos)
329 out_buf += "IP4 " + origin.conn.address + "\r\n";
330 else
331 out_buf += "IP6 " + origin.conn.address + "\r\n";
332 else if (!conn.address.empty())
333 if (conn.address.find(".") != std::string::npos)
334 out_buf += "IP4 " + conn.address + "\r\n";
335 else
336 out_buf += "IP6 " + conn.address + "\r\n";
337 else if (media.size() && !media[0].conn.address.empty())
338 if (media[0].conn.address.find(".") != std::string::npos)
339 out_buf += "IP4 " + media[0].conn.address + "\r\n";
340 else
341 out_buf += "IP6 " + media[0].conn.address + "\r\n";
342 else
343 out_buf += "IP4 0.0.0.0\r\n";
344
345 out_buf +=
346 "s="+sessionName+"\r\n";
347 if (!conn.address.empty()) {
348 if (conn.address.find(".") != std::string::npos)
349 out_buf += "c=IN IP4 ";
350 else
351 out_buf += "c=IN IP6 ";
352 out_buf += conn.address + "\r\n";
353 }
354
355 out_buf += "t=0 0\r\n";
356
357 // add attributes (session level)
358 for (std::vector<SdpAttribute>::const_iterator a_it=
359 attributes.begin(); a_it != attributes.end(); a_it++) {
360 out_buf += a_it->print();
361 }
362
363 for(std::vector<SdpMedia>::const_iterator media_it = media.begin();
364 media_it != media.end(); media_it++) {
365
366 out_buf += "m=" + media_t_2_str(media_it->type) + " " + int2str(media_it->port) + " " + transport_p_2_str(media_it->transport);
367
368 string options;
369
370 if (media_it->transport == TP_RTPAVP || media_it->transport == TP_RTPAVPF || media_it->transport == TP_RTPSAVP || media_it->transport == TP_RTPSAVPF || media_it->transport == TP_UDPTLSRTPSAVP || media_it->transport == TP_UDPTLSRTPSAVPF) {
371 for(std::vector<SdpPayload>::const_iterator pl_it = media_it->payloads.begin();
372 pl_it != media_it->payloads.end(); pl_it++) {
373
374 out_buf += " " + int2str(pl_it->payload_type);
375
376 // "a=rtpmap:" line
377 if (!pl_it->encoding_name.empty()) {
378 options += "a=rtpmap:" + int2str(pl_it->payload_type) + " "
379 + pl_it->encoding_name + "/" + int2str(pl_it->clock_rate);
380
381 if(pl_it->encoding_param > 0){
382 options += "/" + int2str(pl_it->encoding_param);
383 }
384
385 options += "\r\n";
386 }
387
388 // "a=fmtp:" line
389 if(pl_it->sdp_format_parameters.size()){
390 options += "a=fmtp:" + int2str(pl_it->payload_type) + " "
391 + pl_it->sdp_format_parameters + "\r\n";
392 }
393
394 }
395 }
396 else {
397 // for other transports (UDP/UDPTL) just print out fmt
398 out_buf += " " + media_it->fmt;
399 // ... and continue with c=, attributes, ...
400 }
401
402 if (!media_it->conn.address.empty())
403 out_buf += "\r\nc=IN " + addr_t_2_str(media_it->conn.addrType) +
404 " " + media_it->conn.address;
405
406 out_buf += "\r\n" + options;
407
408 if(media_it->send){
409 if(media_it->recv){
410 out_buf += "a=sendrecv\r\n";
411 }
412 else {
413 out_buf += "a=sendonly\r\n";
414 }
415 }
416 else {
417 if(media_it->recv){
418 out_buf += "a=recvonly\r\n";
419 }
420 else {
421 out_buf += "a=inactive\r\n";
422 }
423 }
424
425 // add attributes (media level)
426 for (std::vector<SdpAttribute>::const_iterator a_it=
427 media_it->attributes.begin(); a_it != media_it->attributes.end(); a_it++) {
428 out_buf += a_it->print();
429 }
430
431 switch (media_it->dir) {
432 case SdpMedia::DirActive: out_buf += "a=direction:active\r\n"; break;
433 case SdpMedia::DirPassive: out_buf += "a=direction:passive\r\n"; break;
434 case SdpMedia::DirBoth: out_buf += "a=direction:both\r\n"; break;
435 case SdpMedia::DirUndefined: break;
436 }
437 }
438
439 body = out_buf;
440 }
441
telephoneEventPayload() const442 const SdpPayload* AmSdp::telephoneEventPayload() const
443 {
444 return findPayload("telephone-event");
445 }
446
findPayload(const string & name) const447 const SdpPayload *AmSdp::findPayload(const string& name) const
448 {
449 vector<SdpMedia>::const_iterator m_it;
450
451 for (m_it = media.begin(); m_it != media.end(); ++m_it)
452 {
453 vector<SdpPayload>::const_iterator it = m_it->payloads.begin();
454 for(; it != m_it->payloads.end(); ++it)
455
456 {
457 if (it->encoding_name == name)
458 {
459 return new SdpPayload(*it);
460 }
461 }
462 }
463 return NULL;
464 }
465
operator ==(const AmSdp & other) const466 bool AmSdp::operator == (const AmSdp& other) const
467 {
468 if (attributes.empty()) {
469 if (!other.attributes.empty())
470 return false;
471
472 } else if (other.attributes.empty()) {
473 return false;
474
475 } else {
476 std::pair<vector<SdpAttribute>::const_iterator, vector<SdpAttribute>::const_iterator> a_mismatch
477 = std::mismatch(attributes.begin(), attributes.end(), other.attributes.begin());
478
479 if (a_mismatch.first != attributes.end() || a_mismatch.second != other.attributes.end())
480 return false;
481 }
482
483 if (media.empty()) {
484 if (!other.media.empty())
485 return false;
486
487 } else if (other.media.empty()) {
488 return false;
489
490 } else {
491 std::pair<vector<SdpMedia>::const_iterator, vector<SdpMedia>::const_iterator> m_mismatch
492 = std::mismatch(media.begin(), media.end(), other.media.begin());
493
494 if (m_mismatch.first != media.end() || m_mismatch.second != other.media.end())
495 return false;
496 }
497
498 return version == other.version && origin == other.origin
499 && sessionName == other.sessionName && uri == other.uri && conn == other.conn;
500 }
501
clear()502 void AmSdp::clear()
503 {
504 version = 0;
505 origin = SdpOrigin();
506 sessionName.clear();
507 uri.clear();
508 conn = SdpConnection();
509 attributes.clear();
510 media.clear();
511 l_origin = SdpOrigin();
512 }
513
calcAnswer(const AmPayloadProvider * payload_prov,SdpMedia & answer) const514 void SdpMedia::calcAnswer(const AmPayloadProvider* payload_prov,
515 SdpMedia& answer) const
516 {
517 if(!recv) answer.send = false;
518 if(!send) answer.recv = false;
519
520 switch(dir){
521 case SdpMedia::DirBoth:
522 answer.dir = SdpMedia::DirBoth;
523 break;
524 case SdpMedia::DirActive:
525 answer.dir = SdpMedia::DirPassive;
526 break;
527 case SdpMedia::DirPassive:
528 answer.dir = SdpMedia::DirActive;
529 break;
530 case SdpMedia::DirUndefined:
531 answer.dir = SdpMedia::DirUndefined;
532 break;
533 }
534
535 // Calculate the intersection with the offered set of payloads
536 vector<SdpPayload>::const_iterator it = payloads.begin();
537 for(; it!= payloads.end(); ++it) {
538 amci_payload_t* a_pl = NULL;
539 if(it->payload_type < DYNAMIC_PAYLOAD_TYPE_START) {
540 // try static payloads
541 a_pl = payload_prov->payload(it->payload_type);
542 }
543
544 if( a_pl) {
545 answer.payloads.push_back(SdpPayload(a_pl->payload_id,
546 a_pl->name,
547 a_pl->advertised_sample_rate,
548 0));
549 }
550 else {
551 // Try dynamic payloads
552 // and give a chance to broken
553 // implementation using a static payload number
554 // for dynamic ones.
555
556 int int_pt = payload_prov->
557 getDynPayload(it->encoding_name,
558 it->clock_rate,
559 it->encoding_param);
560 if(int_pt != -1){
561 answer.payloads.push_back(*it);
562 }
563 }
564 }
565 }
566
567 //parser
parse_sdp_line_ex(AmSdp * sdp_msg,char * & s)568 static bool parse_sdp_line_ex(AmSdp* sdp_msg, char*& s)
569 {
570 if (!s) return true; // SDP can't be empty, return error (true really used for failure?)
571
572 char* next=0; size_t line_len = 0;
573 register parse_st state;
574 //default state
575 state=SDP_DESCR;
576 DBG("parsing SDP message...\n");
577
578 while(*s != '\0'){
579 switch(state){
580 case SDP_DESCR:
581 switch(*s){
582 case 'v':
583 {
584 s = is_eql_next(s);
585 next = skip_till_next_line(s, line_len);
586 if (line_len) {
587 string version(s, line_len);
588 str2i(version, sdp_msg->version);
589 // DBG("parse_sdp_line_ex: found version '%s'\n", version.c_str());
590 } else {
591 sdp_msg->version = 0;
592 }
593 s = next;
594 state = SDP_DESCR;
595 break;
596
597 }
598 case 'o':
599 //DBG("parse_sdp_line_ex: found origin\n");
600 s = is_eql_next(s);
601 parse_sdp_origin(sdp_msg, s);
602 s = get_next_line(s);
603 state = SDP_DESCR;
604 break;
605 case 's':
606 {
607 s = is_eql_next(s);
608 next = skip_till_next_line(s, line_len);
609 if (line_len) {
610 sdp_msg->sessionName = string(s, line_len);
611 } else {
612 sdp_msg->sessionName.clear();
613 }
614 s = next;
615 break;
616 }
617
618 case 'u': {
619 s = is_eql_next(s);
620 next = skip_till_next_line(s, line_len);
621 if (line_len) {
622 sdp_msg->uri = string(s, line_len);
623 } else {
624 sdp_msg->uri.clear();
625 }
626 s = next;
627 } break;
628
629 case 'i':
630 case 'e':
631 case 'p':
632 case 'b':
633 case 't':
634 case 'k':
635 s = is_eql_next(s);
636 s = skip_till_next_line(s, line_len);
637 state = SDP_DESCR;
638 break;
639 case 'a':
640 s = is_eql_next(s);
641 parse_session_attr(sdp_msg, s, &next);
642 // next = get_next_line(s);
643 // parse_sdp_attr(sdp_msg, s);
644 s = next;
645 state = SDP_DESCR;
646 break;
647 case 'c':
648 s = is_eql_next(s);
649 s = parse_sdp_connection(sdp_msg, s, 'd');
650 state = SDP_DESCR;
651 break;
652 case 'm':
653 //DBG("parse_sdp_line_ex: found media\n");
654 state = SDP_MEDIA;
655 break;
656
657 default:
658 {
659 next = skip_till_next_line(s, line_len);
660 if (line_len) {
661 sdp_msg->uri = string(s, line_len);
662 } else {
663 sdp_msg->uri.clear();
664 }
665
666 next = skip_till_next_line(s, line_len);
667 if (line_len) {
668 DBG("parse_sdp_line: skipping unknown Session description %s=\n",
669 string(s, line_len).c_str());
670 }
671 s = next;
672 break;
673 }
674 }
675 break;
676
677 case SDP_MEDIA:
678 switch(*s){
679 case 'm':
680 s = is_eql_next(s);
681 parse_sdp_media(sdp_msg, s);
682 s = skip_till_next_line(s, line_len);
683 state = SDP_MEDIA;
684 break;
685 case 'i':
686 s = is_eql_next(s);
687 s = skip_till_next_line(s, line_len);
688 state = SDP_MEDIA;
689 break;
690 case 'c':
691 s = is_eql_next(s);
692 //DBG("parse_sdp_line: found media connection\n");
693 s = parse_sdp_connection(sdp_msg, s, 'm');
694 state = SDP_MEDIA;
695 break;
696 case 'b':
697 s = is_eql_next(s);
698 s = skip_till_next_line(s, line_len);
699 state = SDP_MEDIA;
700 break;
701 case 'k':
702 s = is_eql_next(s);
703 s = skip_till_next_line(s, line_len);
704 state = SDP_MEDIA;
705 break;
706 case 'a':
707 s = is_eql_next(s);
708 s = parse_sdp_attr(sdp_msg, s);
709 state = SDP_MEDIA;
710 break;
711
712 default :
713 {
714 next = skip_till_next_line(s, line_len);
715 if (line_len) {
716 DBG("parse_sdp_line: skipping unknown Media description '%.*s'\n",
717 (int)line_len, s);
718 }
719 s = next;
720 break;
721 }
722 }
723 break;
724 }
725 }
726
727 return false;
728 }
729
730
parse_sdp_connection(AmSdp * sdp_msg,char * s,char t)731 static char* parse_sdp_connection(AmSdp* sdp_msg, char* s, char t)
732 {
733
734 char* connection_line=s;
735 char* next=0;
736 char* next_line=0;
737 size_t line_len = 0;
738 int parsing=1;
739
740 SdpConnection c;
741
742 next_line = skip_till_next_line(s, line_len);
743 if (line_len <= 7) { // should be at least c=IN IP4 ...
744 DBG("short connection line '%.*s'\n", (int)line_len, s);
745 return next_line;
746 }
747
748 register sdp_connection_st state;
749 state = NET_TYPE;
750
751 //DBG("parse_sdp_line_ex: parse_sdp_connection: parsing sdp connection\n");
752
753 while(parsing){
754 switch(state){
755 case NET_TYPE:
756 //Ignore NET_TYPE since it is always IN, fixme
757 c.network = NT_IN; // fixme
758 connection_line +=3; // fixme
759 state = ADDR_TYPE;
760 break;
761 case ADDR_TYPE:
762 {
763 string addr_type(connection_line,3);
764
765 string addr_type_uc = addr_type;
766 std::transform(addr_type_uc.begin(), addr_type_uc.end(), addr_type_uc.begin(), toupper);
767 connection_line +=4;
768 if(addr_type_uc == "IP4"){
769 c.addrType = AT_V4;
770 state = IP4;
771 }else if(addr_type_uc == "IP6"){
772 c.addrType = AT_V6;
773 state = IP6;
774 }else{
775 DBG("parse_sdp_connection: Unknown addr_type in c-line: '%s'\n", addr_type.c_str());
776 c.addrType = AT_NONE;
777 parsing = 0; // ???
778 }
779 break;
780 }
781 case IP4:
782 {
783 if(contains(connection_line, next_line, '/')){
784 next = parse_until(s, '/');
785 c.address = string(connection_line,int(next-connection_line)-2);
786 }else{
787 c.address = string(connection_line, line_len-7);
788 }
789 parsing = 0;
790 break;
791 }
792
793 case IP6:
794 {
795 if(contains(connection_line, next_line, '/')){
796 next = parse_until(s, '/');
797 c.address = string(connection_line, int(next-connection_line)-2);
798 }else{
799 c.address = string(connection_line, line_len-7);
800 }
801 parsing = 0;
802 break;
803 }
804 }
805 }
806 if(t == 'd') {
807 sdp_msg->conn = c;
808 DBG("SDP: got session level connection: %s\n", c.debugPrint().c_str());
809 } else if(t == 'm'){
810 SdpMedia& media = sdp_msg->media.back();
811 media.conn = c;
812 DBG("SDP: got media level connection: %s\n", c.debugPrint().c_str());
813 }
814
815 //DBG("parse_sdp_line_ex: parse_sdp_connection: done parsing sdp connection\n");
816 return next_line;
817 }
818
819
parse_sdp_media(AmSdp * sdp_msg,char * s)820 static void parse_sdp_media(AmSdp* sdp_msg, char* s)
821 {
822 SdpMedia m;
823
824 register sdp_media_st state;
825 state = MEDIA;
826 int parsing = 1;
827 char* media_line=s;
828 char* next=0;
829 char* line_end=0;
830 line_end = get_next_line(media_line);
831 SdpPayload payload;
832 unsigned int payload_type;
833
834 //DBG("parse_sdp_line_ex: parse_sdp_media: parsing media description...\n");
835 m.dir = SdpMedia::DirBoth;
836
837 while(parsing){
838 switch(state){
839 case MEDIA:
840 {
841 next = parse_until(media_line, ' ');
842 string media;
843 if (next > media_line)
844 media = string(media_line, int(next-media_line)-1);
845 m.type = media_type(media);
846 if(m.type == MT_NONE) {
847 ERROR("parse_sdp_media: Unknown media type\n");
848 }
849 media_line = next;
850 state = PORT;
851 break;
852 }
853 case PORT:
854 {
855 next = parse_until(media_line, ' ');
856 //check for multiple ports
857 if(contains(media_line, next, '/')){
858 //port number
859 next = parse_until(media_line, '/');
860 string port;
861 if (next > media_line)
862 port = string(media_line, int(next-media_line)-1);
863 str2i(port, m.port);
864 //number of ports
865 media_line = next;
866 next = parse_until(media_line, ' ');
867 string nports;
868 if (next > media_line)
869 nports = string(media_line, int(next-media_line)-1);
870 str2i(nports, m.nports);
871 }else{
872 //port number
873 next = parse_until(media_line, ' ');
874 string port;
875 if (next > media_line)
876 port = string(media_line, int(next-media_line)-1);
877 str2i(port, m.port);
878 media_line = next;
879 }
880 state = PROTO;
881 break;
882 }
883 case PROTO:
884 {
885 next = parse_until(media_line, ' ');
886 string proto;
887 if (next > media_line)
888 proto = string(media_line, int(next-media_line)-1);
889 // if(transport_type(proto) < 0){
890 // ERROR("parse_sdp_media: Unknown transport protocol\n");
891 // state = FMT;
892 // break;
893 // }
894 m.transport = transport_type(proto);
895 if(m.transport == TP_NONE){
896 DBG("Unknown transport protocol: %s\n",proto.c_str());
897 }
898 media_line = next;
899 state = FMT;
900 break;
901 }
902 case FMT:
903 {
904 if (m.transport == TP_RTPAVP || m.transport == TP_RTPAVPF || m.transport == TP_RTPSAVP || m.transport == TP_RTPSAVPF || m.transport == TP_UDPTLSRTPSAVP || m.transport == TP_UDPTLSRTPSAVPF) {
905 if (contains(media_line, line_end, ' ')) {
906 next = parse_until(media_line, ' ');
907 string value;
908 if (next > media_line)
909 value = string(media_line, int(next-media_line)-1);
910
911 if (!value.empty()) {
912 payload.type = m.type;
913 str2i(value, payload_type);
914 payload.payload_type = payload_type;
915 m.payloads.push_back(payload);
916 }
917 media_line = next;
918 } else {
919 string last_value;
920 if (line_end>media_line) {
921 if (*line_end == '\0') {
922 // last line in message
923 last_value = string(media_line, int(line_end-media_line));
924 } else {
925 last_value = string(media_line, int(line_end-media_line)-1);
926 }
927 }
928 if (!last_value.empty()) {
929 payload.type = m.type;
930 str2i(last_value, payload_type);
931 payload.payload_type = payload_type;
932 m.payloads.push_back(payload);
933 }
934 parsing = 0;
935 }
936 } else {
937 line_end--;
938 while (line_end > media_line &&
939 (*line_end == '\r' || *line_end == '\n'))
940 line_end--;
941 if (line_end>media_line)
942 m.fmt = string(media_line, line_end-media_line+1);
943 DBG("set media fmt to '%s'\n", m.fmt.c_str());
944 parsing = 0;
945 }
946 } break;
947 }
948 }
949 sdp_msg->media.push_back(m);
950
951 DBG("SDP: got media: %s\n", m.debugPrint().c_str());
952 //DBG("parse_sdp_line_ex: parse_sdp_media: done parsing media description \n");
953 return;
954 }
955
956 // session level attribute
parse_session_attr(AmSdp * sdp_msg,char * s,char ** next)957 static void parse_session_attr(AmSdp* sdp_msg, char* s, char** next) {
958 size_t line_len = 0;
959 *next = skip_till_next_line(s, line_len);
960 if (*next == s) {
961 WARN("premature end of SDP in session attr\n");
962 while (**next != '\0') (*next)++;
963 return;
964 }
965 char* attr_end = *next-1;
966 while (attr_end >= s &&
967 ((*attr_end == LF) || (*attr_end == CR)))
968 attr_end--;
969
970 if (*attr_end == ':') {
971 WARN("incorrect SDP: value attrib without value: '%s'\n",
972 string(s, attr_end-s+1).c_str());
973 return;
974 }
975
976 char* col = parse_until(s, attr_end, ':');
977
978 if (col == attr_end) {
979 // property attribute
980 sdp_msg->attributes.push_back(SdpAttribute(string(s, attr_end-s+1)));
981 // DBG("got session attribute '%.*s\n", (int)(attr_end-s+1), s);
982 } else {
983 // value attribute
984 sdp_msg->attributes.push_back(SdpAttribute(string(s, col-s-1),
985 string(col, attr_end-col+1)));
986 // DBG("got session attribute '%.*s:%.*s'\n", (int)(col-s-1), s, (int)(attr_end-col+1), col);
987 }
988 }
989
990 // media level attribute
parse_sdp_attr(AmSdp * sdp_msg,char * s)991 static char* parse_sdp_attr(AmSdp* sdp_msg, char* s)
992 {
993 if(sdp_msg->media.empty()){
994 ERROR("While parsing media options: no actual media !\n");
995 return s;
996 }
997
998 SdpMedia& media = sdp_msg->media.back();
999
1000 SdpPayload payload;
1001
1002 register sdp_attr_rtpmap_st rtpmap_st;
1003 register sdp_attr_fmtp_st fmtp_st;
1004 rtpmap_st = TYPE;
1005 fmtp_st = FORMAT;
1006 char* attr_line=s;
1007 char* next=0;
1008 char* line_end=0;
1009 size_t line_len = 0;
1010 int parsing = 1;
1011 line_end = skip_till_next_line(attr_line, line_len);
1012
1013 unsigned int payload_type, clock_rate, encoding_param = 0;
1014 string encoding_name, params;
1015
1016 string attr;
1017 if (!contains(attr_line, line_end, ':')) {
1018 attr = string(attr_line, line_len);
1019 attr_check(attr);
1020 parsing = 0;
1021 } else {
1022 next = parse_until(attr_line, ':');
1023 attr = string(attr_line, int(next-attr_line)-1);
1024 attr_line = next;
1025 }
1026
1027 if(attr == "rtpmap"){
1028 while(parsing){
1029 switch(rtpmap_st){
1030 case TYPE:
1031 {
1032 next = parse_until(attr_line, ' ');
1033 string type(attr_line, int(next-attr_line)-1);
1034 str2i(type,payload_type);
1035 attr_line = next;
1036 rtpmap_st = ENC_NAME;
1037 break;
1038 }
1039 case ENC_NAME:
1040 {
1041 if(contains(s, line_end, '/')){
1042 next = parse_until(attr_line, '/');
1043 string enc_name(attr_line, int(next-attr_line)-1);
1044 encoding_name = enc_name;
1045 attr_line = next;
1046 rtpmap_st = CLK_RATE;
1047 break;
1048 } else {
1049 rtpmap_st = ENC_PARAM;
1050 break;
1051 }
1052 }
1053 case CLK_RATE:
1054 {
1055 // check for posible encoding parameters after clock rate
1056 if(contains(attr_line, line_end, '/')){
1057 next = parse_until(attr_line, '/');
1058 string clk_rate(attr_line, int(next-attr_line)-1);
1059 str2i(clk_rate, clock_rate);
1060 attr_line = next;
1061 rtpmap_st = ENC_PARAM;
1062 //last line check
1063 }else if (*line_end == '\0') {
1064 string clk_rate(attr_line, int(line_end-attr_line));
1065 str2i(clk_rate, clock_rate);
1066 parsing = 0;
1067 //more lines to come
1068 }else{
1069 string clk_rate(attr_line, int(line_end-attr_line)-1);
1070 str2i(clk_rate, clock_rate);
1071 parsing=0;
1072 }
1073
1074 break;
1075 }
1076 case ENC_PARAM:
1077 {
1078 next = parse_until(attr_line, ' ');
1079 if(next < line_end){
1080 string value(attr_line, int(next-attr_line)-1);
1081 str2i(value, encoding_param);
1082 attr_line = next;
1083 rtpmap_st = ENC_PARAM;
1084 }else{
1085 string last_value(attr_line, int(line_end-attr_line)-1);
1086 str2i(last_value, encoding_param);
1087 parsing = 0;
1088 }
1089 break;
1090 }
1091 break;
1092 }
1093 }
1094
1095 //DBG("found media attr 'rtpmap' type '%d'\n", payload_type);
1096
1097 vector<SdpPayload>::iterator pl_it;
1098
1099 for( pl_it=media.payloads.begin();
1100 (pl_it != media.payloads.end())
1101 && (pl_it->payload_type != int(payload_type));
1102 ++pl_it);
1103
1104 if(pl_it != media.payloads.end()){
1105 *pl_it = SdpPayload( int(payload_type),
1106 encoding_name,
1107 int(clock_rate),
1108 int(encoding_param));
1109 }
1110
1111 } else if(attr == "fmtp"){
1112 while(parsing){
1113 switch(fmtp_st){ // fixme
1114 case FORMAT:
1115 {
1116 next = parse_until(attr_line, line_end, ' ');
1117 string fmtp_format(attr_line, int(next-attr_line)-1);
1118 str2i(fmtp_format, payload_type);
1119 attr_line = next;
1120 fmtp_st = FORMAT_PARAM;
1121 break;
1122 }
1123 case FORMAT_PARAM:
1124 {
1125 char* param_end = line_end-1;
1126 while (is_wsp(*param_end))
1127 param_end--;
1128
1129 if(attr_line >= param_end) {
1130 DBG("empty param for fmtp. ignore it");
1131 } else {
1132 params = string(attr_line, param_end-attr_line+1);
1133 }
1134 parsing = 0;
1135 }
1136 break;
1137 }
1138 }
1139
1140 //DBG("found media attr 'fmtp' for payload '%d': '%s'\n",
1141 // payload_type, params.c_str());
1142
1143 vector<SdpPayload>::iterator pl_it;
1144
1145 for(pl_it=media.payloads.begin();
1146 (pl_it != media.payloads.end())
1147 && (pl_it->payload_type != int(payload_type));
1148 pl_it++);
1149
1150 if(pl_it != media.payloads.end())
1151 pl_it->sdp_format_parameters = params;
1152
1153 } else if (attr == "direction") {
1154 if (parsing) {
1155 size_t dir_len = 0;
1156 next = skip_till_next_line(attr_line, dir_len);
1157 string value(attr_line, dir_len);
1158 if (value == "active") {
1159 media.dir=SdpMedia::DirActive;
1160 // DBG("found media attr 'direction' value '%s'\n", (char*)value.c_str());
1161 } else if (value == "passive") {
1162 media.dir=SdpMedia::DirPassive;
1163 //DBG("found media attr 'direction' value '%s'\n", (char*)value.c_str());
1164 } else if (attr == "both") {
1165 media.dir=SdpMedia::DirBoth;
1166 //DBG("found media attr 'direction' value '%s'\n", (char*)value.c_str());
1167 } else {
1168 DBG("found unknown value for media attribute 'direction'\n");
1169 }
1170 } else {
1171 DBG("ignoring direction attribute without value\n");
1172 }
1173 } else if (attr == "sendrecv") {
1174 media.send = true;
1175 media.recv = true;
1176 } else if (attr == "sendonly") {
1177 media.send = true;
1178 media.recv = false;
1179 } else if (attr == "recvonly") {
1180 media.send = false;
1181 media.recv = true;
1182 } else if (attr == "inactive") {
1183 media.send = false;
1184 media.recv = false;
1185 } else {
1186 attr_check(attr);
1187 string value;
1188 if (parsing) {
1189 size_t attr_len = 0;
1190 next = skip_till_next_line(attr_line, attr_len);
1191 value = string (attr_line, attr_len);
1192 }
1193
1194 // if (value.empty()) {
1195 // DBG("got media attribute '%s'\n", attr.c_str());
1196 // } else {
1197 // DBG("got media attribute '%s':'%s'\n", attr.c_str(), value.c_str());
1198 // }
1199 media.attributes.push_back(SdpAttribute(attr, value));
1200 }
1201 return line_end;
1202 }
1203
parse_sdp_origin(AmSdp * sdp_msg,char * s)1204 static void parse_sdp_origin(AmSdp* sdp_msg, char* s)
1205 {
1206 char* origin_line = s;
1207 char* next=0;
1208 char* line_end=0;
1209 size_t line_len=0;
1210 line_end = skip_till_next_line(s, line_len);
1211
1212 register sdp_origin_st origin_st;
1213 origin_st = USER;
1214 int parsing = 1;
1215
1216 SdpOrigin origin;
1217
1218 //DBG("parse_sdp_line_ex: parse_sdp_origin: parsing sdp origin\n");
1219
1220 while(parsing){
1221 switch(origin_st)
1222 {
1223 case USER:
1224 {
1225 next = parse_until(origin_line, ' ');
1226 if(next > line_end){
1227 DBG("parse_sdp_origin: ST_USER: Incorrect number of value in o=\n");
1228 origin_st = UNICAST_ADDR;
1229 break;
1230 }
1231 string user(origin_line, int(next-origin_line)-1);
1232 origin.user = user;
1233 origin_line = next;
1234 origin_st = ID;
1235 break;
1236 }
1237 case ID:
1238 {
1239 next = parse_until(origin_line, ' ');
1240 if(next > line_end){
1241 DBG("parse_sdp_origin: ST_ID: Incorrect number of value in o=\n");
1242 origin_st = UNICAST_ADDR;
1243 break;
1244 }
1245 string id(origin_line, int(next-origin_line)-1);
1246 str2i(id, origin.sessId);
1247 origin_line = next;
1248 origin_st = VERSION_ST;
1249 break;
1250 }
1251 case VERSION_ST:
1252 {
1253 next = parse_until(origin_line, ' ');
1254 if(next > line_end){
1255 DBG("parse_sdp_origin: ST_VERSION: Incorrect number of value in o=\n");
1256 origin_st = UNICAST_ADDR;
1257 break;
1258 }
1259 string version(origin_line, int(next-origin_line)-1);
1260 str2i(version, origin.sessV);
1261 origin_line = next;
1262 origin_st = NETTYPE;
1263 break;
1264 }
1265 case NETTYPE:
1266 {
1267 next = parse_until(origin_line, ' ');
1268 if(next > line_end){
1269 DBG("parse_sdp_origin: ST_NETTYPE: Incorrect number of value in o=\n");
1270 origin_st = UNICAST_ADDR;
1271 break;
1272 }
1273 string net_type(origin_line, int(next-origin_line)-1);
1274 origin.conn.network = NT_IN; // fixme
1275 origin_line = next;
1276 origin_st = ADDR;
1277 break;
1278 }
1279 case ADDR:
1280 {
1281 next = parse_until(origin_line, ' ');
1282 if(next > line_end){
1283 DBG("parse_sdp_origin: ST_ADDR: Incorrect number of value in o=\n");
1284 origin_st = UNICAST_ADDR;
1285 break;
1286 }
1287
1288 string addr_type(origin_line, int(next-origin_line)-1);
1289 if(addr_type == "IP4"){
1290 origin.conn.addrType = AT_V4;
1291 }else if(addr_type == "IP6"){
1292 origin.conn.addrType = AT_V6;
1293 }else{
1294 DBG("parse_sdp_connection: Unknown addr_type in o line: '%s'\n", addr_type.c_str());
1295 origin.conn.addrType = AT_NONE;
1296 }
1297
1298 origin_line = next;
1299 origin_st = UNICAST_ADDR;
1300 break;
1301 }
1302 case UNICAST_ADDR:
1303 {
1304 next = parse_until(origin_line, ' ');
1305 if (next != origin_line) {
1306 //check if line contains more values than allowed
1307 if(next > line_end){
1308 size_t addr_len = 0;
1309 skip_till_next_line(origin_line, addr_len);
1310 origin.conn.address = string(origin_line, addr_len);
1311 }else{
1312 DBG("parse_sdp_origin: 'o=' contains more values than allowed; these values will be ignored\n");
1313 origin.conn.address = string(origin_line, int(next-origin_line)-1);
1314 }
1315 } else {
1316 origin.conn.address = "";
1317 }
1318 parsing = 0;
1319 break;
1320 }
1321 }
1322 }
1323
1324 sdp_msg->origin = origin;
1325
1326 //DBG("parse_sdp_line_ex: parse_sdp_origin: done parsing sdp origin\n");
1327 return;
1328 }
1329
1330
1331 /*
1332 *HELPER FUNCTIONS
1333 */
1334
contains(char * s,char * next_line,char c)1335 static bool contains(char* s, char* next_line, char c)
1336 {
1337 char* line=s;
1338 while((line != next_line-1) && (*line)){
1339 if(*line == c)
1340 return true;
1341 line++;
1342 }
1343 return false;
1344 }
1345
is_wsp(char s)1346 static bool is_wsp(char s) {
1347 return s==' ' || s == '\r' || s == '\n' || s == '\t';
1348 }
1349
parse_until(char * s,char end)1350 static char* parse_until(char* s, char end)
1351 {
1352 char* line=s;
1353 while(*line && *line != end ){
1354 line++;
1355 }
1356 line++;
1357 return line;
1358 }
1359
parse_until(char * s,char * end,char c)1360 static char* parse_until(char* s, char* end, char c)
1361 {
1362 char* line=s;
1363 while(line<end && *line && *line != c ){
1364 line++;
1365 }
1366 if (line<end)
1367 line++;
1368 return line;
1369 }
1370
is_eql_next(char * s)1371 static char* is_eql_next(char* s)
1372 {
1373 char* current_line=s;
1374 if(*(++current_line) != '='){
1375 DBG("parse_sdp_line: expected '=' but found <%c> \n", *current_line);
1376 }
1377 current_line +=1;
1378 return current_line;
1379 }
1380
get_next_line(char * s)1381 inline char* get_next_line(char* s)
1382 {
1383 char* next_line=s;
1384 //search for next line
1385 while( *next_line != '\0') {
1386 if(*next_line == CR){
1387 next_line++;
1388 if (*next_line == LF) {
1389 next_line++;
1390 break;
1391 } else {
1392 continue;
1393 }
1394 } else if (*next_line == LF){
1395 next_line++;
1396 break;
1397 }
1398 next_line++;
1399 }
1400
1401 return next_line;
1402 }
1403
1404 /* skip to 0, CRLF or LF;
1405 @return line_len length of current line
1406 @return start of next line
1407 */
skip_till_next_line(char * s,size_t & line_len)1408 inline char* skip_till_next_line(char* s, size_t& line_len)
1409 {
1410 char* next_line=s;
1411 line_len = 0;
1412
1413 //search for next line
1414 while( *next_line != '\0') {
1415 if (*next_line == CR) {
1416 next_line++;
1417 if (*next_line == LF) {
1418 next_line++;
1419 break;
1420 } else {
1421 continue;
1422 }
1423 } else if (*next_line == LF){
1424 next_line++;
1425 break;
1426 }
1427 line_len++;
1428 next_line++;
1429 }
1430
1431 return next_line;
1432 }
1433
1434 /*
1435 *Check if known media type is used
1436 */
media_type(std::string media)1437 static MediaType media_type(std::string media)
1438 {
1439 if(media == "audio")
1440 return MT_AUDIO;
1441 else if(media == "video")
1442 return MT_VIDEO;
1443 else if(media == "application")
1444 return MT_APPLICATION;
1445 else if(media == "text")
1446 return MT_TEXT;
1447 else if(media == "message")
1448 return MT_MESSAGE;
1449 else if(media == "image")
1450 return MT_IMAGE;
1451 else
1452 return MT_NONE;
1453 }
1454
transport_type(string transport)1455 static TransProt transport_type(string transport)
1456 {
1457 string transport_uc = transport;
1458 std::transform(transport_uc.begin(), transport_uc.end(), transport_uc.begin(), toupper);
1459
1460 if(transport_uc == "RTP/AVP")
1461 return TP_RTPAVP;
1462 else if(transport_uc == "RTP/AVPF")
1463 return TP_RTPAVPF;
1464 else if(transport_uc == "UDP")
1465 return TP_UDP;
1466 else if(transport_uc == "RTP/SAVP")
1467 return TP_RTPSAVP;
1468 else if(transport_uc == "RTP/SAVPF")
1469 return TP_RTPSAVPF;
1470 else if(transport_uc == "UDP/TLS/RTP/SAVP")
1471 return TP_UDPTLSRTPSAVP;
1472 else if(transport_uc == "UDP/TLS/RTP/SAVPF")
1473 return TP_UDPTLSRTPSAVPF;
1474 else if(transport_uc == "UDPTL")
1475 return TP_UDPTL;
1476 else
1477 return TP_NONE;
1478 }
1479
1480 /*
1481 *Check if known attribute name is used
1482 */
attr_check(std::string attr)1483 static bool attr_check(std::string attr)
1484 {
1485 if(attr == "cat")
1486 return true;
1487 else if(attr == "keywds")
1488 return true;
1489 else if(attr == "tool")
1490 return true;
1491 else if(attr == "ptime")
1492 return true;
1493 else if(attr == "maxptime")
1494 return true;
1495 else if(attr == "recvonly")
1496 return true;
1497 else if(attr == "sendrecv")
1498 return true;
1499 else if(attr == "sendonly")
1500 return true;
1501 else if(attr == "inactive")
1502 return true;
1503 else if(attr == "orient")
1504 return true;
1505 else if(attr == "type")
1506 return true;
1507 else if(attr == "charset")
1508 return true;
1509 else if(attr == "sdplang")
1510 return true;
1511 else if(attr == "lang")
1512 return true;
1513 else if(attr == "framerate")
1514 return true;
1515 else if(attr == "quality")
1516 return true;
1517 else if(attr == "both")
1518 return true;
1519 else if(attr == "active")
1520 return true;
1521 else if(attr == "passive")
1522 return true;
1523 else {
1524 DBG("unknown attribute: %s\n", (char*)attr.c_str());
1525 return false;
1526 }
1527 }
1528
1529