Example #1
0
gboolean janus_dtls_retry(gpointer stack) {
	janus_dtls_srtp *dtls = (janus_dtls_srtp *)stack;
	if(dtls == NULL)
		return FALSE;
	janus_ice_component *component = (janus_ice_component *)dtls->component;
	if(component == NULL)
		return FALSE;
	janus_ice_stream *stream = component->stream;
	if(!stream)
		return FALSE;
	janus_ice_handle *handle = stream->handle;
	if(!handle)
		return FALSE;
	if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP))
		return FALSE;
	if(dtls->dtls_state == JANUS_DTLS_STATE_CONNECTED) {
		JANUS_LOG(LOG_VERB, "[%"SCNu64"]  DTLS already set up, disabling retransmission timer!\n", handle->handle_id);
		if(component->source != NULL) {
			g_source_destroy(component->source);
			g_source_unref(component->source);
			component->source = NULL;
		}
		return FALSE;
	}
	struct timeval timeout = {0};
	DTLSv1_get_timeout(dtls->ssl, &timeout);
	guint64 timeout_value = timeout.tv_sec*1000 + timeout.tv_usec/1000;
	JANUS_LOG(LOG_HUGE, "[%"SCNu64"] DTLSv1_get_timeout: %"SCNu64"\n", handle->handle_id, timeout_value);
	if(timeout_value == 0) {
		JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS timeout on component %d of stream %d, retransmitting\n", handle->handle_id, component->component_id, stream->stream_id);
		DTLSv1_handle_timeout(dtls->ssl);
		janus_dtls_fd_bridge(dtls);
	}
	return TRUE;
}
Example #2
0
gboolean janus_dtls_retry(gpointer stack) {
	janus_dtls_srtp *dtls = (janus_dtls_srtp *)stack;
	if(dtls == NULL)
		return FALSE;
	janus_ice_component *component = (janus_ice_component *)dtls->component;
	if(component == NULL)
		return FALSE;
	janus_ice_stream *stream = component->stream;
	if(!stream)
		return FALSE;
	janus_ice_handle *handle = stream->handle;
	if(!handle)
		return FALSE;
	if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP))
		return FALSE;
	//~ struct timeval timeout;
	//~ DTLSv1_get_timeout(dtls->ssl, &timeout);
	//~ JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLSv1_get_timeout: %"SCNu64"\n", handle->handle_id, timeout.tv_sec*1000000+timeout.tv_usec);
	//~ DTLSv1_handle_timeout(dtls->ssl);
	//~ janus_dtls_fd_bridge(dtls);
	JANUS_LOG(LOG_VERB, "[%"SCNu64"] A second has passed on component %d of stream %d\n", handle->handle_id, component->component_id, stream->stream_id);
	if(dtls->dtls_state == JANUS_DTLS_STATE_CONNECTED) {
		JANUS_LOG(LOG_VERB, "[%"SCNu64"]  DTLS already set up, disabling retransmission timer!\n", handle->handle_id);
		return FALSE;
	}
	if(dtls->dtls_last_msg != NULL) {
		JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Retransmitting last message (len=%d)\n", handle->handle_id, dtls->dtls_last_len);
		nice_agent_send(handle->agent, component->stream_id, component->component_id, dtls->dtls_last_len, dtls->dtls_last_msg);
	}
	return TRUE;
}
Example #3
0
gboolean janus_dtls_retry(gpointer stack) {
	janus_dtls_srtp *dtls = (janus_dtls_srtp *)stack;
	if(dtls == NULL)
		return FALSE;
	janus_ice_component *component = (janus_ice_component *)dtls->component;
	if(component == NULL)
		return FALSE;
	janus_ice_stream *stream = component->stream;
	if(!stream)
		goto stoptimer;
	janus_ice_handle *handle = stream->handle;
	if(!handle)
		goto stoptimer;
	if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP))
		goto stoptimer;
	if(dtls->dtls_state == JANUS_DTLS_STATE_CONNECTED) {
		JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS already set up, disabling retransmission timer!\n", handle->handle_id);
		goto stoptimer;
	}
	if(janus_get_monotonic_time() - dtls->dtls_started >= 20*G_USEC_PER_SEC) {
		/* FIXME Should we really give up after 20 seconds waiting for DTLS? */
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] DTLS taking too much time for component %d in stream %d...\n",
			handle->handle_id, component->component_id, stream->stream_id);
		janus_ice_webrtc_hangup(handle, "DTLS timeout");
		goto stoptimer;
	}
	struct timeval timeout = {0};
	DTLSv1_get_timeout(dtls->ssl, &timeout);
	guint64 timeout_value = timeout.tv_sec*1000 + timeout.tv_usec/1000;
	JANUS_LOG(LOG_HUGE, "[%"SCNu64"] DTLSv1_get_timeout: %"SCNu64"\n", handle->handle_id, timeout_value);
	if(timeout_value == 0) {
		dtls->retransmissions++;
		JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS timeout on component %d of stream %d, retransmitting\n", handle->handle_id, component->component_id, stream->stream_id);
		/* Notify event handlers */
		janus_dtls_notify_state_change(dtls);
		/* Retransmit the packet */
		DTLSv1_handle_timeout(dtls->ssl);
		janus_dtls_fd_bridge(dtls);
	}
	return TRUE;

stoptimer:
	if(component->dtlsrt_source != NULL) {
		g_source_destroy(component->dtlsrt_source);
		g_source_unref(component->dtlsrt_source);
		component->dtlsrt_source = NULL;
	}
	return FALSE;
}
Example #4
0
/* DTLS alert callback */
void janus_dtls_callback(const SSL *ssl, int where, int ret) {
	/* We only care about alerts */
	if (!(where & SSL_CB_ALERT)) {
		return;
	}
	janus_dtls_srtp *dtls = SSL_get_ex_data(ssl, 0);
	if(!dtls) {
		JANUS_LOG(LOG_ERR, "No DTLS session related to this alert...\n");
		return;
	}
	janus_ice_component *component = dtls->component;
	if(component == NULL) {
		JANUS_LOG(LOG_ERR, "No ICE component related to this alert...\n");
		return;
	}
	janus_ice_stream *stream = component->stream;
	if(!stream) {
		JANUS_LOG(LOG_ERR, "No ICE stream related to this alert...\n");
		return;
	}
	janus_ice_handle *handle = stream->handle;
	if(!handle) {
		JANUS_LOG(LOG_ERR, "No ICE handle related to this alert...\n");
		return;
	}
	if(stream->stream_id == handle->data_id) {
		/* FIXME BADLY We got a DTLS alert on the Data channel, we ignore it for now */
		JANUS_LOG(LOG_WARN, "[%"SCNu64"] DTLS alert triggered on stream %"SCNu16", but it's the data channel so we don't care...\n", handle->handle_id, stream->stream_id);
		return;
	}
	JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS alert triggered on stream %"SCNu16" (component %"SCNu16"), closing...\n", handle->handle_id, stream->stream_id, component->component_id);
	janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING);
	if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
		janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT);
		if(handle->iceloop)
			g_main_loop_quit(handle->iceloop);
		janus_plugin *plugin = (janus_plugin *)handle->app;
		if(plugin != NULL) {
			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Telling the plugin about it (%s)\n", handle->handle_id, plugin->get_name());
			if(plugin && plugin->hangup_media)
				plugin->hangup_media(handle->app_handle);
			janus_ice_notify_hangup(handle, "DTLS alert");
		}
	}
}
Example #5
0
int janus_dtls_srtp_create_sctp(janus_dtls_srtp *dtls) {
#ifdef HAVE_SCTP
	if(dtls == NULL)
		return -1;
	janus_ice_component *component = (janus_ice_component *)dtls->component;
	if(component == NULL)
		return -2;
	janus_ice_stream *stream = component->stream;
	if(!stream)
		return -3;
	janus_ice_handle *handle = stream->handle;
	if(!handle || !handle->agent)
		return -4;
	if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
		return -5;
	dtls->sctp = janus_sctp_association_create(dtls, handle->handle_id, 5000);
	if(dtls->sctp == NULL) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error creating SCTP association...\n", handle->handle_id);
		return -6;
	}
	/* We need to start it in a thread, since it has blocking accept/connect stuff */
	janus_refcount_increase(&dtls->ref);
	janus_refcount_increase(&dtls->sctp->ref);
	GError *error = NULL;
	char tname[16];
	g_snprintf(tname, sizeof(tname), "sctpinit %"SCNu64, handle->handle_id);
	g_thread_try_new(tname, janus_dtls_sctp_setup_thread, dtls, &error);
	if(error != NULL) {
		/* Something went wrong... */
		janus_refcount_decrease(&dtls->ref);
		janus_refcount_decrease(&dtls->sctp->ref);
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Got error %d (%s) trying to launch the DTLS-SCTP thread...\n", handle->handle_id, error->code, error->message ? error->message : "??");
		return -7;
	}
	return 0;
#else
	/* Support for datachannels hasn't been built in */
	return -1;
