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