1 /**
2 * session.cpp
3 * This file is part of the YATE Project http://YATE.null.ro
4 *
5 * SDP media handling
6 *
7 * Yet Another Telephony Engine - a fully featured software PBX and IVR
8 * Copyright (C) 2004-2014 Null Team
9 *
10 * This software is distributed under multiple licenses;
11 * see the COPYING file in the main directory for licensing
12 * information for this specific distribution.
13 *
14 * This use of this software may be subject to additional restrictions.
15 * See the LEGAL file in the main directory for details.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22 #include <yatesdp.h>
23
24 namespace TelEngine {
25
26 /*
27 * SDPSession
28 */
SDPSession(SDPParser * parser)29 SDPSession::SDPSession(SDPParser* parser)
30 : m_parser(parser), m_mediaStatus(MediaMissing),
31 m_rtpForward(false), m_sdpForward(parser->sdpForward()), m_rtpMedia(0),
32 m_sdpSession(0), m_sdpVersion(0), m_sdpHash(YSTRING_INIT_HASH),
33 m_secure(m_parser->m_secure), m_rfc2833(m_parser->m_rfc2833),
34 m_ipv6(false), m_amrExtra(""), m_enabler(0), m_ptr(0)
35 {
36 setSdpDebug();
37 }
38
SDPSession(SDPParser * parser,NamedList & params)39 SDPSession::SDPSession(SDPParser* parser, NamedList& params)
40 : m_parser(parser), m_mediaStatus(MediaMissing),
41 m_rtpForward(false), m_sdpForward(parser->sdpForward()), m_rtpMedia(0),
42 m_sdpSession(0), m_sdpVersion(0), m_sdpHash(YSTRING_INIT_HASH),
43 m_ipv6(false), m_amrExtra(""), m_enabler(0), m_ptr(0)
44 {
45 setSdpDebug();
46 m_rtpForward = params.getBoolValue("rtp_forward");
47 m_sdpForward = params.getBoolValue(YSTRING("forward_sdp"),m_sdpForward);
48 m_secure = params.getBoolValue("secure",parser->m_secure);
49 m_rfc2833 = parser->m_rfc2833;
50 setRfc2833(params.getParam("rfc2833"));
51 }
52
~SDPSession()53 SDPSession::~SDPSession()
54 {
55 resetSdp();
56 }
57
58 // Set new media list. Return true if changed
setMedia(ObjList * media,bool preserveExisting)59 bool SDPSession::setMedia(ObjList* media, bool preserveExisting)
60 {
61 if (media == m_rtpMedia)
62 return false;
63 DDebug(m_enabler,DebugAll,"SDPSession::setMedia(%p) [%p]",media,m_ptr);
64 ObjList* tmp = m_rtpMedia;
65 m_rtpMedia = media;
66 bool chg = m_rtpMedia != 0;
67 if (tmp) {
68 chg = false;
69 for (ObjList* o = tmp->skipNull(); o; o = o->skipNext()) {
70 SDPMedia* m = static_cast<SDPMedia*>(o->get());
71 if (media) {
72 ObjList* l = media->find(*m);
73 SDPMedia* newMedia = l ? static_cast<SDPMedia*>(l->get()) : 0;
74 if (newMedia && m->sameAs(newMedia,m_parser->ignorePort(),preserveExisting)) {
75 if (preserveExisting && m->isStarted()) {
76 XDebug(m_enabler,DebugAll,
77 "SDPSession::setMedia(%p) keeping existing media='%s' format='%s' [%p]",
78 media,m->c_str(),m->format().c_str(),m_ptr);
79 newMedia->keepRtp(*m);
80 }
81 continue;
82 }
83 }
84 chg = true;
85 mediaChanged(*m);
86 }
87 TelEngine::destruct(tmp);
88 }
89 printRtpMedia("Set media");
90 return chg;
91 }
92
93 // Put the list of net media in a parameter list
putMedia(NamedList & msg,ObjList * mList,bool putPort)94 void SDPSession::putMedia(NamedList& msg, ObjList* mList, bool putPort)
95 {
96 if (!mList)
97 return;
98 bool audio = false;
99 bool other = false;
100 for (mList = mList->skipNull(); mList; mList = mList->skipNext()) {
101 SDPMedia* m = static_cast<SDPMedia*>(mList->get());
102 m->putMedia(msg,putPort);
103 if (m->isAudio())
104 audio = true;
105 else
106 other = true;
107 }
108 if (other && !audio)
109 msg.setParam("media",String::boolText(false));
110 }
111
112 // Update the RFC 2833 availability and payload
setRfc2833(const String & value)113 void SDPSession::setRfc2833(const String& value)
114 {
115 if (value.toBoolean(true)) {
116 m_rfc2833 = value.toInteger(m_parser->m_rfc2833);
117 if (m_rfc2833 < 96 || m_rfc2833 > 127)
118 m_rfc2833 = value.toBoolean(false) ? 101 : m_parser->m_rfc2833;
119 }
120 else
121 m_rfc2833 = -1;
122 }
123
124 // Build and dispatch a chan.rtp message for a given media. Update media on success
dispatchRtp(SDPMedia * media,const char * addr,bool start,bool pick,RefObject * context)125 bool SDPSession::dispatchRtp(SDPMedia* media, const char* addr, bool start,
126 bool pick, RefObject* context)
127 {
128 DDebug(m_enabler,DebugAll,"SDPSession::dispatchRtp(%p,%s,%u,%u,%p) [%p]",
129 media,addr,start,pick,context,m_ptr);
130 Message* m = buildChanRtp(media,addr,start,context);
131 if (m)
132 dispatchingRtp(m,media);
133 if (!(m && Engine::dispatch(m))) {
134 TelEngine::destruct(m);
135 return false;
136 }
137 media->update(*m,start);
138 if (!pick) {
139 TelEngine::destruct(m);
140 return true;
141 }
142 m_rtpForward = false;
143 m_rtpLocalAddr = m->getValue("localip",m_rtpLocalAddr);
144 m_mediaStatus = m_rtpLocalAddr.null() ? MediaMuted : MediaStarted;
145 String sdpPrefix = m->getValue("osdp-prefix","osdp");
146 if (sdpPrefix) {
147 if (!sdpPrefix.endsWith("_"))
148 sdpPrefix += "_";
149 unsigned int n = m->length();
150 for (unsigned int j = 0; j < n; j++) {
151 const NamedString* param = m->getParam(j);
152 if (!param)
153 continue;
154 String tmp = param->name();
155 if (tmp.startSkip(sdpPrefix,false) && tmp)
156 media->parameter(tmp,*param,false);
157 }
158 }
159 if (m_secure) {
160 int tag = m->getIntValue("crypto_tag",1);
161 tag = m->getIntValue("ocrypto_tag",tag);
162 const String* suite = m->getParam("ocrypto_suite");
163 const String* key = m->getParam("ocrypto_key");
164 const String* params = m->getParam("ocrypto_params");
165 if (suite && key && (tag >= 0)) {
166 String sdes(tag);
167 sdes << " " << *suite << " " << *key;
168 if (params)
169 sdes << " " << *params;
170 media->crypto(sdes,false);
171 }
172 }
173 TelEngine::destruct(m);
174 return true;
175 }
176
177 // Repeatedly calls dispatchRtp() for each media in the list
178 // Update it on success. Remove it on failure
dispatchRtp(const char * addr,bool start,RefObject * context)179 bool SDPSession::dispatchRtp(const char* addr, bool start, RefObject* context)
180 {
181 if (!m_rtpMedia)
182 return false;
183 DDebug(m_enabler,DebugAll,"SDPSession::dispatchRtp(%s,%u,%p) [%p]",
184 addr,start,context,m_ptr);
185 bool ok = false;
186 ObjList* o = m_rtpMedia->skipNull();
187 while (o) {
188 SDPMedia* m = static_cast<SDPMedia*>(o->get());
189 if (dispatchRtp(m,addr,start,true,context)) {
190 ok = true;
191 o = o->skipNext();
192 }
193 else {
194 TraceDebug(m_traceId,m_enabler,DebugMild,
195 "Removing failed SDP media '%s' format '%s' from offer [%p]",
196 m->c_str(),m->format().safe(),m_ptr);
197 o->remove();
198 o = o->skipNull();
199 }
200 }
201 return ok;
202 }
203
204 // Try to start RTP for all media
startRtp(RefObject * context)205 bool SDPSession::startRtp(RefObject* context)
206 {
207 if (m_rtpForward || !m_rtpMedia || (m_mediaStatus != MediaStarted))
208 return false;
209 DDebug(m_enabler,DebugAll,"SDPSession::startRtp(%p) [%p]",context,m_ptr);
210 bool ok = false;
211 for (ObjList* o = m_rtpMedia->skipNull(); o; o = o->skipNext()) {
212 SDPMedia* m = static_cast<SDPMedia*>(o->get());
213 ok = dispatchRtp(m,m_rtpAddr,true,false,context) || ok;
214 }
215 return ok;
216 }
217
218 // Update from parameters. Build a default SDP if no media is found in params
updateSDP(const NamedList & params,bool defaults)219 bool SDPSession::updateSDP(const NamedList& params, bool defaults)
220 {
221 DDebug(m_enabler,DebugAll,"SDPSession::updateSdp('%s',%s) [%p]",
222 params.c_str(),String::boolText(defaults),m_ptr);
223 const char* sdpPrefix = params.getValue("osdp-prefix","osdp");
224 ObjList* lst = 0;
225 unsigned int n = params.length();
226 String defFormats;
227 m_parser->getAudioFormats(defFormats);
228 for (unsigned int i = 0; i < n; i++) {
229 const NamedString* p = params.getParam(i);
230 if (!p)
231 continue;
232 // search for media or media_MEDIANAME parameters
233 String tmp(p->name());
234 if (!tmp.startSkip("media",false))
235 continue;
236 if (tmp && (tmp[0] != '_'))
237 continue;
238 // since we found at least one media declaration disable defaults
239 defaults = false;
240 // now tmp holds the suffix for the media, null for audio
241 bool audio = tmp.null();
242 // check if media is supported, default only for audio
243 if (!p->toBoolean(audio))
244 continue;
245 String fmts = params.getValue("formats" + tmp);
246 if (audio && fmts.null())
247 fmts = defFormats;
248 if (fmts.null())
249 continue;
250 String trans = params.getValue("transport" + tmp,"RTP/AVP");
251 String crypto;
252 if (m_secure)
253 crypto = params.getValue("crypto" + tmp);
254 if (audio)
255 tmp = "audio";
256 else
257 tmp >> "_";
258 SDPMedia* rtp = 0;
259 // try to take the media descriptor from the old list
260 if (m_rtpMedia) {
261 ObjList* om = m_rtpMedia->find(tmp);
262 if (om)
263 rtp = static_cast<SDPMedia*>(om->remove(false));
264 }
265 bool append = false;
266 if (rtp)
267 rtp->update(fmts);
268 else {
269 rtp = new SDPMedia(tmp,trans,fmts);
270 append = true;
271 }
272 rtp->crypto(crypto,false);
273 if (sdpPrefix) {
274 String prefix;
275 prefix << sdpPrefix << rtp->suffix() << "_";
276 for (unsigned int j = 0; j < n; j++) {
277 const NamedString* param = params.getParam(j);
278 if (!param)
279 continue;
280 tmp = param->name();
281 if (tmp.startSkip(prefix,false) && (tmp.find('_') < 0))
282 rtp->parameter(tmp,*param,append);
283 }
284 }
285 if (!lst)
286 lst = new ObjList;
287 lst->append(rtp);
288 }
289 if (defaults && !lst) {
290 lst = new ObjList;
291 lst->append(new SDPMedia("audio","RTP/AVP",params.getValue("formats",defFormats)));
292 }
293 return setMedia(lst);
294 }
295
296
297 // Update RTP/SDP data from parameters
298 // Return true if media changed
updateRtpSDP(const NamedList & params)299 bool SDPSession::updateRtpSDP(const NamedList& params)
300 {
301 DDebug(m_enabler,DebugAll,"SDPSession::updateRtpSDP(%s) [%p]",params.c_str(),m_ptr);
302 String addr;
303 ObjList* tmp = updateRtpSDP(params,addr,m_rtpMedia);
304 if (tmp) {
305 bool chg = (m_rtpLocalAddr != addr);
306 m_rtpLocalAddr = addr;
307 return setMedia(tmp) || chg;
308 }
309 return false;
310 }
311
312 // Utility used in createSDP
addIP(String & buf,const char * addr,int family=SocketAddr::Unknown)313 static int addIP(String& buf, const char* addr, int family = SocketAddr::Unknown)
314 {
315 if (family != SocketAddr::IPv4 && family != SocketAddr::IPv6) {
316 if (addr) {
317 family = SocketAddr::family(addr);
318 if (family != SocketAddr::IPv4 && family != SocketAddr::IPv6)
319 family = SocketAddr::IPv4;
320 }
321 else
322 family = SocketAddr::IPv4;
323 }
324 if (family == SocketAddr::IPv4)
325 buf << "IN IP4 ";
326 else
327 buf << "IN IP6 ";
328 if (!TelEngine::null(addr))
329 buf << addr;
330 else if (family == SocketAddr::IPv4)
331 buf << SocketAddr::ipv4NullAddr();
332 else
333 buf << SocketAddr::ipv6NullAddr();
334 return family;
335 }
336
337 // Utility used in createSDP
338 // Build a 'fmtp' line if not already done. Add a parameter to it
setFmtpLine(String * & line,int payload,const char * param)339 static inline void setFmtpLine(String*& line, int payload, const char* param)
340 {
341 if (line)
342 line->append(param,";");
343 else {
344 line = new String("fmtp:");
345 *line << payload << " " << param;
346 }
347 }
348
349 // Creates a SDP body from transport address and list of media descriptors
350 // Use own list if given media list is 0
createSDP(const char * addr,ObjList * mediaList)351 MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
352 {
353 DDebug(m_enabler,DebugAll,"SDPSession::createSDP('%s',%p) [%p]",addr,mediaList,m_ptr);
354 ObjList* mList = mediaList;
355 if (!mList)
356 mList = m_rtpMedia;
357 // if we got no media descriptors we simply create no SDP
358 if (!mList)
359 return 0;
360 if (!m_sdpSession)
361 m_sdpVersion = m_sdpSession = Time::secNow();
362
363 // override the address with the externally advertised if needed
364 if (addr && m_rtpNatAddr)
365 addr = m_rtpNatAddr;
366 if (!m_originAddr)
367 m_originAddr = addr ? addr : m_host.safe();
368 // no address means on hold or muted
369 String origin;
370 int f = addIP(origin,m_originAddr);
371 String conn;
372 addIP(conn,addr,f);
373
374 MimeSdpBody* sdp = new MimeSdpBody(true);
375 sdp->addLine("v","0");
376 // insert incomplete origin just for hashing purpose
377 NamedString* org = sdp->addLine("o",origin);
378 sdp->addLine("s",m_parser->m_sessionName);
379 sdp->addLine("c",conn);
380 sdp->addLine("t","0 0");
381
382 Lock lock(m_parser);
383 bool defcodecs = m_parser->m_codecs.getBoolValue("default",true);
384 for (ObjList* ml = mList->skipNull(); ml; ml = ml->skipNext()) {
385 SDPMedia* m = static_cast<SDPMedia*>(ml->get());
386 int rfc2833 = 0;
387 if ((m_rfc2833 >= 0) && m->isAudio()) {
388 if (!m_rtpForward) {
389 rfc2833 = m->rfc2833().toInteger(m_rfc2833);
390 if (rfc2833 < 96 || rfc2833 > 127)
391 rfc2833 = 101;
392 }
393 else if (m->rfc2833().toBoolean(true)) {
394 rfc2833 = m->rfc2833().toInteger();
395 if (rfc2833 < 96 || rfc2833 > 127)
396 rfc2833 = 0;
397 }
398 }
399 String mline(m->fmtList());
400 ObjList* l = mline.split(',',false);
401 mline = *m;
402 mline << " " << (m->localPort() ? m->localPort().c_str() : "0") << " " << m->transport();
403 ObjList* map = m->mappings().split(',',false);
404 ObjList rtpmap;
405 ObjList* dest = &rtpmap;
406 String frm;
407 int ptime = 0;
408 ObjList* f = l;
409 for (; f; f = f->next()) {
410 const String* s = static_cast<const String*>(f->get());
411 if (s) {
412 if (*s == "g729b")
413 continue;
414 int payload = s->toInteger(SDPParser::s_payloads,-1);
415 int defcode = payload;
416 String tmp = *s;
417 tmp << "=";
418 bool found = false;
419 for (ObjList* pl = map; pl; pl = pl->next()) {
420 const String* mapping = static_cast<const String*>(pl->get());
421 if (!mapping)
422 continue;
423 found = mapping->startsWith(tmp);
424 if (!found) {
425 for (ObjList* o = pl->skipNext(); o; o = o->skipNext()) {
426 const String* tmpM = static_cast<const String*>(o->get());
427 if (tmpM->startsWith(tmp)) {
428 found = true;
429 mapping = tmpM;
430 break;
431 }
432 }
433 }
434 if (found) {
435 payload = -1;
436 tmp = *mapping;
437 tmp >> "=" >> payload;
438 found = true;
439 XDebug(m_enabler,DebugAll,"RTP mapped payload %d for '%s' [%p]",
440 payload,s->c_str(),m_ptr);
441 break;
442 }
443 String tmp2 = *mapping;
444 int pload;
445 tmp2 >> "=" >> pload;
446 if (payload == pload) {
447 XDebug(m_enabler,DebugAll,"RTP conflict for payload %d, allocating new [%p]",
448 payload,m_ptr);
449 payload = -1;
450 u_int32_t bmap = 0;
451 for (ObjList* sl = map; sl; sl = sl->next()) {
452 mapping = static_cast<const String*>(sl->get());
453 if (!mapping)
454 continue;
455 tmp2 = *mapping;
456 pload = 0;
457 tmp2 >> "=" >> pload;
458 if (pload >= 96 && pload < 127)
459 bmap |= 1 << (pload - 96);
460 }
461 // allocate free and non-standard is possible
462 for (pload = 96; pload < 127; pload++) {
463 if (pload == rfc2833)
464 continue;
465 if (lookup(pload,SDPParser::s_rtpmap))
466 continue;
467 if ((bmap & (1 << (pload - 96))) == 0) {
468 payload = pload;
469 break;
470 }
471 }
472 if (payload >= 0)
473 break;
474 // none free, allocate from "standard" ones too
475 for (pload = 96; pload < 127; pload++) {
476 if (pload == rfc2833)
477 continue;
478 if ((bmap & (1 << (pload - 96))) == 0) {
479 payload = pload;
480 break;
481 }
482 }
483 break;
484 }
485 }
486 if (payload >= 0) {
487 if (!found) {
488 tmp = *s;
489 tmp << "=" << payload;
490 map->append(new String(tmp));
491 }
492 if (defcode < 0)
493 defcode = payload;
494 const char* map = lookup(defcode,SDPParser::s_rtpmap);
495 if (map && m_parser->m_codecs.getBoolValue(*s,defcodecs && DataTranslator::canConvert(*s))) {
496 frm << " " << payload;
497 String* temp = new String("rtpmap:");
498 *temp << payload << " " << map;
499 dest = dest->append(temp);
500 const String* fmtp = m->getParam("fmtp:" + *s);
501 temp = 0;
502 if (*s == YSTRING("ilbc20")) {
503 ptime = 20;
504 setFmtpLine(temp,payload,"mode=20");
505 }
506 else if (*s == YSTRING("ilbc30")) {
507 ptime = 30;
508 setFmtpLine(temp,payload,"mode=30");
509 }
510 else if (*s == YSTRING("g729")) {
511 setFmtpLine(temp,payload,"annexb=");
512 *temp << ((0 != l->find("g729b")) ? "yes" : "no");
513 }
514 else if (*s == YSTRING("amr") || s->startsWith("amr/")) {
515 setFmtpLine(temp,payload,"octet-align=0");
516 if (!m_rtpForward && m_amrExtra) {
517 addFmtpAmrExtra(*temp,fmtp);
518 fmtp = 0;
519 }
520 }
521 else if (*s == YSTRING("amr-o") || s->startsWith("amr-o/")) {
522 setFmtpLine(temp,payload,"octet-align=1");
523 if (!m_rtpForward && m_amrExtra) {
524 addFmtpAmrExtra(*temp,fmtp);
525 fmtp = 0;
526 }
527 }
528 if (fmtp)
529 setFmtpLine(temp,payload,*fmtp);
530 if (temp)
531 dest = dest->append(temp);
532 if (mediaList) {
533 // RTP forward propagates General Purpose Media Descriptor
534 const String* gpmd = m->getParam("gpmd:" + *s);
535 if (gpmd) {
536 temp = new String("gpmd:");
537 *temp << payload << " " << *gpmd;
538 dest = dest->append(temp);
539 }
540 }
541 }
542 }
543 }
544 }
545 TelEngine::destruct(l);
546 TelEngine::destruct(map);
547
548 if (rfc2833 && frm) {
549 // claim to support telephone events
550 frm << " " << rfc2833;
551 String* s = new String;
552 *s << "rtpmap:" << rfc2833 << " telephone-event/8000";
553 dest = dest->append(s);
554 }
555
556 if (frm.null()) {
557 if (m->isAudio() || !m->fmtList()) {
558 TraceDebug(m_traceId,m_enabler,DebugMild,"No formats for '%s', excluding from SDP [%p]",
559 m->c_str(),m_ptr);
560 continue;
561 }
562 TraceDebug(m_traceId,m_enabler,DebugInfo,"Assuming formats '%s' for media '%s' [%p]",
563 m->fmtList(),m->c_str(),m_ptr);
564 frm << " " << m->fmtList();
565 // brutal but effective
566 for (char* p = const_cast<char*>(frm.c_str()); *p; p++) {
567 if (*p == ',')
568 *p = ' ';
569 }
570 }
571
572 if (ptime) {
573 String* temp = new String("ptime:");
574 *temp << ptime;
575 dest = dest->append(temp);
576 }
577
578 sdp->addLine("m",mline + frm);
579 bool enc = false;
580 if (m->isModified()) {
581 unsigned int n = m->length();
582 for (unsigned int i = 0; i < n; i++) {
583 const NamedString* param = m->getParam(i);
584 if (param && (param->name().find(':') < 0)) {
585 const char* type = "a";
586 String tmp = param->name();
587 if (tmp.startSkip("BW-",false)) {
588 if (!tmp)
589 continue;
590 type = "b";
591 }
592 else
593 enc = enc || (tmp == "encryption");
594 if (*param)
595 tmp << ":" << *param;
596 sdp->addLine(type,tmp);
597 }
598 }
599 }
600 for (f = rtpmap.skipNull(); f; f = f->skipNext()) {
601 String* s = static_cast<String*>(f->get());
602 if (s)
603 sdp->addLine("a",*s);
604 }
605 if (addr && m->localCrypto()) {
606 sdp->addLine("a","crypto:" + m->localCrypto());
607 if (!enc)
608 sdp->addLine("a","encryption:optional");
609 }
610 }
611 // increment version if body hash changed
612 if ((YSTRING_INIT_HASH != m_sdpHash) && (sdp->hash() != m_sdpHash))
613 m_sdpVersion++;
614 m_sdpHash = sdp->hash();
615 // insert version in the origin line
616 origin.clear();
617 origin << "yate " << m_sdpSession << " " << m_sdpVersion << " " << *org;
618 *org = origin;
619
620 return sdp;
621 }
622
623 // Creates a SDP body for the current media status
createSDP()624 MimeSdpBody* SDPSession::createSDP()
625 {
626 switch (m_mediaStatus) {
627 case MediaStarted:
628 return createSDP(getRtpAddr());
629 case MediaMuted:
630 return createSDP(0);
631 default:
632 return 0;
633 }
634 }
635
636 // Creates a SDP from RTP address data present in message
createPasstroughSDP(NamedList & msg,bool update,bool allowEmptyAddr)637 MimeSdpBody* SDPSession::createPasstroughSDP(NamedList& msg, bool update,
638 bool allowEmptyAddr)
639 {
640 XDebug(m_enabler,DebugAll,"createPasstroughSDP(%s,%u,%u) [%p]",
641 msg.c_str(),update,allowEmptyAddr,m_ptr);
642 String tmp = msg.getValue("rtp_forward");
643 msg.clearParam("rtp_forward");
644 if (!(m_rtpForward && tmp.toBoolean()))
645 return 0;
646 String* raw = msg.getParam("sdp_raw");
647 if (raw && m_sdpForward) {
648 msg.setParam("rtp_forward","accepted");
649 return new MimeSdpBody("application/sdp",raw->safe(),raw->length());
650 }
651 String addr;
652 ObjList* lst = updateRtpSDP(msg,addr,update ? m_rtpMedia : 0,allowEmptyAddr);
653 if (!lst)
654 return 0;
655 MimeSdpBody* sdp = createSDP(addr,lst);
656 if (update) {
657 m_rtpLocalAddr = addr;
658 setMedia(lst);
659 }
660 else
661 TelEngine::destruct(lst);
662 if (sdp)
663 msg.setParam("rtp_forward","accepted");
664 return sdp;
665 }
666
667 // Update media format lists from parameters
updateFormats(const NamedList & msg,bool changeMedia)668 void SDPSession::updateFormats(const NamedList& msg, bool changeMedia)
669 {
670 if (!m_rtpMedia)
671 return;
672 unsigned int n = msg.length();
673 unsigned int i;
674 if (changeMedia) {
675 // check if any media is to be removed
676 for (i = 0; i < n; i++) {
677 const NamedString* p = msg.getParam(i);
678 if (!p)
679 continue;
680 // search for media or media_MEDIANAME parameters
681 String tmp = p->name();
682 if (!tmp.startSkip("media",false))
683 continue;
684 if (tmp && (tmp[0] != '_'))
685 continue;
686 // only check for explicit disabled media
687 if (p->toBoolean(true))
688 continue;
689 if (tmp.null())
690 tmp = "audio";
691 else
692 tmp = tmp.substr(1);
693 SDPMedia* rtp = static_cast<SDPMedia*>(m_rtpMedia->operator[](tmp));
694 if (!rtp)
695 continue;
696 TraceDebug(m_traceId,m_enabler,DebugNote,"Removing disabled media '%s' [%p]",
697 tmp.c_str(),m_ptr);
698 m_rtpMedia->remove(rtp,false);
699 mediaChanged(*rtp);
700 TelEngine::destruct(rtp);
701 }
702 }
703 for (i = 0; i < n; i++) {
704 const NamedString* p = msg.getParam(i);
705 if (!p)
706 continue;
707 // search for formats_MEDIANAME parameters
708 String tmp = p->name();
709 if (!tmp.startSkip("formats",false))
710 continue;
711 if (tmp && (tmp[0] != '_'))
712 continue;
713 const char* trans = 0;
714 // make sure we don't re-add explicitely disabled media
715 if (changeMedia && msg.getBoolValue("media"+tmp,true))
716 trans = msg.getValue("transport"+tmp);
717 if (tmp.null())
718 tmp = "audio";
719 else
720 tmp = tmp.substr(1);
721 SDPMedia* rtp = static_cast<SDPMedia*>(m_rtpMedia->operator[](tmp));
722 if (rtp) {
723 if (rtp->update(*p))
724 TraceDebug(m_traceId,m_enabler,DebugNote,"Formats for '%s' changed to '%s' [%p]",
725 tmp.c_str(),rtp->formats().c_str(),m_ptr);
726 }
727 else if (*p) {
728 TraceDebug(m_traceId,m_enabler,DebugNote,"Got formats '%s' for absent media '%s' [%p]",
729 p->c_str(),tmp.c_str(),m_ptr);
730 if (trans) {
731 rtp = new SDPMedia(tmp,trans,p->c_str());
732 m_rtpMedia->append(rtp);
733 mediaChanged(*rtp);
734 }
735 }
736 }
737 String sdpPrefix = msg.getValue("osdp-prefix");
738 if (!sdpPrefix)
739 return;
740 if (!sdpPrefix.endsWith("_"))
741 sdpPrefix += "_";
742 for (i = 0; i < n; i++) {
743 const NamedString* param = msg.getParam(i);
744 if (!param)
745 continue;
746 String tmp = param->name();
747 if (!tmp.startSkip(sdpPrefix,false))
748 continue;
749 int sep = tmp.find("_");
750 String media("audio");
751 if (sep > 0) {
752 media = tmp.substr(0,sep);
753 tmp = tmp.substr(sep + 1);
754 }
755 if (tmp.null() || (tmp.find("_") >= 0))
756 continue;
757 SDPMedia* rtp = static_cast<SDPMedia*>(m_rtpMedia->operator[](media));
758 if (rtp) {
759 DDebug(m_enabler,DebugInfo,"Updating %s parameter '%s' to '%s'",
760 media.c_str(),tmp.c_str(),param->c_str());
761 rtp->parameter(tmp,*param,false);
762 }
763 }
764 }
765
766 // Add raw SDP forwarding parameter
addSdpParams(NamedList & msg,const MimeBody * body)767 bool SDPSession::addSdpParams(NamedList& msg, const MimeBody* body)
768 {
769 if (!(m_sdpForward && body))
770 return false;
771 const MimeSdpBody* sdp =
772 static_cast<const MimeSdpBody*>(body->isSDP() ? body : body->getFirst("application/sdp"));
773 if (!sdp)
774 return false;
775 const DataBlock& raw = sdp->getBody();
776 String tmp((const char*)raw.data(),raw.length());
777 return addSdpParams(msg,tmp);
778 }
779
780 // Add raw SDP forwarding parameter
addSdpParams(NamedList & msg,const String & rawSdp)781 bool SDPSession::addSdpParams(NamedList& msg, const String& rawSdp)
782 {
783 if (!m_sdpForward)
784 return false;
785 msg.setParam("rtp_forward","yes");
786 msg.addParam("sdp_raw",rawSdp);
787 return true;
788 }
789
790 // Add RTP forwarding parameters to a message
addRtpParams(NamedList & msg,const String & natAddr,const MimeBody * body,bool force,bool allowEmptyAddr)791 bool SDPSession::addRtpParams(NamedList& msg, const String& natAddr,
792 const MimeBody* body, bool force, bool allowEmptyAddr)
793 {
794 XDebug(m_enabler,DebugAll,"addRtpParams(%s,%s,%p,%u,%u) media=%p rtpaddr=%s [%p]",
795 msg.c_str(),natAddr.c_str(),body,force,allowEmptyAddr,m_rtpMedia,
796 m_rtpAddr.c_str(),m_ptr);
797 if (!(m_rtpMedia && (m_rtpAddr || allowEmptyAddr)))
798 return false;
799 putMedia(msg,false);
800 if (force || (!startRtp() && m_rtpForward)) {
801 if (natAddr)
802 msg.addParam("rtp_nat_addr",natAddr);
803 msg.addParam("rtp_forward","yes");
804 msg.addParam("rtp_addr",m_rtpAddr);
805 for (ObjList* o = m_rtpMedia->skipNull(); o; o = o->skipNext()) {
806 SDPMedia* m = static_cast<SDPMedia*>(o->get());
807 msg.addParam("rtp_port" + m->suffix(),m->remotePort());
808 if (m->isAudio())
809 msg.addParam("rtp_rfc2833",m->rfc2833());
810 }
811 addSdpParams(msg,body);
812 return true;
813 }
814 return false;
815 }
816
817 // Reset this object to default values
resetSdp(bool all)818 void SDPSession::resetSdp(bool all)
819 {
820 m_mediaStatus = MediaMissing;
821 TelEngine::destruct(m_rtpMedia);
822 m_rtpForward = false;
823 m_sdpForward = false;
824 m_externalAddr.clear();
825 m_rtpAddr.clear();
826 m_rtpLocalAddr.clear();
827 m_sdpSession = 0;
828 m_sdpVersion = 0;
829 m_host.clear();
830 if (all) {
831 m_secure = m_parser->secure();
832 m_rfc2833 = m_parser->rfc2833();
833 }
834 }
835
836 // Build a populated chan.rtp message
buildChanRtp(SDPMedia * media,const char * addr,bool start,RefObject * context)837 Message* SDPSession::buildChanRtp(SDPMedia* media, const char* addr, bool start, RefObject* context)
838 {
839 if (!(media && addr))
840 return 0;
841 Message* m = buildChanRtp(context);
842 if (!m)
843 return 0;
844 if (media->id())
845 m->addParam("rtpid",media->id());
846 m->addParam("media",*media);
847 m->addParam("transport",media->transport());
848 m->addParam("direction","bidir");
849 if (media->format())
850 m->addParam("format",media->format());
851 m->addParam("ipv6_support",String::boolText(m_ipv6));
852 if (m_rtpLocalAddr)
853 m->addParam("localip",m_rtpLocalAddr);
854 m->addParam("remoteip",addr);
855 if (start) {
856 m->addParam("remoteport",media->remotePort());
857 int payload = SDPMedia::payloadMapping(media->mappings(),media->format());
858 if (payload >= 0)
859 m->addParam("payload",String(payload));
860 m->addParam("evpayload",media->rfc2833());
861 }
862 if (m_secure) {
863 if (media->remoteCrypto()) {
864 String sdes = media->remoteCrypto();
865 static const Regexp r("^\\([0-9]\\+\\) \\+\\([^ ]\\+\\) \\+\\([^ ]\\+\\) *\\(.*\\)$");
866 if (sdes.matches(r)) {
867 m->addParam("secure",String::boolText(true));
868 m->addParam("crypto_tag",sdes.matchString(1));
869 m->addParam("crypto_suite",sdes.matchString(2));
870 m->addParam("crypto_key",sdes.matchString(3));
871 if (sdes.matchLength(4))
872 m->addParam("crypto_params",sdes.matchString(4));
873 }
874 else
875 TraceDebug(m_traceId,m_enabler,DebugWarn,"Invalid SDES: '%s' [%p]",sdes.c_str(),m_ptr);
876 }
877 else if (media->securable())
878 m->addParam("secure",String::boolText(true));
879 }
880 else
881 media->crypto(0,true);
882 unsigned int n = media->length();
883 for (unsigned int i = 0; i < n; i++) {
884 const NamedString* param = media->getParam(i);
885 if (!param)
886 continue;
887 m->addParam("sdp_" + param->name(),*param);
888 }
889 return m;
890 }
891
892 // Check if local RTP data changed for at least one media
localRtpChanged() const893 bool SDPSession::localRtpChanged() const
894 {
895 if (!m_rtpMedia)
896 return false;
897 for (ObjList* o = m_rtpMedia->skipNull(); o; o = o->skipNext()) {
898 SDPMedia* m = static_cast<SDPMedia*>(o->get());
899 if (m->localChanged())
900 return true;
901 }
902 return false;
903 }
904
905 // Set or reset the local RTP data changed flag for all media
setLocalRtpChanged(bool chg)906 void SDPSession::setLocalRtpChanged(bool chg)
907 {
908 if (!m_rtpMedia)
909 return;
910 for (ObjList* o = m_rtpMedia->skipNull(); o; o = o->skipNext())
911 (static_cast<SDPMedia*>(o->get()))->setLocalChanged(chg);
912 }
913
914 // Update RTP/SDP data from parameters
updateRtpSDP(const NamedList & params,String & rtpAddr,ObjList * oldList,bool allowEmptyAddr)915 ObjList* SDPSession::updateRtpSDP(const NamedList& params, String& rtpAddr, ObjList* oldList,
916 bool allowEmptyAddr)
917 {
918 XDebug(DebugAll,"SDPSession::updateRtpSDP(%s,%s,%p,%u)",
919 params.c_str(),rtpAddr.c_str(),oldList,allowEmptyAddr);
920 rtpAddr = params.getValue("rtp_addr");
921 if (!(rtpAddr || allowEmptyAddr))
922 return 0;
923 const char* sdpPrefix = params.getValue("osdp-prefix","osdp");
924 ObjList* lst = 0;
925 unsigned int n = params.length();
926 for (unsigned int i = 0; i < n; i++) {
927 const NamedString* p = params.getParam(i);
928 if (!p)
929 continue;
930 // search for rtp_port or rtp_port_MEDIANAME parameters
931 String tmp = p->name();
932 if (!tmp.startSkip("rtp_port",false))
933 continue;
934 if (tmp && (tmp[0] != '_'))
935 continue;
936 // now tmp holds the suffix for the media, null for audio
937 bool audio = tmp.null();
938 // check if media is supported, default only for audio
939 if (!params.getBoolValue("media" + tmp,audio))
940 continue;
941 int port = p->toInteger();
942 if (!(port || allowEmptyAddr))
943 continue;
944 const char* fmts = params.getValue("formats" + tmp);
945 if (!fmts)
946 continue;
947 String trans = params.getValue("transport" + tmp,"RTP/AVP");
948 if (audio)
949 tmp = "audio";
950 else
951 tmp >> "_";
952 SDPMedia* rtp = 0;
953 // try to take the media descriptor from the old list
954 if (oldList) {
955 ObjList* om = oldList->find(tmp);
956 if (om)
957 rtp = static_cast<SDPMedia*>(om->remove(false));
958 }
959 bool append = false;
960 if (rtp)
961 rtp->update(fmts,-1,port);
962 else {
963 rtp = new SDPMedia(tmp,trans,fmts,-1,port);
964 append = true;
965 }
966 if (sdpPrefix) {
967 for (unsigned int j = 0; j < n; j++) {
968 const NamedString* param = params.getParam(j);
969 if (!param)
970 continue;
971 tmp = param->name();
972 if (tmp.startSkip(sdpPrefix + rtp->suffix() + "_",false) && (tmp.find('_') < 0))
973 rtp->parameter(tmp,*param,append);
974 }
975 }
976 rtp->mappings(params.getValue("rtp_mapping" + rtp->suffix()));
977 if (audio)
978 rtp->rfc2833(params.getIntValue("rtp_rfc2833",-1));
979 rtp->crypto(params.getValue("crypto" + rtp->suffix()),false);
980 if (!lst)
981 lst = new ObjList;
982 lst->append(rtp);
983 }
984 return lst;
985 }
986
987 // Media changed notification.
mediaChanged(const SDPMedia & media)988 void SDPSession::mediaChanged(const SDPMedia& media)
989 {
990 XDebug(m_enabler,DebugAll,"SDPSession::mediaChanged('%s' %p)%s%s [%p]",
991 media.c_str(),&media,(media.id() ? " id=" : ""),media.id().safe(),m_ptr);
992 }
993
994 // Dispatch rtp notification
dispatchingRtp(Message * & msg,SDPMedia * media)995 void SDPSession::dispatchingRtp(Message*& msg, SDPMedia* media)
996 {
997 XDebug(m_enabler,DebugAll,"SDPSession::dispatchingRtp(%p,%p) [%p]",msg,media,m_ptr);
998 }
999
1000 // Set data used in debug
setSdpDebug(DebugEnabler * enabler,void * ptr,const String & traceId)1001 void SDPSession::setSdpDebug(DebugEnabler* enabler, void* ptr, const String& traceId)
1002 {
1003 m_enabler = enabler ? enabler : static_cast<DebugEnabler*>(m_parser);
1004 m_ptr = ptr ? ptr : (void*)this;
1005 m_traceId = traceId;
1006 }
1007
1008 // Print current media to output
printRtpMedia(const char * reason)1009 void SDPSession::printRtpMedia(const char* reason)
1010 {
1011 if (!(m_rtpMedia && m_enabler->debugAt(DebugAll)))
1012 return;
1013 String tmp;
1014 for (ObjList* o = m_rtpMedia->skipNull(); o; o = o->skipNext()) {
1015 SDPMedia* m = static_cast<SDPMedia*>(o->get());
1016 if (tmp)
1017 tmp << " ";
1018 tmp << m->c_str() << "=" << m->formats();
1019 }
1020 TraceDebug(m_traceId,m_enabler,DebugAll,"%s: %s [%p]",reason,tmp.c_str(),m_ptr);
1021 }
1022
1023 // Set extra parameters for formats
setFormatsExtra(const NamedList & list,bool out)1024 void SDPSession::setFormatsExtra(const NamedList& list, bool out)
1025 {
1026 const String* amr = list.getParam(out ? YSTRING("oamr_extra") : YSTRING("iamr_extra"));
1027 m_amrExtra.assign("");
1028 m_amrExtra.clearParams();
1029 if (!TelEngine::null(amr)) {
1030 ObjList* l = amr->split(';',false);
1031 ObjList* o = l->skipNull();
1032 if (o) {
1033 m_amrExtra.assign(*amr);
1034 for (; o; o = o->skipNext()) {
1035 const String* s = static_cast<String*>(o->get());
1036 int pos = s->find('=');
1037 if (pos >= 0) {
1038 String n = s->substr(0,pos + 1).trimBlanks();
1039 if (n) {
1040 m_amrExtra.addParam(n,s->substr(pos + 1));
1041 continue;
1042 }
1043 }
1044 m_amrExtra.addParam(*s,0);
1045 }
1046 }
1047 TelEngine::destruct(l);
1048 }
1049 }
1050
1051 // Add extra AMR params to fmtp line
addFmtpAmrExtra(String & buf,const String * fmtp)1052 void SDPSession::addFmtpAmrExtra(String& buf, const String* fmtp)
1053 {
1054 if (TelEngine::null(fmtp)) {
1055 buf.append(m_amrExtra,";");
1056 return;
1057 }
1058 // Remove from 'fmtp' parameters we are setting from extra
1059 ObjList* l = fmtp->split(';',false);
1060 for (ObjList* f = l->skipNull(); f;) {
1061 String& s = *static_cast<String*>(f->get());
1062 s = s.trimBlanks();
1063 NamedString* found = 0;
1064 if (s) {
1065 int pos = s.find('=');
1066 if (pos >= 0) {
1067 for (ObjList* o = m_amrExtra.paramList()->skipNull(); o; o = o->skipNext()) {
1068 NamedString* ns = static_cast<NamedString*>(o->get());
1069 if (s.startsWith(ns->name())) {
1070 found = ns;
1071 break;
1072 }
1073 }
1074 }
1075 else
1076 found = m_amrExtra.getParam(s);
1077 }
1078 if (found) {
1079 f->remove();
1080 f = f->skipNull();
1081 }
1082 else
1083 f = f->skipNext();
1084 }
1085 buf.append(m_amrExtra,";");
1086 buf.append(l,";");
1087 TelEngine::destruct(l);
1088 }
1089
1090 }; // namespace TelEngine
1091
1092 /* vi: set ts=8 sw=4 sts=4 noet: */
1093