#endif
}
Example #6
0
/* DTLS alert callback */
void janus_dtls_callback(const SSL *ssl, int where, int ret) {
	/* We only care about alerts */
	if (!(where & SSL_CB_ALERT)) {
		return;
	}
	janus_dtls_srtp *dtls = SSL_get_ex_data(ssl, 0);
	if(!dtls) {
		JANUS_LOG(LOG_ERR, "No DTLS session related to this alert...\n");
		return;
	}
	dtls->srtp_valid = 0;
	janus_ice_component *component = dtls->component;
	if(component == NULL) {
		JANUS_LOG(LOG_ERR, "No ICE component related to this alert...\n");
		return;
	}
	janus_ice_stream *stream = component->stream;
	if(!stream) {
		JANUS_LOG(LOG_ERR, "No ICE stream related to this alert...\n");
		return;
	}
	janus_ice_handle *handle = stream->handle;
	if(!stream) {
		JANUS_LOG(LOG_ERR, "No ICE handle related to this alert...\n");
		return;
	}
	JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS alert received, closing...\n", handle->handle_id);
	if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
		janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT);
		if(handle->iceloop)
			g_main_loop_quit(handle->iceloop);
		janus_plugin *plugin = (janus_plugin *)handle->app;
		if(plugin != NULL) {
			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Telling the plugin about it (%s)\n", handle->handle_id, plugin->get_name());
			if(plugin && plugin->hangup_media)
				plugin->hangup_media(handle->app_handle);
		}
	}
}
Example #7
0
void janus_dtls_srtp_incoming_msg(janus_dtls_srtp *dtls, char *buf, uint16_t len) {
	if(dtls == NULL) {
		JANUS_LOG(LOG_ERR, "No DTLS-SRTP stack, no incoming message...\n");
		return;
	}
	janus_ice_component *component = (janus_ice_component *)dtls->component;
	if(component == NULL) {
		JANUS_LOG(LOG_ERR, "No component, no DTLS...\n");
		return;
	}
	janus_ice_stream *stream = component->stream;
	if(!stream) {
		JANUS_LOG(LOG_ERR, "No stream, no DTLS...\n");
		return;
	}
	janus_ice_handle *handle = stream->handle;
	if(!handle || !handle->agent) {
		JANUS_LOG(LOG_ERR, "No handle/agent, no DTLS...\n");
		return;
	}
	if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
		JANUS_LOG(LOG_WARN, "[%"SCNu64"] Alert already triggered, clearing up...\n", handle->handle_id);
		return;
	}
	if(!dtls->ssl || !dtls->read_bio) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] No DTLS stuff for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id);
		return;
	}
	janus_dtls_fd_bridge(dtls);
	int written = BIO_write(dtls->read_bio, buf, len);
	JANUS_LOG(LOG_HUGE, "[%"SCNu64"]     Written %d of those bytes on the read BIO...\n", handle->handle_id, written);
	janus_dtls_fd_bridge(dtls);
	/* Try to read data */
	char data[1500];	/* FIXME */
	memset(&data, 0, 1500);
	int read = SSL_read(dtls->ssl, &data, 1500);
	JANUS_LOG(LOG_HUGE, "[%"SCNu64"]     ... and read %d of them from SSL...\n", handle->handle_id, read);
	if(read < 0) {
		unsigned long err = SSL_get_error(dtls->ssl, read);
		if(err == SSL_ERROR_SSL) {
			/* Ops, something went wrong with the DTLS handshake */
			char error[200];
			ERR_error_string_n(ERR_get_error(), error, 200);
			JANUS_LOG(LOG_ERR, "[%"SCNu64"] Handshake error: %s\n", handle->handle_id, error);
			return;
		}
	}
	janus_dtls_fd_bridge(dtls);
	if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP) || janus_is_stopping()) {
		/* DTLS alert triggered, we should end it here */
		JANUS_LOG(LOG_VERB, "[%"SCNu64"] Forced to stop it here...\n", handle->handle_id);
		return;
	}
	if(!SSL_is_init_finished(dtls->ssl)) {
		/* Nothing else to do for now */
		JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Initialization not finished yet...\n", handle->handle_id);
		return;
	}
	if(dtls->ready) {
		/* There's data to be read? */
		JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Any data available?\n", handle->handle_id);
#ifdef HAVE_SCTP
		if(dtls->sctp != NULL && read > 0) {
			JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Sending data (%d bytes) to the SCTP stack...\n", handle->handle_id, read);
			janus_sctp_data_from_dtls(dtls->sctp, data, read);
		}
#else
		if(read > 0) {
			JANUS_LOG(LOG_WARN, "[%"SCNu64"] Data available but Data Channels support disabled...\n", handle->handle_id);
		}
#endif
	} else {
		JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS established, yay!\n", handle->handle_id);
		/* Check the remote fingerprint */
		X509 *rcert = SSL_get_peer_certificate(dtls->ssl);
		if(!rcert) {
			JANUS_LOG(LOG_ERR, "[%"SCNu64"] No remote certificate??\n", handle->handle_id);
		} else {
			unsigned int rsize;
			unsigned char rfingerprint[EVP_MAX_MD_SIZE];
			char remote_fingerprint[160];
			char *rfp = (char *)&remote_fingerprint;
			if(handle->remote_hashing && !strcasecmp(handle->remote_hashing, "sha-1")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-1 fingerprint of remote certificate...\n", handle->handle_id);
				X509_digest(rcert, EVP_sha1(), (unsigned char *)rfingerprint, &rsize);
			} else {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-256 fingerprint of remote certificate...\n", handle->handle_id);
				X509_digest(rcert, EVP_sha256(), (unsigned char *)rfingerprint, &rsize);
			}
			X509_free(rcert);
			rcert = NULL;
			unsigned int i = 0;
			for(i = 0; i < rsize; i++) {
				g_snprintf(rfp, 4, "%.2X:", rfingerprint[i]);
				rfp += 3;
			}
			*(rfp-1) = 0;
			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Remote fingerprint (%s) of the client is %s\n",
				handle->handle_id, handle->remote_hashing ? handle->remote_hashing : "sha-256", remote_fingerprint);
			if(!strcasecmp(remote_fingerprint, handle->remote_fingerprint ? handle->remote_fingerprint : "(none)")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Fingerprint is a match!\n", handle->handle_id);
				dtls->dtls_state = JANUS_DTLS_STATE_CONNECTED;
				dtls->dtls_connected = janus_get_monotonic_time();
			} else {
				/* FIXME NOT a match! MITM? */
				JANUS_LOG(LOG_ERR, "[%"SCNu64"]  Fingerprint is NOT a match! got %s, expected %s\n", handle->handle_id, remote_fingerprint, handle->remote_fingerprint);
				dtls->dtls_state = JANUS_DTLS_STATE_FAILED;
				goto done;
			}
			if(dtls->dtls_state == JANUS_DTLS_STATE_CONNECTED) {
				if(component->stream_id == handle->audio_id || component->stream_id == handle->video_id) {
					/* Complete with SRTP setup */
					unsigned char material[SRTP_MASTER_LENGTH*2];
					unsigned char *local_key, *local_salt, *remote_key, *remote_salt;
					/* Export keying material for SRTP */
					if (!SSL_export_keying_material(dtls->ssl, material, SRTP_MASTER_LENGTH*2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
						/* Oops... */
						JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, couldn't extract SRTP keying material for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id);
						goto done;
					}
					/* Key derivation (http://tools.ietf.org/html/rfc5764#section-4.2) */
					if(dtls->dtls_role == JANUS_DTLS_ROLE_CLIENT) {
						local_key = material;
						remote_key = local_key + SRTP_MASTER_KEY_LENGTH;
						local_salt = remote_key + SRTP_MASTER_KEY_LENGTH;
						remote_salt = local_salt + SRTP_MASTER_SALT_LENGTH;
					} else {
						remote_key = material;
						local_key = remote_key + SRTP_MASTER_KEY_LENGTH;
						remote_salt = local_key + SRTP_MASTER_KEY_LENGTH;
						local_salt = remote_salt + SRTP_MASTER_SALT_LENGTH;
					}
					/* Build master keys and set SRTP policies */
						/* Remote (inbound) */
					crypto_policy_set_rtp_default(&(dtls->remote_policy.rtp));
					crypto_policy_set_rtcp_default(&(dtls->remote_policy.rtcp));
					dtls->remote_policy.ssrc.type = ssrc_any_inbound;
					unsigned char remote_policy_key[SRTP_MASTER_LENGTH];
					dtls->remote_policy.key = (unsigned char *)&remote_policy_key;
					memcpy(dtls->remote_policy.key, remote_key, SRTP_MASTER_KEY_LENGTH);
					memcpy(dtls->remote_policy.key + SRTP_MASTER_KEY_LENGTH, remote_salt, SRTP_MASTER_SALT_LENGTH);
#if HAS_DTLS_WINDOW_SIZE
					dtls->remote_policy.window_size = 128;
					dtls->remote_policy.allow_repeat_tx = 0;
#endif
					dtls->remote_policy.next = NULL;
						/* Local (outbound) */
					crypto_policy_set_rtp_default(&(dtls->local_policy.rtp));
					crypto_policy_set_rtcp_default(&(dtls->local_policy.rtcp));
					dtls->local_policy.ssrc.type = ssrc_any_outbound;
					unsigned char local_policy_key[SRTP_MASTER_LENGTH];
					dtls->local_policy.key = (unsigned char *)&local_policy_key;
					memcpy(dtls->local_policy.key, local_key, SRTP_MASTER_KEY_LENGTH);
					memcpy(dtls->local_policy.key + SRTP_MASTER_KEY_LENGTH, local_salt, SRTP_MASTER_SALT_LENGTH);
#if HAS_DTLS_WINDOW_SIZE
					dtls->local_policy.window_size = 128;
					dtls->local_policy.allow_repeat_tx = 0;
#endif
					dtls->local_policy.next = NULL;
					/* Create SRTP sessions */
					err_status_t res = srtp_create(&(dtls->srtp_in), &(dtls->remote_policy));
					if(res != err_status_ok) {
						/* Something went wrong... */
						JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating inbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id);
						JANUS_LOG(LOG_ERR, "[%"SCNu64"]  -- %d (%s)\n", handle->handle_id, res, janus_get_srtp_error(res));
						goto done;
					}
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created inbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id);
					res = srtp_create(&(dtls->srtp_out), &(dtls->local_policy));
					if(res != err_status_ok) {
						/* Something went wrong... */
						JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating outbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id);
						JANUS_LOG(LOG_ERR, "[%"SCNu64"]  -- %d (%s)\n", handle->handle_id, res, janus_get_srtp_error(res));
						goto done;
					}
					dtls->srtp_valid = 1;
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created outbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id);
				}
#ifdef HAVE_SCTP
				if(component->stream_id == handle->data_id ||
						(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) &&
						janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS))) {
					/* FIXME Create SCTP association as well (5000 should be dynamic, from the SDP...) */
					dtls->sctp = janus_sctp_association_create(dtls, handle->handle_id, 5000);
					if(dtls->sctp != NULL) {
						/* FIXME We need to start it in a thread, though, since it has blocking accept/connect stuff */
						GError *error = NULL;
						g_thread_try_new("DTLS-SCTP", janus_dtls_sctp_setup_thread, dtls, &error);
						if(error != NULL) {
							/* Something went wrong... */
							JANUS_LOG(LOG_ERR, "[%"SCNu64"] Got error %d (%s) trying to launch the DTLS-SCTP thread...\n", handle->handle_id, error->code, error->message ? error->message : "??");
						}
						dtls->srtp_valid = 1;
					}
				}
#endif
				dtls->ready = 1;
			}
