1 /*! \file    sdp.c
2  * \author   Lorenzo Miniero <lorenzo@meetecho.com>
3  * \copyright GNU General Public License v3
4  * \brief    SDP processing
5  * \details  Implementation of an SDP
6  * parser/merger/generator in the server. Each SDP coming from peers is
7  * stripped/anonymized before it is passed to the plugins: all
8  * DTLS/ICE/transport related information is removed, only leaving the
9  * relevant information in place. SDP coming from plugins is stripped/anonymized
10  * as well, and merged with the proper DTLS/ICE/transport information before
11  * it is sent to the peers. The actual SDP processing (parsing SDP strings,
12  * representation of SDP as an internal format, and so on) is done via
13  * the tools provided in sdp-utils.h.
14  *
15  * \ingroup protocols
16  * \ref protocols
17  */
18 
19 #include <netdb.h>
20 
21 #include <gio/gio.h>
22 
23 #include "janus.h"
24 #include "ice.h"
25 #include "sdp.h"
26 #include "utils.h"
27 #include "ip-utils.h"
28 #include "debug.h"
29 #include "events.h"
30 
31 
32 /* Pre-parse SDP: is this SDP valid? how many audio/video lines? any features to take into account? */
janus_sdp_preparse(void * ice_handle,const char * jsep_sdp,char * error_str,size_t errlen,int * audio,int * video,int * data)33 janus_sdp *janus_sdp_preparse(void *ice_handle, const char *jsep_sdp, char *error_str, size_t errlen,
34 		int *audio, int *video, int *data) {
35 	if(!ice_handle || !jsep_sdp || !audio || !video || !data) {
36 		JANUS_LOG(LOG_ERR, "  Can't preparse, invalid arguments\n");
37 		return NULL;
38 	}
39 	janus_ice_handle *handle = (janus_ice_handle *)ice_handle;
40 	janus_sdp *parsed_sdp = janus_sdp_parse(jsep_sdp, error_str, errlen);
41 	if(!parsed_sdp) {
42 		JANUS_LOG(LOG_ERR, "  Error parsing SDP? %s\n", error_str ? error_str : "(unknown reason)");
43 		/* Invalid SDP */
44 		return NULL;
45 	}
46 	/* Look for m-lines */
47 	GList *temp = parsed_sdp->m_lines;
48 	while(temp) {
49 		janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
50 		if(m->type == JANUS_SDP_AUDIO) {
51 			*audio = *audio + 1;
52 		} else if(m->type == JANUS_SDP_VIDEO) {
53 			*video = *video + 1;
54 		}
55 		/* Preparse the mid as well, and check if bundle-only is used */
56 		GList *tempA = m->attributes;
57 		while(tempA) {
58 			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
59 			if(a->name) {
60 				if(!strcasecmp(a->name, "bundle-only") && m->port == 0) {
61 					/* Port 0 but bundle-only is used, don't disable this m-line */
62 					m->port = 9;
63 				} else if(!strcasecmp(a->name, "mid")) {
64 					/* Found mid attribute */
65 					if(a->value == NULL) {
66 						JANUS_LOG(LOG_ERR, "[%"SCNu64"] Invalid mid attribute (no value)\n", handle->handle_id);
67 						janus_sdp_destroy(parsed_sdp);
68 						return NULL;
69 					}
70 					if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
71 						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio mid: %s\n", handle->handle_id, a->value);
72 						if(strlen(a->value) > 16) {
73 							JANUS_LOG(LOG_ERR, "[%"SCNu64"] Audio mid too large: (%zu > 16)\n", handle->handle_id, strlen(a->value));
74 							janus_sdp_destroy(parsed_sdp);
75 							return NULL;
76 						}
77 						if(handle->audio_mid == NULL)
78 							handle->audio_mid = g_strdup(a->value);
79 						if(handle->stream_mid == NULL)
80 							handle->stream_mid = handle->audio_mid;
81 					} else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
82 						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video mid: %s\n", handle->handle_id, a->value);
83 						if(strlen(a->value) > 16) {
84 							JANUS_LOG(LOG_ERR, "[%"SCNu64"] Video mid too large: (%zu > 16)\n", handle->handle_id, strlen(a->value));
85 							janus_sdp_destroy(parsed_sdp);
86 							return NULL;
87 						}
88 						if(handle->video_mid == NULL)
89 							handle->video_mid = g_strdup(a->value);
90 						if(handle->stream_mid == NULL)
91 							handle->stream_mid = handle->video_mid;
92 					}
93 				}
94 			}
95 			/* If the m-line is disabled don't actually increase the count */
96 			if(m->port == 0) {
97 				if(m->type == JANUS_SDP_AUDIO) {
98 					*audio = *audio - 1;
99 				} else if(m->type == JANUS_SDP_VIDEO) {
100 					*video = *video - 1;
101 				}
102 			}
103 			tempA = tempA->next;
104 		}
105 		temp = temp->next;
106 	}
107 #ifdef HAVE_SCTP
108 	*data = (strstr(jsep_sdp, "DTLS/SCTP") && !strstr(jsep_sdp, " 0 DTLS/SCTP") &&
109 		!strstr(jsep_sdp, " 0 UDP/DTLS/SCTP")) ? 1 : 0;	/* FIXME This is a really hacky way of checking... */
110 #else
111 	*data = 0;
112 #endif
113 
114 	return parsed_sdp;
115 }
116 
117 /* Parse SDP */
janus_sdp_process(void * ice_handle,janus_sdp * remote_sdp,gboolean rids_hml,gboolean update)118 int janus_sdp_process(void *ice_handle, janus_sdp *remote_sdp, gboolean rids_hml, gboolean update) {
119 	if(!ice_handle || !remote_sdp)
120 		return -1;
121 	janus_ice_handle *handle = (janus_ice_handle *)ice_handle;
122 	janus_ice_stream *stream = handle->stream;
123 	if(!stream)
124 		return -1;
125 	gchar *ruser = NULL, *rpass = NULL, *rhashing = NULL, *rfingerprint = NULL;
126 	int audio = 0, video = 0;
127 #ifdef HAVE_SCTP
128 	int data = 0;
129 #endif
130 	gboolean rtx = FALSE;
131 	/* Ok, let's start with global attributes */
132 	GList *temp = remote_sdp->attributes;
133 	while(temp) {
134 		janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data;
135 		if(a && a->name && a->value) {
136 			if(!strcasecmp(a->name, "fingerprint")) {
137 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint (global) : %s\n", handle->handle_id, a->value);
138 				if(strcasestr(a->value, "sha-256 ") == a->value) {
139 					rhashing = g_strdup("sha-256");
140 					rfingerprint = g_strdup(a->value + strlen("sha-256 "));
141 				} else if(strcasestr(a->value, "sha-1 ") == a->value) {
142 					JANUS_LOG(LOG_WARN, "[%"SCNu64"]  Hashing algorithm not the one we expected (sha-1 instead of sha-256), but that's ok\n", handle->handle_id);
143 					rhashing = g_strdup("sha-1");
144 					rfingerprint = g_strdup(a->value + strlen("sha-1 "));
145 				} else {
146 					/* FIXME We should handle this somehow anyway... OpenSSL supports them all */
147 					JANUS_LOG(LOG_WARN, "[%"SCNu64"]  Hashing algorithm not the one we expected (sha-256/sha-1), *NOT* cool\n", handle->handle_id);
148 				}
149 			} else if(!strcasecmp(a->name, "ice-ufrag")) {
150 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE ufrag (global):   %s\n", handle->handle_id, a->value);
151 				ruser = g_strdup(a->value);
152 			} else if(!strcasecmp(a->name, "ice-pwd")) {
153 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE pwd (global):     %s\n", handle->handle_id, a->value);
154 				rpass = g_strdup(a->value);
155 			}
156 		}
157 		temp = temp->next;
158 	}
159 	/* Now go on with m-line and their attributes */
160 	int mlines = 0;
161 	temp = remote_sdp->m_lines;
162 	while(temp) {
163 		mlines++;
164 		janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
165 		if(m->type == JANUS_SDP_AUDIO) {
166 			if(handle->rtp_profile == NULL && m->proto != NULL)
167 				handle->rtp_profile = g_strdup(m->proto);
168 			audio++;
169 			if(audio > 1) {
170 				temp = temp->next;
171 				continue;
172 			}
173 			if(m->port > 0) {
174 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing audio candidates (stream=%d)...\n", handle->handle_id, stream->stream_id);
175 				if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
176 					janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
177 					stream->audio_ssrc = janus_random_uint32();	/* FIXME Should we look for conflicts? */
178 					if(stream->audio_rtcp_ctx == NULL) {
179 						stream->audio_rtcp_ctx = g_malloc0(sizeof(rtcp_context));
180 						stream->audio_rtcp_ctx->tb = 48000;	/* May change later */
181 					}
182 				}
183 				gboolean receiving = (stream->audio_recv == TRUE);
184 				switch(m->direction) {
185 					case JANUS_SDP_INACTIVE:
186 					case JANUS_SDP_INVALID:
187 						stream->audio_send = FALSE;
188 						stream->audio_recv = FALSE;
189 						break;
190 					case JANUS_SDP_SENDONLY:
191 						/* A sendonly peer means recvonly for Janus */
192 						stream->audio_send = FALSE;
193 						stream->audio_recv = TRUE;
194 						break;
195 					case JANUS_SDP_RECVONLY:
196 						/* A recvonly peer means sendonly for Janus */
197 						stream->audio_send = TRUE;
198 						stream->audio_recv = FALSE;
199 						break;
200 					case JANUS_SDP_SENDRECV:
201 					case JANUS_SDP_DEFAULT:
202 					default:
203 						stream->audio_send = TRUE;
204 						stream->audio_recv = TRUE;
205 						break;
206 				}
207 				if(receiving != stream->audio_recv)
208 					janus_ice_notify_media_stopped(handle);
209 				if(m->ptypes != NULL) {
210 					g_list_free(stream->audio_payload_types);
211 					stream->audio_payload_types = g_list_copy(m->ptypes);
212 				}
213 			} else {
214 				/* Audio rejected? */
215 				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
216 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio rejected by peer...\n", handle->handle_id);
217 			}
218 		} else if(m->type == JANUS_SDP_VIDEO) {
219 			if(handle->rtp_profile == NULL && m->proto != NULL)
220 				handle->rtp_profile = g_strdup(m->proto);
221 			video++;
222 			if(video > 1) {
223 				temp = temp->next;
224 				continue;
225 			}
226 			if(m->port > 0) {
227 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing video candidates (stream=%d)...\n", handle->handle_id, stream->stream_id);
228 				if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
229 					janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);
230 					stream->video_ssrc = janus_random_uint32();	/* FIXME Should we look for conflicts? */
231 					if(stream->video_rtcp_ctx[0] == NULL) {
232 						stream->video_rtcp_ctx[0] = g_malloc0(sizeof(rtcp_context));
233 						stream->video_rtcp_ctx[0]->tb = 90000;	/* May change later */
234 					}
235 				}
236 				gboolean receiving = (stream->video_recv == TRUE);
237 				switch(m->direction) {
238 					case JANUS_SDP_INACTIVE:
239 					case JANUS_SDP_INVALID:
240 						stream->video_send = FALSE;
241 						stream->video_recv = FALSE;
242 						break;
243 					case JANUS_SDP_SENDONLY:
244 						/* A sendonly peer means recvonly for Janus */
245 						stream->video_send = FALSE;
246 						stream->video_recv = TRUE;
247 						break;
248 					case JANUS_SDP_RECVONLY:
249 						/* A recvonly peer means sendonly for Janus */
250 						stream->video_send = TRUE;
251 						stream->video_recv = FALSE;
252 						break;
253 					case JANUS_SDP_SENDRECV:
254 					case JANUS_SDP_DEFAULT:
255 					default:
256 						stream->video_send = TRUE;
257 						stream->video_recv = TRUE;
258 						break;
259 				}
260 				if(receiving != stream->video_recv)
261 					janus_ice_notify_media_stopped(handle);
262 				if(m->ptypes != NULL) {
263 					g_list_free(stream->video_payload_types);
264 					stream->video_payload_types = g_list_copy(m->ptypes);
265 				}
266 			} else {
267 				/* Video rejected? */
268 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video rejected by peer...\n", handle->handle_id);
269 				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);
270 			}
271 #ifdef HAVE_SCTP
272 		} else if(m->type == JANUS_SDP_APPLICATION) {
273 			/* Is this SCTP for DataChannels? */
274 			if(!strcasecmp(m->proto, "DTLS/SCTP") || !strcasecmp(m->proto, "UDP/DTLS/SCTP")) {
275 				data++;
276 				if(data > 1) {
277 					temp = temp->next;
278 					continue;
279 				}
280 				if(m->port > 0) {
281 					/* Yep */
282 					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing SCTP candidates (stream=%d)...\n", handle->handle_id, stream->stream_id);
283 					if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS)) {
284 						janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS);
285 					}
286 					if(!strcasecmp(m->proto, "UDP/DTLS/SCTP")) {
287 						janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP);
288 					} else {
289 						janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP);
290 					}
291 				} else {
292 					/* Data channels rejected? */
293 					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data channels rejected by peer...\n", handle->handle_id);
294 					janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS);
295 					janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP);
296 				}
297 			} else {
298 				/* Unsupported data channels format. */
299 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data channels format %s unsupported, skipping\n", handle->handle_id, m->proto);
300 				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS);
301 				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP);
302 			}
303 #endif
304 		} else {
305 			JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping disabled/unsupported media line...\n", handle->handle_id);
306 		}
307 		if(stream == NULL) {
308 			temp = temp->next;
309 			continue;
310 		}
311 		/* Look for mid, ICE credentials and fingerprint first: check media attributes */
312 		GList *tempA = m->attributes;
313 		while(tempA) {
314 			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
315 			if(a->name && a->value) {
316 				if(!strcasecmp(a->name, "mid")) {
317 					/* Found mid attribute */
318 					if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
319 						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio mid: %s\n", handle->handle_id, a->value);
320 						if(strlen(a->value) > 16) {
321 							JANUS_LOG(LOG_ERR, "[%"SCNu64"] Audio mid too large: (%zu > 16)\n", handle->handle_id, strlen(a->value));
322 							return -2;
323 						}
324 						if(handle->audio_mid == NULL)
325 							handle->audio_mid = g_strdup(a->value);
326 						if(handle->stream_mid == NULL)
327 							handle->stream_mid = handle->audio_mid;
328 					} else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
329 						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video mid: %s\n", handle->handle_id, a->value);
330 						if(strlen(a->value) > 16) {
331 							JANUS_LOG(LOG_ERR, "[%"SCNu64"] Video mid too large: (%zu > 16)\n", handle->handle_id, strlen(a->value));
332 							return -2;
333 						}
334 						if(handle->video_mid == NULL)
335 							handle->video_mid = g_strdup(a->value);
336 						if(handle->stream_mid == NULL)
337 							handle->stream_mid = handle->video_mid;
338 #ifdef HAVE_SCTP
339 					} else if(m->type == JANUS_SDP_APPLICATION) {
340 						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data Channel mid: %s\n", handle->handle_id, a->value);
341 						if(handle->data_mid == NULL)
342 							handle->data_mid = g_strdup(a->value);
343 						if(handle->stream_mid == NULL)
344 							handle->stream_mid = handle->data_mid;
345 #endif
346 					}
347 				} else if(!strcasecmp(a->name, "fingerprint")) {
348 					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint (local) : %s\n", handle->handle_id, a->value);
349 					if(strcasestr(a->value, "sha-256 ") == a->value) {
350 						g_free(rhashing);	/* FIXME We're overwriting the global one, if any */
351 						rhashing = g_strdup("sha-256");
352 						g_free(rfingerprint);	/* FIXME We're overwriting the global one, if any */
353 						rfingerprint = g_strdup(a->value + strlen("sha-256 "));
354 					} else if(strcasestr(a->value, "sha-1 ") == a->value) {
355 						JANUS_LOG(LOG_WARN, "[%"SCNu64"]  Hashing algorithm not the one we expected (sha-1 instead of sha-256), but that's ok\n", handle->handle_id);
356 						g_free(rhashing);	/* FIXME We're overwriting the global one, if any */
357 						rhashing = g_strdup("sha-1");
358 						g_free(rfingerprint);	/* FIXME We're overwriting the global one, if any */
359 						rfingerprint = g_strdup(a->value + strlen("sha-1 "));
360 					} else {
361 						/* FIXME We should handle this somehow anyway... OpenSSL supports them all */
362 						JANUS_LOG(LOG_WARN, "[%"SCNu64"]  Hashing algorithm not the one we expected (sha-256), *NOT* cool\n", handle->handle_id);
363 					}
364 				} else if(!strcasecmp(a->name, "setup")) {
365 					JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS setup (local):  %s\n", handle->handle_id, a->value);
366 					if(!update) {
367 						if(!strcasecmp(a->value, "actpass") || !strcasecmp(a->value, "passive")) {
368 							JANUS_LOG(LOG_VERB, "[%"SCNu64"] Setting connect state (DTLS client)\n", handle->handle_id);
369 							stream->dtls_role = JANUS_DTLS_ROLE_CLIENT;
370 						} else if(!strcasecmp(a->value, "active")) {
371 							JANUS_LOG(LOG_VERB, "[%"SCNu64"] Setting accept state (DTLS server)\n", handle->handle_id);
372 							stream->dtls_role = JANUS_DTLS_ROLE_SERVER;
373 						}
374 						if(stream->component && stream->component->dtls)
375 							stream->component->dtls->dtls_role = stream->dtls_role;
376 					}
377 					/* TODO Handle holdconn... */
378 				} else if(!strcasecmp(a->name, "ice-ufrag")) {
379 					JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE ufrag (local):   %s\n", handle->handle_id, a->value);
380 					g_free(ruser);	/* FIXME We're overwriting the global one, if any */
381 					ruser = g_strdup(a->value);
382 				} else if(!strcasecmp(a->name, "ice-pwd")) {
383 					JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE pwd (local):     %s\n", handle->handle_id, a->value);
384 					g_free(rpass);	/* FIXME We're overwriting the global one, if any */
385 					rpass = g_strdup(a->value);
386 				}
387 			}
388 			tempA = tempA->next;
389 		}
390 		if(mlines == 1) {
391 			if(!ruser || !rpass || (janus_is_webrtc_encryption_enabled() && (!rfingerprint || !rhashing))) {
392 				/* Missing mandatory information, failure... */
393 				JANUS_LOG(LOG_ERR, "[%"SCNu64"] SDP missing mandatory information\n", handle->handle_id);
394 				JANUS_LOG(LOG_ERR, "[%"SCNu64"] %p, %p, %p, %p\n", handle->handle_id, ruser, rpass, rfingerprint, rhashing);
395 				if(ruser)
396 					g_free(ruser);
397 				ruser = NULL;
398 				if(rpass)
399 					g_free(rpass);
400 				rpass = NULL;
401 				if(rhashing)
402 					g_free(rhashing);
403 				rhashing = NULL;
404 				if(rfingerprint)
405 					g_free(rfingerprint);
406 				rfingerprint = NULL;
407 				return -2;
408 			}
409 			/* If we received the ICE credentials for the first time, enforce them */
410 			if(ruser && !stream->ruser && rpass && !stream->rpass) {
411 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Setting remote credentials...\n", handle->handle_id);
412 				if(!nice_agent_set_remote_credentials(handle->agent, handle->stream_id, ruser, rpass)) {
413 					JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to set remote credentials!\n", handle->handle_id);
414 				}
415 			} else
416 			/* If this is a renegotiation, check if this is an ICE restart */
417 			if((ruser && stream->ruser && strcmp(ruser, stream->ruser)) ||
418 					(rpass && stream->rpass && strcmp(rpass, stream->rpass))) {
419 				JANUS_LOG(LOG_INFO, "[%"SCNu64"] ICE restart detected\n", handle->handle_id);
420 				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES);
421 				janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ICE_RESTART);
422 			}
423 			/* Store fingerprint and hashing */
424 			if(janus_is_webrtc_encryption_enabled()) {
425 				g_free(stream->remote_hashing);
426 				stream->remote_hashing = g_strdup(rhashing);
427 				g_free(stream->remote_fingerprint);
428 				stream->remote_fingerprint = g_strdup(rfingerprint);
429 			}
430 			/* Store the ICE username and password for this stream */
431 			g_free(stream->ruser);
432 			stream->ruser = g_strdup(ruser);
433 			g_free(stream->rpass);
434 			stream->rpass = g_strdup(rpass);
435 		}
436 		/* Is simulcasting enabled, using rid? (we need to check this before parsing SSRCs) */
437 		tempA = m->attributes;
438 		stream->rids_hml = rids_hml;
439 		while(tempA) {
440 			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
441 			if(a->name && !strcasecmp(a->name, "rid") && a->value) {
442 				/* This attribute is used for simulcasting */
443 				char rid[16];
444 				if(sscanf(a->value, "%15s send", rid) != 1) {
445 					JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse rid attribute...\n", handle->handle_id);
446 				} else {
447 					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsed rid: %s\n", handle->handle_id, rid);
448 					if(stream->rid[rids_hml ? 2 : 0] == NULL) {
449 						stream->rid[rids_hml ? 2 : 0] = g_strdup(rid);
450 					} else if(stream->rid[1] == NULL) {
451 						stream->rid[1] = g_strdup(rid);
452 					} else if(stream->rid[rids_hml ? 0 : 2] == NULL) {
453 						stream->rid[rids_hml ? 0 : 2] = g_strdup(rid);
454 					} else {
455 						JANUS_LOG(LOG_WARN, "[%"SCNu64"] Too many RTP Stream IDs, ignoring '%s'...\n", handle->handle_id, rid);
456 					}
457 				}
458 			} else if(a->name && !strcasecmp(a->name, "simulcast") && a->value) {
459 				/* Firefox and Chrome signal simulcast support differently */
460 				stream->legacy_rid = strstr(a->value, "rid=") ? TRUE : FALSE;
461 			}
462 			tempA = tempA->next;
463 		}
464 		/* If rid is involved, check how many of them we have (it may be less than 3) */
465 		if(stream->rid[0] == NULL && stream->rid[2] != NULL) {
466 			stream->rid[0] = stream->rid[1];
467 			stream->rid[1] = stream->rid[2];
468 			stream->rid[2] = NULL;
469 		}
470 		if(stream->rid[0] == NULL && stream->rid[1] != NULL) {
471 			stream->rid[0] = stream->rid[1];
472 			stream->rid[1] = NULL;
473 		}
474 		/* Let's start figuring out the SSRCs, and any grouping that may be there */
475 		stream->audio_ssrc_peer_new = 0;
476 		stream->video_ssrc_peer_new[0] = 0;
477 		stream->video_ssrc_peer_new[1] = 0;
478 		stream->video_ssrc_peer_new[2] = 0;
479 		stream->video_ssrc_peer_rtx_new[0] = 0;
480 		stream->video_ssrc_peer_rtx_new[1] = 0;
481 		stream->video_ssrc_peer_rtx_new[2] = 0;
482 		/* Any SSRC SIM group? */
483 		tempA = m->attributes;
484 		while(tempA) {
485 			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
486 			if(a->name && a->value) {
487 				if(!strcasecmp(a->name, "ssrc-group") && strstr(a->value, "SIM")) {
488 					int res = janus_sdp_parse_ssrc_group(stream, (const char *)a->value, m->type == JANUS_SDP_VIDEO);
489 					if(res != 0) {
490 						JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse SSRC SIM group attribute... (%d)\n", handle->handle_id, res);
491 					}
492 				}
493 			}
494 			tempA = tempA->next;
495 		}
496 		/* Any SSRC FID group? */
497 		tempA = m->attributes;
498 		while(tempA) {
499 			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
500 			if(a->name && a->value) {
501 				if(!strcasecmp(a->name, "ssrc-group") && strstr(a->value, "FID")) {
502 					int res = janus_sdp_parse_ssrc_group(stream, (const char *)a->value, m->type == JANUS_SDP_VIDEO);
503 					if(res != 0) {
504 						JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse SSRC FID group attribute... (%d)\n", handle->handle_id, res);
505 					}
506 				}
507 			}
508 			tempA = tempA->next;
509 		}
510 		/* Any SSRC in general? */
511 		tempA = m->attributes;
512 		while(tempA) {
513 			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
514 			if(a->name && a->value) {
515 				if(!strcasecmp(a->name, "ssrc")) {
516 					int res = janus_sdp_parse_ssrc(stream, (const char *)a->value, m->type == JANUS_SDP_VIDEO);
517 					if(res != 0) {
518 						JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse SSRC attribute... (%d)\n", handle->handle_id, res);
519 					}
520 				}
521 			}
522 			tempA = tempA->next;
523 		}
524 		/* Now look for candidates and other info */
525 		tempA = m->attributes;
526 		while(tempA) {
527 			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
528 			if(a->name) {
529 				if(!strcasecmp(a->name, "candidate")) {
530 					if(m->type == JANUS_SDP_AUDIO && mlines > 1) {
531 						JANUS_LOG(LOG_VERB, "[%"SCNu64"] This is an audio candidate but we're bundling on another stream, ignoring...\n", handle->handle_id);
532 					} else if(m->type == JANUS_SDP_VIDEO && mlines > 1) {
533 						JANUS_LOG(LOG_VERB, "[%"SCNu64"] This is a video candidate but we're bundling on another stream, ignoring...\n", handle->handle_id);
534 #ifdef HAVE_SCTP
535 					} else if(m->type == JANUS_SDP_APPLICATION && mlines > 1) {
536 						JANUS_LOG(LOG_VERB, "[%"SCNu64"] This is a SCTP candidate but we're bundling on another stream, ignoring...\n", handle->handle_id);
537 #endif
538 					} else {
539 						int res = janus_sdp_parse_candidate(stream, (const char *)a->value, 0);
540 						if(res != 0) {
541 							JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse candidate... (%d)\n", handle->handle_id, res);
542 						}
543 					}
544 				} else if(!strcasecmp(a->name, "rtcp-fb")) {
545 					if(a->value && strstr(a->value, "nack") && stream->component) {
546 						if(m->type == JANUS_SDP_AUDIO) {
547 							/* Enable NACKs for audio */
548 							stream->component->do_audio_nacks = TRUE;
549 						} else if(m->type == JANUS_SDP_VIDEO) {
550 							/* Enable NACKs for video */
551 							stream->component->do_video_nacks = TRUE;
552 						}
553 					}
554 				} else if(!strcasecmp(a->name, "fmtp")) {
555 					if(a->value && strstr(a->value, "apt=")) {
556 						/* RFC4588 rtx payload type mapping */
557 						int ptype = -1, rtx_ptype = -1;
558 						if(sscanf(a->value, "%d apt=%d", &rtx_ptype, &ptype) != 2) {
559 							JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse fmtp/apt attribute...\n", handle->handle_id);
560 						} else {
561 							rtx = TRUE;
562 							janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX);
563 							if(stream->rtx_payload_types == NULL)
564 								stream->rtx_payload_types = g_hash_table_new(NULL, NULL);
565 							g_hash_table_insert(stream->rtx_payload_types, GINT_TO_POINTER(ptype), GINT_TO_POINTER(rtx_ptype));
566 						}
567 					}
568 				} else if(!strcasecmp(a->name, "rtpmap")) {
569 					if(a->value) {
570 						int ptype = atoi(a->value);
571 						if(ptype > -1) {
572 							char *cr = strchr(a->value, '/');
573 							if(cr != NULL) {
574 								cr++;
575 								uint32_t clock_rate = 0;
576 								if(janus_string_to_uint32(cr, &clock_rate) == 0) {
577 									if(stream->clock_rates == NULL)
578 										stream->clock_rates = g_hash_table_new(NULL, NULL);
579 									g_hash_table_insert(stream->clock_rates, GINT_TO_POINTER(ptype), GUINT_TO_POINTER(clock_rate));
580 								}
581 							}
582 						}
583 					}
584 				}
585 #ifdef HAVE_SCTP
586 				else if(!strcasecmp(a->name, "sctpmap")) {
587 					/* We don't really care */
588 					JANUS_LOG(LOG_VERB, "Got a sctpmap attribute: %s\n", a->value);
589 				}
590 #endif
591 			}
592 			tempA = tempA->next;
593 		}
594 		/* Any change in SSRCs we should be aware of? */
595 		if(m->type == JANUS_SDP_AUDIO) {
596 			if(stream->audio_ssrc_peer_new > 0) {
597 				if(stream->audio_ssrc_peer > 0 && stream->audio_ssrc_peer != stream->audio_ssrc_peer_new) {
598 					JANUS_LOG(LOG_INFO, "[%"SCNu64"] Audio SSRC changed: %"SCNu32" --> %"SCNu32"\n",
599 						handle->handle_id, stream->audio_ssrc_peer, stream->audio_ssrc_peer_new);
600 					/* FIXME Reset the RTCP context */
601 					janus_ice_component *component = stream->component;
602 					janus_mutex_lock(&component->mutex);
603 					if(stream->audio_rtcp_ctx) {
604 						memset(stream->audio_rtcp_ctx, 0, sizeof(*stream->audio_rtcp_ctx));
605 						stream->audio_rtcp_ctx->tb = 48000;	/* May change later */
606 					}
607 					if(component->last_seqs_audio)
608 						janus_seq_list_free(&component->last_seqs_audio);
609 					janus_mutex_unlock(&component->mutex);
610 				}
611 				stream->audio_ssrc_peer = stream->audio_ssrc_peer_new;
612 				stream->audio_ssrc_peer_new = 0;
613 			}
614 		} else if(m->type == JANUS_SDP_VIDEO) {
615 			int vindex = 0;
616 			for(vindex=0; vindex<3; vindex++) {
617 				if(stream->video_ssrc_peer_new[vindex] > 0) {
618 					if(stream->video_ssrc_peer[vindex] > 0 && stream->video_ssrc_peer[vindex] != stream->video_ssrc_peer_new[vindex]) {
619 						JANUS_LOG(LOG_INFO, "[%"SCNu64"] Video SSRC (#%d) changed: %"SCNu32" --> %"SCNu32"\n",
620 							handle->handle_id, vindex, stream->video_ssrc_peer[vindex], stream->video_ssrc_peer_new[vindex]);
621 						/* FIXME Reset the RTCP context */
622 						janus_ice_component *component = stream->component;
623 						if(component != NULL) {
624 							janus_mutex_lock(&component->mutex);
625 							if(stream->video_rtcp_ctx[vindex]) {
626 								memset(stream->video_rtcp_ctx[vindex], 0, sizeof(*stream->video_rtcp_ctx[vindex]));
627 								stream->video_rtcp_ctx[vindex]->tb = 90000;
628 							}
629 							if(component->last_seqs_video[vindex])
630 								janus_seq_list_free(&component->last_seqs_video[vindex]);
631 							janus_mutex_unlock(&component->mutex);
632 						}
633 					}
634 					stream->video_ssrc_peer[vindex] = stream->video_ssrc_peer_new[vindex];
635 					stream->video_ssrc_peer_new[vindex] = 0;
636 				}
637 				/* Do the same with the related rtx SSRC, if any */
638 				if(stream->video_ssrc_peer_rtx_new[vindex] > 0) {
639 					if(stream->video_ssrc_peer_rtx[vindex] > 0 && stream->video_ssrc_peer_rtx[vindex] != stream->video_ssrc_peer_rtx_new[vindex]) {
640 						JANUS_LOG(LOG_INFO, "[%"SCNu64"] Video SSRC (#%d rtx) changed: %"SCNu32" --> %"SCNu32"\n",
641 							handle->handle_id, vindex, stream->video_ssrc_peer_rtx[vindex], stream->video_ssrc_peer_rtx_new[vindex]);
642 					}
643 					stream->video_ssrc_peer_rtx[vindex] = stream->video_ssrc_peer_rtx_new[vindex];
644 					stream->video_ssrc_peer_rtx_new[vindex] = 0;
645 					if(stream->video_ssrc_rtx == 0)
646 						stream->video_ssrc_rtx = janus_random_uint32();	/* FIXME Should we look for conflicts? */
647 				}
648 			}
649 			if((stream->video_ssrc_peer[1] || stream->rid[1] != NULL) && stream->video_rtcp_ctx[1] == NULL) {
650 				stream->video_rtcp_ctx[1] = g_malloc0(sizeof(rtcp_context));
651 				stream->video_rtcp_ctx[1]->tb = 90000;
652 			}
653 			if((stream->video_ssrc_peer[2] || stream->rid[rids_hml ? 2 : 0] != NULL) && stream->video_rtcp_ctx[2] == NULL) {
654 				stream->video_rtcp_ctx[2] = g_malloc0(sizeof(rtcp_context));
655 				stream->video_rtcp_ctx[2]->tb = 90000;
656 			}
657 		}
658 		temp = temp->next;
659 	}
660 	/* Disable RFC4588 if the peer didn't negotiate it */
661 	if(!rtx) {
662 		janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX);
663 		stream->video_ssrc_rtx = 0;
664 	}
665 	/* Cleanup */
666 	g_free(ruser);
667 	g_free(rpass);
668 	g_free(rhashing);
669 	g_free(rfingerprint);
670 
671 	return 0;	/* FIXME Handle errors better */
672 }
673 
674 typedef struct janus_sdp_mdns_candidate {
675 	janus_ice_handle *handle;
676 	char *candidate, *local;
677 	GCancellable *cancellable;
678 } janus_sdp_mdns_candidate;
janus_sdp_mdns_resolved(GObject * source_object,GAsyncResult * res,gpointer user_data)679 static void janus_sdp_mdns_resolved(GObject *source_object, GAsyncResult *res, gpointer user_data) {
680 	/* This callback is invoked when the address is resolved */
681 	janus_sdp_mdns_candidate *mc = (janus_sdp_mdns_candidate *)user_data;
682 	GResolver *resolver = g_resolver_get_default();
683 	GError *error = NULL;
684 	GList *list = g_resolver_lookup_by_name_finish(resolver, res, &error);
685 	if(mc == NULL) {
686 		g_resolver_free_addresses(list);
687 		g_object_unref(resolver);
688 		return;
689 	}
690 	char *resolved = NULL;
691 	if(error != NULL || list == NULL || list->data == NULL) {
692 		JANUS_LOG(LOG_WARN, "[%"SCNu64"] Error resolving mDNS address (%s): %s\n",
693 			mc->handle->handle_id, mc->local, error ? error->message : "no results");
694 	} else {
695 		resolved = g_inet_address_to_string((GInetAddress *)list->data);
696 		JANUS_LOG(LOG_VERB, "[%"SCNu64"] mDNS address (%s) resolved: %s\n",
697 			mc->handle->handle_id, mc->local, resolved);
698 	}
699 	g_resolver_free_addresses(list);
700 	g_object_unref(resolver);
701 	if(resolved != NULL && mc->handle->stream && mc->handle->app_handle &&
702 			!g_atomic_int_get(&mc->handle->app_handle->stopped) &&
703 			!g_atomic_int_get(&mc->handle->destroyed)) {
704 		/* Replace the .local address with the resolved one in the candidate string */
705 		mc->candidate = janus_string_replace(mc->candidate, mc->local, resolved);
706 		/* Parse the candidate again */
707 		janus_mutex_lock(&mc->handle->mutex);
708 		(void)janus_sdp_parse_candidate(mc->handle->stream, mc->candidate, 1);
709 		janus_mutex_unlock(&mc->handle->mutex);
710 	}
711 	g_free(resolved);
712 	/* Get rid of the helper struct */
713 	janus_refcount_decrease(&mc->handle->ref);
714 	g_free(mc->candidate);
715 	g_free(mc->local);
716 	g_free(mc);
717 }
718 
janus_sdp_parse_candidate(void * ice_stream,const char * candidate,int trickle)719 int janus_sdp_parse_candidate(void *ice_stream, const char *candidate, int trickle) {
720 	if(ice_stream == NULL || candidate == NULL)
721 		return -1;
722 	janus_ice_stream *stream = (janus_ice_stream *)ice_stream;
723 	janus_ice_handle *handle = stream->handle;
724 	if(handle == NULL)
725 		return -2;
726 	janus_ice_component *component = NULL;
727 	if(strlen(candidate) == 0 || strstr(candidate, "end-of-candidates")) {
728 		/* FIXME Should we do something with this? */
729 		JANUS_LOG(LOG_VERB, "[%"SCNu64"] end-of-candidates received\n", handle->handle_id);
730 		return 0;
731 	}
732 	if(strstr(candidate, "candidate:") == candidate) {
733 		/* Skipping the 'candidate:' prefix Firefox puts in trickle candidates */
734 		candidate += strlen("candidate:");
735 	}
736 	char rfoundation[33], rtransport[4], rip[50], rtype[6], rrelip[40];
737 	guint32 rcomponent, rpriority, rport, rrelport;
738 	int res = sscanf(candidate, "%32s %30u %3s %30u %49s %30u typ %5s %*s %39s %*s %30u",
739 		rfoundation, &rcomponent, rtransport, &rpriority,
740 			rip, &rport, rtype, rrelip, &rrelport);
741 	if(res < 7) {
742 		/* Failed to parse this address, can it be IPv6? */
743 		if(!janus_ice_is_ipv6_enabled()) {
744 			JANUS_LOG(LOG_WARN, "[%"SCNu64"] Received IPv6 candidate, but IPv6 support is disabled...\n", handle->handle_id);
745 			return res;
746 		}
747 	}
748 	if(res >= 7) {
749 		if(strstr(rip, ".local")) {
750 			/* The IP is actually an mDNS address, try to resolve it
751 			 * https://tools.ietf.org/html/draft-ietf-rtcweb-mdns-ice-candidates-04 */
752 			if(!janus_ice_is_mdns_enabled()) {
753 				/* ...unless mDNS resolution is disabled, in which case ignore this candidate */
754 				JANUS_LOG(LOG_VERB, "[%"SCNu64"] mDNS candidate ignored\n", handle->handle_id);
755 				return 0;
756 			}
757 			/* We'll resolve this address asynchronously, in order not to keep this thread busy */
758 			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Resolving mDNS address (%s) asynchronously\n",
759 				handle->handle_id, rip);
760 			janus_sdp_mdns_candidate *mc = g_malloc(sizeof(janus_sdp_mdns_candidate));
761 			janus_refcount_increase(&handle->ref);
762 			mc->handle = handle;
763 			mc->candidate = g_strdup(candidate);
764 			mc->local = g_strdup(rip);
765 			mc->cancellable = NULL;
766 			GResolver *resolver = g_resolver_get_default();
767 			g_resolver_lookup_by_name_async(resolver, rip, NULL,
768 				(GAsyncReadyCallback)janus_sdp_mdns_resolved, mc);
769 			g_object_unref(resolver);
770 			return 0;
771 		}
772 		/* Add remote candidate */
773 		component = stream->component;
774 		if(component == NULL || rcomponent > 1) {
775 			JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Skipping component %d in stream %d (rtcp-muxing)\n", handle->handle_id, rcomponent, stream->stream_id);
776 		} else {
777 			//~ if(trickle) {
778 				//~ if(component->dtls != NULL) {
779 					//~ /* This component is already ready, ignore this further candidate */
780 					//~ JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Ignoring this candidate, the component is already ready\n", handle->handle_id);
781 					//~ return 0;
782 				//~ }
783 			//~ }
784 			component->component_id = rcomponent;
785 			component->stream_id = stream->stream_id;
786 			NiceCandidate *c = NULL;
787 			if(!strcasecmp(rtype, "host")) {
788 				JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Adding remote candidate component:%d stream:%d type:host %s:%d\n",
789 					handle->handle_id, rcomponent, stream->stream_id, rip, rport);
790 				/* Unless this is libnice >= 0.1.8, we only support UDP... */
791 				if(!strcasecmp(rtransport, "udp")) {
792 					c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
793 #ifdef HAVE_LIBNICE_TCP
794 				} else if(!strcasecmp(rtransport, "tcp") && janus_ice_is_ice_tcp_enabled()) {
795 					c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
796 #endif
797 				} else {
798 					JANUS_LOG(LOG_VERB, "[%"SCNu64"]    Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
799 				}
800 			} else if(!strcasecmp(rtype, "srflx")) {
801 				JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Adding remote candidate component:%d stream:%d type:srflx %s:%d --> %s:%d \n",
802 					handle->handle_id, rcomponent, stream->stream_id,  rrelip, rrelport, rip, rport);
803 				/* Unless this is libnice >= 0.1.8, we only support UDP... */
804 				if(!strcasecmp(rtransport, "udp")) {
805 					c = nice_candidate_new(NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
806 #ifdef HAVE_LIBNICE_TCP
807 				} else if(!strcasecmp(rtransport, "tcp") && janus_ice_is_ice_tcp_enabled()) {
808 					c = nice_candidate_new(NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
809 #endif
810 				} else {
811 					JANUS_LOG(LOG_VERB, "[%"SCNu64"]    Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
812 				}
813 			} else if(!strcasecmp(rtype, "prflx")) {
814 				JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Adding remote candidate component:%d stream:%d type:prflx %s:%d --> %s:%d\n",
815 					handle->handle_id, rcomponent, stream->stream_id, rrelip, rrelport, rip, rport);
816 				/* Unless this is libnice >= 0.1.8, we only support UDP... */
817 				if(!strcasecmp(rtransport, "udp")) {
818 					c = nice_candidate_new(NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
819 #ifdef HAVE_LIBNICE_TCP
820 				} else if(!strcasecmp(rtransport, "tcp") && janus_ice_is_ice_tcp_enabled()) {
821 					c = nice_candidate_new(NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
822 #endif
823 				} else {
824 					JANUS_LOG(LOG_VERB, "[%"SCNu64"]    Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
825 				}
826 			} else if(!strcasecmp(rtype, "relay")) {
827 				JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Adding remote candidate component:%d stream:%d type:relay %s:%d --> %s:%d\n",
828 					handle->handle_id, rcomponent, stream->stream_id, rrelip, rrelport, rip, rport);
829 				/* We only support UDP/TCP/TLS... */
830 				if(strcasecmp(rtransport, "udp") && strcasecmp(rtransport, "tcp") && strcasecmp(rtransport, "tls")) {
831 					JANUS_LOG(LOG_VERB, "[%"SCNu64"]    Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
832 				} else {
833 					c = nice_candidate_new(NICE_CANDIDATE_TYPE_RELAYED);
834 				}
835 			} else {
836 				/* FIXME What now? */
837 				JANUS_LOG(LOG_ERR, "[%"SCNu64"]  Unknown remote candidate type:%s for component:%d stream:%d!\n",
838 					handle->handle_id, rtype, rcomponent, stream->stream_id);
839 			}
840 			if(c != NULL) {
841 				c->component_id = rcomponent;
842 				c->stream_id = stream->stream_id;
843 #ifndef HAVE_LIBNICE_TCP
844 				c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
845 #else
846 				if(!strcasecmp(rtransport, "udp")) {
847 					JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Transport: UDP\n", handle->handle_id);
848 					c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
849 				} else {
850 					/* Check the type (https://tools.ietf.org/html/rfc6544#section-4.5) */
851 					const char *type = NULL;
852 					int ctype = 0;
853 					if(strstr(candidate, "tcptype active")) {
854 						type = "active";
855 						ctype = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
856 					} else if(strstr(candidate, "tcptype passive")) {
857 						type = "passive";
858 						ctype = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
859 					} else if(strstr(candidate, "tcptype so")) {
860 						type = "so";
861 						ctype = NICE_CANDIDATE_TRANSPORT_TCP_SO;
862 					} else {
863 						/* TODO: We should actually stop here... */
864 						JANUS_LOG(LOG_ERR, "[%"SCNu64"] Missing tcptype info for the TCP candidate!\n", handle->handle_id);
865 					}
866 					JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Transport: TCP (%s)\n", handle->handle_id, type);
867 					c->transport = ctype;
868 				}
869 #endif
870 				g_strlcpy(c->foundation, rfoundation, NICE_CANDIDATE_MAX_FOUNDATION);
871 				c->priority = rpriority;
872 				gboolean added = nice_address_set_from_string(&c->addr, rip);
873 				if(!added) {
874 					JANUS_LOG(LOG_WARN, "[%"SCNu64"]    Invalid address '%s', skipping %s candidate (%s)\n",
875 						handle->handle_id, rip, rtype, candidate);
876 					nice_candidate_free(c);
877 					return 0;
878 				}
879 				nice_address_set_port(&c->addr, rport);
880 				if(c->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE || c->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) {
881 					added = nice_address_set_from_string(&c->base_addr, rrelip);
882 					if(added)
883 						nice_address_set_port(&c->base_addr, rrelport);
884 				} else if(c->type == NICE_CANDIDATE_TYPE_RELAYED) {
885 					/* FIXME Do we really need the base address for TURN? */
886 					added = nice_address_set_from_string(&c->base_addr, rrelip);
887 					if(added)
888 						nice_address_set_port(&c->base_addr, rrelport);
889 				}
890 				if(!added) {
891 					JANUS_LOG(LOG_WARN, "[%"SCNu64"]    Invalid base address '%s', skipping %s candidate (%s)\n",
892 						handle->handle_id, rrelip, rtype, candidate);
893 					nice_candidate_free(c);
894 					return 0;
895 				}
896 				component->candidates = g_slist_append(component->candidates, c);
897 				JANUS_LOG(LOG_HUGE, "[%"SCNu64"]    Candidate added to the list! (%u elements for %d/%d)\n", handle->handle_id,
898 					g_slist_length(component->candidates), stream->stream_id, component->component_id);
899 				/* Save for the summary, in case we need it */
900 				component->remote_candidates = g_slist_append(component->remote_candidates, g_strdup(candidate));
901 				/* Notify event handlers */
902 				if(janus_events_is_enabled()) {
903 					janus_session *session = (janus_session *)handle->session;
904 					json_t *info = json_object();
905 					json_object_set_new(info, "remote-candidate", json_string(candidate));
906 					json_object_set_new(info, "stream_id", json_integer(stream->stream_id));
907 					json_object_set_new(info, "component_id", json_integer(component->component_id));
908 					janus_events_notify_handlers(JANUS_EVENT_TYPE_WEBRTC, JANUS_EVENT_SUBTYPE_WEBRTC_RCAND,
909 						session->session_id, handle->handle_id, handle->opaque_id, info);
910 				}
911 				/* See if we need to process this */
912 				if(trickle) {
913 					if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START)) {
914 						/* This is a trickle candidate and ICE has started, we should process it right away */
915 						if(!component->process_started) {
916 							/* Actually, ICE has JUST started for this component, take care of the candidates we've added so far */
917 							JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE already started for this component, setting candidates we have up to now\n", handle->handle_id);
918 							janus_ice_setup_remote_candidates(handle, component->stream_id, component->component_id);
919 						} else {
920 							/* Queue the candidate, we'll process it in the loop */
921 							janus_ice_add_remote_candidate(handle, c);
922 						}
923 					} else {
924 						/* ICE hasn't started yet: to make sure we're not stuck, also check if we stopped processing the SDP */
925 						if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER)) {
926 							janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START);
927 							/* This is a trickle candidate and ICE has started, we should process it right away */
928 							if(!component->process_started) {
929 								/* Actually, ICE has JUST started for this component, take care of the candidates we've added so far */
930 								JANUS_LOG(LOG_VERB, "[%"SCNu64"] SDP processed but ICE not started yet for this component, setting candidates we have up to now\n", handle->handle_id);
931 								janus_ice_setup_remote_candidates(handle, component->stream_id, component->component_id);
932 							} else {
933 								/* Queue the candidate, we'll process it in the loop */
934 								janus_ice_add_remote_candidate(handle, c);
935 							}
936 						} else {
937 							/* Still processing the offer/answer: queue the trickle candidate for now, we'll process it later */
938 							JANUS_LOG(LOG_VERB, "[%"SCNu64"] Queueing trickle candidate, status is not START yet\n", handle->handle_id);
939 						}
940 					}
941 				}
942 			}
943 		}
944 	} else {
945 		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse candidate (res=%d)...\n", handle->handle_id, res);
946 		return res;
947 	}
948 	return 0;
949 }
950 
janus_sdp_parse_ssrc_group(void * ice_stream,const char * group_attr,int video)951 int janus_sdp_parse_ssrc_group(void *ice_stream, const char *group_attr, int video) {
952 	if(ice_stream == NULL || group_attr == NULL)
953 		return -1;
954 	janus_ice_stream *stream = (janus_ice_stream *)ice_stream;
955 	janus_ice_handle *handle = stream->handle;
956 	if(handle == NULL)
957 		return -2;
958 	if(!video)	/* We only do rtx for video, return */
959 		return 0;
960 	if(stream->rid[0] != NULL) {
961 		/* Simulcasting is rid-based, don't parse SSRCs for now */
962 		return 0;
963 	}
964 	gboolean fid = strstr(group_attr, "FID") != NULL;
965 	gboolean sim = strstr(group_attr, "SIM") != NULL;
966 	guint64 ssrc = 0;
967 	guint32 first_ssrc = 0;
968 	gchar **list = g_strsplit(group_attr, " ", -1);
969 	gchar *index = list[0];
970 	if(index != NULL) {
971 		int i=0;
972 		while(index != NULL) {
973 			if(i > 0 && strlen(index) > 0) {
974 				ssrc = g_ascii_strtoull(index, NULL, 0);
975 				switch(i) {
976 					case 1:
977 						first_ssrc = ssrc;
978 						if(stream->video_ssrc_peer_new[0] == ssrc || stream->video_ssrc_peer_new[1] == ssrc
979 								|| stream->video_ssrc_peer_new[2] == ssrc) {
980 							JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Already parsed this SSRC: %"SCNu64" (%s group)\n",
981 								handle->handle_id, ssrc, (fid ? "FID" : (sim ? "SIM" : "??")));
982 						} else {
983 							if(stream->video_ssrc_peer_new[0] == 0) {
984 								stream->video_ssrc_peer_new[0] = ssrc;
985 								JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC: %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_new[0]);
986 							} else {
987 								/* We already have a video SSRC: check if rid is involved, and we'll keep track of this for simulcasting */
988 								if(stream->rid[0]) {
989 									if(stream->video_ssrc_peer_new[1] == 0) {
990 										stream->video_ssrc_peer_new[1] = ssrc;
991 										JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-1): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_new[1]);
992 									} else if(stream->video_ssrc_peer_new[2] == 0) {
993 										stream->video_ssrc_peer_new[2] = ssrc;
994 										JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-2): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_new[2]);
995 									} else {
996 										JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with video SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
997 									}
998 								}
999 							}
1000 						}
1001 						break;
1002 					case 2:
1003 						if(fid) {
1004 							if(stream->video_ssrc_peer_new[0] == first_ssrc && stream->video_ssrc_peer_rtx_new[0] == 0) {
1005 								stream->video_ssrc_peer_rtx_new[0] = ssrc;
1006 								JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (rtx): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_rtx_new[0]);
1007 							} else if(stream->video_ssrc_peer_new[1] == first_ssrc && stream->video_ssrc_peer_rtx_new[1] == 0) {
1008 								stream->video_ssrc_peer_rtx_new[1] = ssrc;
1009 								JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-1 rtx): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_rtx_new[1]);
1010 							} else if(stream->video_ssrc_peer_new[2] == first_ssrc && stream->video_ssrc_peer_rtx_new[2] == 0) {
1011 								stream->video_ssrc_peer_rtx_new[2] = ssrc;
1012 								JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-2 rtx): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_rtx_new[2]);
1013 							} else {
1014 								JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with rtx SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
1015 							}
1016 						} else if(sim) {
1017 							stream->video_ssrc_peer_new[1] = ssrc;
1018 							JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-1): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_new[1]);
1019 						} else {
1020 							JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
1021 						}
1022 						break;
1023 					case 3:
1024 						if(fid) {
1025 							JANUS_LOG(LOG_WARN, "[%"SCNu64"] Found one too many retransmission SSRC (rtx): %"SCNu64"\n", handle->handle_id, ssrc);
1026 						} else if(sim) {
1027 							stream->video_ssrc_peer_new[2] = ssrc;
1028 							JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC (sim-2): %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_new[2]);
1029 						} else {
1030 							JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
1031 						}
1032 						break;
1033 					default:
1034 						JANUS_LOG(LOG_WARN, "[%"SCNu64"] Don't know what to do with video SSRC: %"SCNu64"\n", handle->handle_id, ssrc);
1035 						break;
1036 				}
1037 			}
1038 			i++;
1039 			index = list[i];
1040 		}
1041 	}
1042 	g_clear_pointer(&list, g_strfreev);
1043 	return 0;
1044 }
1045 
janus_sdp_parse_ssrc(void * ice_stream,const char * ssrc_attr,int video)1046 int janus_sdp_parse_ssrc(void *ice_stream, const char *ssrc_attr, int video) {
1047 	if(ice_stream == NULL || ssrc_attr == NULL)
1048 		return -1;
1049 	janus_ice_stream *stream = (janus_ice_stream *)ice_stream;
1050 	janus_ice_handle *handle = stream->handle;
1051 	if(handle == NULL)
1052 		return -2;
1053 	guint64 ssrc = g_ascii_strtoull(ssrc_attr, NULL, 0);
1054 	if(ssrc == 0 || ssrc > G_MAXUINT32)
1055 		return -3;
1056 	if(video) {
1057 		if(stream->rid[0] != NULL) {
1058 			/* Simulcasting is rid-based, only keep track of a single SSRC for fallback */
1059 			if(stream->video_ssrc_peer_temp == 0) {
1060 				stream->video_ssrc_peer_temp = ssrc;
1061 				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Peer video fallback SSRC: %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_temp);
1062 			}
1063 			return 0;
1064 		}
1065 		if(stream->video_ssrc_peer_new[0] == 0) {
1066 			stream->video_ssrc_peer_new[0] = ssrc;
1067 			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer video SSRC: %"SCNu32"\n", handle->handle_id, stream->video_ssrc_peer_new[0]);
1068 		}
1069 	} else {
1070 		if(stream->audio_ssrc_peer_new == 0) {
1071 			stream->audio_ssrc_peer_new = ssrc;
1072 			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Peer audio SSRC: %"SCNu32"\n", handle->handle_id, stream->audio_ssrc_peer_new);
1073 		}
1074 	}
1075 	return 0;
1076 }
1077 
janus_sdp_anonymize(janus_sdp * anon)1078 int janus_sdp_anonymize(janus_sdp *anon) {
1079 	if(anon == NULL)
1080 		return -1;
1081 	int audio = 0, video = 0, data = 0;
1082 		/* o= */
1083 	if(anon->o_addr != NULL) {
1084 		g_free(anon->o_addr);
1085 		anon->o_ipv4 = TRUE;
1086 		anon->o_addr = g_strdup("1.1.1.1");
1087 	}
1088 		/* a= */
1089 	GList *temp = anon->attributes;
1090 	while(temp) {
1091 		janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data;
1092 		/* These are attributes we handle ourselves, the plugins don't need them */
1093 		if(!strcasecmp(a->name, "ice-ufrag")
1094 				|| !strcasecmp(a->name, "ice-pwd")
1095 				|| !strcasecmp(a->name, "ice-options")
1096 				|| !strcasecmp(a->name, "fingerprint")
1097 				|| !strcasecmp(a->name, "group")
1098 				|| !strcasecmp(a->name, "msid-semantic")
1099 				|| !strcasecmp(a->name, "extmap-allow-mixed")
1100 				|| !strcasecmp(a->name, "rtcp-rsize")) {
1101 			anon->attributes = g_list_remove(anon->attributes, a);
1102 			temp = anon->attributes;
1103 			janus_sdp_attribute_destroy(a);
1104 			continue;
1105 		}
1106 		temp = temp->next;
1107 		continue;
1108 	}
1109 		/* m= */
1110 	temp = anon->m_lines;
1111 	while(temp) {
1112 		janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
1113 		if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
1114 			audio++;
1115 			m->port = audio == 1 ? 9 : 0;
1116 		} else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
1117 			video++;
1118 			m->port = video == 1 ? 9 : 0;
1119 		} else if(m->type == JANUS_SDP_APPLICATION && m->port > 0) {
1120 			if(m->proto != NULL && (!strcasecmp(m->proto, "DTLS/SCTP") || !strcasecmp(m->proto, "UDP/DTLS/SCTP"))) {
1121 				data++;
1122 				m->port = data == 1 ? 9 : 0;
1123 			} else {
1124 				m->port = 0;
1125 			}
1126 		} else {
1127 			m->port = 0;
1128 		}
1129 			/* c= */
1130 		if(m->c_addr != NULL) {
1131 			g_free(m->c_addr);
1132 			m->c_ipv4 = TRUE;
1133 			m->c_addr = g_strdup("1.1.1.1");
1134 		}
1135 			/* a= */
1136 		GList *tempA = m->attributes;
1137 		while(tempA) {
1138 			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
1139 			if(!a->name) {
1140 				tempA = tempA->next;
1141 				continue;
1142 			}
1143 			/* These are attributes we handle ourselves, the plugins don't need them */
1144 			if(!strcasecmp(a->name, "ice-ufrag")
1145 					|| !strcasecmp(a->name, "ice-pwd")
1146 					|| !strcasecmp(a->name, "ice-options")
1147 					|| !strcasecmp(a->name, "crypto")
1148 					|| !strcasecmp(a->name, "fingerprint")
1149 					|| !strcasecmp(a->name, "setup")
1150 					|| !strcasecmp(a->name, "connection")
1151 					|| !strcasecmp(a->name, "group")
1152 					|| !strcasecmp(a->name, "mid")
1153 					|| !strcasecmp(a->name, "msid")
1154 					|| !strcasecmp(a->name, "msid-semantic")
1155 					|| !strcasecmp(a->name, "rid")
1156 					|| !strcasecmp(a->name, "simulcast")
1157 					|| !strcasecmp(a->name, "rtcp")
1158 					|| !strcasecmp(a->name, "rtcp-mux")
1159 					|| !strcasecmp(a->name, "rtcp-rsize")
1160 					|| !strcasecmp(a->name, "candidate")
1161 					|| !strcasecmp(a->name, "end-of-candidates")
1162 					|| !strcasecmp(a->name, "ssrc")
1163 					|| !strcasecmp(a->name, "ssrc-group")
1164 					|| !strcasecmp(a->name, "sctpmap")
1165 					|| !strcasecmp(a->name, "sctp-port")
1166 					|| !strcasecmp(a->name, "max-message-size")) {
1167 				m->attributes = g_list_remove(m->attributes, a);
1168 				tempA = m->attributes;
1169 				janus_sdp_attribute_destroy(a);
1170 				continue;
1171 			}
1172 			tempA = tempA->next;
1173 		}
1174 		/* We don't support encrypted RTP extensions yet, so get rid of them */
1175 		tempA = m->attributes;
1176 		while(tempA) {
1177 			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
1178 			if(a->value && strstr(a->value, JANUS_RTP_EXTMAP_ENCRYPTED)) {
1179 				m->attributes = g_list_remove(m->attributes, a);
1180 				tempA = m->attributes;
1181 				janus_sdp_attribute_destroy(a);
1182 				continue;
1183 			}
1184 			tempA = tempA->next;
1185 		}
1186 		/* Also remove attributes/formats we know we don't support (or don't want to support) now */
1187 		tempA = m->attributes;
1188 		GList *purged_ptypes = NULL;
1189 		while(tempA) {
1190 			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
1191 			if(a->value && (strstr(a->value, "red/90000") || strstr(a->value, "ulpfec/90000") ||
1192 					strstr(a->value, "flexfec-03/90000") || strstr(a->value, "rtx/90000"))) {
1193 				int ptype = atoi(a->value);
1194 				if(ptype < 0) {
1195 					JANUS_LOG(LOG_ERR, "Invalid payload type (%d)\n", ptype);
1196 				} else {
1197 					JANUS_LOG(LOG_VERB, "Will remove payload type %d (%s)\n", ptype, a->value);
1198 					purged_ptypes = g_list_append(purged_ptypes, GINT_TO_POINTER(ptype));
1199 				}
1200 			}
1201 			tempA = tempA->next;
1202 		}
1203 		if(purged_ptypes) {
1204 			tempA = purged_ptypes;
1205 			while(tempA) {
1206 				int ptype = GPOINTER_TO_INT(tempA->data);
1207 				janus_sdp_remove_payload_type(anon, ptype);
1208 				tempA = tempA->next;
1209 			}
1210 			g_list_free(purged_ptypes);
1211 			purged_ptypes = NULL;
1212 		}
1213 		temp = temp->next;
1214 	}
1215 
1216 	JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
1217 	JANUS_LOG(LOG_VERB, "  >> Anonymized\n");
1218 	JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
1219 
1220 	return 0;
1221 }
1222 
janus_sdp_merge(void * ice_handle,janus_sdp * anon,gboolean offer)1223 char *janus_sdp_merge(void *ice_handle, janus_sdp *anon, gboolean offer) {
1224 	if(ice_handle == NULL || anon == NULL)
1225 		return NULL;
1226 	janus_ice_handle *handle = (janus_ice_handle *)ice_handle;
1227 	janus_ice_stream *stream = handle->stream;
1228 	if(stream == NULL)
1229 		return NULL;
1230 	char *rtp_profile = handle->rtp_profile ? handle->rtp_profile : (char *)"UDP/TLS/RTP/SAVPF";
1231 	if(!janus_is_webrtc_encryption_enabled())
1232 		rtp_profile = (char *)"RTP/AVPF";
1233 	gboolean ipv4 = !strstr(janus_get_public_ip(0), ":");
1234 	/* Origin o= */
1235 	gint64 sessid = janus_get_real_time();
1236 	if(anon->o_name == NULL)
1237 		anon->o_name = g_strdup("-");
1238 	if(anon->o_sessid == 0 || anon->o_version == 0) {
1239 		anon->o_sessid = sessid;
1240 		anon->o_version = 1;
1241 	}
1242 	anon->o_ipv4 = ipv4;
1243 	g_free(anon->o_addr);
1244 	anon->o_addr = g_strdup(janus_get_public_ip(0));
1245 	/* Session name s= */
1246 	if(anon->s_name == NULL)
1247 		anon->s_name = g_strdup("Meetecho Janus");
1248 	/* Chrome doesn't like global c= lines, remove it */
1249 	g_free(anon->c_addr);
1250 	anon->c_addr = NULL;
1251 	/* bundle: add new global attribute */
1252 	char buffer[2048], buffer_part[512];
1253 	buffer[0] = '\0';
1254 	buffer_part[0] = '\0';
1255 	g_snprintf(buffer, sizeof(buffer), "BUNDLE");
1256 	/* Iterate on available media */
1257 	int audio = 0;
1258 	int video = 0;
1259 #ifdef HAVE_SCTP
1260 	int data = 0;
1261 #endif
1262 	GList *temp = anon->m_lines;
1263 	while(temp) {
1264 		janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
1265 		if(m->type == JANUS_SDP_AUDIO) {
1266 			audio++;
1267 			if(audio == 1 && m->port > 0) {
1268 				g_snprintf(buffer_part, sizeof(buffer_part),
1269 					" %s", handle->audio_mid ? handle->audio_mid : "audio");
1270 				janus_strlcat(buffer, buffer_part, sizeof(buffer));
1271 			}
1272 		} else if(m->type == JANUS_SDP_VIDEO) {
1273 			video++;
1274 			if(video == 1 && m->port > 0) {
1275 				g_snprintf(buffer_part, sizeof(buffer_part),
1276 					" %s", handle->video_mid ? handle->video_mid : "video");
1277 				janus_strlcat(buffer, buffer_part, sizeof(buffer));
1278 			}
1279 #ifdef HAVE_SCTP
1280 		} else if(m->type == JANUS_SDP_APPLICATION) {
1281 			if(m->proto && (!strcasecmp(m->proto, "DTLS/SCTP") || !strcasecmp(m->proto, "UDP/DTLS/SCTP")))
1282 				data++;
1283 			if(data == 1 && m->port > 0) {
1284 				g_snprintf(buffer_part, sizeof(buffer_part),
1285 					" %s", handle->data_mid ? handle->data_mid : "data");
1286 				janus_strlcat(buffer, buffer_part, sizeof(buffer));
1287 			}
1288 #endif
1289 		}
1290 		temp = temp->next;
1291 	}
1292 	/* Global attributes: start with group */
1293 	GList *first = anon->attributes;
1294 	janus_sdp_attribute *a = janus_sdp_attribute_create("group", "%s", buffer);
1295 	anon->attributes = g_list_insert_before(anon->attributes, first, a);
1296 	/* msid-semantic: add new global attribute */
1297 	a = janus_sdp_attribute_create("msid-semantic", " WMS janus");
1298 	anon->attributes = g_list_insert_before(anon->attributes, first, a);
1299 	/* ICE Full or Lite? */
1300 	if(janus_ice_is_ice_lite_enabled()) {
1301 		/* Janus is acting in ICE Lite mode, advertize this */
1302 		a = janus_sdp_attribute_create("ice-lite", NULL);
1303 		anon->attributes = g_list_insert_before(anon->attributes, first, a);
1304 	}
1305 	/* Media lines now */
1306 	audio = 0;
1307 	video = 0;
1308 #ifdef HAVE_SCTP
1309 	data = 0;
1310 #endif
1311 	temp = anon->m_lines;
1312 	while(temp) {
1313 		janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
1314 		first = m->attributes;
1315 		/* Overwrite RTP profile for audio and video */
1316 		if(m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO) {
1317 			g_free(m->proto);
1318 			m->proto = g_strdup(rtp_profile);
1319 		}
1320 		/* Media connection c= */
1321 		g_free(m->c_addr);
1322 		m->c_ipv4 = ipv4;
1323 		m->c_addr = g_strdup(janus_get_public_ip(0));
1324 		/* Check if we need to refuse the media or not */
1325 		if(m->type == JANUS_SDP_AUDIO) {
1326 			audio++;
1327 			/* Audio */
1328 			if(audio > 1) {
1329 				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping audio line (we have one already)\n", handle->handle_id);
1330 				m->port = 0;
1331 			}
1332 			if(m->port == 0) {
1333 				m->direction = JANUS_SDP_INACTIVE;
1334 				stream->audio_ssrc = 0;
1335 			}
1336 			if(audio == 1) {
1337 				gboolean receiving = (stream->audio_recv == TRUE);
1338 				switch(m->direction) {
1339 					case JANUS_SDP_INACTIVE:
1340 						stream->audio_send = FALSE;
1341 						stream->audio_recv = FALSE;
1342 						break;
1343 					case JANUS_SDP_SENDONLY:
1344 						stream->audio_send = TRUE;
1345 						stream->audio_recv = FALSE;
1346 						break;
1347 					case JANUS_SDP_RECVONLY:
1348 						stream->audio_send = FALSE;
1349 						stream->audio_recv = TRUE;
1350 						break;
1351 					case JANUS_SDP_SENDRECV:
1352 					case JANUS_SDP_DEFAULT:
1353 					default:
1354 						stream->audio_send = TRUE;
1355 						stream->audio_recv = TRUE;
1356 						break;
1357 				}
1358 				if(receiving != stream->audio_recv)
1359 					janus_ice_notify_media_stopped(handle);
1360 			}
1361 		} else if(m->type == JANUS_SDP_VIDEO) {
1362 			video++;
1363 			/* Video */
1364 			if(video > 1) {
1365 				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping video line (we have one already)\n", handle->handle_id);
1366 				m->port = 0;
1367 			}
1368 			if(m->port == 0) {
1369 				m->direction = JANUS_SDP_INACTIVE;
1370 				stream->video_ssrc = 0;
1371 			}
1372 			if(video == 1) {
1373 				gboolean receiving = (stream->video_recv == TRUE);
1374 				switch(m->direction) {
1375 					case JANUS_SDP_INACTIVE:
1376 						stream->video_send = FALSE;
1377 						stream->video_recv = FALSE;
1378 						break;
1379 					case JANUS_SDP_SENDONLY:
1380 						stream->video_send = TRUE;
1381 						stream->video_recv = FALSE;
1382 						break;
1383 					case JANUS_SDP_RECVONLY:
1384 						stream->video_send = FALSE;
1385 						stream->video_recv = TRUE;
1386 						break;
1387 					case JANUS_SDP_SENDRECV:
1388 					case JANUS_SDP_DEFAULT:
1389 					default:
1390 						stream->video_send = TRUE;
1391 						stream->video_recv = TRUE;
1392 						break;
1393 				}
1394 				if(receiving != stream->video_recv)
1395 					janus_ice_notify_media_stopped(handle);
1396 				if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX)) {
1397 					/* Add RFC4588 stuff */
1398 					if(stream->rtx_payload_types && g_hash_table_size(stream->rtx_payload_types) > 0) {
1399 						janus_sdp_attribute *a = NULL;
1400 						GList *ptypes = g_list_copy(m->ptypes), *tempP = ptypes;
1401 						while(tempP) {
1402 							int ptype = GPOINTER_TO_INT(tempP->data);
1403 							int rtx_ptype = GPOINTER_TO_INT(g_hash_table_lookup(stream->rtx_payload_types, GINT_TO_POINTER(ptype)));
1404 							if(rtx_ptype > 0) {
1405 								m->ptypes = g_list_append(m->ptypes, GINT_TO_POINTER(rtx_ptype));
1406 								a = janus_sdp_attribute_create("rtpmap", "%d rtx/90000", rtx_ptype);
1407 								m->attributes = g_list_append(m->attributes, a);
1408 								a = janus_sdp_attribute_create("fmtp", "%d apt=%d", rtx_ptype, ptype);
1409 								m->attributes = g_list_append(m->attributes, a);
1410 							}
1411 							tempP = tempP->next;
1412 						}
1413 						g_list_free(ptypes);
1414 					}
1415 				}
1416 			}
1417 #ifdef HAVE_SCTP
1418 		} else if(m->type == JANUS_SDP_APPLICATION) {
1419 			/* Is this SCTP for DataChannels? */
1420 			if(m->port > 0 && (!strcasecmp(m->proto, "DTLS/SCTP") || !strcasecmp(m->proto, "UDP/DTLS/SCTP"))) {
1421 				/* Yep */
1422 				data++;
1423 				if(data > 1) {
1424 					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping SCTP line (we have one already)\n", handle->handle_id);
1425 					m->port = 0;
1426 					m->direction = JANUS_SDP_INACTIVE;
1427 					temp = temp->next;
1428 					continue;
1429 				}
1430 			} else {
1431 				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping unsupported application media line...\n", handle->handle_id);
1432 				m->port = 0;
1433 				m->direction = JANUS_SDP_INACTIVE;
1434 				temp = temp->next;
1435 				continue;
1436 			}
1437 #endif
1438 		} else {
1439 			JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping disabled/unsupported media line...\n", handle->handle_id);
1440 			m->port = 0;
1441 			m->direction = JANUS_SDP_INACTIVE;
1442 			temp = temp->next;
1443 			continue;
1444 		}
1445 		/* a=mid:(audio|video|data) */
1446 		if(m->type == JANUS_SDP_AUDIO && audio == 1) {
1447 			a = janus_sdp_attribute_create("mid", "%s", handle->audio_mid);
1448 			m->attributes = g_list_insert_before(m->attributes, first, a);
1449 		} else if(m->type == JANUS_SDP_VIDEO && video == 1) {
1450 			a = janus_sdp_attribute_create("mid", "%s", handle->video_mid);
1451 			m->attributes = g_list_insert_before(m->attributes, first, a);
1452 #ifdef HAVE_SCTP
1453 		} else if(m->type == JANUS_SDP_APPLICATION && data == 1) {
1454 			if(!strcasecmp(m->proto, "UDP/DTLS/SCTP"))
1455 				janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP);
1456 			if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP)) {
1457 				a = janus_sdp_attribute_create("sctpmap", "5000 webrtc-datachannel 16");
1458 				m->attributes = g_list_insert_before(m->attributes, first, a);
1459 			} else {
1460 				a = janus_sdp_attribute_create("sctp-port", "5000");
1461 				m->attributes = g_list_insert_before(m->attributes, first, a);
1462 			}
1463 			a = janus_sdp_attribute_create("mid", "%s", handle->data_mid);
1464 			m->attributes = g_list_insert_before(m->attributes, first, a);
1465 #endif
1466 		}
1467 		if(m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO) {
1468 			a = janus_sdp_attribute_create("rtcp-mux", NULL);
1469 			m->attributes = g_list_insert_before(m->attributes, first, a);
1470 		}
1471 		/* ICE ufrag and pwd, DTLS fingerprint setup and connection a= */
1472 		gchar *ufrag = NULL;
1473 		gchar *password = NULL;
1474 		nice_agent_get_local_credentials(handle->agent, stream->stream_id, &ufrag, &password);
1475 		a = janus_sdp_attribute_create("ice-ufrag", "%s", ufrag);
1476 		m->attributes = g_list_insert_before(m->attributes, first, a);
1477 		a = janus_sdp_attribute_create("ice-pwd", "%s", password);
1478 		m->attributes = g_list_insert_before(m->attributes, first, a);
1479 		g_free(ufrag);
1480 		g_free(password);
1481 		a = janus_sdp_attribute_create("ice-options", "trickle");
1482 		m->attributes = g_list_insert_before(m->attributes, first, a);
1483 		if(janus_is_webrtc_encryption_enabled()) {
1484 			a = janus_sdp_attribute_create("fingerprint", "sha-256 %s", janus_dtls_get_local_fingerprint());
1485 			m->attributes = g_list_insert_before(m->attributes, first, a);
1486 			a = janus_sdp_attribute_create("setup", "%s", janus_get_dtls_srtp_role(offer ? JANUS_DTLS_ROLE_ACTPASS : stream->dtls_role));
1487 			m->attributes = g_list_insert_before(m->attributes, first, a);
1488 		}
1489 		/* Add last attributes, rtcp and ssrc (msid) */
1490 		if(m->type == JANUS_SDP_VIDEO && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX) &&
1491 				(m->direction == JANUS_SDP_DEFAULT || m->direction == JANUS_SDP_SENDRECV || m->direction == JANUS_SDP_SENDONLY)) {
1492 			/* Add FID group to negotiate the RFC4588 stuff */
1493 			a = janus_sdp_attribute_create("ssrc-group", "FID %"SCNu32" %"SCNu32, stream->video_ssrc, stream->video_ssrc_rtx);
1494 			m->attributes = g_list_append(m->attributes, a);
1495 		}
1496 		if(m->type == JANUS_SDP_AUDIO) {
1497 			a = janus_sdp_attribute_create("msid", "janus janusa0");
1498 			m->attributes = g_list_append(m->attributes, a);
1499 			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" cname:janus", stream->audio_ssrc);
1500 			m->attributes = g_list_append(m->attributes, a);
1501 			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" msid:janus janusa0", stream->audio_ssrc);
1502 			m->attributes = g_list_append(m->attributes, a);
1503 			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" mslabel:janus", stream->audio_ssrc);
1504 			m->attributes = g_list_append(m->attributes, a);
1505 			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" label:janusa0", stream->audio_ssrc);
1506 			m->attributes = g_list_append(m->attributes, a);
1507 		} else if(m->type == JANUS_SDP_VIDEO) {
1508 			a = janus_sdp_attribute_create("msid", "janus janusv0");
1509 			m->attributes = g_list_append(m->attributes, a);
1510 			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" cname:janus", stream->video_ssrc);
1511 			m->attributes = g_list_append(m->attributes, a);
1512 			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" msid:janus janusv0", stream->video_ssrc);
1513 			m->attributes = g_list_append(m->attributes, a);
1514 			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" mslabel:janus", stream->video_ssrc);
1515 			m->attributes = g_list_append(m->attributes, a);
1516 			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" label:janusv0", stream->video_ssrc);
1517 			m->attributes = g_list_append(m->attributes, a);
1518 			if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX) &&
1519 					(m->direction == JANUS_SDP_DEFAULT || m->direction == JANUS_SDP_SENDRECV || m->direction == JANUS_SDP_SENDONLY)) {
1520 				/* Add rtx SSRC group to negotiate the RFC4588 stuff */
1521 				a = janus_sdp_attribute_create("ssrc", "%"SCNu32" cname:janus", stream->video_ssrc_rtx);
1522 				m->attributes = g_list_append(m->attributes, a);
1523 				a = janus_sdp_attribute_create("ssrc", "%"SCNu32" msid:janus janusv0", stream->video_ssrc_rtx);
1524 				m->attributes = g_list_append(m->attributes, a);
1525 				a = janus_sdp_attribute_create("ssrc", "%"SCNu32" mslabel:janus", stream->video_ssrc_rtx);
1526 				m->attributes = g_list_append(m->attributes, a);
1527 				a = janus_sdp_attribute_create("ssrc", "%"SCNu32" label:janusv0", stream->video_ssrc_rtx);
1528 				m->attributes = g_list_append(m->attributes, a);
1529 			}
1530 		}
1531 		/* FIXME If the peer is Firefox and is negotiating simulcasting, add the rid attributes */
1532 		if(m->type == JANUS_SDP_VIDEO && stream->rid[0] != NULL) {
1533 			char rids[50];
1534 			rids[0] = '\0';
1535 			int i=0, index=0;
1536 			for(i=2; i>=0; i--) {
1537 				index = (stream->rids_hml ? i : (2-i));
1538 				if(stream->rid[index] == NULL)
1539 					continue;
1540 				a = janus_sdp_attribute_create("rid", "%s recv", stream->rid[index]);
1541 				m->attributes = g_list_append(m->attributes, a);
1542 				if(strlen(rids) == 0) {
1543 					janus_strlcat(rids, stream->rid[index], sizeof(rids));
1544 				} else {
1545 					janus_strlcat(rids, ";", sizeof(rids));
1546 					janus_strlcat(rids, stream->rid[index], sizeof(rids));
1547 				}
1548 			}
1549 			if(stream->legacy_rid) {
1550 				a = janus_sdp_attribute_create("simulcast", " recv rid=%s", rids);
1551 			} else {
1552 				a = janus_sdp_attribute_create("simulcast", "recv %s", rids);
1553 			}
1554 			m->attributes = g_list_append(m->attributes, a);
1555 		}
1556 		if(!janus_ice_is_full_trickle_enabled()) {
1557 			/* And now the candidates (but only if we're half-trickling) */
1558 			janus_ice_candidates_to_sdp(handle, m, stream->stream_id, 1);
1559 			/* Since we're half-trickling, we need to notify the peer that these are all the
1560 			 * candidates we have for this media stream, via an end-of-candidates attribute:
1561 			 * https://tools.ietf.org/html/draft-ietf-mmusic-trickle-ice-02#section-4.1 */
1562 			janus_sdp_attribute *end = janus_sdp_attribute_create("end-of-candidates", NULL);
1563 			m->attributes = g_list_append(m->attributes, end);
1564 		}
1565 		/* Next */
1566 		temp = temp->next;
1567 	}
1568 
1569 	char *sdp = janus_sdp_write(anon);
1570 
1571 	JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
1572 	JANUS_LOG(LOG_VERB, "  >> Merged (%zu bytes)\n", strlen(sdp));
1573 	JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
1574 	JANUS_LOG(LOG_VERB, "%s\n", sdp);
1575 
1576 	return sdp;
1577 }
1578