done:
			if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT) && dtls->srtp_valid) {
				/* Handshake successfully completed */
				janus_ice_dtls_handshake_done(handle, component);
			} else {
				/* Something went wrong in either DTLS or SRTP... tell the plugin about it */
				janus_dtls_callback(dtls->ssl, SSL_CB_ALERT, 0);
				janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING);
			}
		}
	}
}
Example #8
0
char *janus_sdp_merge(janus_ice_handle *handle, const char *origsdp) {
	if(handle == NULL || origsdp == NULL)
		return NULL;
	sdp_session_t *anon = NULL;
	sdp_parser_t *parser = sdp_parse(home, origsdp, strlen(origsdp), 0);
	if(!(anon = sdp_session(parser))) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error parsing/merging SDP: %s\n", handle->handle_id, sdp_parsing_error(parser));
		sdp_parser_free(parser);
		return NULL;
	}
	/* Prepare SDP to merge */
	gchar buffer[512];
	memset(buffer, 0, 512);
	char *sdp = (char*)calloc(BUFSIZE, sizeof(char));
	if(sdp == NULL) {
		JANUS_LOG(LOG_FATAL, "Memory error!\n");
		sdp_parser_free(parser);
		return NULL;
	}
	sdp[0] = '\0';
	/* FIXME Any Plan B to take into account? */
	int planb = strstr(origsdp, "a=planb:") ? 1 : 0;
	if(planb) {
		janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B);
	} else {
		janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B);
	}
	/* Version v= */
	g_strlcat(sdp,
		"v=0\r\n", BUFSIZE);
	/* Origin o= */
	if(anon->sdp_origin) {
		g_snprintf(buffer, 512,
			"o=%s %"SCNu64" %"SCNu64" IN IP4 127.0.0.1\r\n",	/* FIXME Should we fix the address? */
				anon->sdp_origin->o_username ? anon->sdp_origin->o_username : "******",
				anon->sdp_origin->o_id, anon->sdp_origin->o_version);
		g_strlcat(sdp, buffer, BUFSIZE);
	} else {
		gint64 sessid = janus_get_monotonic_time();
		gint64 version = sessid;	/* FIXME This needs to be increased when it changes, so time should be ok */
		g_snprintf(buffer, 512,
			"o=%s %"SCNi64" %"SCNi64" IN IP4 127.0.0.1\r\n",	/* FIXME Should we fix the address? */
				"-", sessid, version);
		g_strlcat(sdp, buffer, BUFSIZE);
	}
	/* Session name s= */
	if(anon->sdp_subject && strlen(anon->sdp_subject) > 0) {
		g_snprintf(buffer, 512, "s=%s\r\n", anon->sdp_subject);
	} else {
		g_snprintf(buffer, 512, "s=%s\r\n", "Meetecho Janus");
	}
	g_strlcat(sdp, buffer, BUFSIZE);
	/* Timing t= */
	g_snprintf(buffer, 512,
		"t=%lu %lu\r\n", anon->sdp_time ? anon->sdp_time->t_start : 0, anon->sdp_time ? anon->sdp_time->t_stop : 0);
	g_strlcat(sdp, buffer, BUFSIZE);
	/* ICE Full or Lite? */
	if(janus_ice_is_ice_lite_enabled()) {
		/* Janus is acting in ICE Lite mode, advertize this */
		g_strlcat(sdp, "a=ice-lite\r\n", BUFSIZE);
	}
	/* bundle: add new global attribute */
	int audio = (strstr(origsdp, "m=audio") != NULL);
	int video = (strstr(origsdp, "m=video") != NULL);
#ifdef HAVE_SCTP
	int data = (strstr(origsdp, "DTLS/SCTP") && !strstr(origsdp, " 0 DTLS/SCTP"));
#else
	int data = 0;
#endif
	g_strlcat(sdp, "a=group:BUNDLE", BUFSIZE);
	if(audio) {
		g_snprintf(buffer, 512,
			" %s", handle->audio_mid ? handle->audio_mid : "audio");
		g_strlcat(sdp, buffer, BUFSIZE);
	}
	if(video) {
		g_snprintf(buffer, 512,
			" %s", handle->video_mid ? handle->video_mid : "video");
		g_strlcat(sdp, buffer, BUFSIZE);
	}
	if(data) {
		g_snprintf(buffer, 512,
			" %s", handle->data_mid ? handle->data_mid : "data");
		g_strlcat(sdp, buffer, BUFSIZE);
	}
	g_strlcat(sdp, "\r\n", BUFSIZE);
	/* msid-semantic: add new global attribute */
	g_strlcat(sdp,
		"a=msid-semantic: WMS janus\r\n",
		BUFSIZE);
	char wms[BUFSIZE];
	memset(wms, 0, BUFSIZE);
	g_strlcat(wms, "WMS", BUFSIZE);
	/* Copy other global attributes, if any */
	if(anon->sdp_attributes) {
		sdp_attribute_t *a = anon->sdp_attributes;
		while(a) {
			if(a->a_value == NULL) {
				g_snprintf(buffer, 512,
					"a=%s\r\n", a->a_name);
				g_strlcat(sdp, buffer, BUFSIZE);
			} else {
				g_snprintf(buffer, 512,
					"a=%s:%s\r\n", a->a_name, a->a_value);
				g_strlcat(sdp, buffer, BUFSIZE);
			}
			a = a->a_next;
		}
	}
	gboolean ipv6 = strstr(janus_get_public_ip(), ":") != NULL;
	/* Media lines now */
	if(anon->sdp_media) {
		int audio = 0, video = 0;
#ifdef HAVE_SCTP
		int data = 0;
#endif
		sdp_media_t *m = anon->sdp_media;
		janus_ice_stream *stream = NULL;
		while(m) {
			if(m->m_type == sdp_media_audio && m->m_port > 0) {
				audio++;
				if(audio > 1 || !handle->audio_id) {
					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping audio line (we have %d audio lines, and the id is %d)\n", handle->handle_id, audio, handle->audio_id);
					g_strlcat(sdp, "m=audio 0 RTP/SAVPF 0\r\n", BUFSIZE);
					/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
					g_snprintf(buffer, 512,
						"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
					g_strlcat(sdp, buffer, BUFSIZE);
					m = m->m_next;
					continue;
				}
				/* Audio */
				stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id));
				if(stream == NULL) {
					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping audio line (invalid stream %d)\n", handle->handle_id, handle->audio_id);
					g_strlcat(sdp, "m=audio 0 RTP/SAVPF 0\r\n", BUFSIZE);
					/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
					g_snprintf(buffer, 512,
						"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
					g_strlcat(sdp, buffer, BUFSIZE);
					m = m->m_next;
					continue;
				}
				g_strlcat(sdp, "m=audio 1 RTP/SAVPF", BUFSIZE);
			} else if(m->m_type == sdp_media_video && m->m_port > 0) {
				video++;
				gint id = handle->video_id;
				if(id == 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE))
					id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
				if(video > 1 || !id) {
					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping video line (we have %d video lines, and the id is %d)\n", handle->handle_id, video,
						janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) ? handle->audio_id : handle->video_id);
					g_strlcat(sdp, "m=video 0 RTP/SAVPF 0\r\n", BUFSIZE);
					/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
					g_snprintf(buffer, 512,
						"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
					g_strlcat(sdp, buffer, BUFSIZE);
					m = m->m_next;
					continue;
				}
				/* Video */
				stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
				if(stream == NULL) {
					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping video line (invalid stream %d)\n", handle->handle_id, id);
					g_strlcat(sdp, "m=video 0 RTP/SAVPF 0\r\n", BUFSIZE);
					/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
					g_snprintf(buffer, 512,
						"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
					g_strlcat(sdp, buffer, BUFSIZE);
					m = m->m_next;
					continue;
				}
				g_strlcat(sdp, "m=video 1 RTP/SAVPF", BUFSIZE);
#ifdef HAVE_SCTP
			} else if(m->m_type == sdp_media_application) {
				/* Is this SCTP for DataChannels? */
				if(m->m_port > 0 && m->m_proto_name != NULL && !strcasecmp(m->m_proto_name, "DTLS/SCTP") && m->m_port > 0) {
					/* Yep */
					data++;
					gint id = handle->data_id;
					if(id == 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE))
						id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
					if(data > 1 || !id) {
						JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping SCTP line (we have %d SCTP lines, and the id is %d)\n", handle->handle_id, data, id);
						g_snprintf(buffer, 512,
							"m=%s 0 %s 0\r\n",
							m->m_type_name, m->m_proto_name);
						g_strlcat(sdp, buffer, BUFSIZE);
						/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
						g_snprintf(buffer, 512,
							"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
						g_strlcat(sdp, buffer, BUFSIZE);
						m = m->m_next;
						continue;
					}
					/* SCTP */
					stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
					if(stream == NULL) {
						JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping SCTP line (invalid stream %d)\n", handle->handle_id, id);
						g_snprintf(buffer, 512,
							"m=%s 0 %s 0\r\n",
							m->m_type_name, m->m_proto_name);
						g_strlcat(sdp, buffer, BUFSIZE);
						/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
						g_snprintf(buffer, 512,
							"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
						g_strlcat(sdp, buffer, BUFSIZE);
						m = m->m_next;
						continue;
					}
					g_strlcat(sdp, "m=application 1 DTLS/SCTP", BUFSIZE);
				} else {
					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping unsupported application media line...\n", handle->handle_id);
					g_snprintf(buffer, 512,
						"m=%s 0 %s 0\r\n",
						m->m_type_name, m->m_proto_name);
					g_strlcat(sdp, buffer, BUFSIZE);
					m = m->m_next;
					continue;
				}
#endif
			} else {
				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping disabled/unsupported media line...\n", handle->handle_id);
				g_snprintf(buffer, 512,
					"m=%s 0 %s 0\r\n",
					m->m_type_name, m->m_proto_name);
				g_strlcat(sdp, buffer, BUFSIZE);
				/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
				g_snprintf(buffer, 512,
					"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
				g_strlcat(sdp, buffer, BUFSIZE);
				m = m->m_next;
				continue;
			}
			/* Add formats now */
			if(!m->m_rtpmaps) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] No RTP maps?? trying formats...\n", handle->handle_id);
				if(!m->m_format) {
					JANUS_LOG(LOG_ERR, "[%"SCNu64"] No formats either?? this sucks!\n", handle->handle_id);
					g_strlcat(sdp, " 0", BUFSIZE);	/* FIXME Won't work apparently */
				} else {
					sdp_list_t *fmt = m->m_format;
					while(fmt) {
						g_snprintf(buffer, 512, " %s", fmt->l_text);
						g_strlcat(sdp, buffer, BUFSIZE);
						fmt = fmt->l_next;
					}
				}
			} else {
				sdp_rtpmap_t *r = m->m_rtpmaps;
				while(r) {
					g_snprintf(buffer, 512, " %d", r->rm_pt);
					g_strlcat(sdp, buffer, BUFSIZE);
					r = r->rm_next;
				}
			}
			g_strlcat(sdp, "\r\n", BUFSIZE);
			/* Media connection c= */
			g_snprintf(buffer, 512,
				"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
			g_strlcat(sdp, buffer, BUFSIZE);
			/* Any bandwidth? */
			if(m->m_bandwidths) {
				g_snprintf(buffer, 512,
					"b=%s:%lu\r\n",	/* FIXME Are we doing this correctly? */
						m->m_bandwidths->b_modifier_name ? m->m_bandwidths->b_modifier_name : "AS",
						m->m_bandwidths->b_value);
				g_strlcat(sdp, buffer, BUFSIZE);
			}
			/* a=mid:(audio|video|data) */
			switch(m->m_type) {
				case sdp_media_audio:
					g_snprintf(buffer, 512, "a=mid:%s\r\n", handle->audio_mid ? handle->audio_mid : "audio");
					break;
				case sdp_media_video:
					g_snprintf(buffer, 512, "a=mid:%s\r\n", handle->video_mid ? handle->video_mid : "video");
					break;
#ifdef HAVE_SCTP
				case sdp_media_application:
					/* FIXME sctpmap and webrtc-datachannel should be dynamic */
					g_snprintf(buffer, 512, "a=mid:%s\r\na=sctpmap:5000 webrtc-datachannel 16\r\n",
						handle->data_mid ? handle->data_mid : "data");
					break;
#endif
				default:
					break;
			}
			g_strlcat(sdp, buffer, BUFSIZE);
			if(m->m_type != sdp_media_application) {
				/* What is the direction? */
				switch(m->m_mode) {
					case sdp_sendonly:
						g_strlcat(sdp, "a=sendonly\r\n", BUFSIZE);
						break;
					case sdp_recvonly:
						g_strlcat(sdp, "a=recvonly\r\n", BUFSIZE);
						break;
					case sdp_inactive:
						g_strlcat(sdp, "a=inactive\r\n", BUFSIZE);
						break;
					case sdp_sendrecv:
					default:
						g_strlcat(sdp, "a=sendrecv\r\n", BUFSIZE);
						break;
				}
				/* rtcp-mux */
				g_snprintf(buffer, 512, "a=rtcp-mux\n");
				g_strlcat(sdp, buffer, BUFSIZE);
				/* RTP maps */
				if(m->m_rtpmaps) {
					sdp_rtpmap_t *rm = NULL;
					for(rm = m->m_rtpmaps; rm; rm = rm->rm_next) {
						g_snprintf(buffer, 512, "a=rtpmap:%u %s/%lu%s%s\r\n",
							rm->rm_pt, rm->rm_encoding, rm->rm_rate,
							rm->rm_params ? "/" : "", 
							rm->rm_params ? rm->rm_params : "");
						g_strlcat(sdp, buffer, BUFSIZE);
					}
					for(rm = m->m_rtpmaps; rm; rm = rm->rm_next) {
						if(rm->rm_fmtp) {
							g_snprintf(buffer, 512, "a=fmtp:%u %s\r\n", rm->rm_pt, rm->rm_fmtp);
							g_strlcat(sdp, buffer, BUFSIZE);
						}
					}
				}
			}
			/* ICE ufrag and pwd, DTLS fingerprint setup and connection a= */
			gchar *ufrag = NULL;
			gchar *password = NULL;
			nice_agent_get_local_credentials(handle->agent, stream->stream_id, &ufrag, &password);
			memset(buffer, 0, 100);
			g_snprintf(buffer, 512,
				"a=ice-ufrag:%s\r\n"
				"a=ice-pwd:%s\r\n"
				"a=ice-options:trickle\r\n"
				"a=fingerprint:sha-256 %s\r\n"
				"a=setup:%s\r\n"
				"a=connection:new\r\n",
					ufrag, password,
					janus_dtls_get_local_fingerprint(),
					janus_get_dtls_srtp_role(stream->dtls_role));
			if(ufrag != NULL)
				g_free(ufrag);
			ufrag = NULL;
			if(password != NULL)
				g_free(password);
			password = NULL;
			g_strlcat(sdp, buffer, BUFSIZE);
			/* Copy existing media attributes, if any */
			if(m->m_attributes) {
				sdp_attribute_t *a = m->m_attributes;
				while(a) {
					if(!strcmp(a->a_name, "planb")) {
						/* Skip the fake planb attribute, it's for internal use only */
						a = a->a_next;
						continue;
					}
					if(a->a_value == NULL) {
						g_snprintf(buffer, 512,
							"a=%s\r\n", a->a_name);
						g_strlcat(sdp, buffer, BUFSIZE);
					} else {
						g_snprintf(buffer, 512,
							"a=%s:%s\r\n", a->a_name, a->a_value);
						g_strlcat(sdp, buffer, BUFSIZE);
					}
					a = a->a_next;
				}
			}
			/* Add last attributes, rtcp and ssrc (msid) */
			if(!planb) {
				/* Single SSRC */
				if(m->m_type == sdp_media_audio && m->m_mode != sdp_inactive && m->m_mode != sdp_recvonly) {
					g_snprintf(buffer, 512,
						"a=ssrc:%"SCNu32" cname:janusaudio\r\n"
						"a=ssrc:%"SCNu32" msid:janus janusa0\r\n"
						"a=ssrc:%"SCNu32" mslabel:janus\r\n"
						"a=ssrc:%"SCNu32" label:janusa0\r\n",
							stream->audio_ssrc, stream->audio_ssrc, stream->audio_ssrc, stream->audio_ssrc);
					g_strlcat(sdp, buffer, BUFSIZE);
				} else if(m->m_type == sdp_media_video && m->m_mode != sdp_inactive && m->m_mode != sdp_recvonly) {
					g_snprintf(buffer, 512,
						"a=ssrc:%"SCNu32" cname:janusvideo\r\n"
						"a=ssrc:%"SCNu32" msid:janus janusv0\r\n"
						"a=ssrc:%"SCNu32" mslabel:janus\r\n"
						"a=ssrc:%"SCNu32" label:janusv0\r\n",
							stream->video_ssrc, stream->video_ssrc, stream->video_ssrc, stream->video_ssrc);
					g_strlcat(sdp, buffer, BUFSIZE);
				}
			} else {
				/* Multiple SSRCs */
				char mslabel[255];
				memset(mslabel, 0, 255);
				if(m->m_attributes) {
					char id[256];
					uint32_t ssrc = 0;
					sdp_attribute_t *a = m->m_attributes;
					while(a) {
						if(a->a_name == NULL || a->a_value == NULL || strcmp(a->a_name, "planb")) {
							a = a->a_next;
							continue;
						}
						if(sscanf(a->a_value, "%255s %"SCNu32"", id, &ssrc) != 2) {
							JANUS_LOG(LOG_ERR, "Error parsing 'planb' attribute, skipping it...\n");
							a = a->a_next;
							continue;
						}
						JANUS_LOG(LOG_VERB, "Parsing 'planb' attribute: %s\n", a->a_value);
						/* Add proper SSRC attributes */
						if(m->m_type == sdp_media_audio) {
							g_snprintf(buffer, 512,
								"a=ssrc:%"SCNu32" cname:%saudio\r\n"
								"a=ssrc:%"SCNu32" msid:%s %sa0\r\n"
								"a=ssrc:%"SCNu32" mslabel:%s\r\n"
								"a=ssrc:%"SCNu32" label:%sa0\r\n",
									ssrc, id, ssrc, id, id, ssrc, id, ssrc, id);
						} else if(m->m_type == sdp_media_video) {
							g_snprintf(buffer, 512,
								"a=ssrc:%"SCNu32" cname:%svideo\r\n"
								"a=ssrc:%"SCNu32" msid:%s %sv0\r\n"
								"a=ssrc:%"SCNu32" mslabel:%s\r\n"
								"a=ssrc:%"SCNu32" label:%sv0\r\n",
									ssrc, id, ssrc, id, id, ssrc, id, ssrc, id);
						}
						g_strlcat(sdp, buffer, BUFSIZE);
						/* Add to msid-semantic, if needed */
						if(!strstr(wms, id)) {
							g_snprintf(mslabel, 255, " %s", id);
							g_strlcat(wms, mslabel, BUFSIZE);
						}
						/* Go on */
						a = a->a_next;
					}
				}
			}
			/* And now the candidates */
			janus_ice_candidates_to_sdp(handle, sdp, stream->stream_id, 1);
			if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) &&
					m->m_type != sdp_media_application)
				janus_ice_candidates_to_sdp(handle, sdp, stream->stream_id, 2);
			/* Next */
			m = m->m_next;
		}
	}

	/* Do we need to update the msid-semantic attribute? */
	if(planb) {
		sdp = janus_string_replace(sdp, "WMS janus", wms);
	}
	
	sdp_parser_free(parser);

	JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
	JANUS_LOG(LOG_VERB, "  >> Merged (%zu --> %zu bytes)\n", strlen(origsdp), strlen(sdp));
	JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
	JANUS_LOG(LOG_VERB, "%s\n", sdp);

	return sdp;
}
Example #9
0
int janus_sdp_parse_candidate(janus_ice_stream *stream, const char *candidate, int trickle) {
	if(stream == NULL || candidate == NULL)
		return -1;
	janus_ice_handle *handle = stream->handle;
	if(handle == NULL)
		return -2;
	janus_mutex_lock(&handle->mutex);
	janus_ice_component *component = NULL;
	if(strstr(candidate, "candidate:") == candidate) {
		/* Skipping the 'candidate:' prefix Firefox puts in trickle candidates */
		candidate += strlen("candidate:");
	}
	char rfoundation[32], rtransport[4], rip[40], rtype[6], rrelip[40];
	guint32 rcomponent, rpriority, rport, rrelport;
	int res = sscanf(candidate, "%31s %30u %3s %30u %39s %30u typ %5s %*s %39s %*s %30u",
		rfoundation, &rcomponent, rtransport, &rpriority,
			rip, &rport, rtype, rrelip, &rrelport);
	if(res < 7) {
		/* Failed to parse this address, can it be IPv6? */
		if(!janus_ice_is_ipv6_enabled()) {
			JANUS_LOG(LOG_WARN, "[%"SCNu64"] Received IPv6 candidate, but IPv6 support is disabled...\n", handle->handle_id);
			janus_mutex_unlock(&handle->mutex);
			return res;
		}
	}
	if(res >= 7) {
		/* Add remote candidate */
		component = g_hash_table_lookup(stream->components, GUINT_TO_POINTER(rcomponent));
		if(component == NULL) {
			if(rcomponent == 2 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX)) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Skipping component %d in stream %d (rtcp-muxing)\n", handle->handle_id, rcomponent, stream->stream_id);
			} else {
				JANUS_LOG(LOG_ERR, "[%"SCNu64"]   -- No such component %d in stream %d?\n", handle->handle_id, rcomponent, stream->stream_id);
			}
		} else {
			if(rcomponent == 2 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX)) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Skipping component %d in stream %d (rtcp-muxing)\n", handle->handle_id, rcomponent, stream->stream_id);
				janus_mutex_unlock(&handle->mutex);
				return 0;
			}
			//~ if(trickle) {
				//~ if(component->dtls != NULL) {
					//~ /* This component is already ready, ignore this further candidate */
					//~ JANUS_LOG(LOG_VERB, "[%"SCNu64"]   -- Ignoring this candidate, the component is already ready\n", handle->handle_id);
					//~ janus_mutex_unlock(&handle->mutex);
					//~ return 0;
				//~ }
			//~ }
			component->component_id = rcomponent;
			component->stream_id = stream->stream_id;
			NiceCandidate *c = NULL;
			if(!strcasecmp(rtype, "host")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Adding remote candidate component:%d stream:%d type:host %s:%d\n",
					handle->handle_id, rcomponent, stream->stream_id, rip, rport);
				/* Unless this is libnice >= 0.1.8, we only support UDP... */
				if(!strcasecmp(rtransport, "udp")) {
					c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
#ifdef HAVE_LIBNICE_TCP
				} else if(!strcasecmp(rtransport, "tcp") && janus_ice_is_ice_tcp_enabled()) {
					c = nice_candidate_new(NICE_CANDIDATE_TYPE_HOST);
#endif
				} else {
					JANUS_LOG(LOG_VERB, "[%"SCNu64"]    Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
				}
			} else if(!strcasecmp(rtype, "srflx")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Adding remote candidate component:%d stream:%d type:srflx %s:%d --> %s:%d \n",
					handle->handle_id, rcomponent, stream->stream_id,  rrelip, rrelport, rip, rport);
				/* Unless this is libnice >= 0.1.8, we only support UDP... */
				if(!strcasecmp(rtransport, "udp")) {
					c = nice_candidate_new(NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
#ifdef HAVE_LIBNICE_TCP
				} else if(!strcasecmp(rtransport, "tcp") && janus_ice_is_ice_tcp_enabled()) {
					c = nice_candidate_new(NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
#endif
				} else {
					JANUS_LOG(LOG_VERB, "[%"SCNu64"]    Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
				}
			} else if(!strcasecmp(rtype, "prflx")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Adding remote candidate component:%d stream:%d type:prflx %s:%d --> %s:%d\n",
					handle->handle_id, rcomponent, stream->stream_id, rrelip, rrelport, rip, rport);
				/* Unless this is libnice >= 0.1.8, we only support UDP... */
				if(!strcasecmp(rtransport, "udp")) {
					c = nice_candidate_new(NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
#ifdef HAVE_LIBNICE_TCP
				} else if(!strcasecmp(rtransport, "tcp") && janus_ice_is_ice_tcp_enabled()) {
					c = nice_candidate_new(NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
#endif
				} else {
					JANUS_LOG(LOG_VERB, "[%"SCNu64"]    Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
				}
			} else if(!strcasecmp(rtype, "relay")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Adding remote candidate component:%d stream:%d type:relay %s:%d --> %s:%d\n",
					handle->handle_id, rcomponent, stream->stream_id, rrelip, rrelport, rip, rport);
				/* We only support UDP/TCP/TLS... */
				if(strcasecmp(rtransport, "udp") && strcasecmp(rtransport, "tcp") && strcasecmp(rtransport, "tls")) {
					JANUS_LOG(LOG_VERB, "[%"SCNu64"]    Skipping unsupported transport '%s' for media\n", handle->handle_id, rtransport);
				} else {
					c = nice_candidate_new(NICE_CANDIDATE_TYPE_RELAYED);
				}
			} else {
				/* FIXME What now? */
				JANUS_LOG(LOG_ERR, "[%"SCNu64"]  Unknown remote candidate type:%s for component:%d stream:%d!\n",
					handle->handle_id, rtype, rcomponent, stream->stream_id);
			}
			if(c != NULL) {
				c->component_id = rcomponent;
				c->stream_id = stream->stream_id;
#ifndef HAVE_LIBNICE_TCP
				c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
#else
				if(!strcasecmp(rtransport, "udp")) {
					JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Transport: UDP\n", handle->handle_id);
					c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
				} else {
					/* Check the type (https://tools.ietf.org/html/rfc6544#section-4.5) */
					const char *type = NULL;
					int ctype = 0;
					if(strstr(candidate, "tcptype active")) {
						type = "active";
						ctype = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
					} else if(strstr(candidate, "tcptype passive")) {
						type = "passive";
						ctype = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
					} else if(strstr(candidate, "tcptype so")) {
						type = "so";
						ctype = NICE_CANDIDATE_TRANSPORT_TCP_SO;
					} else {
						/* TODO: We should actually stop here... */
						JANUS_LOG(LOG_ERR, "[%"SCNu64"] Missing tcptype info for the TCP candidate!\n", handle->handle_id);
					}
					JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Transport: TCP (%s)\n", handle->handle_id, type);
					c->transport = ctype;
				}
#endif
				strncpy(c->foundation, rfoundation, NICE_CANDIDATE_MAX_FOUNDATION);
				c->priority = rpriority;
				nice_address_set_from_string(&c->addr, rip);
				nice_address_set_port(&c->addr, rport);
				c->username = g_strdup(stream->ruser);
				c->password = g_strdup(stream->rpass);
				if(c->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE || c->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE) {
					nice_address_set_from_string(&c->base_addr, rrelip);
					nice_address_set_port(&c->base_addr, rrelport);
				} else if(c->type == NICE_CANDIDATE_TYPE_RELAYED) {
					/* FIXME Do we really need the base address for TURN? */
					nice_address_set_from_string(&c->base_addr, rrelip);
					nice_address_set_port(&c->base_addr, rrelport);
				}
				component->candidates = g_slist_append(component->candidates, c);
				JANUS_LOG(LOG_HUGE, "[%"SCNu64"]    Candidate added to the list! (%u elements for %d/%d)\n", handle->handle_id,
					g_slist_length(component->candidates), stream->stream_id, component->component_id);
				/* Save for the summary, in case we need it */
				component->remote_candidates = g_slist_append(component->remote_candidates, g_strdup(candidate));
				if(trickle) {
					if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START)) {
						/* This is a trickle candidate and ICE has started, we should process it right away */
						if(!component->process_started) {
							/* Actually, ICE has JUST started for this component, take care of the candidates we've added so far */
							JANUS_LOG(LOG_INFO, "[%"SCNu64"] ICE already started for this component, setting candidates we have up to now\n", handle->handle_id);
							janus_ice_setup_remote_candidates(handle, component->stream_id, component->component_id);
						} else {
							GSList *candidates = NULL;
							candidates = g_slist_append(candidates, c);
							if(nice_agent_set_remote_candidates(handle->agent, stream->stream_id, component->component_id, candidates) < 1) {
								JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to add trickle candidate :-(\n", handle->handle_id);
							} else {
								JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Trickle candidate added!\n", handle->handle_id);
							}
							g_slist_free(candidates);
						}
					} else {
						/* ICE hasn't started yet: to make sure we're not stuck, also check if we stopped processing the SDP */
						if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER)) {
							janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START);
							/* This is a trickle candidate and ICE has started, we should process it right away */
							if(!component->process_started) {
								/* Actually, ICE has JUST started for this component, take care of the candidates we've added so far */
								JANUS_LOG(LOG_INFO, "[%"SCNu64"] SDP processed but ICE not started yet for this component, setting candidates we have up to now\n", handle->handle_id);
								janus_ice_setup_remote_candidates(handle, component->stream_id, component->component_id);
							} else {
								GSList *candidates = NULL;
								candidates = g_slist_append(candidates, c);
								if(nice_agent_set_remote_candidates(handle->agent, stream->stream_id, component->component_id, candidates) < 1) {
									JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to add trickle candidate :-(\n", handle->handle_id);
								} else {
									JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Trickle candidate added!\n", handle->handle_id);
								}
								g_slist_free(candidates);
							}
						} else {
							/* Still processing the offer/answer: queue the trickle candidate for now, we'll process it later */
							JANUS_LOG(LOG_VERB, "[%"SCNu64"] Queueing trickle candidate, status is not START yet\n", handle->handle_id);
						}
					}
				}
			}
		}
	} else {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse candidate (res=%d)...\n", handle->handle_id, res);
		janus_mutex_unlock(&handle->mutex);
		return res;
	}
	janus_mutex_unlock(&handle->mutex);
	return 0;
}
Example #10
0
/* Parse SDP */
int janus_sdp_parse(janus_ice_handle *handle, janus_sdp *sdp) {
	if(!handle || !sdp)
		return -1;
	sdp_session_t *remote_sdp = (sdp_session_t *)sdp->sdp;
	if(!remote_sdp)
		return -1;
	janus_ice_stream *stream = NULL;
	gchar *ruser = NULL, *rpass = NULL, *rhashing = NULL, *rfingerprint = NULL;
	int audio = 0, video = 0;
#ifdef HAVE_SCTP
	int data = 0;
#endif
	/* Ok, let's start */
	sdp_attribute_t *a = remote_sdp->sdp_attributes;
	while(a) {
		if(a->a_name) {
			if(!strcasecmp(a->a_name, "fingerprint")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint (global) : %s\n", handle->handle_id, a->a_value);
				if(strcasestr(a->a_value, "sha-256 ") == a->a_value) {
					rhashing = g_strdup("sha-256");
					rfingerprint = g_strdup(a->a_value + strlen("sha-256 "));
				} else if(strcasestr(a->a_value, "sha-1 ") == a->a_value) {
					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);
					rhashing = g_strdup("sha-1");
					rfingerprint = g_strdup(a->a_value + strlen("sha-1 "));
				} else {
					/* FIXME We should handle this somehow anyway... OpenSSL supports them all */
					JANUS_LOG(LOG_WARN, "[%"SCNu64"]  Hashing algorithm not the one we expected (sha-256/sha-1), *NOT* cool\n", handle->handle_id);
				}
			} else if(!strcasecmp(a->a_name, "ice-ufrag")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE ufrag (global):   %s\n", handle->handle_id, a->a_value);
				ruser = g_strdup(a->a_value);
			} else if(!strcasecmp(a->a_name, "ice-pwd")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE pwd (global):     %s\n", handle->handle_id, a->a_value);
				rpass = g_strdup(a->a_value);
			}
		}
		a = a->a_next;
	}
	sdp_media_t *m = remote_sdp->sdp_media;
	while(m) {
		/* What media type is this? */
		if(m->m_type == sdp_media_audio) {
			if(m->m_port > 0) {
				audio++;
				if(audio > 1) {
					m = m->m_next;
					continue;
				}
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing audio candidates (stream=%d)...\n", handle->handle_id, handle->audio_id);
				stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id));
			} else {
				/* Audio rejected? */
				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio rejected by peer...\n", handle->handle_id);
			}
		} else if(m->m_type == sdp_media_video) {
			if(m->m_port > 0) {
				video++;
				if(video > 1) {
					m = m->m_next;
					continue;
				}
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing video candidates (stream=%d)...\n", handle->handle_id, handle->video_id);
				if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
					stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->video_id));
				} else {
					gint id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
					stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
				}
			} else {
				/* Video rejected? */
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video rejected by peer...\n", handle->handle_id);
				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);
			}
#ifdef HAVE_SCTP
		} else if(m->m_type == sdp_media_application) {
			/* Is this SCTP for DataChannels? */
			if(m->m_proto_name != NULL && !strcasecmp(m->m_proto_name, "DTLS/SCTP")) {
				if(m->m_port > 0) {
					/* Yep */
					data++;
					if(data > 1) {
						m = m->m_next;
						continue;
					}
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing SCTP candidates (stream=%d)...\n", handle->handle_id, handle->video_id);
					if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
						stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->data_id));
					} else {
						gint id = handle->audio_id > 0 ? handle->audio_id : (handle->video_id > 0 ? handle->video_id : handle->data_id);
						stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
					}
					if(stream == NULL) {
						JANUS_LOG(LOG_WARN, "No valid stream for data??\n");
						continue;
					}
				}
			} else {
				/* Data channels rejected? */
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data channels rejected by peer...\n", handle->handle_id);
				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS);
			}
#endif
		} else {
			JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping disabled/unsupported media line...\n", handle->handle_id);
			m = m->m_next;
			continue;
		}
		/* Look for mid, ICE credentials and fingerprint first: check media attributes */
		a = m->m_attributes;
		while(a) {
			if(a->a_name) {
				if(!strcasecmp(a->a_name, "mid")) {
					/* Found mid attribute */
					if(m->m_type == sdp_media_audio && m->m_port > 0) {
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio mid: %s\n", handle->handle_id, a->a_value);
						handle->audio_mid = g_strdup(a->a_value);
					} else if(m->m_type == sdp_media_video && m->m_port > 0) {
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video mid: %s\n", handle->handle_id, a->a_value);
						handle->video_mid = g_strdup(a->a_value);
#ifdef HAVE_SCTP
					} else if(m->m_type == sdp_media_application) {
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data Channel mid: %s\n", handle->handle_id, a->a_value);
						handle->data_mid = g_strdup(a->a_value);
#endif
					}
				} else if(!strcasecmp(a->a_name, "fingerprint")) {
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint (local) : %s\n", handle->handle_id, a->a_value);
					if(strcasestr(a->a_value, "sha-256 ") == a->a_value) {
						if(rhashing)
							g_free(rhashing);	/* FIXME We're overwriting the global one, if any */
						rhashing = g_strdup("sha-256");
						if(rfingerprint)
							g_free(rfingerprint);	/* FIXME We're overwriting the global one, if any */
						rfingerprint = g_strdup(a->a_value + strlen("sha-256 "));
					} else if(strcasestr(a->a_value, "sha-1 ") == a->a_value) {
						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);
						if(rhashing)
							g_free(rhashing);	/* FIXME We're overwriting the global one, if any */
						rhashing = g_strdup("sha-1");
						if(rfingerprint)
							g_free(rfingerprint);	/* FIXME We're overwriting the global one, if any */
						rfingerprint = g_strdup(a->a_value + strlen("sha-1 "));
					} else {
						/* FIXME We should handle this somehow anyway... OpenSSL supports them all */
						JANUS_LOG(LOG_WARN, "[%"SCNu64"]  Hashing algorithm not the one we expected (sha-256), *NOT* cool\n", handle->handle_id);
					}
				} else if(!strcasecmp(a->a_name, "setup")) {
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS setup (local):  %s\n", handle->handle_id, a->a_value);
					if(!strcasecmp(a->a_value, "actpass") || !strcasecmp(a->a_value, "passive"))
						stream->dtls_role = JANUS_DTLS_ROLE_CLIENT;
					else if(!strcasecmp(a->a_value, "active"))
						stream->dtls_role = JANUS_DTLS_ROLE_SERVER;
					/* TODO Handle holdconn... */
				} else if(!strcasecmp(a->a_name, "ice-ufrag")) {
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE ufrag (local):   %s\n", handle->handle_id, a->a_value);
					if(ruser)
						g_free(ruser);	/* FIXME We're overwriting the global one, if any */
					ruser = g_strdup(a->a_value);
				} else if(!strcasecmp(a->a_name, "ice-pwd")) {
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE pwd (local):     %s\n", handle->handle_id, a->a_value);
					if(rpass)
						g_free(rpass);	/* FIXME We're overwriting the global one, if any */
					rpass = g_strdup(a->a_value);
				}
			}
			a = a->a_next;
		}
		if(!ruser || !rpass || !rfingerprint || !rhashing) {
			/* Missing mandatory information, failure... */
			if(ruser)
				g_free(ruser);
			ruser = NULL;
			if(rpass)
				g_free(rpass);
			rpass = NULL;
			if(rhashing)
				g_free(rhashing);
			rhashing = NULL;
			if(rfingerprint)
				g_free(rfingerprint);
			rfingerprint = NULL;
			return -2;
		}
		/* FIXME We're replacing the fingerprint info, assuming it's going to be the same for all media */
		if(handle->remote_hashing != NULL)
			g_free(handle->remote_hashing);
		handle->remote_hashing = g_strdup(rhashing);
		if(handle->remote_fingerprint != NULL)
			g_free(handle->remote_fingerprint);
		handle->remote_fingerprint = g_strdup(rfingerprint);
		/* Store the ICE username and password for this stream */
		if(stream->ruser != NULL)
			g_free(stream->ruser);
		stream->ruser = g_strdup(ruser);
		if(stream->rpass != NULL)
			g_free(stream->rpass);
		stream->rpass = g_strdup(rpass);
		/* Now look for candidates and other info */
		a = m->m_attributes;
		while(a) {
			if(a->a_name) {
				if(!strcasecmp(a->a_name, "candidate")) {
					if(m->m_type == sdp_media_video && handle->audio_id > 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] This is a video candidate but we're bundling, ignoring...\n", handle->handle_id);
#ifdef HAVE_SCTP
					} else if(m->m_type == sdp_media_application && (handle->audio_id > 0 || handle->video_id > 0) && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] This is a SCTP candidate but we're bundling, ignoring...\n", handle->handle_id);
#endif
					} else {
						int res = janus_sdp_parse_candidate(stream, (const char *)a->a_value, 0);
						if(res != 0) {
							JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse candidate... (%d)\n", handle->handle_id, res);
						}
					}
				}
				if(!strcasecmp(a->a_name, "ssrc")) {
					int res = janus_sdp_parse_ssrc(stream, (const char *)a->a_value, m->m_type == sdp_media_video);
					if(res != 0) {
						JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse SSRC attribute... (%d)\n", handle->handle_id, res);
					}
				}
#ifdef HAVE_SCTP
				if(!strcasecmp(a->a_name, "sctpmap")) {
					/* TODO Parse sctpmap line to get the UDP-port value and the number of channels */
					JANUS_LOG(LOG_VERB, "Got a sctpmap attribute: %s\n", a->a_value);
				}
#endif
			}
			a = a->a_next;
		}
		m = m->m_next;
	}
	if(ruser)
		g_free(ruser);
	ruser = NULL;
	if(rpass)
		g_free(rpass);
	rpass = NULL;
	if(rhashing)
		g_free(rhashing);
	rhashing = NULL;
	if(rfingerprint)
		g_free(rfingerprint);
	rfingerprint = NULL;
	return 0;	/* FIXME Handle errors better */
}
Example #11
0
void janus_dtls_srtp_incoming_msg(janus_dtls_srtp *dtls, char *buf, uint16_t len) {
	if(dtls == NULL) {
		JANUS_LOG(LOG_ERR, "No DTLS-SRTP stack, no incoming message...\n");
		return;
	}
	janus_ice_component *component = (janus_ice_component *)dtls->component;
	if(component == NULL) {
		JANUS_LOG(LOG_ERR, "No component, no DTLS...\n");
		return;
	}
	janus_ice_stream *stream = component->stream;
	if(!stream) {
		JANUS_LOG(LOG_ERR, "No stream, no DTLS...\n");
		return;
	}
	janus_ice_handle *handle = stream->handle;
	if(!handle || !handle->agent) {
		JANUS_LOG(LOG_ERR, "No handle/agent, no DTLS...\n");
		return;
	}
	if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
		JANUS_LOG(LOG_ERR, "Alert already received, clearing up...\n");
		return;
	}
	if(!dtls->ssl || !dtls->read_bio) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] No DTLS stuff for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id);
		return;
	}
	/* We just got a message, can we get rid of the last sent message? */
	if(dtls->dtls_last_msg != NULL) {
		g_free(dtls->dtls_last_msg);
		dtls->dtls_last_msg = NULL;
		dtls->dtls_last_len = 0;
	}
	janus_dtls_fd_bridge(dtls);
	int written = BIO_write(dtls->read_bio, buf, len);
	JANUS_LOG(LOG_HUGE, "    Written %d of those bytes on the read BIO...\n", written);
	janus_dtls_fd_bridge(dtls);
	int read = SSL_read(dtls->ssl, buf, len);
	JANUS_LOG(LOG_HUGE, "    ...and read %d of them from SSL...\n", read);
	janus_dtls_fd_bridge(dtls);
	if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP) || janus_is_stopping()) {
		/* DTLS alert received, we should end it here */
		JANUS_LOG(LOG_VERB, "[%"SCNu64"] Forced to stop it here...\n", handle->handle_id);
		return;
	}
	if(SSL_is_init_finished(dtls->ssl)) {
		JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS established, yay!\n", handle->handle_id);
		/* Check the remote fingerprint */
		X509 *rcert = SSL_get_peer_certificate(dtls->ssl);
		if(!rcert) {
			JANUS_LOG(LOG_ERR, "[%"SCNu64"] No remote certificate??\n", handle->handle_id);
		} else {
			unsigned int rsize;
			unsigned char rfingerprint[EVP_MAX_MD_SIZE];
			char remote_fingerprint[160];
			char *rfp = (char *)&remote_fingerprint;
			if(handle->remote_hashing && !strcasecmp(handle->remote_hashing, "sha-1")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-1 fingerprint of remote certificate...\n", handle->handle_id);
				X509_digest(rcert, EVP_sha1(), (unsigned char *)rfingerprint, &rsize);
			} else {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-256 fingerprint of remote certificate...\n", handle->handle_id);
				X509_digest(rcert, EVP_sha256(), (unsigned char *)rfingerprint, &rsize);
			}
			X509_free(rcert);
			rcert = NULL;
			int i = 0;
			for(i = 0; i < rsize; i++) {
				sprintf(rfp, "%.2X:", rfingerprint[i]);
				rfp += 3;
			}
			*(rfp-1) = 0;
			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Remote fingerprint (%s) of the client is %s\n",
				handle->handle_id, handle->remote_hashing ? handle->remote_hashing : "sha-256", remote_fingerprint);
			if(!strcasecmp(remote_fingerprint, handle->remote_fingerprint ? handle->remote_fingerprint : "(none)")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Fingerprint is a match!\n", handle->handle_id);
				dtls->dtls_state = JANUS_DTLS_STATE_CONNECTED;
			} else {
				/* FIXME NOT a match! MITM? */
				JANUS_LOG(LOG_ERR, "[%"SCNu64"]  Fingerprint is NOT a match! expected %s\n", handle->handle_id, handle->remote_fingerprint);
				dtls->dtls_state = JANUS_DTLS_STATE_FAILED;
				goto done;
			}
			if(dtls->dtls_state == JANUS_DTLS_STATE_CONNECTED) {
				/* Complete with SRTP setup */
				unsigned char material[SRTP_MASTER_LENGTH*2];
				unsigned char *local_key, *local_salt, *remote_key, *remote_salt;
				/* Export keying material for SRTP */
				if (!SSL_export_keying_material(dtls->ssl, material, SRTP_MASTER_LENGTH*2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
					/* Oops... */
					JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, couldn't extract SRTP keying material for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id);
					goto done;
				}
				/* Key derivation (http://tools.ietf.org/html/rfc5764#section-4.2) */
				if(dtls->dtls_role == JANUS_DTLS_ROLE_CLIENT) {
					local_key = material;
					remote_key = local_key + SRTP_MASTER_KEY_LENGTH;
					local_salt = remote_key + SRTP_MASTER_KEY_LENGTH;
					remote_salt = local_salt + SRTP_MASTER_SALT_LENGTH;
				} else {
					remote_key = material;
					local_key = remote_key + SRTP_MASTER_KEY_LENGTH;
					remote_salt = local_key + SRTP_MASTER_KEY_LENGTH;
					local_salt = remote_salt + SRTP_MASTER_SALT_LENGTH;
				}
				/* Build master keys and set SRTP policies */
					/* Remote (inbound) */
				crypto_policy_set_rtp_default(&(dtls->remote_policy.rtp));
				crypto_policy_set_rtcp_default(&(dtls->remote_policy.rtcp));
				dtls->remote_policy.ssrc.type = ssrc_any_inbound;
				dtls->remote_policy.key = calloc(SRTP_MASTER_LENGTH+8, sizeof(char));
				if(dtls->remote_policy.key == NULL) {
					JANUS_LOG(LOG_FATAL, "Memory error!\n");
					goto done;
				}
				memcpy(dtls->remote_policy.key, remote_key, SRTP_MASTER_KEY_LENGTH);
				memcpy(dtls->remote_policy.key + SRTP_MASTER_KEY_LENGTH, remote_salt, SRTP_MASTER_SALT_LENGTH);
				dtls->remote_policy.window_size = 128;
				dtls->remote_policy.allow_repeat_tx = 0;
				dtls->remote_policy.next = NULL;
					/* Local (outbound) */
				crypto_policy_set_rtp_default(&(dtls->local_policy.rtp));
				crypto_policy_set_rtcp_default(&(dtls->local_policy.rtcp));
				dtls->local_policy.ssrc.type = ssrc_any_outbound;
				dtls->local_policy.key = calloc(SRTP_MASTER_LENGTH+8, sizeof(char));
				if(dtls->local_policy.key == NULL) {
					JANUS_LOG(LOG_FATAL, "Memory error!\n");
					goto done;
				}
				memcpy(dtls->local_policy.key, local_key, SRTP_MASTER_KEY_LENGTH);
				memcpy(dtls->local_policy.key + SRTP_MASTER_KEY_LENGTH, local_salt, SRTP_MASTER_SALT_LENGTH);
				dtls->local_policy.window_size = 128;
				dtls->local_policy.allow_repeat_tx = 0;
				dtls->local_policy.next = NULL;
				/* Create SRTP sessions */
				err_status_t res = srtp_create(&(dtls->srtp_in), &(dtls->remote_policy));
				if(res != err_status_ok) {
					/* Something went wrong... */
					JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating inbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id);
					JANUS_LOG(LOG_ERR, "[%"SCNu64"]  -- %d (%s)\n", handle->handle_id, res, janus_get_srtp_error(res));
					goto done;
				}
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created inbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id);
				res = srtp_create(&(dtls->srtp_out), &(dtls->local_policy));
				if(res != err_status_ok) {
					/* Something went wrong... */
					JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating outbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id);
					JANUS_LOG(LOG_ERR, "[%"SCNu64"]  -- %d (%s)\n", handle->handle_id, res, janus_get_srtp_error(res));
					goto done;
				}
				dtls->srtp_valid = 1;
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created outbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id);
			}
done:
			if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT) && dtls->srtp_valid) {
				/* Handshake successfully completed */
				janus_ice_dtls_handshake_done(handle, component);
			} else {
				/* Something went wrong in either DTLS or SRTP... tell the plugin about it */
				janus_dtls_callback(dtls->ssl, SSL_CB_ALERT, 0);
			}
		}
	}
}
Example #12
0
void janus_dtls_srtp_incoming_msg(janus_dtls_srtp *dtls, char *buf, uint16_t len) {
	if(dtls == NULL) {
		JANUS_LOG(LOG_ERR, "No DTLS-SRTP stack, no incoming message...\n");
		return;
	}
	janus_ice_component *component = (janus_ice_component *)dtls->component;
	if(component == NULL) {
		JANUS_LOG(LOG_ERR, "No component, no DTLS...\n");
		return;
	}
	janus_ice_stream *stream = component->stream;
	if(!stream) {
		JANUS_LOG(LOG_ERR, "No stream, no DTLS...\n");
		return;
	}
	janus_ice_handle *handle = stream->handle;
	if(!handle || !handle->agent) {
		JANUS_LOG(LOG_ERR, "No handle/agent, no DTLS...\n");
		return;
	}
	if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
		JANUS_LOG(LOG_WARN, "[%"SCNu64"] Alert already triggered, clearing up...\n", handle->handle_id);
		return;
	}
	if(!dtls->ssl || !dtls->read_bio) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] No DTLS stuff for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id);
		return;
	}
	if(dtls->dtls_started == 0) {
		/* Handshake not started yet: maybe we're still waiting for the answer and the DTLS role? */
		return;
	}
	janus_dtls_fd_bridge(dtls);
	int written = BIO_write(dtls->read_bio, buf, len);
	if(written != len) {
		JANUS_LOG(LOG_WARN, "[%"SCNu64"]     Only written %d/%d of those bytes on the read BIO...\n", handle->handle_id, written, len);
	} else {
		JANUS_LOG(LOG_HUGE, "[%"SCNu64"]     Written %d bytes on the read BIO...\n", handle->handle_id, written);
	}
	janus_dtls_fd_bridge(dtls);
	/* Try to read data */
	char data[1500];	/* FIXME */
	memset(&data, 0, 1500);
	int read = SSL_read(dtls->ssl, &data, 1500);
	JANUS_LOG(LOG_HUGE, "[%"SCNu64"]     ... and read %d of them from SSL...\n", handle->handle_id, read);
	if(read < 0) {
		unsigned long err = SSL_get_error(dtls->ssl, read);
		if(err == SSL_ERROR_SSL) {
			/* Ops, something went wrong with the DTLS handshake */
			char error[200];
			ERR_error_string_n(ERR_get_error(), error, 200);
			JANUS_LOG(LOG_ERR, "[%"SCNu64"] Handshake error: %s\n", handle->handle_id, error);
			return;
		}
	}
	janus_dtls_fd_bridge(dtls);
	if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP) || janus_is_stopping()) {
		/* DTLS alert triggered, we should end it here */
		JANUS_LOG(LOG_VERB, "[%"SCNu64"] Forced to stop it here...\n", handle->handle_id);
		return;
	}
	if(!SSL_is_init_finished(dtls->ssl)) {
		/* Nothing else to do for now */
		JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Initialization not finished yet...\n", handle->handle_id);
		return;
	}
	if(dtls->ready) {
		/* There's data to be read? */
		JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Any data available?\n", handle->handle_id);
#ifdef HAVE_SCTP
		if(dtls->sctp != NULL && read > 0) {
			JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Sending data (%d bytes) to the SCTP stack...\n", handle->handle_id, read);
			janus_sctp_data_from_dtls(dtls->sctp, data, read);
		}
#else
		if(read > 0) {
			JANUS_LOG(LOG_WARN, "[%"SCNu64"] Data available but Data Channels support disabled...\n", handle->handle_id);
		}
#endif
	} else {
		JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS established, yay!\n", handle->handle_id);
		/* Check the remote fingerprint */
		X509 *rcert = SSL_get_peer_certificate(dtls->ssl);
		if(!rcert) {
			JANUS_LOG(LOG_ERR, "[%"SCNu64"] No remote certificate?? (%s)\n",
				handle->handle_id, ERR_reason_error_string(ERR_get_error()));
		} else {
			unsigned int rsize;
			unsigned char rfingerprint[EVP_MAX_MD_SIZE];
			char remote_fingerprint[160];
			char *rfp = (char *)&remote_fingerprint;
			if(stream->remote_hashing && !strcasecmp(stream->remote_hashing, "sha-1")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-1 fingerprint of remote certificate...\n", handle->handle_id);
				X509_digest(rcert, EVP_sha1(), (unsigned char *)rfingerprint, &rsize);
			} else {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-256 fingerprint of remote certificate...\n", handle->handle_id);
				X509_digest(rcert, EVP_sha256(), (unsigned char *)rfingerprint, &rsize);
			}
			X509_free(rcert);
			rcert = NULL;
			unsigned int i = 0;
			for(i = 0; i < rsize; i++) {
				g_snprintf(rfp, 4, "%.2X:", rfingerprint[i]);
				rfp += 3;
			}
			*(rfp-1) = 0;
			JANUS_LOG(LOG_VERB, "[%"SCNu64"] Remote fingerprint (%s) of the client is %s\n",
				handle->handle_id, stream->remote_hashing ? stream->remote_hashing : "sha-256", remote_fingerprint);
			if(!strcasecmp(remote_fingerprint, stream->remote_fingerprint ? stream->remote_fingerprint : "(none)")) {
				JANUS_LOG(LOG_VERB, "[%"SCNu64"]  Fingerprint is a match!\n", handle->handle_id);
				dtls->dtls_state = JANUS_DTLS_STATE_CONNECTED;
				dtls->dtls_connected = janus_get_monotonic_time();
				/* Notify event handlers */
				janus_dtls_notify_state_change(dtls);
			} else {
				/* FIXME NOT a match! MITM? */
				JANUS_LOG(LOG_ERR, "[%"SCNu64"]  Fingerprint is NOT a match! got %s, expected %s\n", handle->handle_id, remote_fingerprint, stream->remote_fingerprint);
				dtls->dtls_state = JANUS_DTLS_STATE_FAILED;
				/* Notify event handlers */
				janus_dtls_notify_state_change(dtls);
				goto done;
			}
			if(dtls->dtls_state == JANUS_DTLS_STATE_CONNECTED) {
				/* Which SRTP profile is being negotiated? */
				SRTP_PROTECTION_PROFILE *srtp_profile = SSL_get_selected_srtp_profile(dtls->ssl);
				if(srtp_profile == NULL) {
					/* Should never happen, but just in case... */
					JANUS_LOG(LOG_ERR, "[%"SCNu64"] No SRTP profile selected...\n", handle->handle_id);
					dtls->dtls_state = JANUS_DTLS_STATE_FAILED;
					/* Notify event handlers */
					janus_dtls_notify_state_change(dtls);
					goto done;
				}
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] %s\n", handle->handle_id, srtp_profile->name);
				int key_length = 0, salt_length = 0, master_length = 0;
				switch(srtp_profile->id) {
					case SRTP_AES128_CM_SHA1_80:
					case SRTP_AES128_CM_SHA1_32:
						key_length = SRTP_MASTER_KEY_LENGTH;
						salt_length = SRTP_MASTER_SALT_LENGTH;
						master_length = SRTP_MASTER_LENGTH;
						break;
#ifdef HAVE_SRTP_AESGCM
					case SRTP_AEAD_AES_256_GCM:
						key_length = SRTP_AESGCM256_MASTER_KEY_LENGTH;
						salt_length = SRTP_AESGCM256_MASTER_SALT_LENGTH;
						master_length = SRTP_AESGCM256_MASTER_LENGTH;
						break;
					case SRTP_AEAD_AES_128_GCM:
						key_length = SRTP_AESGCM128_MASTER_KEY_LENGTH;
						salt_length = SRTP_AESGCM128_MASTER_SALT_LENGTH;
						master_length = SRTP_AESGCM128_MASTER_LENGTH;
						break;
#endif
					default:
						/* Will never happen? */
						JANUS_LOG(LOG_WARN, "[%"SCNu64"] Unsupported SRTP profile %lu\n", handle->handle_id, srtp_profile->id);
						break;
				}
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Key/Salt/Master: %d/%d/%d\n",
					handle->handle_id, master_length, key_length, salt_length);
				/* Complete with SRTP setup */
				unsigned char material[master_length*2];
				unsigned char *local_key, *local_salt, *remote_key, *remote_salt;
				/* Export keying material for SRTP */
				if(!SSL_export_keying_material(dtls->ssl, material, master_length*2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
					/* Oops... */
					JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, couldn't extract SRTP keying material for component %d in stream %d?? (%s)\n",
						handle->handle_id, component->component_id, stream->stream_id, ERR_reason_error_string(ERR_get_error()));
					goto done;
				}
				/* Key derivation (http://tools.ietf.org/html/rfc5764#section-4.2) */
				if(dtls->dtls_role == JANUS_DTLS_ROLE_CLIENT) {
					local_key = material;
					remote_key = local_key + key_length;
					local_salt = remote_key + key_length;
					remote_salt = local_salt + salt_length;
				} else {
					remote_key = material;
					local_key = remote_key + key_length;
					remote_salt = local_key + key_length;
					local_salt = remote_salt + salt_length;
				}
				/* Build master keys and set SRTP policies */
					/* Remote (inbound) */
				switch(srtp_profile->id) {
					case SRTP_AES128_CM_SHA1_80:
						srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->remote_policy.rtp));
						srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->remote_policy.rtcp));
						break;
					case SRTP_AES128_CM_SHA1_32:
						srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&(dtls->remote_policy.rtp));
						srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->remote_policy.rtcp));
						break;
#ifdef HAVE_SRTP_AESGCM
					case SRTP_AEAD_AES_256_GCM:
						srtp_crypto_policy_set_aes_gcm_256_16_auth(&(dtls->remote_policy.rtp));
						srtp_crypto_policy_set_aes_gcm_256_16_auth(&(dtls->remote_policy.rtcp));
						break;
					case SRTP_AEAD_AES_128_GCM:
						srtp_crypto_policy_set_aes_gcm_128_16_auth(&(dtls->remote_policy.rtp));
						srtp_crypto_policy_set_aes_gcm_128_16_auth(&(dtls->remote_policy.rtcp));
						break;
#endif
					default:
						/* Will never happen? */
						JANUS_LOG(LOG_WARN, "[%"SCNu64"] Unsupported SRTP profile %s\n", handle->handle_id, srtp_profile->name);
						break;
				}
				dtls->remote_policy.ssrc.type = ssrc_any_inbound;
				unsigned char remote_policy_key[master_length];
				dtls->remote_policy.key = (unsigned char *)&remote_policy_key;
				memcpy(dtls->remote_policy.key, remote_key, key_length);
				memcpy(dtls->remote_policy.key + key_length, remote_salt, salt_length);
#if HAS_DTLS_WINDOW_SIZE
				dtls->remote_policy.window_size = 128;
				dtls->remote_policy.allow_repeat_tx = 0;
#endif
				dtls->remote_policy.next = NULL;
					/* Local (outbound) */
				switch(srtp_profile->id) {
					case SRTP_AES128_CM_SHA1_80:
						srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->local_policy.rtp));
						srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->local_policy.rtcp));
						break;
					case SRTP_AES128_CM_SHA1_32:
						srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&(dtls->local_policy.rtp));
						srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->local_policy.rtcp));
						break;
#ifdef HAVE_SRTP_AESGCM
					case SRTP_AEAD_AES_256_GCM:
						srtp_crypto_policy_set_aes_gcm_256_16_auth(&(dtls->local_policy.rtp));
						srtp_crypto_policy_set_aes_gcm_256_16_auth(&(dtls->local_policy.rtcp));
						break;
					case SRTP_AEAD_AES_128_GCM:
						srtp_crypto_policy_set_aes_gcm_128_16_auth(&(dtls->local_policy.rtp));
						srtp_crypto_policy_set_aes_gcm_128_16_auth(&(dtls->local_policy.rtcp));
						break;
#endif
					default:
						/* Will never happen? */
						JANUS_LOG(LOG_WARN, "[%"SCNu64"] Unsupported SRTP profile %s\n", handle->handle_id, srtp_profile->name);
						break;
				}
				dtls->local_policy.ssrc.type = ssrc_any_outbound;
				unsigned char local_policy_key[master_length];
				dtls->local_policy.key = (unsigned char *)&local_policy_key;
				memcpy(dtls->local_policy.key, local_key, key_length);
				memcpy(dtls->local_policy.key + key_length, local_salt, salt_length);
#if HAS_DTLS_WINDOW_SIZE
				dtls->local_policy.window_size = 128;
				dtls->local_policy.allow_repeat_tx = 0;
#endif
				dtls->local_policy.next = NULL;
				/* Create SRTP sessions */
				srtp_err_status_t res = srtp_create(&(dtls->srtp_in), &(dtls->remote_policy));
				if(res != srtp_err_status_ok) {
					/* Something went wrong... */
					JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating inbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id);
					JANUS_LOG(LOG_ERR, "[%"SCNu64"]  -- %d (%s)\n", handle->handle_id, res, janus_srtp_error_str(res));
					goto done;
				}
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created inbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id);
				res = srtp_create(&(dtls->srtp_out), &(dtls->local_policy));
				if(res != srtp_err_status_ok) {
					/* Something went wrong... */
					JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating outbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id);
					JANUS_LOG(LOG_ERR, "[%"SCNu64"]  -- %d (%s)\n", handle->handle_id, res, janus_srtp_error_str(res));
					goto done;
				}
				dtls->srtp_profile = srtp_profile->id;
				dtls->srtp_valid = 1;
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created outbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id);
#ifdef HAVE_SCTP
				if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS)) {
					/* Create SCTP association as well */
					janus_dtls_srtp_create_sctp(dtls);
				}
#endif
				dtls->ready = 1;
			}
done:
			if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT) && dtls->srtp_valid) {
				/* Handshake successfully completed */
				janus_ice_dtls_handshake_done(handle, component);
			} else {
				/* Something went wrong in either DTLS or SRTP... tell the plugin about it */
				janus_dtls_callback(dtls->ssl, SSL_CB_ALERT, 0);
				janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING);
			}
		}
	}
}