1 /*! \file janus.c
2 * \author Lorenzo Miniero <lorenzo@meetecho.com>
3 * \copyright GNU General Public License v3
4 * \brief Janus core
5 * \details Implementation of the Janus core. This code takes care of
6 * the server initialization (command line/configuration) and setup,
7 * and makes use of the available transport plugins (by default HTTP,
8 * WebSockets, RabbitMQ, if compiled) and Janus protocol (a JSON-based
9 * protocol) to interact with the applications, whether they're web based
10 * or not. The core also takes care of bridging peers and plugins
11 * accordingly, in terms of both messaging and real-time media transfer
12 * via WebRTC.
13 *
14 * \ingroup core
15 * \ref core
16 */
17
18 #include <dlfcn.h>
19 #include <dirent.h>
20 #include <net/if.h>
21 #include <netdb.h>
22 #include <signal.h>
23 #include <getopt.h>
24 #include <sys/resource.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <poll.h>
28
29 #include <openssl/rand.h>
30 #ifdef HAVE_TURNRESTAPI
31 #include <curl/curl.h>
32 #endif
33
34 #include "janus.h"
35 #include "version.h"
36 #include "cmdline.h"
37 #include "config.h"
38 #include "apierror.h"
39 #include "debug.h"
40 #include "ip-utils.h"
41 #include "rtcp.h"
42 #include "auth.h"
43 #include "record.h"
44 #include "events.h"
45
46
47 #define JANUS_NAME "Janus WebRTC Server"
48 #define JANUS_AUTHOR "Meetecho s.r.l."
49 #define JANUS_SERVER_NAME "MyJanusInstance"
50
51 #ifdef __MACH__
52 #define SHLIB_EXT "0.dylib"
53 #else
54 #define SHLIB_EXT ".so"
55 #endif
56
57
58 static janus_config *config = NULL;
59 static char *config_file = NULL;
60 static char *configs_folder = NULL;
61
62 static GHashTable *transports = NULL;
63 static GHashTable *transports_so = NULL;
64
65 static GHashTable *eventhandlers = NULL;
66 static GHashTable *eventhandlers_so = NULL;
67
68 static GHashTable *loggers = NULL;
69 static GHashTable *loggers_so = NULL;
70
71 static GHashTable *plugins = NULL;
72 static GHashTable *plugins_so = NULL;
73
74
75 /* Daemonization */
76 static gboolean daemonize = FALSE;
77 static int pipefd[2];
78
79
80 #ifdef REFCOUNT_DEBUG
81 /* Reference counters debugging */
82 GHashTable *counters = NULL;
83 janus_mutex counters_mutex;
84 #endif
85
86
87 /* API secrets */
88 static char *api_secret = NULL, *admin_api_secret = NULL;
89
90 /* JSON parameters */
91 static int janus_process_error_string(janus_request *request, uint64_t session_id, const char *transaction, gint error, gchar *error_string);
92
93 static struct janus_json_parameter incoming_request_parameters[] = {
94 {"transaction", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
95 {"janus", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
96 {"id", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
97 };
98 static struct janus_json_parameter attach_parameters[] = {
99 {"plugin", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
100 {"opaque_id", JSON_STRING, 0},
101 {"loop_index", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
102 };
103 static struct janus_json_parameter body_parameters[] = {
104 {"body", JSON_OBJECT, JANUS_JSON_PARAM_REQUIRED}
105 };
106 static struct janus_json_parameter jsep_parameters[] = {
107 {"type", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
108 {"sdp", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
109 {"trickle", JANUS_JSON_BOOL, 0},
110 {"rid_order", JSON_STRING, 0},
111 {"force_relay", JANUS_JSON_BOOL, 0},
112 {"e2ee", JANUS_JSON_BOOL, 0}
113 };
114 static struct janus_json_parameter add_token_parameters[] = {
115 {"token", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
116 {"plugins", JSON_ARRAY, 0}
117 };
118 static struct janus_json_parameter token_parameters[] = {
119 {"token", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
120 };
121 static struct janus_json_parameter admin_parameters[] = {
122 {"transaction", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
123 {"janus", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
124 };
125 static struct janus_json_parameter debug_parameters[] = {
126 {"debug", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
127 };
128 static struct janus_json_parameter timeout_parameters[] = {
129 {"timeout", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
130 };
131 static struct janus_json_parameter session_timeout_parameters[] = {
132 {"timeout", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED}
133 };
134 static struct janus_json_parameter level_parameters[] = {
135 {"level", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
136 };
137 static struct janus_json_parameter timestamps_parameters[] = {
138 {"timestamps", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
139 };
140 static struct janus_json_parameter colors_parameters[] = {
141 {"colors", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
142 };
143 static struct janus_json_parameter mnq_parameters[] = {
144 {"min_nack_queue", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
145 };
146 static struct janus_json_parameter nopt_parameters[] = {
147 {"nack_optimizations", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
148 };
149 static struct janus_json_parameter nmt_parameters[] = {
150 {"no_media_timer", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
151 };
152 static struct janus_json_parameter st_parameters[] = {
153 {"slowlink_threshold", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
154 };
155 static struct janus_json_parameter ans_parameters[] = {
156 {"accept", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
157 };
158 static struct janus_json_parameter querytransport_parameters[] = {
159 {"transport", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
160 {"request", JSON_OBJECT, 0}
161 };
162 static struct janus_json_parameter queryhandler_parameters[] = {
163 {"handler", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
164 {"request", JSON_OBJECT, 0}
165 };
166 static struct janus_json_parameter querylogger_parameters[] = {
167 {"logger", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
168 {"request", JSON_OBJECT, 0}
169 };
170 static struct janus_json_parameter messageplugin_parameters[] = {
171 {"plugin", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
172 {"request", JSON_OBJECT, 0}
173 };
174 static struct janus_json_parameter customevent_parameters[] = {
175 {"schema", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
176 {"data", JSON_OBJECT, JANUS_JSON_PARAM_REQUIRED}
177 };
178 static struct janus_json_parameter customlogline_parameters[] = {
179 {"line", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
180 {"level", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
181 };
182 static struct janus_json_parameter text2pcap_parameters[] = {
183 {"folder", JSON_STRING, 0},
184 {"filename", JSON_STRING, 0},
185 {"truncate", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
186 };
187 static struct janus_json_parameter handleinfo_parameters[] = {
188 {"plugin_only", JANUS_JSON_BOOL, 0}
189 };
190 static struct janus_json_parameter resaddr_parameters[] = {
191 {"address", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
192 };
193 static struct janus_json_parameter teststun_parameters[] = {
194 {"address", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
195 {"port", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
196 {"localport", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
197 };
198
199 /* Admin/Monitor helpers */
200 json_t *janus_admin_stream_summary(janus_ice_stream *stream);
201 json_t *janus_admin_component_summary(janus_ice_component *component);
202
203
204 /* IP addresses */
205 static gchar *local_ip = NULL;
janus_get_local_ip(void)206 gchar *janus_get_local_ip(void) {
207 return local_ip;
208 }
209 static GHashTable *public_ips_table = NULL;
210 static GList *public_ips = NULL;
211 gboolean public_ips_ipv4 = FALSE, public_ips_ipv6 = FALSE;
janus_get_public_ip_count(void)212 guint janus_get_public_ip_count(void) {
213 return public_ips_table ? g_hash_table_size(public_ips_table) : 0;
214 }
janus_get_public_ip(guint index)215 gchar *janus_get_public_ip(guint index) {
216 if (!janus_get_public_ip_count()) {
217 /* Fallback to the local IP, if we have no public one */
218 return local_ip;
219 }
220 if (index >= g_hash_table_size(public_ips_table)) {
221 index = g_hash_table_size(public_ips_table) - 1;
222 }
223 return (char *)g_list_nth(public_ips, index)->data;
224 }
janus_add_public_ip(const gchar * ip)225 void janus_add_public_ip(const gchar *ip) {
226 if(ip == NULL) {
227 return;
228 }
229
230 if(!public_ips_table) {
231 public_ips_table = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
232 }
233 if (g_hash_table_insert(public_ips_table, g_strdup(ip), NULL)) {
234 g_list_free(public_ips);
235 public_ips = g_hash_table_get_keys(public_ips_table);
236 }
237 /* Take note of whether we received at least one IPv4 and/or IPv6 address */
238 if(strchr(ip, ':')) {
239 public_ips_ipv6 = TRUE;
240 } else {
241 public_ips_ipv4 = TRUE;
242 }
243 }
janus_has_public_ipv4_ip(void)244 gboolean janus_has_public_ipv4_ip(void) {
245 return public_ips_ipv4;
246 }
janus_has_public_ipv6_ip(void)247 gboolean janus_has_public_ipv6_ip(void) {
248 return public_ips_ipv6;
249 }
250
251 static volatile gint stop = 0;
252 static gint stop_signal = 0;
janus_is_stopping(void)253 gint janus_is_stopping(void) {
254 return g_atomic_int_get(&stop);
255 }
256 static GMainLoop *mainloop = NULL;
257
258
259 /* Public instance name */
260 static gchar *server_name = NULL;
261
janus_create_message(const char * status,uint64_t session_id,const char * transaction)262 static json_t *janus_create_message(const char *status, uint64_t session_id, const char *transaction) {
263 json_t *msg = json_object();
264 json_object_set_new(msg, "janus", json_string(status));
265 if(session_id > 0)
266 json_object_set_new(msg, "session_id", json_integer(session_id));
267 if(transaction != NULL)
268 json_object_set_new(msg, "transaction", json_string(transaction));
269 return msg;
270 }
271
272 /* The default timeout for sessions is 60 seconds: this means that, if
273 * we don't get any activity (i.e., no request) on this session for more
274 * than 60 seconds, then it's considered expired and we destroy it. That's
275 * why we have a keep-alive method in the API. This can be overridden in
276 * either janus.cfg/.jcfg or from the command line. Setting this to 0 will
277 * disable the timeout mechanism, which is NOT suggested as it may risk
278 * having orphaned sessions (sessions not controlled by any transport
279 * and never freed). Besides, notice that if you make this shorter than
280 * 30s, you'll have to update the timers in janus.js when the long
281 * polling mechanism is used and shorten them as well, or you'll risk
282 * incurring in unexpected timeouts (when HTTP is used in janus.js, the
283 * long poll is used as a keepalive mechanism). */
284 #define DEFAULT_SESSION_TIMEOUT 60
285 static uint global_session_timeout = DEFAULT_SESSION_TIMEOUT;
286
287 #define DEFAULT_RECLAIM_SESSION_TIMEOUT 0
288 static uint reclaim_session_timeout = DEFAULT_RECLAIM_SESSION_TIMEOUT;
289
290 /* We can programmatically change whether we want to accept new sessions
291 * or not: the default is of course TRUE, but we may want to temporarily
292 * change that in some cases, e.g., if we don't want the load on this
293 * server to grow too much, or because we're draining the server. */
294 static gboolean accept_new_sessions = TRUE;
295
296 /* We don't hold (trickle) candidates indefinitely either: by default, we
297 * only store them for 45 seconds. After that, they're discarded, in order
298 * to avoid leaks or orphaned media details. This means that, if for instance
299 * you're trying to set up a call with someone, and that someone only answers
300 * a minute later, the candidates you sent initially will be discarded and
301 * the call will fail. You can modify the default value in janus.jcfg */
302 #define DEFAULT_CANDIDATES_TIMEOUT 45
303 static uint candidates_timeout = DEFAULT_CANDIDATES_TIMEOUT;
304
305 /* By default we list dependencies details, but some may prefer not to */
306 static gboolean hide_dependencies = FALSE;
307
308 /* By default we do not exit if a shared library cannot be loaded or is missing an expected symbol */
309 static gboolean exit_on_dl_error = FALSE;
310
311 /* WebRTC encryption is obviously enabled by default. In the rare cases
312 * you want to disable it for debugging purposes, though, you can do
313 * that either via command line (-w) or in the main configuration file */
314 static gboolean webrtc_encryption = TRUE;
janus_is_webrtc_encryption_enabled(void)315 gboolean janus_is_webrtc_encryption_enabled(void) {
316 return webrtc_encryption;
317 }
318
319 /* Information */
janus_info(const char * transaction)320 static json_t *janus_info(const char *transaction) {
321 /* Prepare a summary on the Janus instance */
322 json_t *info = janus_create_message("server_info", 0, transaction);
323 json_object_set_new(info, "name", json_string(JANUS_NAME));
324 json_object_set_new(info, "version", json_integer(janus_version));
325 json_object_set_new(info, "version_string", json_string(janus_version_string));
326 json_object_set_new(info, "author", json_string(JANUS_AUTHOR));
327 json_object_set_new(info, "commit-hash", json_string(janus_build_git_sha));
328 json_object_set_new(info, "compile-time", json_string(janus_build_git_time));
329 json_object_set_new(info, "log-to-stdout", janus_log_is_stdout_enabled() ? json_true() : json_false());
330 json_object_set_new(info, "log-to-file", janus_log_is_logfile_enabled() ? json_true() : json_false());
331 if(janus_log_is_logfile_enabled())
332 json_object_set_new(info, "log-path", json_string(janus_log_get_logfile_path()));
333 #ifdef HAVE_SCTP
334 json_object_set_new(info, "data_channels", json_true());
335 #else
336 json_object_set_new(info, "data_channels", json_false());
337 #endif
338 json_object_set_new(info, "accepting-new-sessions", accept_new_sessions ? json_true() : json_false());
339 json_object_set_new(info, "session-timeout", json_integer(global_session_timeout));
340 json_object_set_new(info, "reclaim-session-timeout", json_integer(reclaim_session_timeout));
341 json_object_set_new(info, "candidates-timeout", json_integer(candidates_timeout));
342 json_object_set_new(info, "server-name", json_string(server_name ? server_name : JANUS_SERVER_NAME));
343 json_object_set_new(info, "local-ip", json_string(local_ip));
344 guint public_ip_count = janus_get_public_ip_count();
345 if(public_ip_count > 0) {
346 json_object_set_new(info, "public-ip", json_string(janus_get_public_ip(0)));
347 }
348 if(public_ip_count > 1) {
349 guint i;
350 json_t *ips = json_array();
351 for (i = 0; i < public_ip_count; i++) {
352 json_array_append_new(ips, json_string(janus_get_public_ip(i)));
353 }
354 json_object_set_new(info, "public-ips", ips);
355 }
356 json_object_set_new(info, "ipv6", janus_ice_is_ipv6_enabled() ? json_true() : json_false());
357 if(janus_ice_is_ipv6_enabled())
358 json_object_set_new(info, "ipv6-link-local", janus_ice_is_ipv6_linklocal_enabled() ? json_true() : json_false());
359 json_object_set_new(info, "ice-lite", janus_ice_is_ice_lite_enabled() ? json_true() : json_false());
360 json_object_set_new(info, "ice-tcp", janus_ice_is_ice_tcp_enabled() ? json_true() : json_false());
361 #ifdef HAVE_ICE_NOMINATION
362 json_object_set_new(info, "ice-nomination", json_string(janus_ice_get_nomination_mode()));
363 #endif
364 json_object_set_new(info, "ice-keepalive-conncheck", janus_ice_is_keepalive_conncheck_enabled() ? json_true() : json_false());
365 json_object_set_new(info, "full-trickle", janus_ice_is_full_trickle_enabled() ? json_true() : json_false());
366 json_object_set_new(info, "mdns-enabled", janus_ice_is_mdns_enabled() ? json_true() : json_false());
367 json_object_set_new(info, "min-nack-queue", json_integer(janus_get_min_nack_queue()));
368 json_object_set_new(info, "nack-optimizations", janus_is_nack_optimizations_enabled() ? json_true() : json_false());
369 json_object_set_new(info, "twcc-period", json_integer(janus_get_twcc_period()));
370 if(janus_get_dscp() > 0)
371 json_object_set_new(info, "dscp", json_integer(janus_get_dscp()));
372 json_object_set_new(info, "dtls-mtu", json_integer(janus_dtls_bio_agent_get_mtu()));
373 if(janus_ice_get_stun_server() != NULL) {
374 char server[255];
375 g_snprintf(server, 255, "%s:%"SCNu16, janus_ice_get_stun_server(), janus_ice_get_stun_port());
376 json_object_set_new(info, "stun-server", json_string(server));
377 }
378 if(janus_ice_get_turn_server() != NULL) {
379 char server[255];
380 g_snprintf(server, 255, "%s:%"SCNu16, janus_ice_get_turn_server(), janus_ice_get_turn_port());
381 json_object_set_new(info, "turn-server", json_string(server));
382 }
383 if(janus_ice_is_force_relay_allowed())
384 json_object_set_new(info, "allow-force-relay", json_true());
385 json_object_set_new(info, "static-event-loops", json_integer(janus_ice_get_static_event_loops()));
386 if(janus_ice_get_static_event_loops())
387 json_object_set_new(info, "loop-indication", janus_ice_is_loop_indication_allowed() ? json_true() : json_false());
388 json_object_set_new(info, "api_secret", api_secret ? json_true() : json_false());
389 json_object_set_new(info, "auth_token", janus_auth_is_enabled() ? json_true() : json_false());
390 json_object_set_new(info, "event_handlers", janus_events_is_enabled() ? json_true() : json_false());
391 json_object_set_new(info, "opaqueid_in_api", janus_is_opaqueid_in_api_enabled() ? json_true() : json_false());
392 if(!webrtc_encryption)
393 json_object_set_new(info, "webrtc_encryption", json_false());
394 /* Dependencies */
395 if(!hide_dependencies) {
396 json_t *deps = json_object();
397 char glib2_version[20];
398 g_snprintf(glib2_version, sizeof(glib2_version), "%d.%d.%d", glib_major_version, glib_minor_version, glib_micro_version);
399 json_object_set_new(deps, "glib2", json_string(glib2_version));
400 json_object_set_new(deps, "jansson", json_string(JANSSON_VERSION));
401 json_object_set_new(deps, "libnice", json_string(libnice_version_string));
402 json_object_set_new(deps, "libsrtp", json_string(srtp_get_version_string()));
403 #ifdef HAVE_TURNRESTAPI
404 curl_version_info_data *curl_version = curl_version_info(CURLVERSION_NOW);
405 if(curl_version && curl_version->version)
406 json_object_set_new(deps, "libcurl", json_string(curl_version->version));
407 #endif
408 json_object_set_new(deps, "crypto", json_string(janus_get_ssl_version()));
409 json_object_set_new(info, "dependencies", deps);
410 }
411 /* Available transports */
412 json_t *t_data = json_object();
413 if(transports && g_hash_table_size(transports) > 0) {
414 GHashTableIter iter;
415 gpointer value;
416 g_hash_table_iter_init(&iter, transports);
417 while (g_hash_table_iter_next(&iter, NULL, &value)) {
418 janus_transport *t = value;
419 if(t == NULL) {
420 continue;
421 }
422 json_t *transport = json_object();
423 json_object_set_new(transport, "name", json_string(t->get_name()));
424 json_object_set_new(transport, "author", json_string(t->get_author()));
425 json_object_set_new(transport, "description", json_string(t->get_description()));
426 json_object_set_new(transport, "version_string", json_string(t->get_version_string()));
427 json_object_set_new(transport, "version", json_integer(t->get_version()));
428 json_object_set_new(t_data, t->get_package(), transport);
429 }
430 }
431 json_object_set_new(info, "transports", t_data);
432 /* Available event handlers */
433 json_t *e_data = json_object();
434 if(eventhandlers && g_hash_table_size(eventhandlers) > 0) {
435 GHashTableIter iter;
436 gpointer value;
437 g_hash_table_iter_init(&iter, eventhandlers);
438 while (g_hash_table_iter_next(&iter, NULL, &value)) {
439 janus_eventhandler *e = value;
440 if(e == NULL) {
441 continue;
442 }
443 json_t *eventhandler = json_object();
444 json_object_set_new(eventhandler, "name", json_string(e->get_name()));
445 json_object_set_new(eventhandler, "author", json_string(e->get_author()));
446 json_object_set_new(eventhandler, "description", json_string(e->get_description()));
447 json_object_set_new(eventhandler, "version_string", json_string(e->get_version_string()));
448 json_object_set_new(eventhandler, "version", json_integer(e->get_version()));
449 json_object_set_new(e_data, e->get_package(), eventhandler);
450 }
451 }
452 json_object_set_new(info, "events", e_data);
453 /* Available external loggers */
454 json_t *l_data = json_object();
455 if(loggers && g_hash_table_size(loggers) > 0) {
456 GHashTableIter iter;
457 gpointer value;
458 g_hash_table_iter_init(&iter, loggers);
459 while (g_hash_table_iter_next(&iter, NULL, &value)) {
460 janus_logger *l = value;
461 if(l == NULL) {
462 continue;
463 }
464 json_t *logger = json_object();
465 json_object_set_new(logger, "name", json_string(l->get_name()));
466 json_object_set_new(logger, "author", json_string(l->get_author()));
467 json_object_set_new(logger, "description", json_string(l->get_description()));
468 json_object_set_new(logger, "version_string", json_string(l->get_version_string()));
469 json_object_set_new(logger, "version", json_integer(l->get_version()));
470 json_object_set_new(l_data, l->get_package(), logger);
471 }
472 }
473 json_object_set_new(info, "loggers", l_data);
474 /* Available plugins */
475 json_t *p_data = json_object();
476 if(plugins && g_hash_table_size(plugins) > 0) {
477 GHashTableIter iter;
478 gpointer value;
479 g_hash_table_iter_init(&iter, plugins);
480 while (g_hash_table_iter_next(&iter, NULL, &value)) {
481 janus_plugin *p = value;
482 if(p == NULL) {
483 continue;
484 }
485 json_t *plugin = json_object();
486 json_object_set_new(plugin, "name", json_string(p->get_name()));
487 json_object_set_new(plugin, "author", json_string(p->get_author()));
488 json_object_set_new(plugin, "description", json_string(p->get_description()));
489 json_object_set_new(plugin, "version_string", json_string(p->get_version_string()));
490 json_object_set_new(plugin, "version", json_integer(p->get_version()));
491 json_object_set_new(p_data, p->get_package(), plugin);
492 }
493 }
494 json_object_set_new(info, "plugins", p_data);
495
496 return info;
497 }
498
499
500 /* Logging */
501 int janus_log_level = LOG_INFO;
502 gboolean janus_log_timestamps = FALSE;
503 gboolean janus_log_colors = FALSE;
504 char *janus_log_global_prefix = NULL;
505 int lock_debug = 0;
506 #ifdef REFCOUNT_DEBUG
507 int refcount_debug = 1;
508 #else
509 int refcount_debug = 0;
510 #endif
511
512
513 /*! \brief Signal handler (just used to intercept CTRL+C and SIGTERM) */
janus_handle_signal(int signum)514 static void janus_handle_signal(int signum) {
515 stop_signal = signum;
516 switch(g_atomic_int_get(&stop)) {
517 case 0:
518 JANUS_PRINT("Stopping server, please wait...\n");
519 break;
520 case 1:
521 JANUS_PRINT("In a hurry? I'm trying to free resources cleanly, here!\n");
522 break;
523 default:
524 JANUS_PRINT("Ok, leaving immediately...\n");
525 break;
526 }
527 g_atomic_int_inc(&stop);
528 if(g_atomic_int_get(&stop) > 2)
529 exit(1);
530 if(mainloop && g_main_loop_is_running(mainloop))
531 g_main_loop_quit(mainloop);
532 }
533
534 /*! \brief Termination handler (atexit) */
janus_termination_handler(void)535 static void janus_termination_handler(void) {
536 /* Free the instance name, if provided */
537 g_free(server_name);
538 /* Remove the PID file if we created it */
539 janus_pidfile_remove();
540 /* Close the logger */
541 janus_log_destroy();
542 /* Get rid of external loggers too, if any */
543 if(loggers != NULL && g_hash_table_size(loggers) > 0) {
544 g_hash_table_foreach(loggers, janus_logger_close, NULL);
545 g_hash_table_destroy(loggers);
546 }
547 if(loggers_so != NULL && g_hash_table_size(loggers_so) > 0) {
548 g_hash_table_foreach(loggers_so, janus_loggerso_close, NULL);
549 g_hash_table_destroy(loggers_so);
550 }
551 /* If we're daemonizing, we send an error code to the parent */
552 if(daemonize) {
553 int code = 1;
554 ssize_t res = 0;
555 do {
556 res = write(pipefd[1], &code, sizeof(int));
557 } while(res == -1 && errno == EINTR);
558 }
559 }
560
561
562 /** @name Transport plugin callback interface
563 * These are the callbacks implemented by the Janus core, as part of
564 * the janus_transport_callbacks interface. Everything the transport
565 * plugins send the core is handled here.
566 */
567 ///@{
568 void janus_transport_incoming_request(janus_transport *plugin, janus_transport_session *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error);
569 void janus_transport_gone(janus_transport *plugin, janus_transport_session *transport);
570 gboolean janus_transport_is_api_secret_needed(janus_transport *plugin);
571 gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret);
572 gboolean janus_transport_is_auth_token_needed(janus_transport *plugin);
573 gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token);
574 void janus_transport_notify_event(janus_transport *plugin, void *transport, json_t *event);
575
576 static janus_transport_callbacks janus_handler_transport =
577 {
578 .incoming_request = janus_transport_incoming_request,
579 .transport_gone = janus_transport_gone,
580 .is_api_secret_needed = janus_transport_is_api_secret_needed,
581 .is_api_secret_valid = janus_transport_is_api_secret_valid,
582 .is_auth_token_needed = janus_transport_is_auth_token_needed,
583 .is_auth_token_valid = janus_transport_is_auth_token_valid,
584 .events_is_enabled = janus_events_is_enabled,
585 .notify_event = janus_transport_notify_event,
586 };
587 static GAsyncQueue *requests = NULL;
588 static janus_request exit_message;
589 static GThreadPool *tasks = NULL;
590 void janus_transport_task(gpointer data, gpointer user_data);
591 ///@}
592
593
594 /** @name Plugin callback interface
595 * These are the callbacks implemented by the Janus core, as part of
596 * the janus_callbacks interface. Everything the plugins send the
597 * core is handled here.
598 */
599 ///@{
600 int janus_plugin_push_event(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *transaction, json_t *message, json_t *jsep);
601 json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *sdp_type, const char *sdp, gboolean restart);
602 void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, janus_plugin_rtp *packet);
603 void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, janus_plugin_rtcp *packet);
604 void janus_plugin_relay_data(janus_plugin_session *plugin_session, janus_plugin_data *message);
605 void janus_plugin_send_pli(janus_plugin_session *plugin_session);
606 void janus_plugin_send_remb(janus_plugin_session *plugin_session, uint32_t bitrate);
607 void janus_plugin_close_pc(janus_plugin_session *plugin_session);
608 void janus_plugin_end_session(janus_plugin_session *plugin_session);
609 void janus_plugin_notify_event(janus_plugin *plugin, janus_plugin_session *plugin_session, json_t *event);
610 gboolean janus_plugin_auth_is_signed(void);
611 gboolean janus_plugin_auth_is_signature_valid(janus_plugin *plugin, const char *token);
612 gboolean janus_plugin_auth_signature_contains(janus_plugin *plugin, const char *token, const char *desc);
613 static janus_callbacks janus_handler_plugin =
614 {
615 .push_event = janus_plugin_push_event,
616 .relay_rtp = janus_plugin_relay_rtp,
617 .relay_rtcp = janus_plugin_relay_rtcp,
618 .relay_data = janus_plugin_relay_data,
619 .send_pli = janus_plugin_send_pli,
620 .send_remb = janus_plugin_send_remb,
621 .close_pc = janus_plugin_close_pc,
622 .end_session = janus_plugin_end_session,
623 .events_is_enabled = janus_events_is_enabled,
624 .notify_event = janus_plugin_notify_event,
625 .auth_is_signed = janus_plugin_auth_is_signed,
626 .auth_is_signature_valid = janus_plugin_auth_is_signature_valid,
627 .auth_signature_contains = janus_plugin_auth_signature_contains,
628 };
629 ///@}
630
631
632 /* Core Sessions */
633 static janus_mutex sessions_mutex;
634 static GHashTable *sessions = NULL;
635 static GMainContext *sessions_watchdog_context = NULL;
636
637
janus_ice_handle_dereference(janus_ice_handle * handle)638 static void janus_ice_handle_dereference(janus_ice_handle *handle) {
639 if(handle)
640 janus_refcount_decrease(&handle->ref);
641 }
642
janus_session_free(const janus_refcount * session_ref)643 static void janus_session_free(const janus_refcount *session_ref) {
644 janus_session *session = janus_refcount_containerof(session_ref, janus_session, ref);
645 /* This session can be destroyed, free all the resources */
646 if(session->ice_handles != NULL) {
647 g_hash_table_destroy(session->ice_handles);
648 session->ice_handles = NULL;
649 }
650 if(session->source != NULL) {
651 janus_request_destroy(session->source);
652 session->source = NULL;
653 }
654 g_free(session);
655 }
656
janus_session_get_request(janus_session * session)657 static janus_request *janus_session_get_request(janus_session *session) {
658 if(session == NULL)
659 return NULL;
660 janus_mutex_lock(&session->mutex);
661 janus_request *source = session->source;
662 if(source != NULL && !g_atomic_int_get(&source->destroyed)) {
663 janus_refcount_increase(&source->ref);
664 } else {
665 source = NULL;
666 }
667 janus_mutex_unlock(&session->mutex);
668 return source;
669 }
janus_request_unref(janus_request * request)670 static void janus_request_unref(janus_request *request) {
671 if(request)
672 janus_refcount_decrease(&request->ref);
673 }
674
janus_check_sessions(gpointer user_data)675 static gboolean janus_check_sessions(gpointer user_data) {
676 janus_mutex_lock(&sessions_mutex);
677 if(sessions && g_hash_table_size(sessions) > 0) {
678 GHashTableIter iter;
679 gpointer value;
680 g_hash_table_iter_init(&iter, sessions);
681 while (g_hash_table_iter_next(&iter, NULL, &value)) {
682 janus_session *session = (janus_session *) value;
683 if (!session || g_atomic_int_get(&session->destroyed)) {
684 continue;
685 }
686 gint64 now = janus_get_monotonic_time();
687
688 /* Use either session-specific timeout or global. */
689 gint64 timeout = (gint64)session->timeout;
690 if (timeout == -1) timeout = (gint64)global_session_timeout;
691
692 if ((timeout > 0 && (now - session->last_activity >= timeout * G_USEC_PER_SEC) &&
693 !g_atomic_int_compare_and_exchange(&session->timedout, 0, 1)) ||
694 ((g_atomic_int_get(&session->transport_gone) && now - session->last_activity >= (gint64)reclaim_session_timeout * G_USEC_PER_SEC) &&
695 !g_atomic_int_compare_and_exchange(&session->timedout, 0, 1))) {
696 JANUS_LOG(LOG_INFO, "Timeout expired for session %"SCNu64"...\n", session->session_id);
697 /* Mark the session as over, we'll deal with it later */
698 janus_session_handles_clear(session);
699 /* Notify the transport */
700 janus_request *source = janus_session_get_request(session);
701 if(source) {
702 json_t *event = janus_create_message("timeout", session->session_id, NULL);
703 /* Send this to the transport client and notify the session's over */
704 source->transport->send_message(source->instance, NULL, FALSE, event);
705 source->transport->session_over(source->instance, session->session_id, TRUE, FALSE);
706 }
707 janus_request_unref(source);
708 /* Notify event handlers as well */
709 if(janus_events_is_enabled())
710 janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, JANUS_EVENT_SUBTYPE_NONE,
711 session->session_id, "timeout", NULL);
712
713 /* FIXME Is this safe? apparently it causes hash table errors on the console */
714 g_hash_table_iter_remove(&iter);
715
716 janus_session_destroy(session);
717 }
718 }
719 }
720 janus_mutex_unlock(&sessions_mutex);
721
722 return G_SOURCE_CONTINUE;
723 }
724
janus_sessions_watchdog(gpointer user_data)725 static gpointer janus_sessions_watchdog(gpointer user_data) {
726 GMainLoop *loop = (GMainLoop *) user_data;
727 GMainContext *watchdog_context = g_main_loop_get_context(loop);
728 GSource *timeout_source;
729
730 timeout_source = g_timeout_source_new_seconds(2);
731 g_source_set_callback(timeout_source, janus_check_sessions, watchdog_context, NULL);
732 g_source_attach(timeout_source, watchdog_context);
733 g_source_unref(timeout_source);
734
735 JANUS_LOG(LOG_INFO, "Sessions watchdog started\n");
736
737 g_main_loop_run(loop);
738
739 JANUS_LOG(LOG_INFO, "Sessions watchdog stopped\n");
740
741 return NULL;
742 }
743
744
janus_session_create(guint64 session_id)745 janus_session *janus_session_create(guint64 session_id) {
746 janus_session *session = NULL;
747 if(session_id == 0) {
748 while(session_id == 0) {
749 session_id = janus_random_uint64();
750 session = janus_session_find(session_id);
751 if(session != NULL) {
752 /* Session ID already taken, try another one */
753 janus_refcount_decrease(&session->ref);
754 session_id = 0;
755 }
756 }
757 }
758 session = (janus_session *)g_malloc(sizeof(janus_session));
759 JANUS_LOG(LOG_INFO, "Creating new session: %"SCNu64"; %p\n", session_id, session);
760 session->session_id = session_id;
761 janus_refcount_init(&session->ref, janus_session_free);
762 session->source = NULL;
763 session->timeout = -1; /* Negative means rely on global timeout */
764 g_atomic_int_set(&session->destroyed, 0);
765 g_atomic_int_set(&session->timedout, 0);
766 g_atomic_int_set(&session->transport_gone, 0);
767 session->last_activity = janus_get_monotonic_time();
768 session->ice_handles = NULL;
769 janus_mutex_init(&session->mutex);
770 janus_mutex_lock(&sessions_mutex);
771 g_hash_table_insert(sessions, janus_uint64_dup(session->session_id), session);
772 janus_mutex_unlock(&sessions_mutex);
773 return session;
774 }
775
janus_session_find(guint64 session_id)776 janus_session *janus_session_find(guint64 session_id) {
777 janus_mutex_lock(&sessions_mutex);
778 janus_session *session = g_hash_table_lookup(sessions, &session_id);
779 if(session != NULL) {
780 /* A successful find automatically increases the reference counter:
781 * it's up to the caller to decrease it again when done */
782 janus_refcount_increase(&session->ref);
783 }
784 janus_mutex_unlock(&sessions_mutex);
785 return session;
786 }
787
janus_session_notify_event(janus_session * session,json_t * event)788 void janus_session_notify_event(janus_session *session, json_t *event) {
789 if(session != NULL && !g_atomic_int_get(&session->destroyed)) {
790 janus_request *source = janus_session_get_request(session);
791 if(source != NULL && source->transport != NULL) {
792 /* Send this to the transport client */
793 JANUS_LOG(LOG_HUGE, "Sending event to %s (%p)\n", source->transport->get_package(), source->instance);
794 source->transport->send_message(source->instance, NULL, FALSE, event);
795 } else {
796 /* No transport, free the event */
797 json_decref(event);
798 }
799 janus_request_unref(source);
800 } else {
801 /* No session, free the event */
802 json_decref(event);
803 }
804 }
805
806
807 /* Destroys a session but does not remove it from the sessions hash table. */
janus_session_destroy(janus_session * session)808 gint janus_session_destroy(janus_session *session) {
809 guint64 session_id = session->session_id;
810 JANUS_LOG(LOG_INFO, "Destroying session %"SCNu64"; %p\n", session_id, session);
811 if(!g_atomic_int_compare_and_exchange(&session->destroyed, 0, 1))
812 return 0;
813 janus_session_handles_clear(session);
814 /* The session will actually be destroyed when the counter gets to 0 */
815 janus_refcount_decrease(&session->ref);
816
817 return 0;
818 }
819
janus_session_handles_find(janus_session * session,guint64 handle_id)820 janus_ice_handle *janus_session_handles_find(janus_session *session, guint64 handle_id) {
821 if(session == NULL)
822 return NULL;
823 janus_mutex_lock(&session->mutex);
824 janus_ice_handle *handle = session->ice_handles ? g_hash_table_lookup(session->ice_handles, &handle_id) : NULL;
825 if(handle != NULL) {
826 /* A successful find automatically increases the reference counter:
827 * it's up to the caller to decrease it again when done */
828 janus_refcount_increase(&handle->ref);
829 }
830 janus_mutex_unlock(&session->mutex);
831 return handle;
832 }
833
janus_session_handles_insert(janus_session * session,janus_ice_handle * handle)834 void janus_session_handles_insert(janus_session *session, janus_ice_handle *handle) {
835 janus_mutex_lock(&session->mutex);
836 if(session->ice_handles == NULL)
837 session->ice_handles = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, (GDestroyNotify)janus_ice_handle_dereference);
838 janus_refcount_increase(&handle->ref);
839 g_hash_table_insert(session->ice_handles, janus_uint64_dup(handle->handle_id), handle);
840 janus_mutex_unlock(&session->mutex);
841 }
842
janus_session_handles_remove(janus_session * session,janus_ice_handle * handle)843 gint janus_session_handles_remove(janus_session *session, janus_ice_handle *handle) {
844 janus_mutex_lock(&session->mutex);
845 gint error = janus_ice_handle_destroy(session, handle);
846 g_hash_table_remove(session->ice_handles, &handle->handle_id);
847 janus_mutex_unlock(&session->mutex);
848 return error;
849 }
850
janus_session_handles_clear(janus_session * session)851 void janus_session_handles_clear(janus_session *session) {
852 janus_mutex_lock(&session->mutex);
853 if(session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
854 GHashTableIter iter;
855 gpointer value;
856 /* Remove all handles */
857 g_hash_table_iter_init(&iter, session->ice_handles);
858 while (g_hash_table_iter_next(&iter, NULL, &value)) {
859 janus_ice_handle *handle = value;
860 if(!handle)
861 continue;
862 janus_ice_handle_destroy(session, handle);
863 g_hash_table_iter_remove(&iter);
864 }
865 }
866 janus_mutex_unlock(&session->mutex);
867 }
868
janus_session_handles_list_json(janus_session * session)869 json_t *janus_session_handles_list_json(janus_session *session) {
870 json_t *list = json_array();
871 janus_mutex_lock(&session->mutex);
872 if(session->ice_handles != NULL && g_hash_table_size(session->ice_handles) > 0) {
873 GHashTableIter iter;
874 gpointer value;
875 g_hash_table_iter_init(&iter, session->ice_handles);
876 while (g_hash_table_iter_next(&iter, NULL, &value)) {
877 janus_ice_handle *handle = value;
878 if(!handle)
879 continue;
880 json_array_append_new(list, json_integer(handle->handle_id));
881 }
882 }
883 janus_mutex_unlock(&session->mutex);
884 return list;
885 }
886
887 /* Requests management */
janus_request_free(const janus_refcount * request_ref)888 static void janus_request_free(const janus_refcount *request_ref) {
889 janus_request *request = janus_refcount_containerof(request_ref, janus_request, ref);
890 /* This request can be destroyed, free all the resources */
891 request->transport = NULL;
892 if(request->instance)
893 janus_refcount_decrease(&request->instance->ref);
894 request->instance = NULL;
895 request->request_id = NULL;
896 if(request->message)
897 json_decref(request->message);
898 request->message = NULL;
899 g_free(request);
900 }
901
janus_request_new(janus_transport * transport,janus_transport_session * instance,void * request_id,gboolean admin,json_t * message)902 janus_request *janus_request_new(janus_transport *transport, janus_transport_session *instance, void *request_id, gboolean admin, json_t *message) {
903 janus_request *request = g_malloc(sizeof(janus_request));
904 request->transport = transport;
905 request->instance = instance;
906 janus_refcount_increase(&instance->ref);
907 request->request_id = request_id;
908 request->admin = admin;
909 request->message = message;
910 g_atomic_int_set(&request->destroyed, 0);
911 janus_refcount_init(&request->ref, janus_request_free);
912 return request;
913 }
914
janus_request_destroy(janus_request * request)915 void janus_request_destroy(janus_request *request) {
916 if(request == NULL || request == &exit_message || !g_atomic_int_compare_and_exchange(&request->destroyed, 0, 1))
917 return;
918 janus_refcount_decrease(&request->ref);
919 }
920
janus_request_check_secret(janus_request * request,guint64 session_id,const gchar * transaction_text)921 static int janus_request_check_secret(janus_request *request, guint64 session_id, const gchar *transaction_text) {
922 gboolean secret_authorized = FALSE, token_authorized = FALSE;
923 if(api_secret == NULL && !janus_auth_is_enabled()) {
924 /* Nothing to check */
925 secret_authorized = TRUE;
926 token_authorized = TRUE;
927 } else {
928 json_t *root = request->message;
929 if(api_secret != NULL) {
930 /* There's an API secret, check that the client provided it */
931 json_t *secret = json_object_get(root, "apisecret");
932 if(secret && json_is_string(secret) && janus_strcmp_const_time(json_string_value(secret), api_secret)) {
933 secret_authorized = TRUE;
934 }
935 }
936 if(janus_auth_is_enabled()) {
937 /* The token based authentication mechanism is enabled, check that the client provided it */
938 json_t *token = json_object_get(root, "token");
939 if(token && json_is_string(token) && janus_auth_check_token(json_string_value(token))) {
940 token_authorized = TRUE;
941 }
942 }
943 /* We consider a request authorized if either the proper API secret or a valid token has been provided */
944 if(!(api_secret != NULL && secret_authorized) && !(janus_auth_is_enabled() && token_authorized))
945 return JANUS_ERROR_UNAUTHORIZED;
946 }
947 return 0;
948 }
949
janus_request_ice_handle_answer(janus_ice_handle * handle,int audio,int video,int data,char * jsep_sdp)950 static void janus_request_ice_handle_answer(janus_ice_handle *handle, int audio, int video, int data, char *jsep_sdp) {
951 /* We got our answer */
952 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
953 /* Any pending trickles? */
954 if(handle->pending_trickles) {
955 JANUS_LOG(LOG_VERB, "[%"SCNu64"] -- Processing %d pending trickle candidates\n", handle->handle_id, g_list_length(handle->pending_trickles));
956 GList *temp = NULL;
957 while(handle->pending_trickles) {
958 temp = g_list_first(handle->pending_trickles);
959 handle->pending_trickles = g_list_remove_link(handle->pending_trickles, temp);
960 janus_ice_trickle *trickle = (janus_ice_trickle *)temp->data;
961 g_list_free(temp);
962 if(trickle == NULL)
963 continue;
964 if((janus_get_monotonic_time() - trickle->received) > candidates_timeout*G_USEC_PER_SEC) {
965 /* FIXME Candidate is too old, discard it */
966 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Discarding candidate (too old)\n", handle->handle_id);
967 janus_ice_trickle_destroy(trickle);
968 /* FIXME We should report that */
969 continue;
970 }
971 json_t *candidate = trickle->candidate;
972 if(candidate == NULL) {
973 janus_ice_trickle_destroy(trickle);
974 continue;
975 }
976 if(json_is_object(candidate)) {
977 /* We got a single candidate */
978 int error = 0;
979 const char *error_string = NULL;
980 if((error = janus_ice_trickle_parse(handle, candidate, &error_string)) != 0) {
981 /* FIXME We should report the error parsing the trickle candidate */
982 }
983 } else if(json_is_array(candidate)) {
984 /* We got multiple candidates in an array */
985 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Got multiple candidates (%zu)\n", handle->handle_id, json_array_size(candidate));
986 if(json_array_size(candidate) > 0) {
987 /* Handle remote candidates */
988 size_t i = 0;
989 for(i=0; i<json_array_size(candidate); i++) {
990 json_t *c = json_array_get(candidate, i);
991 /* FIXME We don't care if any trickle fails to parse */
992 janus_ice_trickle_parse(handle, c, NULL);
993 }
994 }
995 }
996 /* Done, free candidate */
997 janus_ice_trickle_destroy(trickle);
998 }
999 }
1000
1001 gboolean candidates_found = (handle->stream && handle->stream->component && g_slist_length(handle->stream->component->candidates) > 0);
1002 /* This was an answer, check if it's time to start ICE */
1003 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE) && !candidates_found) {
1004 JANUS_LOG(LOG_VERB, "[%"SCNu64"] -- ICE Trickling is supported by the browser, waiting for remote candidates...\n", handle->handle_id);
1005 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START);
1006 } else {
1007 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Done! Sending connectivity checks...\n", handle->handle_id);
1008 janus_ice_setup_remote_candidates(handle, handle->stream_id, 1);
1009 }
1010 }
1011
janus_process_incoming_request(janus_request * request)1012 int janus_process_incoming_request(janus_request *request) {
1013 int ret = -1;
1014 if(request == NULL) {
1015 JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
1016 return ret;
1017 }
1018 int error_code = 0;
1019 char error_cause[100];
1020 json_t *root = request->message;
1021 /* Ok, let's start with the ids */
1022 guint64 session_id = 0, handle_id = 0;
1023 json_t *s = json_object_get(root, "session_id");
1024 if(json_is_null(s))
1025 s = NULL;
1026 if(s && json_is_integer(s))
1027 session_id = json_integer_value(s);
1028 json_t *h = json_object_get(root, "handle_id");
1029 if(json_is_null(h))
1030 h = NULL;
1031 if(h && json_is_integer(h))
1032 handle_id = json_integer_value(h);
1033
1034 janus_session *session = NULL;
1035 janus_ice_handle *handle = NULL;
1036
1037 /* Get transaction and message request */
1038 JANUS_VALIDATE_JSON_OBJECT(root, incoming_request_parameters,
1039 error_code, error_cause, FALSE,
1040 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1041 if(error_code != 0) {
1042 ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
1043 goto jsondone;
1044 }
1045 json_t *transaction = json_object_get(root, "transaction");
1046 const gchar *transaction_text = json_string_value(transaction);
1047 json_t *message = json_object_get(root, "janus");
1048 const gchar *message_text = json_string_value(message);
1049
1050 if(session_id == 0 && handle_id == 0) {
1051 /* Can only be a 'Create new session', a 'Get info' or a 'Ping/Pong' request */
1052 if(!strcasecmp(message_text, "info")) {
1053 ret = janus_process_success(request, janus_info(transaction_text));
1054 goto jsondone;
1055 }
1056 if(!strcasecmp(message_text, "ping")) {
1057 /* Prepare JSON reply */
1058 json_t *reply = janus_create_message("pong", 0, transaction_text);
1059 ret = janus_process_success(request, reply);
1060 goto jsondone;
1061 }
1062 if(strcasecmp(message_text, "create")) {
1063 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1064 goto jsondone;
1065 }
1066 /* Make sure we're accepting new sessions */
1067 if(!accept_new_sessions) {
1068 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_NOT_ACCEPTING_SESSIONS, NULL);
1069 goto jsondone;
1070 }
1071 /* Any secret/token to check? */
1072 ret = janus_request_check_secret(request, session_id, transaction_text);
1073 if(ret != 0) {
1074 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
1075 goto jsondone;
1076 }
1077 session_id = 0;
1078 json_t *id = json_object_get(root, "id");
1079 if(id != NULL) {
1080 /* The application provided the session ID to use */
1081 session_id = json_integer_value(id);
1082 if(session_id > 0 && (session = janus_session_find(session_id)) != NULL) {
1083 /* Session ID already taken */
1084 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_CONFLICT, "Session ID already in use");
1085 goto jsondone;
1086 }
1087 }
1088
1089 /* Handle it */
1090 session = janus_session_create(session_id);
1091 if(session == NULL) {
1092 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
1093 goto jsondone;
1094 }
1095 session_id = session->session_id;
1096 /* We increase the counter as this request is using the session */
1097 janus_refcount_increase(&session->ref);
1098 /* Take note of the request source that originated this session (HTTP, WebSockets, RabbitMQ?) */
1099 session->source = janus_request_new(request->transport, request->instance, NULL, FALSE, NULL);
1100 /* Notify the source that a new session has been created */
1101 request->transport->session_created(request->instance, session->session_id);
1102 /* Notify event handlers */
1103 if(janus_events_is_enabled()) {
1104 /* Session created, add info on the transport that originated it */
1105 json_t *transport = json_object();
1106 json_object_set_new(transport, "transport", json_string(session->source->transport->get_package()));
1107 char id[32];
1108 memset(id, 0, sizeof(id));
1109 /* To avoid sending a stringified version of the transport pointer
1110 * around, we convert it to a number and hash it instead */
1111 uint64_t p = janus_uint64_hash(GPOINTER_TO_UINT(session->source->instance));
1112 g_snprintf(id, sizeof(id), "%"SCNu64, p);
1113 json_object_set_new(transport, "id", json_string(id));
1114 janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, JANUS_EVENT_SUBTYPE_NONE,
1115 session_id, "created", transport);
1116 }
1117 /* Prepare JSON reply */
1118 json_t *reply = janus_create_message("success", 0, transaction_text);
1119 json_t *data = json_object();
1120 json_object_set_new(data, "id", json_integer(session_id));
1121 json_object_set_new(reply, "data", data);
1122 /* Send the success reply */
1123 ret = janus_process_success(request, reply);
1124 goto jsondone;
1125 }
1126 if(session_id < 1) {
1127 JANUS_LOG(LOG_ERR, "Invalid session\n");
1128 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
1129 goto jsondone;
1130 }
1131 if(h && handle_id < 1) {
1132 JANUS_LOG(LOG_ERR, "Invalid handle\n");
1133 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_HANDLE_NOT_FOUND, NULL);
1134 goto jsondone;
1135 }
1136
1137 /* Go on with the processing */
1138 ret = janus_request_check_secret(request, session_id, transaction_text);
1139 if(ret != 0) {
1140 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
1141 goto jsondone;
1142 }
1143
1144 /* If we got here, make sure we have a session (and/or a handle) */
1145 session = janus_session_find(session_id);
1146 if(!session) {
1147 JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
1148 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
1149 goto jsondone;
1150 }
1151 /* Update the last activity timer */
1152 session->last_activity = janus_get_monotonic_time();
1153 handle = NULL;
1154 if(handle_id > 0) {
1155 handle = janus_session_handles_find(session, handle_id);
1156 if(!handle) {
1157 JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
1158 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_HANDLE_NOT_FOUND, "No such handle %"SCNu64" in session %"SCNu64"", handle_id, session_id);
1159 goto jsondone;
1160 }
1161 }
1162
1163 /* What is this? */
1164 if(!strcasecmp(message_text, "keepalive")) {
1165 /* Just a keep-alive message, reply with an ack */
1166 JANUS_LOG(LOG_VERB, "Got a keep-alive on session %"SCNu64"\n", session_id);
1167 json_t *reply = janus_create_message("ack", session_id, transaction_text);
1168 /* Send the success reply */
1169 ret = janus_process_success(request, reply);
1170 } else if(!strcasecmp(message_text, "attach")) {
1171 if(handle != NULL) {
1172 /* Attach is a session-level command */
1173 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1174 goto jsondone;
1175 }
1176 JANUS_VALIDATE_JSON_OBJECT(root, attach_parameters,
1177 error_code, error_cause, FALSE,
1178 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1179 if(error_code != 0) {
1180 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1181 goto jsondone;
1182 }
1183 json_t *plugin = json_object_get(root, "plugin");
1184 const gchar *plugin_text = json_string_value(plugin);
1185 janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1186 if(plugin_t == NULL) {
1187 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, "No such plugin '%s'", plugin_text);
1188 goto jsondone;
1189 }
1190 /* If the auth token mechanism is enabled, we should check if this token can access this plugin */
1191 const char *token_value = NULL;
1192 if(janus_auth_is_enabled()) {
1193 json_t *token = json_object_get(root, "token");
1194 if(token != NULL) {
1195 token_value = json_string_value(token);
1196 if(token_value && !janus_auth_check_plugin(token_value, plugin_t)) {
1197 JANUS_LOG(LOG_ERR, "Token '%s' can't access plugin '%s'\n", token_value, plugin_text);
1198 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED_PLUGIN, "Provided token can't access plugin '%s'", plugin_text);
1199 goto jsondone;
1200 }
1201 }
1202 }
1203 json_t *opaque = json_object_get(root, "opaque_id");
1204 const char *opaque_id = opaque ? json_string_value(opaque) : NULL;
1205 json_t *loop = json_object_get(root, "loop_index");
1206 int loop_index = loop ? json_integer_value(loop) : -1;
1207 /* Create handle */
1208 handle = janus_ice_handle_create(session, opaque_id, token_value);
1209 if(handle == NULL) {
1210 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Memory error");
1211 goto jsondone;
1212 }
1213 handle_id = handle->handle_id;
1214 /* We increase the counter as this request is using the handle */
1215 janus_refcount_increase(&handle->ref);
1216 /* Attach to the plugin */
1217 int error = 0;
1218 if((error = janus_ice_handle_attach_plugin(session, handle, plugin_t, loop_index)) != 0) {
1219 /* TODO Make error struct to pass verbose information */
1220 janus_session_handles_remove(session, handle);
1221 JANUS_LOG(LOG_ERR, "Couldn't attach to plugin '%s', error '%d'\n", plugin_text, error);
1222 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_ATTACH, "Couldn't attach to plugin: error '%d'", error);
1223 goto jsondone;
1224 }
1225 /* Prepare JSON reply */
1226 json_t *reply = janus_create_message("success", session_id, transaction_text);
1227 json_t *data = json_object();
1228 json_object_set_new(data, "id", json_integer(handle_id));
1229 json_object_set_new(reply, "data", data);
1230 /* Send the success reply */
1231 ret = janus_process_success(request, reply);
1232 } else if(!strcasecmp(message_text, "destroy")) {
1233 if(handle != NULL) {
1234 /* Query is a session-level command */
1235 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1236 goto jsondone;
1237 }
1238 janus_mutex_lock(&sessions_mutex);
1239 g_hash_table_remove(sessions, &session->session_id);
1240 janus_mutex_unlock(&sessions_mutex);
1241 /* Notify the source that the session has been destroyed */
1242 janus_request *source = janus_session_get_request(session);
1243 if(source && source->transport)
1244 source->transport->session_over(source->instance, session->session_id, FALSE, FALSE);
1245 janus_request_unref(source);
1246
1247 /* Schedule the session for deletion */
1248 janus_session_destroy(session);
1249
1250 /* Prepare JSON reply */
1251 json_t *reply = janus_create_message("success", session_id, transaction_text);
1252 /* Send the success reply */
1253 ret = janus_process_success(request, reply);
1254 /* Notify event handlers as well */
1255 if(janus_events_is_enabled())
1256 janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, JANUS_EVENT_SUBTYPE_NONE,
1257 session_id, "destroyed", NULL);
1258 } else if(!strcasecmp(message_text, "detach")) {
1259 if(handle == NULL) {
1260 /* Query is an handle-level command */
1261 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1262 goto jsondone;
1263 }
1264 if(handle->app == NULL || handle->app_handle == NULL) {
1265 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin to detach from");
1266 goto jsondone;
1267 }
1268 int error = janus_session_handles_remove(session, handle);
1269 if(error != 0) {
1270 /* TODO Make error struct to pass verbose information */
1271 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "Couldn't detach from plugin: error '%d'", error);
1272 /* TODO Delete handle instance */
1273 goto jsondone;
1274 }
1275 /* Prepare JSON reply */
1276 json_t *reply = janus_create_message("success", session_id, transaction_text);
1277 /* Send the success reply */
1278 ret = janus_process_success(request, reply);
1279 } else if(!strcasecmp(message_text, "hangup")) {
1280 if(handle == NULL) {
1281 /* Query is an handle-level command */
1282 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1283 goto jsondone;
1284 }
1285 if(handle->app == NULL || handle->app_handle == NULL) {
1286 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin attached");
1287 goto jsondone;
1288 }
1289 janus_ice_webrtc_hangup(handle, "Janus API");
1290 /* Prepare JSON reply */
1291 json_t *reply = janus_create_message("success", session_id, transaction_text);
1292 /* Send the success reply */
1293 ret = janus_process_success(request, reply);
1294 } else if(!strcasecmp(message_text, "claim")) {
1295 janus_mutex_lock(&session->mutex);
1296 if(session->source != NULL) {
1297 /* If we're claiming from the same transport, ignore */
1298 if(session->source->instance == request->instance) {
1299 janus_mutex_unlock(&session->mutex);
1300 /* Prepare JSON reply */
1301 json_t *reply = json_object();
1302 json_object_set_new(reply, "janus", json_string("success"));
1303 json_object_set_new(reply, "session_id", json_integer(session_id));
1304 json_object_set_new(reply, "transaction", json_string(transaction_text));
1305 /* Send the success reply */
1306 ret = janus_process_success(request, reply);
1307 goto jsondone;
1308 }
1309 /* Notify the old transport that this session is over for them, but has been reclaimed */
1310 session->source->transport->session_over(session->source->instance, session->session_id, FALSE, TRUE);
1311 janus_request_destroy(session->source);
1312 session->source = NULL;
1313 }
1314 session->source = janus_request_new(request->transport, request->instance, NULL, FALSE, NULL);
1315 /* Notify the new transport that it has claimed a session */
1316 session->source->transport->session_claimed(session->source->instance, session->session_id);
1317 /* Previous transport may be gone, clear flag */
1318 g_atomic_int_set(&session->transport_gone, 0);
1319 janus_mutex_unlock(&session->mutex);
1320 /* Prepare JSON reply */
1321 json_t *reply = json_object();
1322 json_object_set_new(reply, "janus", json_string("success"));
1323 json_object_set_new(reply, "session_id", json_integer(session_id));
1324 json_object_set_new(reply, "transaction", json_string(transaction_text));
1325 /* Send the success reply */
1326 ret = janus_process_success(request, reply);
1327 } else if(!strcasecmp(message_text, "message")) {
1328 if(handle == NULL) {
1329 /* Query is an handle-level command */
1330 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1331 goto jsondone;
1332 }
1333 if(handle->app == NULL || handle->app_handle == NULL) {
1334 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
1335 goto jsondone;
1336 }
1337 janus_plugin *plugin_t = (janus_plugin *)handle->app;
1338 JANUS_LOG(LOG_VERB, "[%"SCNu64"] There's a message for %s\n", handle->handle_id, plugin_t->get_name());
1339 JANUS_VALIDATE_JSON_OBJECT(root, body_parameters,
1340 error_code, error_cause, FALSE,
1341 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1342 if(error_code != 0) {
1343 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1344 goto jsondone;
1345 }
1346 json_t *body = json_object_get(root, "body");
1347 /* Is there an SDP attached? */
1348 json_t *jsep = json_object_get(root, "jsep");
1349 char *jsep_type = NULL;
1350 char *jsep_sdp = NULL, *jsep_sdp_stripped = NULL;
1351 gboolean renegotiation = FALSE;
1352 if(jsep != NULL) {
1353 if(!json_is_object(jsep)) {
1354 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_JSON_OBJECT, "Invalid jsep object");
1355 goto jsondone;
1356 }
1357 JANUS_VALIDATE_JSON_OBJECT_FORMAT("JSEP error: missing mandatory element (%s)",
1358 "JSEP error: invalid element type (%s should be %s)",
1359 jsep, jsep_parameters, error_code, error_cause, FALSE,
1360 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1361 if(error_code != 0) {
1362 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1363 goto jsondone;
1364 }
1365 json_t *type = json_object_get(jsep, "type");
1366 jsep_type = g_strdup(json_string_value(type));
1367 type = NULL;
1368 json_t *jsep_trickle = json_object_get(jsep, "trickle");
1369 gboolean do_trickle = jsep_trickle ? json_is_true(jsep_trickle) : TRUE;
1370 json_t *jsep_rids = json_object_get(jsep, "rid_order");
1371 gboolean rids_hml = TRUE;
1372 if(jsep_rids != NULL) {
1373 const char *jsep_rids_value = json_string_value(jsep_rids);
1374 if(jsep_rids_value != NULL) {
1375 if(!strcasecmp(jsep_rids_value, "hml")) {
1376 rids_hml = TRUE;
1377 } else if(!strcasecmp(jsep_rids_value, "lmh")) {
1378 rids_hml = FALSE;
1379 } else {
1380 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Invalid 'rid_order' value, falling back to 'hml'\n", handle->handle_id);
1381 }
1382 }
1383 json_object_del(jsep, "rid_order");
1384 }
1385 json_t *jsep_e2ee = json_object_get(jsep, "e2ee");
1386 gboolean e2ee = jsep_e2ee ? json_is_true(jsep_e2ee) : FALSE;
1387 /* Are we still cleaning up from a previous media session? */
1388 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
1389 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", handle->handle_id);
1390 gint64 waited = 0;
1391 while(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
1392 g_usleep(100000);
1393 waited += 100000;
1394 if(waited >= 3*G_USEC_PER_SEC) {
1395 JANUS_LOG(LOG_VERB, "[%"SCNu64"] -- Waited 3 seconds, that's enough!\n", handle->handle_id);
1396 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_WEBRTC_STATE, "Still cleaning a previous session");
1397 goto jsondone;
1398 }
1399 }
1400 }
1401 /* Check if we're renegotiating (if we have an answer, we did an offer/answer round already) */
1402 renegotiation = janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEGOTIATED);
1403 /* Check the JSEP type */
1404 janus_mutex_lock(&handle->mutex);
1405 int offer = 0;
1406 if(!strcasecmp(jsep_type, "offer")) {
1407 offer = 1;
1408 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1409 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER);
1410 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
1411 } else if(!strcasecmp(jsep_type, "answer")) {
1412 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
1413 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER))
1414 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEGOTIATED);
1415 offer = 0;
1416 } else {
1417 /* TODO Handle other message types as well */
1418 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_JSEP_UNKNOWN_TYPE, "JSEP error: unknown message type '%s'", jsep_type);
1419 g_free(jsep_type);
1420 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1421 janus_mutex_unlock(&handle->mutex);
1422 goto jsondone;
1423 }
1424 json_t *sdp = json_object_get(jsep, "sdp");
1425 jsep_sdp = (char *)json_string_value(sdp);
1426 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Remote SDP:\n%s", handle->handle_id, jsep_sdp);
1427 /* Is this valid SDP? */
1428 char error_str[512];
1429 error_str[0] = '\0';
1430 int audio = 0, video = 0, data = 0;
1431 janus_sdp *parsed_sdp = janus_sdp_preparse(handle, jsep_sdp, error_str, sizeof(error_str), &audio, &video, &data);
1432 if(parsed_sdp == NULL) {
1433 /* Invalid SDP */
1434 ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_JSEP_INVALID_SDP, error_str);
1435 g_free(jsep_type);
1436 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1437 janus_mutex_unlock(&handle->mutex);
1438 goto jsondone;
1439 }
1440 /* Notify event handlers */
1441 if(janus_events_is_enabled()) {
1442 janus_events_notify_handlers(JANUS_EVENT_TYPE_JSEP, JANUS_EVENT_SUBTYPE_NONE,
1443 session_id, handle_id, handle->opaque_id, "remote", jsep_type, jsep_sdp);
1444 }
1445 /* FIXME We're only handling single audio/video lines for now... */
1446 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio %s been negotiated, Video %s been negotiated, SCTP/DataChannels %s been negotiated\n",
1447 handle->handle_id,
1448 audio ? "has" : "has NOT",
1449 video ? "has" : "has NOT",
1450 data ? "have" : "have NOT");
1451 if(audio > 1) {
1452 JANUS_LOG(LOG_WARN, "[%"SCNu64"] More than one audio line? only going to negotiate one...\n", handle->handle_id);
1453 }
1454 if(video > 1) {
1455 JANUS_LOG(LOG_WARN, "[%"SCNu64"] More than one video line? only going to negotiate one...\n", handle->handle_id);
1456 }
1457 if(data > 1) {
1458 JANUS_LOG(LOG_WARN, "[%"SCNu64"] More than one data line? only going to negotiate one...\n", handle->handle_id);
1459 }
1460 #ifndef HAVE_SCTP
1461 if(data) {
1462 JANUS_LOG(LOG_WARN, "[%"SCNu64"] -- DataChannels have been negotiated, but support for them has not been compiled...\n", handle->handle_id);
1463 }
1464 #endif
1465 /* We behave differently if it's a new session or an update... */
1466 if(!renegotiation) {
1467 /* New session */
1468 if(offer) {
1469 /* Setup ICE locally (we received an offer) */
1470 if(janus_ice_setup_local(handle, offer, audio, video, data, do_trickle) < 0) {
1471 JANUS_LOG(LOG_ERR, "Error setting ICE locally\n");
1472 janus_sdp_destroy(parsed_sdp);
1473 g_free(jsep_type);
1474 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1475 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error setting ICE locally");
1476 janus_mutex_unlock(&handle->mutex);
1477 goto jsondone;
1478 }
1479 } else {
1480 /* Make sure we're waiting for an ANSWER in the first place */
1481 if(!handle->agent) {
1482 JANUS_LOG(LOG_ERR, "Unexpected ANSWER (did we offer?)\n");
1483 janus_sdp_destroy(parsed_sdp);
1484 g_free(jsep_type);
1485 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1486 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNEXPECTED_ANSWER, "Unexpected ANSWER (did we offer?)");
1487 janus_mutex_unlock(&handle->mutex);
1488 goto jsondone;
1489 }
1490 }
1491 /* If for some reason the user is asking us to force using a relay, do that. Notice
1492 * that this only works with libnice >= 0.1.14, and will cause the PeerConnection
1493 * to fail if Janus itself is not configured to use a TURN server. Don't use this
1494 * feature if you don't know what you're doing! You will almost always NOT want
1495 * Janus itself to use TURN: https://janus.conf.meetecho.com/docs/FAQ.html#turn */
1496 if(json_is_true(json_object_get(jsep, "force_relay"))) {
1497 if(!janus_ice_is_force_relay_allowed()) {
1498 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Forcing Janus to use a TURN server is not allowed\n", handle->handle_id);
1499 } else {
1500 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Forcing Janus to use a TURN server\n", handle->handle_id);
1501 g_object_set(G_OBJECT(handle->agent), "force-relay", TRUE, NULL);
1502 }
1503 }
1504 /* Process the remote SDP */
1505 if(janus_sdp_process(handle, parsed_sdp, rids_hml, FALSE) < 0) {
1506 JANUS_LOG(LOG_ERR, "Error processing SDP\n");
1507 janus_sdp_destroy(parsed_sdp);
1508 g_free(jsep_type);
1509 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1510 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_JSEP_INVALID_SDP, "Error processing SDP");
1511 janus_mutex_unlock(&handle->mutex);
1512 goto jsondone;
1513 }
1514 if(!offer) {
1515 /* Set remote candidates now (we received an answer) */
1516 if(do_trickle) {
1517 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
1518 } else {
1519 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
1520 }
1521 janus_request_ice_handle_answer(handle, audio, video, data, jsep_sdp);
1522 /* Check if the answer does contain the mid/abs-send-time/twcc extmaps */
1523 gboolean do_mid = FALSE, do_twcc = FALSE, do_abs_send_time = FALSE;
1524 GList *temp = parsed_sdp->m_lines;
1525 while(temp) {
1526 janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
1527 GList *tempA = m->attributes;
1528 while(tempA) {
1529 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
1530 if(a->name && a->value && !strcasecmp(a->name, "extmap")) {
1531 if(strstr(a->value, JANUS_RTP_EXTMAP_MID))
1532 do_mid = TRUE;
1533 else if(strstr(a->value, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC))
1534 do_twcc = TRUE;
1535 else if(strstr(a->value, JANUS_RTP_EXTMAP_ABS_SEND_TIME))
1536 do_abs_send_time = TRUE;
1537 }
1538 tempA = tempA->next;
1539 }
1540 temp = temp->next;
1541 }
1542 if(!do_mid && handle->stream)
1543 handle->stream->mid_ext_id = 0;
1544 if(!do_twcc && handle->stream) {
1545 handle->stream->do_transport_wide_cc = FALSE;
1546 handle->stream->transport_wide_cc_ext_id = 0;
1547 }
1548 if(!do_abs_send_time && handle->stream)
1549 handle->stream->abs_send_time_ext_id = 0;
1550 } else {
1551 /* Check if the mid RTP extension is being negotiated */
1552 handle->stream->mid_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_MID);
1553 /* Check if the RTP Stream ID extension is being negotiated */
1554 handle->stream->rid_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_RID);
1555 handle->stream->ridrtx_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_REPAIRED_RID);
1556 /* Check if the audio level ID extension is being negotiated */
1557 handle->stream->audiolevel_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_AUDIO_LEVEL);
1558 /* Check if the video orientation ID extension is being negotiated */
1559 handle->stream->videoorientation_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION);
1560 /* Check if the abs-send-time ID extension is being negotiated */
1561 handle->stream->abs_send_time_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_ABS_SEND_TIME);
1562 /* Check if transport wide CC is supported */
1563 int transport_wide_cc_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC);
1564 handle->stream->do_transport_wide_cc = transport_wide_cc_ext_id > 0 ? TRUE : FALSE;
1565 handle->stream->transport_wide_cc_ext_id = transport_wide_cc_ext_id;
1566 }
1567 } else {
1568 /* FIXME This is a renegotiation: we can currently only handle simple changes in media
1569 * direction and ICE restarts: anything more complex than that will result in an error */
1570 JANUS_LOG(LOG_INFO, "[%"SCNu64"] Negotiation update, checking what changed...\n", handle->handle_id);
1571 if(janus_sdp_process(handle, parsed_sdp, rids_hml, TRUE) < 0) {
1572 JANUS_LOG(LOG_ERR, "Error processing SDP\n");
1573 janus_sdp_destroy(parsed_sdp);
1574 g_free(jsep_type);
1575 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1576 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNEXPECTED_ANSWER, "Error processing SDP");
1577 janus_mutex_unlock(&handle->mutex);
1578 goto jsondone;
1579 }
1580 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ICE_RESTART)) {
1581 JANUS_LOG(LOG_INFO, "[%"SCNu64"] Restarting ICE...\n", handle->handle_id);
1582 /* FIXME We only need to do that for offers: if it's an answer, we did that already */
1583 if(offer) {
1584 janus_ice_restart(handle);
1585 } else {
1586 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ICE_RESTART);
1587 }
1588 /* Update remote credentials for ICE */
1589 if(handle->stream) {
1590 nice_agent_set_remote_credentials(handle->agent, handle->stream->stream_id,
1591 handle->stream->ruser, handle->stream->rpass);
1592 }
1593 /* If we're full-trickling, we'll need to resend the candidates later */
1594 if(janus_ice_is_full_trickle_enabled()) {
1595 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RESEND_TRICKLES);
1596 }
1597 }
1598 #ifdef HAVE_SCTP
1599 if(!offer) {
1600 /* Were datachannels just added? */
1601 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS)) {
1602 janus_ice_stream *stream = handle->stream;
1603 if(stream != NULL && stream->component != NULL
1604 && stream->component->dtls != NULL && stream->component->dtls->sctp == NULL) {
1605 /* Create SCTP association as well */
1606 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Creating datachannels...\n", handle->handle_id);
1607 janus_dtls_srtp_create_sctp(stream->component->dtls);
1608 }
1609 }
1610 }
1611 #endif
1612 /* Check if renegotiating has added new RTP extensions */
1613 if(offer) {
1614 /* Check if the mid RTP extension is being negotiated */
1615 handle->stream->mid_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_MID);
1616 /* Check if the RTP Stream ID extension is being negotiated */
1617 handle->stream->rid_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_RID);
1618 handle->stream->ridrtx_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_REPAIRED_RID);
1619 /* Check if the audio level ID extension is being negotiated */
1620 handle->stream->audiolevel_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_AUDIO_LEVEL);
1621 /* Check if the video orientation ID extension is being negotiated */
1622 handle->stream->videoorientation_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION);
1623 /* Check if the abs-send-time ID extension is being negotiated */
1624 handle->stream->abs_send_time_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_ABS_SEND_TIME);
1625 /* Check if transport wide CC is supported */
1626 int transport_wide_cc_ext_id = janus_rtp_header_extension_get_id(jsep_sdp, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC);
1627 handle->stream->do_transport_wide_cc = transport_wide_cc_ext_id > 0 ? TRUE : FALSE;
1628 handle->stream->transport_wide_cc_ext_id = transport_wide_cc_ext_id;
1629 }
1630 }
1631 char *tmp = handle->remote_sdp;
1632 handle->remote_sdp = g_strdup(jsep_sdp);
1633 g_free(tmp);
1634 janus_mutex_unlock(&handle->mutex);
1635 /* Anonymize SDP */
1636 if(janus_sdp_anonymize(parsed_sdp) < 0) {
1637 /* Invalid SDP */
1638 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_JSEP_INVALID_SDP, "JSEP error: invalid SDP");
1639 janus_sdp_destroy(parsed_sdp);
1640 g_free(jsep_type);
1641 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1642 goto jsondone;
1643 }
1644 jsep_sdp_stripped = janus_sdp_write(parsed_sdp);
1645 janus_sdp_destroy(parsed_sdp);
1646 sdp = NULL;
1647 if(e2ee)
1648 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_E2EE);
1649 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1650 }
1651
1652 /* Make sure the app handle is still valid */
1653 if(handle->app == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1654 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this message");
1655 g_free(jsep_type);
1656 g_free(jsep_sdp_stripped);
1657 janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
1658 goto jsondone;
1659 }
1660
1661 /* Send the message to the plugin (which must eventually free transaction_text and unref the two objects, body and jsep) */
1662 json_incref(body);
1663 json_t *body_jsep = NULL;
1664 if(jsep_sdp_stripped) {
1665 body_jsep = json_pack("{ssss}", "type", jsep_type, "sdp", jsep_sdp_stripped);
1666 /* Check if simulcasting is enabled */
1667 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
1668 if(handle->stream && (handle->stream->rid[0] || handle->stream->video_ssrc_peer[1])) {
1669 json_t *simulcast = json_object();
1670 /* If we have rids, pass those, otherwise pass the SSRCs */
1671 if(handle->stream->rid[0]) {
1672 json_t *rids = json_array();
1673 if(handle->stream->rid[2])
1674 json_array_append_new(rids, json_string(handle->stream->rid[2]));
1675 if(handle->stream->rid[1])
1676 json_array_append_new(rids, json_string(handle->stream->rid[1]));
1677 json_array_append_new(rids, json_string(handle->stream->rid[0]));
1678 json_object_set_new(simulcast, "rids", rids);
1679 json_object_set_new(simulcast, "rid-ext", json_integer(handle->stream->rid_ext_id));
1680 } else {
1681 json_t *ssrcs = json_array();
1682 json_array_append_new(ssrcs, json_integer(handle->stream->video_ssrc_peer[0]));
1683 if(handle->stream->video_ssrc_peer[1])
1684 json_array_append_new(ssrcs, json_integer(handle->stream->video_ssrc_peer[1]));
1685 if(handle->stream->video_ssrc_peer[2])
1686 json_array_append_new(ssrcs, json_integer(handle->stream->video_ssrc_peer[2]));
1687 json_object_set_new(simulcast, "ssrcs", ssrcs);
1688 }
1689 json_object_set_new(body_jsep, "simulcast", simulcast);
1690 }
1691 }
1692 /* Check if this is a renegotiation or update */
1693 if(renegotiation)
1694 json_object_set_new(body_jsep, "update", json_true());
1695 /* If media is encrypted end-to-end, the plugin may need to know */
1696 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_E2EE))
1697 json_object_set_new(body_jsep, "e2ee", json_true());
1698 }
1699 janus_plugin_result *result = plugin_t->handle_message(handle->app_handle,
1700 g_strdup((char *)transaction_text), body, body_jsep);
1701 g_free(jsep_type);
1702 g_free(jsep_sdp_stripped);
1703 if(result == NULL) {
1704 /* Something went horribly wrong! */
1705 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "Plugin didn't give a result");
1706 goto jsondone;
1707 }
1708 if(result->type == JANUS_PLUGIN_OK) {
1709 /* The plugin gave a result already (synchronous request/response) */
1710 if(result->content == NULL || !json_is_object(result->content)) {
1711 /* Missing content, or not a JSON object */
1712 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE,
1713 result->content == NULL ?
1714 "Plugin didn't provide any content for this synchronous response" :
1715 "Plugin returned an invalid JSON response");
1716 janus_plugin_result_destroy(result);
1717 goto jsondone;
1718 }
1719 /* Reference the content, as destroying the result instance will decref it */
1720 json_incref(result->content);
1721 /* Prepare JSON response */
1722 json_t *reply = janus_create_message("success", session->session_id, transaction_text);
1723 json_object_set_new(reply, "sender", json_integer(handle->handle_id));
1724 if(janus_is_opaqueid_in_api_enabled() && handle->opaque_id != NULL)
1725 json_object_set_new(reply, "opaque_id", json_string(handle->opaque_id));
1726 json_t *plugin_data = json_object();
1727 json_object_set_new(plugin_data, "plugin", json_string(plugin_t->get_package()));
1728 json_object_set_new(plugin_data, "data", result->content);
1729 json_object_set_new(reply, "plugindata", plugin_data);
1730 /* Send the success reply */
1731 ret = janus_process_success(request, reply);
1732 } else if(result->type == JANUS_PLUGIN_OK_WAIT) {
1733 /* The plugin received the request but didn't process it yet, send an ack (asynchronous notifications may follow) */
1734 json_t *reply = janus_create_message("ack", session_id, transaction_text);
1735 if(result->text)
1736 json_object_set_new(reply, "hint", json_string(result->text));
1737 /* Send the success reply */
1738 ret = janus_process_success(request, reply);
1739 } else {
1740 /* Something went horribly wrong! */
1741 ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE,
1742 (char *)(result->text ? result->text : "Plugin returned a severe (unknown) error"));
1743 janus_plugin_result_destroy(result);
1744 goto jsondone;
1745 }
1746 janus_plugin_result_destroy(result);
1747 } else if(!strcasecmp(message_text, "trickle")) {
1748 if(handle == NULL) {
1749 /* Trickle is an handle-level command */
1750 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
1751 goto jsondone;
1752 }
1753 if(handle->app == NULL || !janus_plugin_session_is_alive(handle->app_handle)) {
1754 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_MESSAGE, "No plugin to handle this trickle candidate");
1755 goto jsondone;
1756 }
1757 json_t *candidate = json_object_get(root, "candidate");
1758 json_t *candidates = json_object_get(root, "candidates");
1759 if(candidate == NULL && candidates == NULL) {
1760 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, "Missing mandatory element (candidate|candidates)");
1761 goto jsondone;
1762 }
1763 if(candidate != NULL && candidates != NULL) {
1764 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_JSON, "Can't have both candidate and candidates");
1765 goto jsondone;
1766 }
1767 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
1768 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Received a trickle, but still cleaning a previous session\n", handle->handle_id);
1769 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_WEBRTC_STATE, "Still cleaning a previous session");
1770 goto jsondone;
1771 }
1772 janus_mutex_lock(&handle->mutex);
1773 if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE)) {
1774 /* It looks like this peer supports Trickle, after all */
1775 JANUS_LOG(LOG_VERB, "Handle %"SCNu64" supports trickle even if it didn't negotiate it...\n", handle->handle_id);
1776 janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE);
1777 }
1778 /* Is there any stream ready? this trickle may get here before the SDP it relates to */
1779 if(handle->stream == NULL) {
1780 JANUS_LOG(LOG_WARN, "[%"SCNu64"] No stream, queueing this trickle as it got here before the SDP...\n", handle->handle_id);
1781 /* Enqueue this trickle candidate(s), we'll process this later */
1782 janus_ice_trickle *early_trickle = janus_ice_trickle_new(transaction_text, candidate ? candidate : candidates);
1783 handle->pending_trickles = g_list_append(handle->pending_trickles, early_trickle);
1784 /* Send the ack right away, an event will tell the application if the candidate(s) failed */
1785 goto trickledone;
1786 }
1787 /* Is the ICE stack ready already? */
1788 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER) ||
1789 !janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER) ||
1790 !janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER)) {
1791 const char *cause = NULL;
1792 if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER))
1793 cause = "processing the offer";
1794 else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER))
1795 cause = "waiting for the answer";
1796 else if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER))
1797 cause = "waiting for the offer";
1798 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still %s, queueing this trickle to wait until we're done there...\n",
1799 handle->handle_id, cause);
1800 /* Enqueue this trickle candidate(s), we'll process this later */
1801 janus_ice_trickle *early_trickle = janus_ice_trickle_new(transaction_text, candidate ? candidate : candidates);
1802 handle->pending_trickles = g_list_append(handle->pending_trickles, early_trickle);
1803 /* Send the ack right away, an event will tell the application if the candidate(s) failed */
1804 goto trickledone;
1805 }
1806 if(candidate != NULL) {
1807 /* We got a single candidate */
1808 int error = 0;
1809 const char *error_string = NULL;
1810 if((error = janus_ice_trickle_parse(handle, candidate, &error_string)) != 0) {
1811 ret = janus_process_error(request, session_id, transaction_text, error, "%s", error_string);
1812 janus_mutex_unlock(&handle->mutex);
1813 goto jsondone;
1814 }
1815 } else {
1816 /* We got multiple candidates in an array */
1817 if(!json_is_array(candidates)) {
1818 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "candidates is not an array");
1819 janus_mutex_unlock(&handle->mutex);
1820 goto jsondone;
1821 }
1822 JANUS_LOG(LOG_VERB, "Got multiple candidates (%zu)\n", json_array_size(candidates));
1823 if(json_array_size(candidates) > 0) {
1824 /* Handle remote candidates */
1825 size_t i = 0;
1826 for(i=0; i<json_array_size(candidates); i++) {
1827 json_t *c = json_array_get(candidates, i);
1828 /* FIXME We don't care if any trickle fails to parse */
1829 janus_ice_trickle_parse(handle, c, NULL);
1830 }
1831 }
1832 }
1833
1834 trickledone:
1835 janus_mutex_unlock(&handle->mutex);
1836 /* We reply right away, not to block the web server... */
1837 json_t *reply = janus_create_message("ack", session_id, transaction_text);
1838 /* Send the success reply */
1839 ret = janus_process_success(request, reply);
1840 } else {
1841 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN_REQUEST, "Unknown request '%s'", message_text);
1842 }
1843
1844 jsondone:
1845 /* Done processing */
1846 if(handle != NULL)
1847 janus_refcount_decrease(&handle->ref);
1848 if(session != NULL)
1849 janus_refcount_decrease(&session->ref);
1850 return ret;
1851 }
1852
janus_json_token_plugin_array(const char * token_value)1853 static json_t *janus_json_token_plugin_array(const char *token_value) {
1854 json_t *plugins_list = json_array();
1855 GList *plugins = janus_auth_list_plugins(token_value);
1856 if(plugins != NULL) {
1857 GList *tmp = plugins;
1858 while(tmp) {
1859 janus_plugin *p = (janus_plugin *)tmp->data;
1860 if(p != NULL)
1861 json_array_append_new(plugins_list, json_string(p->get_package()));
1862 tmp = tmp->next;
1863 }
1864 g_list_free(plugins);
1865 plugins = NULL;
1866 }
1867 return plugins_list;
1868 }
1869
janus_json_list_token_plugins(const char * token_value,const gchar * transaction_text)1870 static json_t *janus_json_list_token_plugins(const char *token_value, const gchar *transaction_text) {
1871 json_t *plugins_list = janus_json_token_plugin_array(token_value);
1872 /* Prepare JSON reply */
1873 json_t *reply = janus_create_message("success", 0, transaction_text);
1874 json_t *data = json_object();
1875 json_object_set_new(data, "plugins", plugins_list);
1876 json_object_set_new(reply, "data", data);
1877 return reply;
1878 }
1879
janus_request_allow_token(janus_request * request,guint64 session_id,const gchar * transaction_text,gboolean allow,gboolean add)1880 static int janus_request_allow_token(janus_request *request, guint64 session_id, const gchar *transaction_text, gboolean allow, gboolean add) {
1881 /* Allow/disallow a valid token valid to access a plugin */
1882 int ret = -1;
1883 int error_code = 0;
1884 char error_cause[100];
1885 json_t *root = request->message;
1886 if(!janus_auth_is_stored_mode()) {
1887 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Stored-Token based authentication disabled");
1888 goto jsondone;
1889 }
1890 JANUS_VALIDATE_JSON_OBJECT(root, add_token_parameters,
1891 error_code, error_cause, FALSE,
1892 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
1893 /* Any plugin this token should be limited to? */
1894 json_t *allowed = json_object_get(root, "plugins");
1895 if(error_code == 0 && !add && (!allowed || json_array_size(allowed) == 0)) {
1896 error_code = JANUS_ERROR_INVALID_ELEMENT_TYPE;
1897 g_strlcpy(error_cause, "Invalid element type (plugins should be a non-empty array)", sizeof(error_cause));
1898 }
1899 if(error_code != 0) {
1900 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
1901 goto jsondone;
1902 }
1903 json_t *token = json_object_get(root, "token");
1904 const char *token_value = json_string_value(token);
1905 if(add) {
1906 /* First of all, add the new token */
1907 if(!janus_auth_add_token(token_value)) {
1908 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error adding token");
1909 goto jsondone;
1910 }
1911 } else {
1912 /* Check if the token is valid, first */
1913 if(!janus_auth_check_token(token_value)) {
1914 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_TOKEN_NOT_FOUND, "Token %s not found", token_value);
1915 goto jsondone;
1916 }
1917 }
1918 if(allowed && json_array_size(allowed) > 0) {
1919 /* Specify which plugins this token has access to */
1920 size_t i = 0;
1921 gboolean ok = TRUE;
1922 for(i=0; i<json_array_size(allowed); i++) {
1923 json_t *p = json_array_get(allowed, i);
1924 if(!p || !json_is_string(p)) {
1925 /* FIXME Should we fail here? */
1926 if(add){
1927 JANUS_LOG(LOG_WARN, "Invalid plugin passed to the new token request, skipping...\n");
1928 continue;
1929 } else {
1930 JANUS_LOG(LOG_ERR, "Invalid plugin passed to the new token request...\n");
1931 ok = FALSE;
1932 break;
1933 }
1934 }
1935 const gchar *plugin_text = json_string_value(p);
1936 janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1937 if(plugin_t == NULL) {
1938 /* FIXME Should we fail here? */
1939 if(add) {
1940 JANUS_LOG(LOG_WARN, "No such plugin '%s' passed to the new token request, skipping...\n", plugin_text);
1941 continue;
1942 } else {
1943 JANUS_LOG(LOG_ERR, "No such plugin '%s' passed to the new token request...\n", plugin_text);
1944 ok = FALSE;
1945 }
1946 break;
1947 }
1948 }
1949 if(!ok) {
1950 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (some of the provided plugins are invalid)");
1951 goto jsondone;
1952 }
1953 /* Take care of the plugins access limitations */
1954 i = 0;
1955 for(i=0; i<json_array_size(allowed); i++) {
1956 json_t *p = json_array_get(allowed, i);
1957 const gchar *plugin_text = json_string_value(p);
1958 janus_plugin *plugin_t = janus_plugin_find(plugin_text);
1959 if(!(allow ? janus_auth_allow_plugin(token_value, plugin_t) : janus_auth_disallow_plugin(token_value, plugin_t))) {
1960 /* FIXME Should we notify individual failures? */
1961 JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_text);
1962 }
1963 }
1964 } else {
1965 /* No plugin limitation specified, allow all plugins */
1966 if(plugins && g_hash_table_size(plugins) > 0) {
1967 GHashTableIter iter;
1968 gpointer value;
1969 g_hash_table_iter_init(&iter, plugins);
1970 while (g_hash_table_iter_next(&iter, NULL, &value)) {
1971 janus_plugin *plugin_t = value;
1972 if(plugin_t == NULL)
1973 continue;
1974 if(!janus_auth_allow_plugin(token_value, plugin_t)) {
1975 JANUS_LOG(LOG_WARN, "Error allowing access to '%s' to the new token, bad things may happen...\n", plugin_t->get_package());
1976 }
1977 }
1978 }
1979 }
1980 /* Get the list of plugins this new token can now access */
1981 json_t *reply = janus_json_list_token_plugins(token_value, transaction_text);
1982 /* Send the success reply */
1983 ret = janus_process_success(request, reply);
1984 jsondone:
1985 return ret;
1986 }
1987
1988 /* Admin/monitor WebServer requests handler */
janus_process_incoming_admin_request(janus_request * request)1989 int janus_process_incoming_admin_request(janus_request *request) {
1990 int ret = -1;
1991 int error_code = 0;
1992 char error_cause[100];
1993 if(request == NULL) {
1994 JANUS_LOG(LOG_ERR, "Missing request or payload to process, giving up...\n");
1995 return ret;
1996 }
1997 json_t *root = request->message;
1998 /* Ok, let's start with the ids */
1999 guint64 session_id = 0, handle_id = 0;
2000 json_t *s = json_object_get(root, "session_id");
2001 if(s && json_is_integer(s))
2002 session_id = json_integer_value(s);
2003 json_t *h = json_object_get(root, "handle_id");
2004 if(h && json_is_integer(h))
2005 handle_id = json_integer_value(h);
2006
2007 janus_session *session = NULL;
2008 janus_ice_handle *handle = NULL;
2009
2010 /* Get transaction and message request */
2011 JANUS_VALIDATE_JSON_OBJECT(root, admin_parameters,
2012 error_code, error_cause, FALSE,
2013 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2014 if(error_code != 0) {
2015 ret = janus_process_error_string(request, session_id, NULL, error_code, error_cause);
2016 goto jsondone;
2017 }
2018 json_t *transaction = json_object_get(root, "transaction");
2019 const gchar *transaction_text = json_string_value(transaction);
2020 json_t *message = json_object_get(root, "janus");
2021 const gchar *message_text = json_string_value(message);
2022
2023 if(session_id == 0 && handle_id == 0) {
2024 /* Can only be a 'Get all sessions' or some general setting manipulation request */
2025 if(!strcasecmp(message_text, "info")) {
2026 /* The generic info request */
2027 ret = janus_process_success(request, janus_info(transaction_text));
2028 goto jsondone;
2029 }
2030 if(!strcasecmp(message_text, "ping")) {
2031 /* Prepare JSON reply */
2032 json_t *reply = janus_create_message("pong", 0, transaction_text);
2033 ret = janus_process_success(request, reply);
2034 goto jsondone;
2035 }
2036 if(admin_api_secret != NULL) {
2037 /* There's an admin/monitor secret, check that the client provided it */
2038 json_t *secret = json_object_get(root, "admin_secret");
2039 if(!secret || !json_is_string(secret) || !janus_strcmp_const_time(json_string_value(secret), admin_api_secret)) {
2040 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
2041 goto jsondone;
2042 }
2043 }
2044 if(!strcasecmp(message_text, "get_status")) {
2045 /* Return some info on the settings (mostly debug-related, at the moment) */
2046 json_t *reply = janus_create_message("success", 0, transaction_text);
2047 json_t *status = json_object();
2048 json_object_set_new(status, "token_auth", janus_auth_is_enabled() ? json_true() : json_false());
2049 json_object_set_new(status, "session_timeout", json_integer(global_session_timeout));
2050 json_object_set_new(status, "reclaim_session_timeout", json_integer(reclaim_session_timeout));
2051 json_object_set_new(status, "candidates_timeout", json_integer(candidates_timeout));
2052 json_object_set_new(status, "log_level", json_integer(janus_log_level));
2053 json_object_set_new(status, "log_timestamps", janus_log_timestamps ? json_true() : json_false());
2054 json_object_set_new(status, "log_colors", janus_log_colors ? json_true() : json_false());
2055 json_object_set_new(status, "locking_debug", lock_debug ? json_true() : json_false());
2056 json_object_set_new(status, "refcount_debug", refcount_debug ? json_true() : json_false());
2057 json_object_set_new(status, "libnice_debug", janus_ice_is_ice_debugging_enabled() ? json_true() : json_false());
2058 json_object_set_new(status, "min_nack_queue", json_integer(janus_get_min_nack_queue()));
2059 json_object_set_new(status, "nack-optimizations", janus_is_nack_optimizations_enabled() ? json_true() : json_false());
2060 json_object_set_new(status, "no_media_timer", json_integer(janus_get_no_media_timer()));
2061 json_object_set_new(status, "slowlink_threshold", json_integer(janus_get_slowlink_threshold()));
2062 json_object_set_new(reply, "status", status);
2063 /* Send the success reply */
2064 ret = janus_process_success(request, reply);
2065 goto jsondone;
2066 } else if(!strcasecmp(message_text, "set_session_timeout")) {
2067 JANUS_VALIDATE_JSON_OBJECT(root, timeout_parameters,
2068 error_code, error_cause, FALSE,
2069 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2070 if(error_code != 0) {
2071 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2072 goto jsondone;
2073 }
2074 json_t *timeout = json_object_get(root, "timeout");
2075 int timeout_num = json_integer_value(timeout);
2076 if(timeout_num < 0) {
2077 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (timeout should be a positive integer)");
2078 goto jsondone;
2079 }
2080
2081 /* Set global session timeout */
2082 global_session_timeout = timeout_num;
2083
2084 /* Prepare JSON reply */
2085 json_t *reply = json_object();
2086 json_object_set_new(reply, "janus", json_string("success"));
2087 json_object_set_new(reply, "transaction", json_string(transaction_text));
2088 json_object_set_new(reply, "timeout", json_integer(timeout_num));
2089
2090 /* Send the success reply */
2091 ret = janus_process_success(request, reply);
2092 goto jsondone;
2093 } else if(!strcasecmp(message_text, "set_log_level")) {
2094 /* Change the debug logging level */
2095 JANUS_VALIDATE_JSON_OBJECT(root, level_parameters,
2096 error_code, error_cause, FALSE,
2097 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2098 if(error_code != 0) {
2099 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2100 goto jsondone;
2101 }
2102 json_t *level = json_object_get(root, "level");
2103 int level_num = json_integer_value(level);
2104 if(level_num < LOG_NONE || level_num > LOG_MAX) {
2105 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (level should be between %d and %d)", LOG_NONE, LOG_MAX);
2106 goto jsondone;
2107 }
2108 janus_log_level = level_num;
2109 /* Prepare JSON reply */
2110 json_t *reply = janus_create_message("success", 0, transaction_text);
2111 json_object_set_new(reply, "level", json_integer(janus_log_level));
2112 /* Send the success reply */
2113 ret = janus_process_success(request, reply);
2114 goto jsondone;
2115 } else if(!strcasecmp(message_text, "set_locking_debug")) {
2116 /* Enable/disable the locking debug (would show a message on the console for every lock attempt) */
2117 JANUS_VALIDATE_JSON_OBJECT(root, debug_parameters,
2118 error_code, error_cause, FALSE,
2119 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2120 if(error_code != 0) {
2121 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2122 goto jsondone;
2123 }
2124 json_t *debug = json_object_get(root, "debug");
2125 lock_debug = json_is_true(debug);
2126 /* Prepare JSON reply */
2127 json_t *reply = janus_create_message("success", 0, transaction_text);
2128 json_object_set_new(reply, "locking_debug", lock_debug ? json_true() : json_false());
2129 /* Send the success reply */
2130 ret = janus_process_success(request, reply);
2131 goto jsondone;
2132 } else if(!strcasecmp(message_text, "set_refcount_debug")) {
2133 /* Enable/disable the reference counter debug (would show a message on the console for every increase/decrease) */
2134 JANUS_VALIDATE_JSON_OBJECT(root, debug_parameters,
2135 error_code, error_cause, FALSE,
2136 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2137 if(error_code != 0) {
2138 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2139 goto jsondone;
2140 }
2141 json_t *debug = json_object_get(root, "debug");
2142 if(json_is_true(debug)) {
2143 refcount_debug = TRUE;
2144 } else {
2145 refcount_debug = FALSE;
2146 }
2147 /* Prepare JSON reply */
2148 json_t *reply = janus_create_message("success", 0, transaction_text);
2149 json_object_set_new(reply, "refcount_debug", refcount_debug ? json_true() : json_false());
2150 /* Send the success reply */
2151 ret = janus_process_success(request, reply);
2152 goto jsondone;
2153 } else if(!strcasecmp(message_text, "set_log_timestamps")) {
2154 /* Enable/disable the log timestamps */
2155 JANUS_VALIDATE_JSON_OBJECT(root, timestamps_parameters,
2156 error_code, error_cause, FALSE,
2157 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2158 if(error_code != 0) {
2159 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2160 goto jsondone;
2161 }
2162 json_t *timestamps = json_object_get(root, "timestamps");
2163 janus_log_timestamps = json_is_true(timestamps);
2164 /* Prepare JSON reply */
2165 json_t *reply = janus_create_message("success", 0, transaction_text);
2166 json_object_set_new(reply, "log_timestamps", janus_log_timestamps ? json_true() : json_false());
2167 /* Send the success reply */
2168 ret = janus_process_success(request, reply);
2169 goto jsondone;
2170 } else if(!strcasecmp(message_text, "set_log_colors")) {
2171 /* Enable/disable the log colors */
2172 JANUS_VALIDATE_JSON_OBJECT(root, colors_parameters,
2173 error_code, error_cause, FALSE,
2174 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2175 if(error_code != 0) {
2176 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2177 goto jsondone;
2178 }
2179 json_t *colors = json_object_get(root, "colors");
2180 janus_log_colors = json_is_true(colors);
2181 /* Prepare JSON reply */
2182 json_t *reply = janus_create_message("success", 0, transaction_text);
2183 json_object_set_new(reply, "log_colors", janus_log_colors ? json_true() : json_false());
2184 /* Send the success reply */
2185 ret = janus_process_success(request, reply);
2186 goto jsondone;
2187 } else if(!strcasecmp(message_text, "set_libnice_debug")) {
2188 /* Enable/disable the libnice debugging (http://nice.freedesktop.org/libnice/libnice-Debug-messages.html) */
2189 JANUS_VALIDATE_JSON_OBJECT(root, debug_parameters,
2190 error_code, error_cause, FALSE,
2191 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2192 if(error_code != 0) {
2193 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2194 goto jsondone;
2195 }
2196 json_t *debug = json_object_get(root, "debug");
2197 if(json_is_true(debug)) {
2198 janus_ice_debugging_enable();
2199 } else {
2200 janus_ice_debugging_disable();
2201 }
2202 /* Prepare JSON reply */
2203 json_t *reply = janus_create_message("success", 0, transaction_text);
2204 json_object_set_new(reply, "libnice_debug", janus_ice_is_ice_debugging_enabled() ? json_true() : json_false());
2205 /* Send the success reply */
2206 ret = janus_process_success(request, reply);
2207 goto jsondone;
2208 } else if(!strcasecmp(message_text, "set_min_nack_queue")) {
2209 /* Change the current value for the min NACK queue */
2210 JANUS_VALIDATE_JSON_OBJECT(root, mnq_parameters,
2211 error_code, error_cause, FALSE,
2212 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2213 if(error_code != 0) {
2214 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2215 goto jsondone;
2216 }
2217 json_t *mnq = json_object_get(root, "min_nack_queue");
2218 int mnq_num = json_integer_value(mnq);
2219 janus_set_min_nack_queue(mnq_num);
2220 /* Prepare JSON reply */
2221 json_t *reply = janus_create_message("success", 0, transaction_text);
2222 json_object_set_new(reply, "min_nack_queue", json_integer(janus_get_min_nack_queue()));
2223 /* Send the success reply */
2224 ret = janus_process_success(request, reply);
2225 goto jsondone;
2226 } else if(!strcasecmp(message_text, "set_nack_optimizations")) {
2227 /* Enable/disable NACK optimizations */
2228 JANUS_VALIDATE_JSON_OBJECT(root, nopt_parameters,
2229 error_code, error_cause, FALSE,
2230 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2231 if(error_code != 0) {
2232 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2233 goto jsondone;
2234 }
2235 gboolean optimize = json_is_true(json_object_get(root, "nack_optimizations"));
2236 janus_set_nack_optimizations_enabled(optimize);
2237 /* Prepare JSON reply */
2238 json_t *reply = janus_create_message("success", 0, transaction_text);
2239 json_object_set_new(reply, "nack-optimizations", janus_is_nack_optimizations_enabled() ? json_true() : json_false());
2240 /* Send the success reply */
2241 ret = janus_process_success(request, reply);
2242 goto jsondone;
2243 } else if(!strcasecmp(message_text, "set_no_media_timer")) {
2244 /* Change the current value for the no-media timer */
2245 JANUS_VALIDATE_JSON_OBJECT(root, nmt_parameters,
2246 error_code, error_cause, FALSE,
2247 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2248 if(error_code != 0) {
2249 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2250 goto jsondone;
2251 }
2252 json_t *nmt = json_object_get(root, "no_media_timer");
2253 int nmt_num = json_integer_value(nmt);
2254 janus_set_no_media_timer(nmt_num);
2255 /* Prepare JSON reply */
2256 json_t *reply = json_object();
2257 json_object_set_new(reply, "janus", json_string("success"));
2258 json_object_set_new(reply, "transaction", json_string(transaction_text));
2259 json_object_set_new(reply, "no_media_timer", json_integer(janus_get_no_media_timer()));
2260 /* Send the success reply */
2261 ret = janus_process_success(request, reply);
2262 goto jsondone;
2263 } else if(!strcasecmp(message_text, "set_slowlink_threshold")) {
2264 /* Change the current value for the slowlink-threshold value */
2265 JANUS_VALIDATE_JSON_OBJECT(root, st_parameters,
2266 error_code, error_cause, FALSE,
2267 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2268 if(error_code != 0) {
2269 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2270 goto jsondone;
2271 }
2272 json_t *nmt = json_object_get(root, "slowlink_threshold");
2273 int nmt_num = json_integer_value(nmt);
2274 janus_set_slowlink_threshold(nmt_num);
2275 /* Prepare JSON reply */
2276 json_t *reply = json_object();
2277 json_object_set_new(reply, "janus", json_string("success"));
2278 json_object_set_new(reply, "transaction", json_string(transaction_text));
2279 json_object_set_new(reply, "slowlink_threshold", json_integer(janus_get_slowlink_threshold()));
2280 /* Send the success reply */
2281 ret = janus_process_success(request, reply);
2282 goto jsondone;
2283 } else if(!strcasecmp(message_text, "accept_new_sessions")) {
2284 /* Configure whether we should accept new incoming sessions or not:
2285 * this can be particularly useful whenever, e.g., we want to stop
2286 * accepting new sessions because we're draining this server */
2287 JANUS_VALIDATE_JSON_OBJECT(root, ans_parameters,
2288 error_code, error_cause, FALSE,
2289 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2290 if(error_code != 0) {
2291 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2292 goto jsondone;
2293 }
2294 json_t *accept = json_object_get(root, "accept");
2295 accept_new_sessions = json_is_true(accept);
2296 /* Prepare JSON reply */
2297 json_t *reply = janus_create_message("success", 0, transaction_text);
2298 json_object_set_new(reply, "accept", accept_new_sessions ? json_true() : json_false());
2299 /* Send the success reply */
2300 ret = janus_process_success(request, reply);
2301 goto jsondone;
2302 } else if(!strcasecmp(message_text, "message_plugin")) {
2303 /* Contact a plugin and expect a response */
2304 JANUS_VALIDATE_JSON_OBJECT(root, messageplugin_parameters,
2305 error_code, error_cause, FALSE,
2306 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2307 if(error_code != 0) {
2308 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2309 goto jsondone;
2310 }
2311 json_t *plugin = json_object_get(root, "plugin");
2312 const char *plugin_value = json_string_value(plugin);
2313 janus_plugin *p = janus_plugin_find(plugin_value);
2314 if(p == NULL) {
2315 /* No such handler... */
2316 g_snprintf(error_cause, sizeof(error_cause), "%s", "Invalid plugin");
2317 ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, error_cause);
2318 goto jsondone;
2319 }
2320 if(p->handle_admin_message == NULL) {
2321 /* Handler doesn't implement the hook... */
2322 g_snprintf(error_cause, sizeof(error_cause), "%s", "Plugin doesn't support Admin API messages");
2323 ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, error_cause);
2324 goto jsondone;
2325 }
2326 json_t *query = json_object_get(root, "request");
2327 json_t *response = p->handle_admin_message(query);
2328 /* Prepare JSON reply */
2329 json_t *reply = json_object();
2330 json_object_set_new(reply, "janus", json_string("success"));
2331 json_object_set_new(reply, "transaction", json_string(transaction_text));
2332 json_object_set_new(reply, "response", response ? response : json_object());
2333 /* Send the success reply */
2334 ret = janus_process_success(request, reply);
2335 goto jsondone;
2336 } else if(!strcasecmp(message_text, "query_transport")) {
2337 /* Contact a transport and expect a response */
2338 JANUS_VALIDATE_JSON_OBJECT(root, querytransport_parameters,
2339 error_code, error_cause, FALSE,
2340 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2341 if(error_code != 0) {
2342 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2343 goto jsondone;
2344 }
2345 json_t *transport = json_object_get(root, "transport");
2346 const char *transport_value = json_string_value(transport);
2347 janus_transport *t = g_hash_table_lookup(transports, transport_value);
2348 if(t == NULL) {
2349 /* No such transport... */
2350 g_snprintf(error_cause, sizeof(error_cause), "%s", "Invalid transport");
2351 ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, error_cause);
2352 goto jsondone;
2353 }
2354 if(t->query_transport == NULL) {
2355 /* Transport doesn't implement the hook... */
2356 g_snprintf(error_cause, sizeof(error_cause), "%s", "Transport plugin doesn't support queries");
2357 ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, error_cause);
2358 goto jsondone;
2359 }
2360 json_t *query = json_object_get(root, "request");
2361 json_t *response = t->query_transport(query);
2362 /* Prepare JSON reply */
2363 json_t *reply = json_object();
2364 json_object_set_new(reply, "janus", json_string("success"));
2365 json_object_set_new(reply, "transaction", json_string(transaction_text));
2366 json_object_set_new(reply, "response", response ? response : json_object());
2367 /* Send the success reply */
2368 ret = janus_process_success(request, reply);
2369 goto jsondone;
2370 } else if(!strcasecmp(message_text, "query_eventhandler")) {
2371 /* Contact an event handler and expect a response */
2372 JANUS_VALIDATE_JSON_OBJECT(root, queryhandler_parameters,
2373 error_code, error_cause, FALSE,
2374 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2375 if(error_code != 0) {
2376 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2377 goto jsondone;
2378 }
2379 json_t *handler = json_object_get(root, "handler");
2380 const char *handler_value = json_string_value(handler);
2381 janus_eventhandler *evh = g_hash_table_lookup(eventhandlers, handler_value);
2382 if(evh == NULL) {
2383 /* No such handler... */
2384 g_snprintf(error_cause, sizeof(error_cause), "%s", "Invalid event handler");
2385 ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, error_cause);
2386 goto jsondone;
2387 }
2388 if(evh->handle_request == NULL) {
2389 /* Handler doesn't implement the hook... */
2390 g_snprintf(error_cause, sizeof(error_cause), "%s", "Event handler doesn't support queries");
2391 ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, error_cause);
2392 goto jsondone;
2393 }
2394 json_t *query = json_object_get(root, "request");
2395 json_t *response = evh->handle_request(query);
2396 /* Prepare JSON reply */
2397 json_t *reply = json_object();
2398 json_object_set_new(reply, "janus", json_string("success"));
2399 json_object_set_new(reply, "transaction", json_string(transaction_text));
2400 json_object_set_new(reply, "response", response ? response : json_object());
2401 /* Send the success reply */
2402 ret = janus_process_success(request, reply);
2403 goto jsondone;
2404 } else if(!strcasecmp(message_text, "query_logger")) {
2405 /* Contact a logger and expect a response */
2406 JANUS_VALIDATE_JSON_OBJECT(root, querylogger_parameters,
2407 error_code, error_cause, FALSE,
2408 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2409 if(error_code != 0) {
2410 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2411 goto jsondone;
2412 }
2413 json_t *logger = json_object_get(root, "logger");
2414 const char *logger_value = json_string_value(logger);
2415 janus_logger *l = g_hash_table_lookup(loggers, logger_value);
2416 if(l == NULL) {
2417 /* No such handler... */
2418 g_snprintf(error_cause, sizeof(error_cause), "%s", "Invalid logger");
2419 ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, error_cause);
2420 goto jsondone;
2421 }
2422 if(l->handle_request == NULL) {
2423 /* Handler doesn't implement the hook... */
2424 g_snprintf(error_cause, sizeof(error_cause), "%s", "Logger doesn't support queries");
2425 ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, error_cause);
2426 goto jsondone;
2427 }
2428 json_t *query = json_object_get(root, "request");
2429 json_t *response = l->handle_request(query);
2430 /* Prepare JSON reply */
2431 json_t *reply = json_object();
2432 json_object_set_new(reply, "janus", json_string("success"));
2433 json_object_set_new(reply, "transaction", json_string(transaction_text));
2434 json_object_set_new(reply, "response", response ? response : json_object());
2435 /* Send the success reply */
2436 ret = janus_process_success(request, reply);
2437 goto jsondone;
2438 } else if(!strcasecmp(message_text, "custom_event")) {
2439 /* Enqueue a custom "external" event to notify via event handlers */
2440 JANUS_VALIDATE_JSON_OBJECT(root, customevent_parameters,
2441 error_code, error_cause, FALSE,
2442 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2443 if(error_code != 0) {
2444 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2445 goto jsondone;
2446 }
2447 json_t *schema = json_object_get(root, "schema");
2448 const char *schema_value = json_string_value(schema);
2449 json_t *data = json_object_get(root, "data");
2450 if(janus_events_is_enabled()) {
2451 json_incref(data);
2452 janus_events_notify_handlers(JANUS_EVENT_TYPE_EXTERNAL, JANUS_EVENT_SUBTYPE_NONE,
2453 0, schema_value, data);
2454 }
2455 /* Prepare JSON reply */
2456 json_t *reply = json_object();
2457 json_object_set_new(reply, "janus", json_string("success"));
2458 json_object_set_new(reply, "transaction", json_string(transaction_text));
2459 /* Send the success reply */
2460 ret = janus_process_success(request, reply);
2461 goto jsondone;
2462 } else if(!strcasecmp(message_text, "custom_logline")) {
2463 /* Print something custom on the logs, using the specified debug level */
2464 JANUS_VALIDATE_JSON_OBJECT(root, customlogline_parameters,
2465 error_code, error_cause, FALSE,
2466 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2467 if(error_code != 0) {
2468 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2469 goto jsondone;
2470 }
2471 json_t *line = json_object_get(root, "line");
2472 const char *log_line = json_string_value(line);
2473 json_t *level = json_object_get(root, "level");
2474 int log_level = LOG_INFO;
2475 if(level) {
2476 log_level = json_integer_value(level);
2477 if(log_level < LOG_NONE || log_level > LOG_MAX)
2478 log_level = LOG_INFO;
2479 }
2480 /* Print the log line on the log */
2481 JANUS_LOG(log_level, "%s\n", log_line);
2482 /* Prepare JSON reply */
2483 json_t *reply = json_object();
2484 json_object_set_new(reply, "janus", json_string("success"));
2485 json_object_set_new(reply, "transaction", json_string(transaction_text));
2486 /* Send the success reply */
2487 ret = janus_process_success(request, reply);
2488 goto jsondone;
2489 } else if(!strcasecmp(message_text, "list_sessions")) {
2490 /* List sessions */
2491 session_id = 0;
2492 json_t *list = json_array();
2493 if(sessions != NULL && g_hash_table_size(sessions) > 0) {
2494 janus_mutex_lock(&sessions_mutex);
2495 GHashTableIter iter;
2496 gpointer value;
2497 g_hash_table_iter_init(&iter, sessions);
2498 while (g_hash_table_iter_next(&iter, NULL, &value)) {
2499 janus_session *session = value;
2500 if(session == NULL) {
2501 continue;
2502 }
2503 json_array_append_new(list, json_integer(session->session_id));
2504 }
2505 janus_mutex_unlock(&sessions_mutex);
2506 }
2507 /* Prepare JSON reply */
2508 json_t *reply = janus_create_message("success", 0, transaction_text);
2509 json_object_set_new(reply, "sessions", list);
2510 /* Send the success reply */
2511 ret = janus_process_success(request, reply);
2512 goto jsondone;
2513 } else if(!strcasecmp(message_text, "add_token")) {
2514 /* Add a token valid for authentication */
2515 ret = janus_request_allow_token(request, session_id, transaction_text, TRUE, TRUE);
2516 goto jsondone;
2517 } else if(!strcasecmp(message_text, "list_tokens")) {
2518 /* List all the valid tokens */
2519 if(!janus_auth_is_stored_mode()) {
2520 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Stored-Token based authentication disabled");
2521 goto jsondone;
2522 }
2523 json_t *tokens_list = json_array();
2524 GList *list = janus_auth_list_tokens();
2525 if(list != NULL) {
2526 GList *tmp = list;
2527 while(tmp) {
2528 char *token = (char *)tmp->data;
2529 if(token != NULL) {
2530 json_t *plugins_list = janus_json_token_plugin_array(token);
2531 if(json_array_size(plugins_list) > 0) {
2532 json_t *t = json_object();
2533 json_object_set_new(t, "token", json_string(token));
2534 json_object_set_new(t, "allowed_plugins", plugins_list);
2535 json_array_append_new(tokens_list, t);
2536 }
2537 else
2538 json_decref(plugins_list);
2539 tmp->data = NULL;
2540 g_free(token);
2541 }
2542 tmp = tmp->next;
2543 }
2544 g_list_free(list);
2545 }
2546 /* Prepare JSON reply */
2547 json_t *reply = janus_create_message("success", 0, transaction_text);
2548 json_t *data = json_object();
2549 json_object_set_new(data, "tokens", tokens_list);
2550 json_object_set_new(reply, "data", data);
2551 /* Send the success reply */
2552 ret = janus_process_success(request, reply);
2553 goto jsondone;
2554 } else if(!strcasecmp(message_text, "allow_token")) {
2555 /* Allow a valid token valid to access a plugin */
2556 ret = janus_request_allow_token(request, session_id, transaction_text, TRUE, FALSE);
2557 goto jsondone;
2558 } else if(!strcasecmp(message_text, "disallow_token")) {
2559 /* Disallow a valid token valid from accessing a plugin */
2560 ret = janus_request_allow_token(request, session_id, transaction_text, FALSE, FALSE);
2561 goto jsondone;
2562 } else if(!strcasecmp(message_text, "remove_token")) {
2563 /* Invalidate a token for authentication purposes */
2564 if(!janus_auth_is_stored_mode()) {
2565 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Stored-Token based authentication disabled");
2566 goto jsondone;
2567 }
2568 JANUS_VALIDATE_JSON_OBJECT(root, token_parameters,
2569 error_code, error_cause, FALSE,
2570 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2571 if(error_code != 0) {
2572 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2573 goto jsondone;
2574 }
2575 json_t *token = json_object_get(root, "token");
2576 const char *token_value = json_string_value(token);
2577 if(!janus_auth_remove_token(token_value)) {
2578 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, "Error removing token");
2579 goto jsondone;
2580 }
2581 /* Prepare JSON reply */
2582 json_t *reply = janus_create_message("success", 0, transaction_text);
2583 /* Send the success reply */
2584 ret = janus_process_success(request, reply);
2585 goto jsondone;
2586 } else if(!strcasecmp(message_text, "resolve_address")) {
2587 /* Helper method to evaluate whether this instance can resolve an address, and how soon */
2588 JANUS_VALIDATE_JSON_OBJECT(root, resaddr_parameters,
2589 error_code, error_cause, FALSE,
2590 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2591 if(error_code != 0) {
2592 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2593 goto jsondone;
2594 }
2595 const char *address = json_string_value(json_object_get(root, "address"));
2596 /* Resolve the address */
2597 gint64 start = janus_get_monotonic_time();
2598 struct addrinfo *res = NULL;
2599 janus_network_address addr;
2600 janus_network_address_string_buffer addr_buf;
2601 if(getaddrinfo(address, NULL, NULL, &res) != 0 ||
2602 janus_network_address_from_sockaddr(res->ai_addr, &addr) != 0 ||
2603 janus_network_address_to_string_buffer(&addr, &addr_buf) != 0) {
2604 JANUS_LOG(LOG_ERR, "Could not resolve %s...\n", address);
2605 if(res)
2606 freeaddrinfo(res);
2607 ret = janus_process_error_string(request, session_id, transaction_text,
2608 JANUS_ERROR_UNKNOWN, (char *)"Could not resolve address");
2609 goto jsondone;
2610 }
2611 gint64 end = janus_get_monotonic_time();
2612 freeaddrinfo(res);
2613 /* Prepare JSON reply */
2614 json_t *reply = janus_create_message("success", 0, transaction_text);
2615 json_object_set_new(reply, "ip", json_string(janus_network_address_string_from_buffer(&addr_buf)));
2616 json_object_set_new(reply, "elapsed", json_integer(end-start));
2617 /* Send the success reply */
2618 ret = janus_process_success(request, reply);
2619 goto jsondone;
2620 } else if(!strcasecmp(message_text, "test_stun")) {
2621 /* Helper method to evaluate whether this instance can use STUN with a specific server */
2622 JANUS_VALIDATE_JSON_OBJECT(root, teststun_parameters,
2623 error_code, error_cause, FALSE,
2624 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2625 if(error_code != 0) {
2626 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2627 goto jsondone;
2628 }
2629 const char *address = json_string_value(json_object_get(root, "address"));
2630 uint16_t port = json_integer_value(json_object_get(root, "port"));
2631 uint16_t local_port = json_integer_value(json_object_get(root, "localport"));
2632 /* Resolve the address */
2633 gint64 start = janus_get_monotonic_time();
2634 struct addrinfo *res = NULL;
2635 janus_network_address addr;
2636 janus_network_address_string_buffer addr_buf;
2637 if(getaddrinfo(address, NULL, NULL, &res) != 0 ||
2638 janus_network_address_from_sockaddr(res->ai_addr, &addr) != 0 ||
2639 janus_network_address_to_string_buffer(&addr, &addr_buf) != 0) {
2640 JANUS_LOG(LOG_ERR, "Could not resolve %s...\n", address);
2641 if(res)
2642 freeaddrinfo(res);
2643 ret = janus_process_error_string(request, session_id, transaction_text,
2644 JANUS_ERROR_UNKNOWN, (char *)"Could not resolve address");
2645 goto jsondone;
2646 }
2647 freeaddrinfo(res);
2648 /* Test the STUN server */
2649 janus_network_address public_addr = { 0 };
2650 uint16_t public_port = 0;
2651 if(janus_ice_test_stun_server(&addr, port, local_port, &public_addr, &public_port) < 0) {
2652 ret = janus_process_error_string(request, session_id, transaction_text,
2653 JANUS_ERROR_UNKNOWN, (char *)"STUN request failed");
2654 goto jsondone;
2655 }
2656 if(janus_network_address_to_string_buffer(&public_addr, &addr_buf) != 0) {
2657 ret = janus_process_error_string(request, session_id, transaction_text,
2658 JANUS_ERROR_UNKNOWN, (char *)"Could not resolve public address");
2659 goto jsondone;
2660 }
2661 const char *public_ip_addr = janus_network_address_string_from_buffer(&addr_buf);
2662 gint64 end = janus_get_monotonic_time();
2663 /* Prepare JSON reply */
2664 json_t *reply = janus_create_message("success", 0, transaction_text);
2665 json_object_set_new(reply, "public_ip", json_string(public_ip_addr));
2666 json_object_set_new(reply, "public_port", json_integer(public_port));
2667 json_object_set_new(reply, "elapsed", json_integer(end-start));
2668 /* Send the success reply */
2669 ret = janus_process_success(request, reply);
2670 goto jsondone;
2671 } else {
2672 /* No message we know of */
2673 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2674 goto jsondone;
2675 }
2676 }
2677 if(session_id < 1) {
2678 JANUS_LOG(LOG_ERR, "Invalid session\n");
2679 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
2680 goto jsondone;
2681 }
2682 if(h && handle_id < 1) {
2683 JANUS_LOG(LOG_ERR, "Invalid handle\n");
2684 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, NULL);
2685 goto jsondone;
2686 }
2687
2688 /* Go on with the processing */
2689 if(admin_api_secret != NULL) {
2690 /* There's an API secret, check that the client provided it */
2691 json_t *secret = json_object_get(root, "admin_secret");
2692 if(!secret || !json_is_string(secret) || !janus_strcmp_const_time(json_string_value(secret), admin_api_secret)) {
2693 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNAUTHORIZED, NULL);
2694 goto jsondone;
2695 }
2696 }
2697
2698 /* If we got here, make sure we have a session (and/or a handle) */
2699 session = janus_session_find(session_id);
2700 if(!session) {
2701 JANUS_LOG(LOG_ERR, "Couldn't find any session %"SCNu64"...\n", session_id);
2702 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_SESSION_NOT_FOUND, "No such session %"SCNu64"", session_id);
2703 goto jsondone;
2704 }
2705 handle = NULL;
2706 if(handle_id > 0) {
2707 handle = janus_session_handles_find(session, handle_id);
2708 if(!handle) {
2709 JANUS_LOG(LOG_ERR, "Couldn't find any handle %"SCNu64" in session %"SCNu64"...\n", handle_id, session_id);
2710 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_HANDLE_NOT_FOUND, "No such handle %"SCNu64" in session %"SCNu64"", handle_id, session_id);
2711 goto jsondone;
2712 }
2713 }
2714
2715 /* What is this? */
2716 if(handle == NULL) {
2717 /* Session-related */
2718 if(!strcasecmp(message_text, "destroy_session")) {
2719 janus_mutex_lock(&sessions_mutex);
2720 g_hash_table_remove(sessions, &session->session_id);
2721 janus_mutex_unlock(&sessions_mutex);
2722 /* Notify the source that the session has been destroyed */
2723 janus_request *source = janus_session_get_request(session);
2724 if(source && source->transport)
2725 source->transport->session_over(source->instance, session->session_id, FALSE, FALSE);
2726 janus_request_unref(source);
2727 /* Schedule the session for deletion */
2728 janus_session_destroy(session);
2729
2730 /* Prepare JSON reply */
2731 json_t *reply = janus_create_message("success", session_id, transaction_text);
2732 /* Send the success reply */
2733 ret = janus_process_success(request, reply);
2734 /* Notify event handlers as well */
2735 if(janus_events_is_enabled())
2736 janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, JANUS_EVENT_SUBTYPE_NONE,
2737 session_id, "destroyed", NULL);
2738 goto jsondone;
2739 } else if (!strcasecmp(message_text, "set_session_timeout")) {
2740 /* Specific session timeout setting. */
2741 JANUS_VALIDATE_JSON_OBJECT(root, session_timeout_parameters,
2742 error_code, error_cause, FALSE,
2743 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2744 if(error_code != 0) {
2745 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2746 goto jsondone;
2747 }
2748
2749 /* Positive timeout is seconds, 0 is unlimited, -1 is global session timeout */
2750 json_t *timeout = json_object_get(root, "timeout");
2751 int timeout_num = json_integer_value(timeout);
2752 if(timeout_num < -1) {
2753 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (timeout should be a non-negative integer or -1)");
2754 goto jsondone;
2755 }
2756
2757 /* Set specific session timeout */
2758 janus_mutex_lock(&session->mutex);
2759 session->timeout = timeout_num;
2760 janus_mutex_unlock(&session->mutex);
2761
2762 /* Prepare JSON reply */
2763 json_t *reply = json_object();
2764 json_object_set_new(reply, "janus", json_string("success"));
2765 json_object_set_new(reply, "transaction", json_string(transaction_text));
2766 json_object_set_new(reply, "timeout", json_integer(timeout_num));
2767 json_object_set_new(reply, "session_id", json_integer(session_id));
2768
2769 /* Send the success reply */
2770 ret = janus_process_success(request, reply);
2771 goto jsondone;
2772 } else if(strcasecmp(message_text, "list_handles")) {
2773 /* If this is not a request to destroy a session, it must be a request to list the handles */
2774 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2775 goto jsondone;
2776 }
2777
2778 /* List handles */
2779 json_t *list = janus_session_handles_list_json(session);
2780 /* Prepare JSON reply */
2781 json_t *reply = janus_create_message("success", session_id, transaction_text);
2782 json_object_set_new(reply, "handles", list);
2783 /* Send the success reply */
2784 ret = janus_process_success(request, reply);
2785 goto jsondone;
2786 } else {
2787 /* Handle-related */
2788 if(!strcasecmp(message_text, "detach_handle")) {
2789 if(handle->app == NULL || handle->app_handle == NULL) {
2790 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin to detach from");
2791 goto jsondone;
2792 }
2793 int error = janus_session_handles_remove(session, handle);
2794 if(error != 0) {
2795 /* TODO Make error struct to pass verbose information */
2796 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "Couldn't detach from plugin: error '%d'", error);
2797 /* TODO Delete handle instance */
2798 goto jsondone;
2799 }
2800 /* Prepare JSON reply */
2801 json_t *reply = janus_create_message("success", session_id, transaction_text);
2802 /* Send the success reply */
2803 ret = janus_process_success(request, reply);
2804 goto jsondone;
2805 } else if(!strcasecmp(message_text, "hangup_webrtc")) {
2806 if(handle->app == NULL || handle->app_handle == NULL) {
2807 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_DETACH, "No plugin attached");
2808 goto jsondone;
2809 }
2810 janus_ice_webrtc_hangup(handle, "Admin API");
2811 /* Prepare JSON reply */
2812 json_t *reply = janus_create_message("success", session_id, transaction_text);
2813 /* Send the success reply */
2814 ret = janus_process_success(request, reply);
2815 goto jsondone;
2816 } else if(!strcasecmp(message_text, "start_pcap") || !strcasecmp(message_text, "start_text2pcap")) {
2817 /* Start dumping RTP and RTCP packets to a pcap or text2pcap file */
2818 JANUS_VALIDATE_JSON_OBJECT(root, text2pcap_parameters,
2819 error_code, error_cause, FALSE,
2820 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2821 if(error_code != 0) {
2822 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2823 goto jsondone;
2824 }
2825 gboolean text = !strcasecmp(message_text, "start_text2pcap");
2826 const char *folder = json_string_value(json_object_get(root, "folder"));
2827 const char *filename = json_string_value(json_object_get(root, "filename"));
2828 int truncate = json_integer_value(json_object_get(root, "truncate"));
2829 if(handle->text2pcap != NULL) {
2830 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN,
2831 text ? "text2pcap already started" : "pcap already started");
2832 goto jsondone;
2833 }
2834 handle->text2pcap = janus_text2pcap_create(folder, filename, truncate, text);
2835 if(handle->text2pcap == NULL) {
2836 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN,
2837 text ? "Error starting text2pcap dump" : "Error starting pcap dump");
2838 goto jsondone;
2839 }
2840 g_atomic_int_set(&handle->dump_packets, 1);
2841 /* Prepare JSON reply */
2842 json_t *reply = json_object();
2843 json_object_set_new(reply, "janus", json_string("success"));
2844 json_object_set_new(reply, "transaction", json_string(transaction_text));
2845 /* Send the success reply */
2846 ret = janus_process_success(request, reply);
2847 goto jsondone;
2848 } else if(!strcasecmp(message_text, "stop_pcap") || !strcasecmp(message_text, "stop_text2pcap")) {
2849 /* Stop dumping RTP and RTCP packets to a pcap or text2pcap file */
2850 if(handle->text2pcap == NULL) {
2851 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN,
2852 "Capture not started");
2853 goto jsondone;
2854 }
2855 if(g_atomic_int_compare_and_exchange(&handle->dump_packets, 1, 0)) {
2856 janus_text2pcap_close(handle->text2pcap);
2857 g_clear_pointer(&handle->text2pcap, janus_text2pcap_free);
2858 }
2859 /* Prepare JSON reply */
2860 json_t *reply = json_object();
2861 json_object_set_new(reply, "janus", json_string("success"));
2862 json_object_set_new(reply, "transaction", json_string(transaction_text));
2863 /* Send the success reply */
2864 ret = janus_process_success(request, reply);
2865 goto jsondone;
2866 }
2867 /* If this is not a request to start/stop debugging to text2pcap, it must be a handle_info */
2868 if(strcasecmp(message_text, "handle_info")) {
2869 ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
2870 goto jsondone;
2871 }
2872 JANUS_VALIDATE_JSON_OBJECT(root, handleinfo_parameters,
2873 error_code, error_cause, FALSE,
2874 JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
2875 if(error_code != 0) {
2876 ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
2877 goto jsondone;
2878 }
2879 /* Check if we should limit the response to the plugin-specific info */
2880 gboolean plugin_only = json_is_true(json_object_get(root, "plugin_only"));
2881 /* Prepare info */
2882 json_t *info = json_object();
2883 json_object_set_new(info, "session_id", json_integer(session_id));
2884 json_object_set_new(info, "session_last_activity", json_integer(session->last_activity));
2885 json_object_set_new(info, "session_timeout", json_integer(session->timeout == -1 ? global_session_timeout : (guint)session->timeout));
2886 janus_mutex_lock(&session->mutex);
2887 if(session->source && session->source->transport)
2888 json_object_set_new(info, "session_transport", json_string(session->source->transport->get_package()));
2889 janus_mutex_unlock(&session->mutex);
2890 janus_mutex_lock(&handle->mutex);
2891 json_object_set_new(info, "handle_id", json_integer(handle_id));
2892 if(handle->opaque_id)
2893 json_object_set_new(info, "opaque_id", json_string(handle->opaque_id));
2894 if(handle->token)
2895 json_object_set_new(info, "token", json_string(handle->token));
2896 json_object_set_new(info, "loop-running", (handle->mainloop != NULL &&
2897 g_main_loop_is_running(handle->mainloop)) ? json_true() : json_false());
2898 json_object_set_new(info, "created", json_integer(handle->created));
2899 json_object_set_new(info, "current_time", json_integer(janus_get_monotonic_time()));
2900 if(handle->app && janus_plugin_session_is_alive(handle->app_handle)) {
2901 janus_plugin *plugin = (janus_plugin *)handle->app;
2902 json_object_set_new(info, "plugin", json_string(plugin->get_package()));
2903 if(plugin->query_session) {
2904 /* FIXME This check will NOT work with legacy plugins that were compiled BEFORE the method was specified in plugin.h */
2905 json_t *query = plugin->query_session(handle->app_handle);
2906 if(query != NULL) {
2907 /* Make sure this is a JSON object */
2908 if(!json_is_object(query)) {
2909 JANUS_LOG(LOG_WARN, "Ignoring invalid query response from the plugin (not an object)\n");
2910 json_decref(query);
2911 } else {
2912 json_object_set_new(info, "plugin_specific", query);
2913 }
2914 query = NULL;
2915 }
2916 }
2917 }
2918 if(plugin_only)
2919 goto info_done;
2920 json_t *flags = json_object();
2921 json_object_set_new(flags, "got-offer", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER) ? json_true() : json_false());
2922 json_object_set_new(flags, "got-answer", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER) ? json_true() : json_false());
2923 json_object_set_new(flags, "negotiated", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEGOTIATED) ? json_true() : json_false());
2924 json_object_set_new(flags, "processing-offer", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER) ? json_true() : json_false());
2925 json_object_set_new(flags, "starting", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_START) ? json_true() : json_false());
2926 json_object_set_new(flags, "ice-restart", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ICE_RESTART) ? json_true() : json_false());
2927 json_object_set_new(flags, "ready", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY) ? json_true() : json_false());
2928 json_object_set_new(flags, "stopped", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP) ? json_true() : json_false());
2929 json_object_set_new(flags, "alert", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT) ? json_true() : json_false());
2930 json_object_set_new(flags, "trickle", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE) ? json_true() : json_false());
2931 json_object_set_new(flags, "all-trickles", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALL_TRICKLES) ? json_true() : json_false());
2932 json_object_set_new(flags, "resend-trickles", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RESEND_TRICKLES) ? json_true() : json_false());
2933 json_object_set_new(flags, "trickle-synced", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_TRICKLE_SYNCED) ? json_true() : json_false());
2934 json_object_set_new(flags, "data-channels", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS) ? json_true() : json_false());
2935 json_object_set_new(flags, "has-audio", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO) ? json_true() : json_false());
2936 json_object_set_new(flags, "has-video", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO) ? json_true() : json_false());
2937 json_object_set_new(flags, "new-datachan-sdp", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEW_DATACHAN_SDP) ? json_true() : json_false());
2938 json_object_set_new(flags, "rfc4588-rtx", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX) ? json_true() : json_false());
2939 json_object_set_new(flags, "cleaning", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING) ? json_true() : json_false());
2940 json_object_set_new(flags, "e2ee", janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_E2EE) ? json_true() : json_false());
2941 json_object_set_new(info, "flags", flags);
2942 if(handle->agent) {
2943 json_object_set_new(info, "agent-created", json_integer(handle->agent_created));
2944 json_object_set_new(info, "ice-mode", json_string(janus_ice_is_ice_lite_enabled() ? "lite" : "full"));
2945 json_object_set_new(info, "ice-role", json_string(handle->controlling ? "controlling" : "controlled"));
2946 }
2947 json_t *sdps = json_object();
2948 if(handle->rtp_profile)
2949 json_object_set_new(sdps, "profile", json_string(handle->rtp_profile));
2950 if(handle->local_sdp)
2951 json_object_set_new(sdps, "local", json_string(handle->local_sdp));
2952 if(handle->remote_sdp)
2953 json_object_set_new(sdps, "remote", json_string(handle->remote_sdp));
2954 json_object_set_new(info, "sdps", sdps);
2955 if(handle->pending_trickles)
2956 json_object_set_new(info, "pending-trickles", json_integer(g_list_length(handle->pending_trickles)));
2957 if(handle->queued_packets)
2958 json_object_set_new(info, "queued-packets", json_integer(g_async_queue_length(handle->queued_packets)));
2959 if(g_atomic_int_get(&handle->dump_packets) && handle->text2pcap) {
2960 if(handle->text2pcap->text) {
2961 json_object_set_new(info, "dump-to-text2pcap", json_true());
2962 json_object_set_new(info, "text2pcap-file", json_string(handle->text2pcap->filename));
2963 } else {
2964 json_object_set_new(info, "dump-to-pcap", json_true());
2965 json_object_set_new(info, "pcap-file", json_string(handle->text2pcap->filename));
2966 }
2967 }
2968 json_t *streams = json_array();
2969 if(handle->stream) {
2970 json_t *s = janus_admin_stream_summary(handle->stream);
2971 if(s)
2972 json_array_append_new(streams, s);
2973 }
2974 json_object_set_new(info, "streams", streams);
2975 info_done:
2976 janus_mutex_unlock(&handle->mutex);
2977 /* Prepare JSON reply */
2978 json_t *reply = janus_create_message("success", session_id, transaction_text);
2979 json_object_set_new(reply, "handle_id", json_integer(handle_id));
2980 json_object_set_new(reply, "info", info);
2981 /* Send the success reply */
2982 ret = janus_process_success(request, reply);
2983 goto jsondone;
2984 }
2985
2986 jsondone:
2987 /* Done processing */
2988 if(handle != NULL)
2989 janus_refcount_decrease(&handle->ref);
2990 if(session != NULL)
2991 janus_refcount_decrease(&session->ref);
2992 return ret;
2993 }
2994
janus_process_success(janus_request * request,json_t * payload)2995 int janus_process_success(janus_request *request, json_t *payload)
2996 {
2997 if(!request || !payload)
2998 return -1;
2999 /* Pass to the right transport plugin */
3000 JANUS_LOG(LOG_HUGE, "Sending %s API response to %s (%p)\n", request->admin ? "admin" : "Janus", request->transport->get_package(), request->instance);
3001 return request->transport->send_message(request->instance, request->request_id, request->admin, payload);
3002 }
3003
janus_process_error_string(janus_request * request,uint64_t session_id,const char * transaction,gint error,gchar * error_string)3004 static int janus_process_error_string(janus_request *request, uint64_t session_id, const char *transaction, gint error, gchar *error_string)
3005 {
3006 if(!request)
3007 return -1;
3008 /* Done preparing error */
3009 JANUS_LOG(LOG_VERB, "[%s] Returning %s API error %d (%s)\n", transaction, request->admin ? "admin" : "Janus", error, error_string);
3010 /* Prepare JSON error */
3011 json_t *reply = janus_create_message("error", session_id, transaction);
3012 json_t *error_data = json_object();
3013 json_object_set_new(error_data, "code", json_integer(error));
3014 json_object_set_new(error_data, "reason", json_string(error_string));
3015 json_object_set_new(reply, "error", error_data);
3016 /* Pass to the right transport plugin */
3017 return request->transport->send_message(request->instance, request->request_id, request->admin, reply);
3018 }
3019
janus_process_error(janus_request * request,uint64_t session_id,const char * transaction,gint error,const char * format,...)3020 int janus_process_error(janus_request *request, uint64_t session_id, const char *transaction, gint error, const char *format, ...)
3021 {
3022 if(!request)
3023 return -1;
3024 gchar *error_string = NULL;
3025 gchar error_buf[512];
3026 if(format == NULL) {
3027 /* No error string provided, use the default one */
3028 error_string = (gchar *)janus_get_api_error(error);
3029 } else {
3030 /* This callback has variable arguments (error string) */
3031 va_list ap;
3032 va_start(ap, format);
3033 g_vsnprintf(error_buf, sizeof(error_buf), format, ap);
3034 va_end(ap);
3035 error_string = error_buf;
3036 }
3037 return janus_process_error_string(request, session_id, transaction, error, error_string);
3038 }
3039
3040 /* Admin/monitor helpers */
janus_admin_stream_summary(janus_ice_stream * stream)3041 json_t *janus_admin_stream_summary(janus_ice_stream *stream) {
3042 if(stream == NULL)
3043 return NULL;
3044 json_t *s = json_object();
3045 json_object_set_new(s, "id", json_integer(stream->stream_id));
3046 json_object_set_new(s, "ready", json_integer(stream->cdone));
3047 json_t *ss = json_object();
3048 if(stream->audio_ssrc)
3049 json_object_set_new(ss, "audio", json_integer(stream->audio_ssrc));
3050 if(stream->video_ssrc)
3051 json_object_set_new(ss, "video", json_integer(stream->video_ssrc));
3052 if(stream->video_ssrc_rtx)
3053 json_object_set_new(ss, "video-rtx", json_integer(stream->video_ssrc_rtx));
3054 if(stream->audio_ssrc_peer)
3055 json_object_set_new(ss, "audio-peer", json_integer(stream->audio_ssrc_peer));
3056 if(stream->video_ssrc_peer[0])
3057 json_object_set_new(ss, "video-peer", json_integer(stream->video_ssrc_peer[0]));
3058 if(stream->video_ssrc_peer[1])
3059 json_object_set_new(ss, "video-peer-sim-1", json_integer(stream->video_ssrc_peer[1]));
3060 if(stream->video_ssrc_peer[2])
3061 json_object_set_new(ss, "video-peer-sim-2", json_integer(stream->video_ssrc_peer[2]));
3062 if(stream->video_ssrc_peer_rtx[0])
3063 json_object_set_new(ss, "video-peer-rtx", json_integer(stream->video_ssrc_peer_rtx[0]));
3064 if(stream->video_ssrc_peer_rtx[1])
3065 json_object_set_new(ss, "video-peer-sim-1-rtx", json_integer(stream->video_ssrc_peer_rtx[1]));
3066 if(stream->video_ssrc_peer_rtx[2])
3067 json_object_set_new(ss, "video-peer-sim-2-rtx", json_integer(stream->video_ssrc_peer_rtx[2]));
3068 json_object_set_new(s, "ssrc", ss);
3069 if(stream->rid[0] && stream->rid_ext_id > 0) {
3070 json_t *sr = json_object();
3071 json_t *rid = json_array();
3072 if(stream->rid[2])
3073 json_array_append_new(rid, json_string(stream->rid[2]));
3074 if(stream->rid[1])
3075 json_array_append_new(rid, json_string(stream->rid[1]));
3076 json_array_append_new(rid, json_string(stream->rid[0]));
3077 json_object_set_new(sr, "rid", rid);
3078 json_object_set_new(sr, "rid-ext-id", json_integer(stream->rid_ext_id));
3079 if(stream->ridrtx_ext_id > 0)
3080 json_object_set_new(sr, "ridrtx-ext-id", json_integer(stream->ridrtx_ext_id));
3081 json_object_set_new(sr, "rid-order", json_string(stream->rids_hml ? "hml" : "lmh"));
3082 if(stream->legacy_rid)
3083 json_object_set_new(sr, "rid-syntax", json_string("legacy"));
3084 json_object_set_new(s, "rid-simulcast", sr);
3085 }
3086 json_t *sd = json_object();
3087 json_object_set_new(sd, "audio-send", stream->audio_send ? json_true() : json_false());
3088 json_object_set_new(sd, "audio-recv", stream->audio_recv ? json_true() : json_false());
3089 json_object_set_new(sd, "video-send", stream->video_send ? json_true() : json_false());
3090 json_object_set_new(sd, "video-recv", stream->video_recv ? json_true() : json_false());
3091 json_object_set_new(s, "direction", sd);
3092 if(stream->audio_payload_type > -1 || stream->video_payload_type > -1) {
3093 json_t *sc = json_object();
3094 if(stream->audio_payload_type > -1)
3095 json_object_set_new(sc, "audio-pt", json_integer(stream->audio_payload_type));
3096 if(stream->audio_codec != NULL)
3097 json_object_set_new(sc, "audio-codec", json_string(stream->audio_codec));
3098 if(stream->video_payload_type > -1)
3099 json_object_set_new(sc, "video-pt", json_integer(stream->video_payload_type));
3100 if(stream->video_rtx_payload_type > -1)
3101 json_object_set_new(sc, "video-rtx-pt", json_integer(stream->video_rtx_payload_type));
3102 if(stream->video_codec != NULL)
3103 json_object_set_new(sc, "video-codec", json_string(stream->video_codec));
3104 json_object_set_new(s, "codecs", sc);
3105 }
3106 json_t *se = json_object();
3107 if(stream->mid_ext_id > 0)
3108 json_object_set_new(se, JANUS_RTP_EXTMAP_MID, json_integer(stream->mid_ext_id));
3109 if(stream->rid_ext_id > 0)
3110 json_object_set_new(se, JANUS_RTP_EXTMAP_RID, json_integer(stream->rid_ext_id));
3111 if(stream->ridrtx_ext_id > 0)
3112 json_object_set_new(se, JANUS_RTP_EXTMAP_REPAIRED_RID, json_integer(stream->ridrtx_ext_id));
3113 if(stream->abs_send_time_ext_id > 0)
3114 json_object_set_new(se, JANUS_RTP_EXTMAP_ABS_SEND_TIME, json_integer(stream->abs_send_time_ext_id));
3115 if(stream->transport_wide_cc_ext_id > 0)
3116 json_object_set_new(se, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC, json_integer(stream->transport_wide_cc_ext_id));
3117 if(stream->audiolevel_ext_id > 0)
3118 json_object_set_new(se, JANUS_RTP_EXTMAP_AUDIO_LEVEL, json_integer(stream->audiolevel_ext_id));
3119 if(stream->videoorientation_ext_id > 0)
3120 json_object_set_new(se, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION, json_integer(stream->videoorientation_ext_id));
3121 json_object_set_new(s, "extensions", se);
3122 json_t *bwe = json_object();
3123 json_object_set_new(bwe, "twcc", stream->do_transport_wide_cc ? json_true() : json_false());
3124 if(stream->transport_wide_cc_ext_id > 0)
3125 json_object_set_new(bwe, "twcc-ext-id", json_integer(stream->transport_wide_cc_ext_id));
3126 json_object_set_new(s, "bwe", bwe);
3127 json_object_set_new(s, "nack-queue-ms", json_integer(stream->nack_queue_ms));
3128 json_t *components = json_array();
3129 if(stream->component) {
3130 json_t *c = janus_admin_component_summary(stream->component);
3131 if(c)
3132 json_array_append_new(components, c);
3133 }
3134 json_t *rtcp_stats = NULL;
3135 if(stream->audio_rtcp_ctx != NULL) {
3136 rtcp_stats = json_object();
3137 json_t *audio_rtcp_stats = json_object();
3138 json_object_set_new(audio_rtcp_stats, "base", json_integer(stream->audio_rtcp_ctx->tb));
3139 json_object_set_new(audio_rtcp_stats, "rtt", json_integer(janus_rtcp_context_get_rtt(stream->audio_rtcp_ctx)));
3140 json_object_set_new(audio_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, FALSE)));
3141 json_object_set_new(audio_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, TRUE)));
3142 json_object_set_new(audio_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, FALSE)));
3143 json_object_set_new(audio_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->audio_rtcp_ctx, TRUE)));
3144 json_object_set_new(audio_rtcp_stats, "in-link-quality", json_integer(janus_rtcp_context_get_in_link_quality(stream->audio_rtcp_ctx)));
3145 json_object_set_new(audio_rtcp_stats, "in-media-link-quality", json_integer(janus_rtcp_context_get_in_media_link_quality(stream->audio_rtcp_ctx)));
3146 json_object_set_new(audio_rtcp_stats, "out-link-quality", json_integer(janus_rtcp_context_get_out_link_quality(stream->audio_rtcp_ctx)));
3147 json_object_set_new(audio_rtcp_stats, "out-media-link-quality", json_integer(janus_rtcp_context_get_out_media_link_quality(stream->audio_rtcp_ctx)));
3148 json_object_set_new(rtcp_stats, "audio", audio_rtcp_stats);
3149 }
3150 int vindex=0;
3151 for(vindex=0; vindex<3; vindex++) {
3152 if(stream->video_rtcp_ctx[vindex] != NULL) {
3153 if(rtcp_stats == NULL)
3154 rtcp_stats = json_object();
3155 json_t *video_rtcp_stats = json_object();
3156 json_object_set_new(video_rtcp_stats, "base", json_integer(stream->video_rtcp_ctx[vindex]->tb));
3157 if(vindex == 0)
3158 json_object_set_new(video_rtcp_stats, "rtt", json_integer(janus_rtcp_context_get_rtt(stream->video_rtcp_ctx[vindex])));
3159 json_object_set_new(video_rtcp_stats, "lost", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx[vindex], FALSE)));
3160 json_object_set_new(video_rtcp_stats, "lost-by-remote", json_integer(janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx[vindex], TRUE)));
3161 json_object_set_new(video_rtcp_stats, "jitter-local", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx[vindex], FALSE)));
3162 json_object_set_new(video_rtcp_stats, "jitter-remote", json_integer(janus_rtcp_context_get_jitter(stream->video_rtcp_ctx[vindex], TRUE)));
3163 json_object_set_new(video_rtcp_stats, "in-link-quality", json_integer(janus_rtcp_context_get_in_link_quality(stream->video_rtcp_ctx[vindex])));
3164 json_object_set_new(video_rtcp_stats, "in-media-link-quality", json_integer(janus_rtcp_context_get_in_media_link_quality(stream->video_rtcp_ctx[vindex])));
3165 json_object_set_new(video_rtcp_stats, "out-link-quality", json_integer(janus_rtcp_context_get_out_link_quality(stream->video_rtcp_ctx[vindex])));
3166 json_object_set_new(video_rtcp_stats, "out-media-link-quality", json_integer(janus_rtcp_context_get_out_media_link_quality(stream->video_rtcp_ctx[vindex])));
3167 if(vindex == 0)
3168 json_object_set_new(rtcp_stats, "video", video_rtcp_stats);
3169 else if(vindex == 1)
3170 json_object_set_new(rtcp_stats, "video-sim1", video_rtcp_stats);
3171 else
3172 json_object_set_new(rtcp_stats, "video-sim2", video_rtcp_stats);
3173 }
3174 }
3175 if(rtcp_stats != NULL)
3176 json_object_set_new(s, "rtcp_stats", rtcp_stats);
3177 if(stream->remb_bitrate > 0)
3178 json_object_set_new(s, "remb-bitrate", json_integer(stream->remb_bitrate));
3179 json_object_set_new(s, "components", components);
3180 return s;
3181 }
3182
janus_admin_component_summary(janus_ice_component * component)3183 json_t *janus_admin_component_summary(janus_ice_component *component) {
3184 if(component == NULL)
3185 return NULL;
3186 janus_ice_handle *handle = component->stream ? component->stream->handle : NULL;
3187 json_t *c = json_object();
3188 json_object_set_new(c, "id", json_integer(component->component_id));
3189 json_object_set_new(c, "state", json_string(janus_get_ice_state_name(component->state)));
3190 if(component->icefailed_detected) {
3191 json_object_set_new(c, "failed-detected", json_integer(component->icefailed_detected));
3192 json_object_set_new(c, "icetimer-started", component->icestate_source ? json_true() : json_false());
3193 }
3194 if(component->component_connected > 0)
3195 json_object_set_new(c, "connected", json_integer(component->component_connected));
3196 if(component->local_candidates) {
3197 json_t *cs = json_array();
3198 GSList *candidates = component->local_candidates, *i = NULL;
3199 for (i = candidates; i; i = i->next) {
3200 gchar *lc = (gchar *) i->data;
3201 if(lc)
3202 json_array_append_new(cs, json_string(lc));
3203 }
3204 json_object_set_new(c, "local-candidates", cs);
3205 }
3206 if(component->remote_candidates) {
3207 json_t *cs = json_array();
3208 GSList *candidates = component->remote_candidates, *i = NULL;
3209 for (i = candidates; i; i = i->next) {
3210 gchar *rc = (gchar *) i->data;
3211 if(rc)
3212 json_array_append_new(cs, json_string(rc));
3213 }
3214 json_object_set_new(c, "remote-candidates", cs);
3215 }
3216 if(component->selected_pair) {
3217 json_object_set_new(c, "selected-pair", json_string(component->selected_pair));
3218 }
3219 json_t *d = json_object();
3220 json_t *in_stats = json_object();
3221 json_t *out_stats = json_object();
3222 if(component->dtls) {
3223 janus_dtls_srtp *dtls = component->dtls;
3224 json_object_set_new(d, "fingerprint", json_string(janus_dtls_get_local_fingerprint()));
3225 if(component->stream) {
3226 if(component->stream->remote_fingerprint)
3227 json_object_set_new(d, "remote-fingerprint", json_string(component->stream->remote_fingerprint));
3228 if(component->stream->remote_hashing)
3229 json_object_set_new(d, "remote-fingerprint-hash", json_string(component->stream->remote_hashing));
3230 json_object_set_new(d, "dtls-role", json_string(janus_get_dtls_srtp_role(component->stream->dtls_role)));
3231 }
3232 json_object_set_new(d, "dtls-state", json_string(janus_get_dtls_srtp_state(dtls->dtls_state)));
3233 json_object_set_new(d, "retransmissions", json_integer(dtls->retransmissions));
3234 json_object_set_new(d, "valid", dtls->srtp_valid ? json_true() : json_false());
3235 const char *srtp_profile = janus_get_dtls_srtp_profile(dtls->srtp_profile);
3236 json_object_set_new(d, "srtp-profile", json_string(srtp_profile ? srtp_profile : "none"));
3237 json_object_set_new(d, "ready", dtls->ready ? json_true() : json_false());
3238 if(dtls->dtls_started > 0)
3239 json_object_set_new(d, "handshake-started", json_integer(dtls->dtls_started));
3240 if(dtls->dtls_connected > 0)
3241 json_object_set_new(d, "connected", json_integer(dtls->dtls_connected));
3242 if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
3243 json_object_set_new(in_stats, "audio_packets", json_integer(component->in_stats.audio.packets));
3244 json_object_set_new(in_stats, "audio_bytes", json_integer(component->in_stats.audio.bytes));
3245 json_object_set_new(in_stats, "audio_bytes_lastsec", json_integer(component->in_stats.audio.bytes_lastsec));
3246 json_object_set_new(in_stats, "do_audio_nacks", component->do_audio_nacks ? json_true() : json_false());
3247 if(component->do_audio_nacks) {
3248 json_object_set_new(in_stats, "audio_nacks", json_integer(component->in_stats.audio.nacks));
3249 if(component->stream && component->stream->audio_rtcp_ctx)
3250 json_object_set_new(in_stats, "audio_retransmissions", json_integer(component->stream->audio_rtcp_ctx->retransmitted));
3251 }
3252 }
3253 if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
3254 int vindex=0;
3255 for(vindex=0; vindex<3; vindex++) {
3256 if(vindex > 0 && component->stream->video_ssrc_peer[vindex] == 0)
3257 continue;
3258 json_t *container = (vindex == 0 ? in_stats : json_object());
3259 json_object_set_new(container, "video_packets", json_integer(component->in_stats.video[vindex].packets));
3260 json_object_set_new(container, "video_bytes", json_integer(component->in_stats.video[vindex].bytes));
3261 json_object_set_new(container, "video_bytes_lastsec", json_integer(component->in_stats.video[vindex].bytes_lastsec));
3262 if(vindex == 0)
3263 json_object_set_new(container, "do_video_nacks", component->do_video_nacks ? json_true() : json_false());
3264 if(component->do_video_nacks) {
3265 json_object_set_new(container, "video_nacks", json_integer(component->in_stats.video[vindex].nacks));
3266 if(component->stream && component->stream->video_rtcp_ctx[vindex])
3267 json_object_set_new(in_stats, "video_retransmissions", json_integer(component->stream->video_rtcp_ctx[vindex]->retransmitted));
3268 }
3269 if(vindex == 1)
3270 json_object_set_new(in_stats, "video-simulcast-1", container);
3271 else if(vindex == 2)
3272 json_object_set_new(in_stats, "video-simulcast-2", container);
3273 }
3274 }
3275 json_object_set_new(in_stats, "data_packets", json_integer(component->in_stats.data.packets));
3276 json_object_set_new(in_stats, "data_bytes", json_integer(component->in_stats.data.bytes));
3277 if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
3278 json_object_set_new(out_stats, "audio_packets", json_integer(component->out_stats.audio.packets));
3279 json_object_set_new(out_stats, "audio_bytes", json_integer(component->out_stats.audio.bytes));
3280 json_object_set_new(out_stats, "audio_bytes_lastsec", json_integer(component->out_stats.audio.bytes_lastsec));
3281 json_object_set_new(out_stats, "audio_nacks", json_integer(component->out_stats.audio.nacks));
3282 }
3283 if(handle && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
3284 json_object_set_new(out_stats, "video_packets", json_integer(component->out_stats.video[0].packets));
3285 json_object_set_new(out_stats, "video_bytes", json_integer(component->out_stats.video[0].bytes));
3286 json_object_set_new(out_stats, "video_bytes_lastsec", json_integer(component->out_stats.video[0].bytes_lastsec));
3287 json_object_set_new(out_stats, "video_nacks", json_integer(component->out_stats.video[0].nacks));
3288 }
3289 json_object_set_new(out_stats, "data_packets", json_integer(component->out_stats.data.packets));
3290 json_object_set_new(out_stats, "data_bytes", json_integer(component->out_stats.data.bytes));
3291 #ifdef HAVE_SCTP
3292 /* FIXME Actually check if this succeeded? */
3293 json_object_set_new(d, "sctp-association", dtls->sctp ? json_true() : json_false());
3294 #endif
3295 }
3296 json_object_set_new(c, "dtls", d);
3297 json_object_set_new(c, "in_stats", in_stats);
3298 json_object_set_new(c, "out_stats", out_stats);
3299 return c;
3300 }
3301
3302
3303 /* Transports */
janus_transport_close(gpointer key,gpointer value,gpointer user_data)3304 void janus_transport_close(gpointer key, gpointer value, gpointer user_data) {
3305 janus_transport *transport = (janus_transport *)value;
3306 if(!transport)
3307 return;
3308 transport->destroy();
3309 }
3310
janus_transportso_close(gpointer key,gpointer value,gpointer user_data)3311 void janus_transportso_close(gpointer key, gpointer value, gpointer user_data) {
3312 void *transport = value;
3313 if(!transport)
3314 return;
3315 /* FIXME We don't dlclose transports to be sure we can detect leaks */
3316 //~ dlclose(transport);
3317 }
3318
3319 /* Transport callback interface */
janus_transport_incoming_request(janus_transport * plugin,janus_transport_session * transport,void * request_id,gboolean admin,json_t * message,json_error_t * error)3320 void janus_transport_incoming_request(janus_transport *plugin, janus_transport_session *transport, void *request_id, gboolean admin, json_t *message, json_error_t *error) {
3321 JANUS_LOG(LOG_VERB, "Got %s API request from %s (%p)\n", admin ? "an admin" : "a Janus", plugin->get_package(), transport);
3322 /* Create a janus_request instance to handle the request */
3323 janus_request *request = janus_request_new(plugin, transport, request_id, admin, message);
3324 /* Enqueue the request, the thread will pick it up */
3325 g_async_queue_push(requests, request);
3326 }
3327
janus_transport_gone(janus_transport * plugin,janus_transport_session * transport)3328 void janus_transport_gone(janus_transport *plugin, janus_transport_session *transport) {
3329 /* Get rid of sessions this transport was handling */
3330 JANUS_LOG(LOG_VERB, "A %s transport instance has gone away (%p)\n", plugin->get_package(), transport);
3331 janus_mutex_lock(&sessions_mutex);
3332 if(sessions && g_hash_table_size(sessions) > 0) {
3333 GHashTableIter iter;
3334 gpointer value;
3335 g_hash_table_iter_init(&iter, sessions);
3336 while(g_hash_table_iter_next(&iter, NULL, &value)) {
3337 janus_session *session = (janus_session *) value;
3338 if(!session || g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&session->timedout) || session->last_activity == 0)
3339 continue;
3340 if(session->source && session->source->instance == transport) {
3341 JANUS_LOG(LOG_VERB, " -- Session %"SCNu64" will be over if not reclaimed\n", session->session_id);
3342 JANUS_LOG(LOG_VERB, " -- Marking Session %"SCNu64" as over\n", session->session_id);
3343 if(reclaim_session_timeout < 1) { /* Reclaim session timeouts are disabled */
3344 /* Mark the session as destroyed */
3345 janus_session_destroy(session);
3346 g_hash_table_iter_remove(&iter);
3347 } else {
3348 /* Set flag for transport_gone. The Janus sessions watchdog will clean this up if not reclaimed */
3349 g_atomic_int_set(&session->transport_gone, 1);
3350 }
3351 }
3352 }
3353 }
3354 janus_mutex_unlock(&sessions_mutex);
3355 }
3356
janus_transport_is_api_secret_needed(janus_transport * plugin)3357 gboolean janus_transport_is_api_secret_needed(janus_transport *plugin) {
3358 return api_secret != NULL;
3359 }
3360
janus_transport_is_api_secret_valid(janus_transport * plugin,const char * apisecret)3361 gboolean janus_transport_is_api_secret_valid(janus_transport *plugin, const char *apisecret) {
3362 if(api_secret == NULL)
3363 return TRUE;
3364 return apisecret && janus_strcmp_const_time(apisecret, api_secret);
3365 }
3366
janus_transport_is_auth_token_needed(janus_transport * plugin)3367 gboolean janus_transport_is_auth_token_needed(janus_transport *plugin) {
3368 return janus_auth_is_enabled();
3369 }
3370
janus_transport_is_auth_token_valid(janus_transport * plugin,const char * token)3371 gboolean janus_transport_is_auth_token_valid(janus_transport *plugin, const char *token) {
3372 if(!janus_auth_is_enabled())
3373 return TRUE;
3374 return token && janus_auth_check_token(token);
3375 }
3376
janus_transport_notify_event(janus_transport * plugin,void * transport,json_t * event)3377 void janus_transport_notify_event(janus_transport *plugin, void *transport, json_t *event) {
3378 /* A plugin asked to notify an event to the handlers */
3379 if(!plugin || !event || !json_is_object(event))
3380 return;
3381 /* Notify event handlers */
3382 if(janus_events_is_enabled()) {
3383 janus_events_notify_handlers(JANUS_EVENT_TYPE_TRANSPORT, JANUS_EVENT_SUBTYPE_NONE,
3384 0, plugin->get_package(), transport, event);
3385 } else {
3386 json_decref(event);
3387 }
3388 }
3389
janus_transport_task(gpointer data,gpointer user_data)3390 void janus_transport_task(gpointer data, gpointer user_data) {
3391 JANUS_LOG(LOG_VERB, "Transport task pool, serving request\n");
3392 janus_request *request = (janus_request *)data;
3393 if(request == NULL) {
3394 JANUS_LOG(LOG_ERR, "Missing request\n");
3395 return;
3396 }
3397 if(!request->admin)
3398 janus_process_incoming_request(request);
3399 else
3400 janus_process_incoming_admin_request(request);
3401 /* Done */
3402 janus_request_destroy(request);
3403 }
3404
3405
3406 /* Thread to handle incoming requests: may involve an asynchronous task for plugin messaging */
janus_transport_requests(void * data)3407 static void *janus_transport_requests(void *data) {
3408 JANUS_LOG(LOG_INFO, "Joining Janus requests handler thread\n");
3409 janus_request *request = NULL;
3410 gboolean destroy = FALSE;
3411 while(!g_atomic_int_get(&stop)) {
3412 request = g_async_queue_pop(requests);
3413 if(request == &exit_message)
3414 break;
3415 /* Should we process the request synchronously or with a task from the thread pool? */
3416 destroy = TRUE;
3417 /* Process the request synchronously only it's not a message for a plugin */
3418 json_t *message = json_object_get(request->message, "janus");
3419 const gchar *message_text = json_string_value(message);
3420 if(message_text && !strcasecmp(message_text, request->admin ? "message_plugin" : "message")) {
3421 /* Spawn a task thread */
3422 GError *tperror = NULL;
3423 g_thread_pool_push(tasks, request, &tperror);
3424 if(tperror != NULL) {
3425 /* Something went wrong... */
3426 JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to push task in thread pool...\n",
3427 tperror->code, tperror->message ? tperror->message : "??");
3428 g_error_free(tperror);
3429 json_t *transaction = json_object_get(message, "transaction");
3430 const char *transaction_text = json_is_string(transaction) ? json_string_value(transaction) : NULL;
3431 janus_process_error(request, 0, transaction_text, JANUS_ERROR_UNKNOWN, "Thread pool error");
3432 } else {
3433 /* Don't destroy the request now, the task will take care of that */
3434 destroy = FALSE;
3435 }
3436 } else {
3437 if(!request->admin)
3438 janus_process_incoming_request(request);
3439 else
3440 janus_process_incoming_admin_request(request);
3441 }
3442 /* Done */
3443 if(destroy)
3444 janus_request_destroy(request);
3445 }
3446 JANUS_LOG(LOG_INFO, "Leaving Janus requests handler thread\n");
3447 return NULL;
3448 }
3449
3450
3451 /* Event handlers */
janus_eventhandler_close(gpointer key,gpointer value,gpointer user_data)3452 void janus_eventhandler_close(gpointer key, gpointer value, gpointer user_data) {
3453 janus_eventhandler *eventhandler = (janus_eventhandler *)value;
3454 if(!eventhandler)
3455 return;
3456 eventhandler->destroy();
3457 }
3458
janus_eventhandlerso_close(gpointer key,gpointer value,gpointer user_data)3459 void janus_eventhandlerso_close(gpointer key, gpointer value, gpointer user_data) {
3460 void *eventhandler = (janus_eventhandler *)value;
3461 if(!eventhandler)
3462 return;
3463 //~ dlclose(eventhandler);
3464 }
3465
3466
3467 /* Loggers */
janus_logger_close(gpointer key,gpointer value,gpointer user_data)3468 void janus_logger_close(gpointer key, gpointer value, gpointer user_data) {
3469 janus_logger *logger = (janus_logger *)value;
3470 if(!logger)
3471 return;
3472 logger->destroy();
3473 }
3474
janus_loggerso_close(gpointer key,gpointer value,gpointer user_data)3475 void janus_loggerso_close(gpointer key, gpointer value, gpointer user_data) {
3476 void *logger = (janus_logger *)value;
3477 if(!logger)
3478 return;
3479 //~ dlclose(logger);
3480 }
3481
3482
3483 /* Plugins */
janus_plugin_close(gpointer key,gpointer value,gpointer user_data)3484 void janus_plugin_close(gpointer key, gpointer value, gpointer user_data) {
3485 janus_plugin *plugin = (janus_plugin *)value;
3486 if(!plugin)
3487 return;
3488 plugin->destroy();
3489 }
3490
janus_pluginso_close(gpointer key,gpointer value,gpointer user_data)3491 void janus_pluginso_close(gpointer key, gpointer value, gpointer user_data) {
3492 void *plugin = value;
3493 if(!plugin)
3494 return;
3495 /* FIXME We don't dlclose plugins to be sure we can detect leaks */
3496 //~ dlclose(plugin);
3497 }
3498
janus_plugin_find(const gchar * package)3499 janus_plugin *janus_plugin_find(const gchar *package) {
3500 if(package != NULL && plugins != NULL) /* FIXME Do we need to fix the key pointer? */
3501 return g_hash_table_lookup(plugins, package);
3502 return NULL;
3503 }
3504
3505
3506 /* Plugin callback interface */
janus_plugin_push_event(janus_plugin_session * plugin_session,janus_plugin * plugin,const char * transaction,json_t * message,json_t * jsep)3507 int janus_plugin_push_event(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *transaction, json_t *message, json_t *jsep) {
3508 if(!plugin || !message)
3509 return -1;
3510 if(!janus_plugin_session_is_alive(plugin_session))
3511 return -2;
3512 janus_refcount_increase(&plugin_session->ref);
3513 janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
3514 if(!ice_handle || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)) {
3515 janus_refcount_decrease(&plugin_session->ref);
3516 return JANUS_ERROR_SESSION_NOT_FOUND;
3517 }
3518 janus_refcount_increase(&ice_handle->ref);
3519 janus_session *session = ice_handle->session;
3520 if(!session || g_atomic_int_get(&session->destroyed)) {
3521 janus_refcount_decrease(&plugin_session->ref);
3522 janus_refcount_decrease(&ice_handle->ref);
3523 return JANUS_ERROR_SESSION_NOT_FOUND;
3524 }
3525 /* Make sure this is a JSON object */
3526 if(!json_is_object(message)) {
3527 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: not an object)\n", ice_handle->handle_id);
3528 janus_refcount_decrease(&plugin_session->ref);
3529 janus_refcount_decrease(&ice_handle->ref);
3530 return JANUS_ERROR_INVALID_JSON_OBJECT;
3531 }
3532 /* Attach JSEP if possible? */
3533 const char *sdp_type = json_string_value(json_object_get(jsep, "type"));
3534 const char *sdp = json_string_value(json_object_get(jsep, "sdp"));
3535 gboolean restart = json_object_get(jsep, "sdp") ? json_is_true(json_object_get(jsep, "restart")) : FALSE;
3536 gboolean e2ee = json_object_get(jsep, "sdp") ? json_is_true(json_object_get(jsep, "e2ee")) : FALSE;
3537 json_t *merged_jsep = NULL;
3538 if(sdp_type != NULL && sdp != NULL) {
3539 merged_jsep = janus_plugin_handle_sdp(plugin_session, plugin, sdp_type, sdp, restart);
3540 if(merged_jsep == NULL) {
3541 if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3542 || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
3543 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (handle not available anymore or negotiation stopped)\n", ice_handle->handle_id);
3544 janus_refcount_decrease(&plugin_session->ref);
3545 janus_refcount_decrease(&ice_handle->ref);
3546 return JANUS_ERROR_HANDLE_NOT_FOUND;
3547 } else {
3548 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Cannot push event (JSON error: problem with the SDP)\n", ice_handle->handle_id);
3549 janus_refcount_decrease(&plugin_session->ref);
3550 janus_refcount_decrease(&ice_handle->ref);
3551 return JANUS_ERROR_JSEP_INVALID_SDP;
3552 }
3553 }
3554 }
3555 /* Reference the payload, as the plugin may still need it and will do a decref itself */
3556 json_incref(message);
3557 /* Prepare JSON event */
3558 json_t *event = janus_create_message("event", session->session_id, transaction);
3559 json_object_set_new(event, "sender", json_integer(ice_handle->handle_id));
3560 if(janus_is_opaqueid_in_api_enabled() && ice_handle->opaque_id != NULL)
3561 json_object_set_new(event, "opaque_id", json_string(ice_handle->opaque_id));
3562 json_t *plugin_data = json_object();
3563 json_object_set_new(plugin_data, "plugin", json_string(plugin->get_package()));
3564 json_object_set_new(plugin_data, "data", message);
3565 json_object_set_new(event, "plugindata", plugin_data);
3566 if(merged_jsep != NULL) {
3567 if(e2ee)
3568 janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_E2EE);
3569 if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_E2EE))
3570 json_object_set_new(merged_jsep, "e2ee", json_true());
3571 json_object_set_new(event, "jsep", merged_jsep);
3572 /* In case event handlers are enabled, push the local SDP to all handlers */
3573 if(janus_events_is_enabled()) {
3574 const char *merged_sdp_type = json_string_value(json_object_get(merged_jsep, "type"));
3575 const char *merged_sdp = json_string_value(json_object_get(merged_jsep, "sdp"));
3576 /* Notify event handlers as well */
3577 janus_events_notify_handlers(JANUS_EVENT_TYPE_JSEP, JANUS_EVENT_SUBTYPE_NONE,
3578 session->session_id, ice_handle->handle_id, ice_handle->opaque_id, "local", merged_sdp_type, merged_sdp);
3579 }
3580 }
3581 /* Send the event */
3582 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Sending event to transport...\n", ice_handle->handle_id);
3583 janus_session_notify_event(session, event);
3584
3585 if((restart || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RESEND_TRICKLES))
3586 && janus_ice_is_full_trickle_enabled()) {
3587 /* We're restarting ICE, send our trickle candidates again */
3588 janus_ice_resend_trickles(ice_handle);
3589 }
3590
3591 janus_refcount_decrease(&plugin_session->ref);
3592 janus_refcount_decrease(&ice_handle->ref);
3593 return JANUS_OK;
3594 }
3595
janus_plugin_handle_sdp(janus_plugin_session * plugin_session,janus_plugin * plugin,const char * sdp_type,const char * sdp,gboolean restart)3596 json_t *janus_plugin_handle_sdp(janus_plugin_session *plugin_session, janus_plugin *plugin, const char *sdp_type, const char *sdp, gboolean restart) {
3597 if(!janus_plugin_session_is_alive(plugin_session) ||
3598 plugin == NULL || sdp_type == NULL || sdp == NULL) {
3599 JANUS_LOG(LOG_ERR, "Invalid arguments\n");
3600 return NULL;
3601 }
3602 janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
3603 //~ if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY)) {
3604 if(ice_handle == NULL) {
3605 JANUS_LOG(LOG_ERR, "Invalid ICE handle\n");
3606 return NULL;
3607 }
3608 int offer = 0;
3609 if(!strcasecmp(sdp_type, "offer")) {
3610 /* This is an offer from a plugin */
3611 offer = 1;
3612 janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER);
3613 janus_flags_clear(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
3614 } else if(!strcasecmp(sdp_type, "answer")) {
3615 /* This is an answer from a plugin */
3616 janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_ANSWER);
3617 if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_GOT_OFFER))
3618 janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_NEGOTIATED);
3619 } else {
3620 /* TODO Handle other messages */
3621 JANUS_LOG(LOG_ERR, "Unknown type '%s'\n", sdp_type);
3622 return NULL;
3623 }
3624 /* Is this valid SDP? */
3625 char error_str[512];
3626 error_str[0] = '\0';
3627 int audio = 0, video = 0, data = 0;
3628 janus_sdp *parsed_sdp = janus_sdp_preparse(ice_handle, sdp, error_str, sizeof(error_str), &audio, &video, &data);
3629 if(parsed_sdp == NULL) {
3630 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Couldn't parse SDP... %s\n", ice_handle->handle_id, error_str);
3631 return NULL;
3632 }
3633 gboolean updating = FALSE;
3634 if(offer) {
3635 /* We may still not have a local ICE setup */
3636 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio %s been negotiated\n", ice_handle->handle_id, audio ? "has" : "has NOT");
3637 if(audio > 1) {
3638 JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one audio line? only going to negotiate one...\n", ice_handle->handle_id);
3639 }
3640 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video %s been negotiated\n", ice_handle->handle_id, video ? "has" : "has NOT");
3641 if(video > 1) {
3642 JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one video line? only going to negotiate one...\n", ice_handle->handle_id);
3643 }
3644 JANUS_LOG(LOG_VERB, "[%"SCNu64"] SCTP/DataChannels %s been negotiated\n", ice_handle->handle_id, data ? "have" : "have NOT");
3645 if(data > 1) {
3646 JANUS_LOG(LOG_ERR, "[%"SCNu64"] More than one data line? only going to negotiate one...\n", ice_handle->handle_id);
3647 }
3648 #ifndef HAVE_SCTP
3649 if(data) {
3650 JANUS_LOG(LOG_WARN, "[%"SCNu64"] -- DataChannels have been negotiated, but support for them has not been compiled...\n", ice_handle->handle_id);
3651 }
3652 #endif
3653 /* Are we still cleaning up from a previous media session? */
3654 if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
3655 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", ice_handle->handle_id);
3656 gint64 waited = 0;
3657 while(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING)) {
3658 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Still cleaning up from a previous media session, let's wait a bit...\n", ice_handle->handle_id);
3659 g_usleep(100000);
3660 waited += 100000;
3661 if(waited >= 3*G_USEC_PER_SEC) {
3662 JANUS_LOG(LOG_VERB, "[%"SCNu64"] -- Waited 3 seconds, that's enough!\n", ice_handle->handle_id);
3663 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Still cleaning a previous session\n", ice_handle->handle_id);
3664 janus_sdp_destroy(parsed_sdp);
3665 return NULL;
3666 }
3667 }
3668 }
3669 if(ice_handle->agent == NULL) {
3670 /* We still need to configure the WebRTC stuff: negotiate RFC4588 by default */
3671 janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX);
3672 /* Process SDP in order to setup ICE locally (this is going to result in an answer from the browser) */
3673 janus_mutex_lock(&ice_handle->mutex);
3674 if(janus_ice_setup_local(ice_handle, 0, audio, video, data, 1) < 0) {
3675 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error setting ICE locally\n", ice_handle->handle_id);
3676 janus_sdp_destroy(parsed_sdp);
3677 janus_mutex_unlock(&ice_handle->mutex);
3678 return NULL;
3679 }
3680 janus_mutex_unlock(&ice_handle->mutex);
3681 } else {
3682 updating = TRUE;
3683 JANUS_LOG(LOG_INFO, "[%"SCNu64"] Updating existing session\n", ice_handle->handle_id);
3684 if(offer && ice_handle->stream) {
3685 /* We might need some new properties set as well */
3686 janus_ice_stream *stream = ice_handle->stream;
3687 if(audio) {
3688 if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO)) {
3689 janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
3690 stream->audio_ssrc = janus_random_uint32(); /* FIXME Should we look for conflicts? */
3691 if(stream->audio_rtcp_ctx == NULL) {
3692 stream->audio_rtcp_ctx = g_malloc0(sizeof(rtcp_context));
3693 stream->audio_rtcp_ctx->tb = 48000; /* May change later */
3694 }
3695 }
3696 if(ice_handle->audio_mid == NULL)
3697 ice_handle->audio_mid = g_strdup("audio");
3698 }
3699 if(video) {
3700 if(!janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO)) {
3701 janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);
3702 stream->video_ssrc = janus_random_uint32(); /* FIXME Should we look for conflicts? */
3703 if(stream->video_rtcp_ctx[0] == NULL) {
3704 stream->video_rtcp_ctx[0] = g_malloc0(sizeof(rtcp_context));
3705 stream->video_rtcp_ctx[0]->tb = 90000; /* May change later */
3706 }
3707 }
3708 if(ice_handle->video_mid == NULL)
3709 ice_handle->video_mid = g_strdup("video");
3710 }
3711 if(data) {
3712 if(ice_handle->data_mid == NULL)
3713 ice_handle->data_mid = g_strdup("data");
3714 }
3715 }
3716 }
3717 /* Make sure we don't send the rid/repaired-rid attributes when offering ourselves */
3718 int mid_ext_id = 0, transport_wide_cc_ext_id = 0, abs_send_time_ext_id = 0,
3719 audiolevel_ext_id = 0, videoorientation_ext_id = 0;
3720 GList *temp = parsed_sdp->m_lines;
3721 while(temp) {
3722 janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
3723 GList *tempA = m->attributes;
3724 while(tempA) {
3725 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
3726 if(a->name && a->value) {
3727 if(strstr(a->value, JANUS_RTP_EXTMAP_MID))
3728 mid_ext_id = atoi(a->value);
3729 else if(strstr(a->value, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC))
3730 transport_wide_cc_ext_id = atoi(a->value);
3731 else if(strstr(a->value, JANUS_RTP_EXTMAP_ABS_SEND_TIME))
3732 abs_send_time_ext_id = atoi(a->value);
3733 else if(strstr(a->value, JANUS_RTP_EXTMAP_AUDIO_LEVEL))
3734 audiolevel_ext_id = atoi(a->value);
3735 else if(strstr(a->value, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION))
3736 videoorientation_ext_id = atoi(a->value);
3737 else if(strstr(a->value, JANUS_RTP_EXTMAP_RID) ||
3738 strstr(a->value, JANUS_RTP_EXTMAP_REPAIRED_RID)) {
3739 m->attributes = g_list_remove(m->attributes, a);
3740 tempA = m->attributes;
3741 janus_sdp_attribute_destroy(a);
3742 continue;
3743 }
3744 }
3745 tempA = tempA->next;
3746 }
3747 temp = temp->next;
3748 }
3749 if(ice_handle->stream && ice_handle->stream->mid_ext_id != mid_ext_id)
3750 ice_handle->stream->mid_ext_id = mid_ext_id;
3751 if(ice_handle->stream && ice_handle->stream->transport_wide_cc_ext_id != transport_wide_cc_ext_id) {
3752 ice_handle->stream->do_transport_wide_cc = transport_wide_cc_ext_id > 0 ? TRUE : FALSE;
3753 ice_handle->stream->transport_wide_cc_ext_id = transport_wide_cc_ext_id;
3754 }
3755 if(ice_handle->stream && ice_handle->stream->abs_send_time_ext_id != abs_send_time_ext_id)
3756 ice_handle->stream->abs_send_time_ext_id = abs_send_time_ext_id;
3757 if(ice_handle->stream && ice_handle->stream->audiolevel_ext_id != audiolevel_ext_id)
3758 ice_handle->stream->audiolevel_ext_id = audiolevel_ext_id;
3759 if(ice_handle->stream && ice_handle->stream->videoorientation_ext_id != videoorientation_ext_id)
3760 ice_handle->stream->videoorientation_ext_id = videoorientation_ext_id;
3761 } else {
3762 /* Check if the answer does contain the mid/rid/repaired-rid/abs-send-time/twcc extmaps */
3763 gboolean do_mid = FALSE, do_rid = FALSE, do_repaired_rid = FALSE,
3764 do_twcc = FALSE, do_abs_send_time = FALSE;
3765 GList *temp = parsed_sdp->m_lines;
3766 while(temp) {
3767 janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
3768 GList *tempA = m->attributes;
3769 while(tempA) {
3770 janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
3771 if(a->name && a->value && !strcasecmp(a->name, "extmap")) {
3772 if(strstr(a->value, JANUS_RTP_EXTMAP_MID))
3773 do_mid = TRUE;
3774 else if(strstr(a->value, JANUS_RTP_EXTMAP_RID))
3775 do_rid = TRUE;
3776 else if(strstr(a->value, JANUS_RTP_EXTMAP_REPAIRED_RID))
3777 do_repaired_rid = TRUE;
3778 else if(strstr(a->value, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC))
3779 do_twcc = TRUE;
3780 else if(strstr(a->value, JANUS_RTP_EXTMAP_ABS_SEND_TIME))
3781 do_abs_send_time = TRUE;
3782 }
3783 tempA = tempA->next;
3784 }
3785 temp = temp->next;
3786 }
3787 if(!do_mid && ice_handle->stream)
3788 ice_handle->stream->mid_ext_id = 0;
3789 if(!do_rid && ice_handle->stream) {
3790 ice_handle->stream->rid_ext_id = 0;
3791 ice_handle->stream->ridrtx_ext_id = 0;
3792 g_free(ice_handle->stream->rid[0]);
3793 ice_handle->stream->rid[0] = NULL;
3794 g_free(ice_handle->stream->rid[1]);
3795 ice_handle->stream->rid[1] = NULL;
3796 g_free(ice_handle->stream->rid[2]);
3797 ice_handle->stream->rid[2] = NULL;
3798 if(ice_handle->stream->video_ssrc_peer_temp > 0) {
3799 ice_handle->stream->video_ssrc_peer[0] = ice_handle->stream->video_ssrc_peer_temp;
3800 ice_handle->stream->video_ssrc_peer_temp = 0;
3801 }
3802 }
3803 if(!do_repaired_rid && ice_handle->stream)
3804 ice_handle->stream->ridrtx_ext_id = 0;
3805 if(!do_twcc && ice_handle->stream) {
3806 ice_handle->stream->do_transport_wide_cc = FALSE;
3807 ice_handle->stream->transport_wide_cc_ext_id = 0;
3808 }
3809 if(!do_abs_send_time && ice_handle->stream)
3810 ice_handle->stream->abs_send_time_ext_id = 0;
3811 }
3812 if(!updating && !janus_ice_is_full_trickle_enabled()) {
3813 /* Wait for candidates-done callback */
3814 int waiting = 0;
3815 while(ice_handle->cdone < 1) {
3816 if(ice_handle == NULL || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3817 || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
3818 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Handle detached or PC closed, giving up...!\n", ice_handle ? ice_handle->handle_id : 0);
3819 janus_sdp_destroy(parsed_sdp);
3820 return NULL;
3821 }
3822 if(ice_handle->cdone < 0) {
3823 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error gathering candidates!\n", ice_handle->handle_id);
3824 janus_sdp_destroy(parsed_sdp);
3825 return NULL;
3826 }
3827 if(waiting && (waiting % 5000) == 0) {
3828 JANUS_LOG(LOG_WARN, "[%"SCNu64"] Waited 5s for candidates, that's way too much... going on with what we have (WebRTC setup might fail)\n", ice_handle->handle_id);
3829 break;
3830 }
3831 if(waiting && (waiting % 1000) == 0) {
3832 JANUS_LOG(LOG_WARN, "[%"SCNu64"] %s for candidates-done callback... (slow gathering, are you using STUN or TURN for Janus too, instead of just for users? Consider enabling full-trickle instead)\n",
3833 ice_handle->handle_id, (waiting == 1000 ? "Waiting" : "Still waiting"));
3834 }
3835 waiting++;
3836 g_usleep(1000);
3837 }
3838 }
3839 /* Anonymize SDP */
3840 if(janus_sdp_anonymize(parsed_sdp) < 0) {
3841 /* Invalid SDP */
3842 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Invalid SDP\n", ice_handle->handle_id);
3843 janus_sdp_destroy(parsed_sdp);
3844 return NULL;
3845 }
3846
3847 /* Check if this is a renegotiation and we need an ICE restart */
3848 if(offer && restart)
3849 janus_ice_restart(ice_handle);
3850 /* Add our details */
3851 janus_mutex_lock(&ice_handle->mutex);
3852 janus_ice_stream *stream = ice_handle->stream;
3853 if (stream == NULL) {
3854 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error stream not found\n", ice_handle->handle_id);
3855 janus_sdp_destroy(parsed_sdp);
3856 janus_mutex_unlock(&ice_handle->mutex);
3857 return NULL;
3858 }
3859 if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RFC4588_RTX) &&
3860 stream->rtx_payload_types == NULL) {
3861 /* Make sure we have a list of rtx payload types to generate, if needed */
3862 janus_sdp_mline *m = janus_sdp_mline_find(parsed_sdp, JANUS_SDP_VIDEO);
3863 if(m && m->ptypes) {
3864 stream->rtx_payload_types = g_hash_table_new(NULL, NULL);
3865 GList *ptypes = g_list_copy(m->ptypes), *tempP = ptypes;
3866 GList *rtx_ptypes = g_hash_table_get_values(stream->rtx_payload_types);
3867 while(tempP) {
3868 int ptype = GPOINTER_TO_INT(tempP->data);
3869 int rtx_ptype = ptype+1;
3870 if(rtx_ptype > 127)
3871 rtx_ptype = 96;
3872 while(g_list_find(m->ptypes, GINT_TO_POINTER(rtx_ptype))
3873 || g_list_find(rtx_ptypes, GINT_TO_POINTER(rtx_ptype))) {
3874 rtx_ptype++;
3875 if(rtx_ptype > 127)
3876 rtx_ptype = 96;
3877 if(rtx_ptype == ptype) {
3878 /* We did a whole round? should never happen... */
3879 rtx_ptype = -1;
3880 break;
3881 }
3882 }
3883 if(rtx_ptype > 0)
3884 g_hash_table_insert(stream->rtx_payload_types, GINT_TO_POINTER(ptype), GINT_TO_POINTER(rtx_ptype));
3885 g_list_free(rtx_ptypes);
3886 rtx_ptypes = g_hash_table_get_values(stream->rtx_payload_types);
3887 tempP = tempP->next;
3888 }
3889 g_list_free(ptypes);
3890 g_list_free(rtx_ptypes);
3891 }
3892 }
3893 /* Enrich the SDP the plugin gave us with all the WebRTC related stuff */
3894 char *sdp_merged = janus_sdp_merge(ice_handle, parsed_sdp, offer ? TRUE : FALSE);
3895 if(sdp_merged == NULL) {
3896 /* Couldn't merge SDP */
3897 JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error merging SDP\n", ice_handle->handle_id);
3898 janus_sdp_destroy(parsed_sdp);
3899 janus_mutex_unlock(&ice_handle->mutex);
3900 return NULL;
3901 }
3902 janus_sdp_destroy(parsed_sdp);
3903
3904 if(!updating) {
3905 if(offer) {
3906 /* We set the flag to wait for an answer before handling trickle candidates */
3907 janus_flags_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PROCESSING_OFFER);
3908 } else {
3909 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Sending answer, ready to setup remote candidates and send connectivity checks...\n", ice_handle->handle_id);
3910 janus_request_ice_handle_answer(ice_handle, audio, video, data, NULL);
3911 }
3912 }
3913 #ifdef HAVE_SCTP
3914 if(!offer && janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_READY)) {
3915 /* Renegotiation: check if datachannels were just added on an existing PeerConnection */
3916 if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS)) {
3917 janus_ice_stream *stream = ice_handle->stream;
3918 if(stream != NULL && stream->component != NULL &&
3919 stream->component->dtls != NULL && stream->component->dtls->sctp == NULL) {
3920 /* Create SCTP association as well */
3921 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Creating datachannels...\n", ice_handle->handle_id);
3922 janus_dtls_srtp_create_sctp(stream->component->dtls);
3923 }
3924 }
3925 }
3926 #endif
3927
3928 /* Prepare JSON event */
3929 json_t *jsep = json_object();
3930 json_object_set_new(jsep, "type", json_string(sdp_type));
3931 json_object_set_new(jsep, "sdp", json_string(sdp_merged));
3932 char *tmp = ice_handle->local_sdp;
3933 ice_handle->local_sdp = sdp_merged;
3934 janus_mutex_unlock(&ice_handle->mutex);
3935 g_free(tmp);
3936 return jsep;
3937 }
3938
janus_plugin_relay_rtp(janus_plugin_session * plugin_session,janus_plugin_rtp * packet)3939 void janus_plugin_relay_rtp(janus_plugin_session *plugin_session, janus_plugin_rtp *packet) {
3940 if((plugin_session < (janus_plugin_session *)0x1000) || g_atomic_int_get(&plugin_session->stopped) ||
3941 packet == NULL || packet->buffer == NULL || packet->length < 1)
3942 return;
3943 janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3944 if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3945 || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3946 return;
3947 janus_ice_relay_rtp(handle, packet);
3948 }
3949
janus_plugin_relay_rtcp(janus_plugin_session * plugin_session,janus_plugin_rtcp * packet)3950 void janus_plugin_relay_rtcp(janus_plugin_session *plugin_session, janus_plugin_rtcp *packet) {
3951 if((plugin_session < (janus_plugin_session *)0x1000) || g_atomic_int_get(&plugin_session->stopped) ||
3952 packet == NULL || packet->buffer == NULL || packet->length < 1)
3953 return;
3954 janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3955 if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3956 || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3957 return;
3958 janus_ice_relay_rtcp(handle, packet);
3959 }
3960
janus_plugin_relay_data(janus_plugin_session * plugin_session,janus_plugin_data * packet)3961 void janus_plugin_relay_data(janus_plugin_session *plugin_session, janus_plugin_data *packet) {
3962 if((plugin_session < (janus_plugin_session *)0x1000) || g_atomic_int_get(&plugin_session->stopped) ||
3963 packet == NULL || packet->buffer == NULL || packet->length < 1)
3964 return;
3965 janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3966 if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3967 || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3968 return;
3969 #ifdef HAVE_SCTP
3970 janus_ice_relay_data(handle, packet);
3971 #else
3972 JANUS_LOG(LOG_WARN, "Asked to relay data, but Data Channels support has not been compiled...\n");
3973 #endif
3974 }
3975
janus_plugin_send_pli(janus_plugin_session * plugin_session)3976 void janus_plugin_send_pli(janus_plugin_session *plugin_session) {
3977 if((plugin_session < (janus_plugin_session *)0x1000) || g_atomic_int_get(&plugin_session->stopped))
3978 return;
3979 janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3980 if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3981 || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3982 return;
3983 janus_ice_send_pli(handle);
3984 }
3985
janus_plugin_send_remb(janus_plugin_session * plugin_session,uint32_t bitrate)3986 void janus_plugin_send_remb(janus_plugin_session *plugin_session, uint32_t bitrate) {
3987 if((plugin_session < (janus_plugin_session *)0x1000) || g_atomic_int_get(&plugin_session->stopped))
3988 return;
3989 janus_ice_handle *handle = (janus_ice_handle *)plugin_session->gateway_handle;
3990 if(!handle || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
3991 || janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT))
3992 return;
3993 janus_ice_send_remb(handle, bitrate);
3994 }
3995
janus_plugin_close_pc_internal(gpointer user_data)3996 static gboolean janus_plugin_close_pc_internal(gpointer user_data) {
3997 /* We actually enforce the close_pc here */
3998 janus_plugin_session *plugin_session = (janus_plugin_session *) user_data;
3999 janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
4000 if(!ice_handle || !g_atomic_int_compare_and_exchange(&ice_handle->closepc, 1, 0)) {
4001 janus_refcount_decrease(&plugin_session->ref);
4002 return G_SOURCE_REMOVE;
4003 }
4004 janus_refcount_increase(&ice_handle->ref);
4005 if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)
4006 || janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) {
4007 janus_refcount_decrease(&plugin_session->ref);
4008 janus_refcount_decrease(&ice_handle->ref);
4009 return G_SOURCE_REMOVE;
4010 }
4011 JANUS_LOG(LOG_VERB, "[%"SCNu64"] Plugin asked to hangup PeerConnection: sending alert\n", ice_handle->handle_id);
4012 /* Send an alert on all the DTLS connections */
4013 janus_ice_webrtc_hangup(ice_handle, "Close PC");
4014 janus_refcount_decrease(&plugin_session->ref);
4015 janus_refcount_decrease(&ice_handle->ref);
4016
4017 return G_SOURCE_REMOVE;
4018 }
4019
janus_plugin_close_pc(janus_plugin_session * plugin_session)4020 void janus_plugin_close_pc(janus_plugin_session *plugin_session) {
4021 /* A plugin asked to get rid of a PeerConnection: enqueue it as a timed source */
4022 if(!janus_plugin_session_is_alive(plugin_session))
4023 return;
4024 janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
4025 if(!ice_handle || !g_atomic_int_compare_and_exchange(&ice_handle->closepc, 0, 1))
4026 return;
4027 janus_refcount_increase(&plugin_session->ref);
4028 GSource *timeout_source = g_timeout_source_new_seconds(0);
4029 g_source_set_callback(timeout_source, janus_plugin_close_pc_internal, plugin_session, NULL);
4030 g_source_attach(timeout_source, sessions_watchdog_context);
4031 g_source_unref(timeout_source);
4032 }
4033
janus_plugin_end_session_internal(gpointer user_data)4034 static gboolean janus_plugin_end_session_internal(gpointer user_data) {
4035 /* We actually enforce the end_session here */
4036 janus_plugin_session *plugin_session = (janus_plugin_session *) user_data;
4037 janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
4038 if(!ice_handle) {
4039 janus_refcount_decrease(&plugin_session->ref);
4040 return G_SOURCE_REMOVE;
4041 }
4042 janus_refcount_increase(&ice_handle->ref);
4043 if(janus_flags_is_set(&ice_handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP)) {
4044 janus_refcount_decrease(&plugin_session->ref);
4045 janus_refcount_decrease(&ice_handle->ref);
4046 return G_SOURCE_REMOVE;
4047 }
4048 janus_session *session = (janus_session *)ice_handle->session;
4049 if(!session) {
4050 janus_refcount_decrease(&plugin_session->ref);
4051 janus_refcount_decrease(&ice_handle->ref);
4052 return G_SOURCE_REMOVE;
4053 }
4054 /* Destroy the handle */
4055 janus_session_handles_remove(session, ice_handle);
4056
4057 janus_refcount_decrease(&plugin_session->ref);
4058 janus_refcount_decrease(&ice_handle->ref);
4059 return G_SOURCE_REMOVE;
4060 }
4061
janus_plugin_end_session(janus_plugin_session * plugin_session)4062 void janus_plugin_end_session(janus_plugin_session *plugin_session) {
4063 /* A plugin asked to get rid of a handle: enqueue it as a timed source */
4064 if(!janus_plugin_session_is_alive(plugin_session))
4065 return;
4066 janus_refcount_increase(&plugin_session->ref);
4067 GSource *timeout_source = g_timeout_source_new_seconds(0);
4068 g_source_set_callback(timeout_source, janus_plugin_end_session_internal, plugin_session, NULL);
4069 g_source_attach(timeout_source, sessions_watchdog_context);
4070 g_source_unref(timeout_source);
4071 }
4072
janus_plugin_notify_event(janus_plugin * plugin,janus_plugin_session * plugin_session,json_t * event)4073 void janus_plugin_notify_event(janus_plugin *plugin, janus_plugin_session *plugin_session, json_t *event) {
4074 /* A plugin asked to notify an event to the handlers */
4075 if(!plugin || !event || !json_is_object(event))
4076 return;
4077 guint64 session_id = 0, handle_id = 0;
4078 char *opaque_id = NULL;
4079 if(plugin_session != NULL) {
4080 if(!janus_plugin_session_is_alive(plugin_session)) {
4081 json_decref(event);
4082 return;
4083 }
4084 janus_ice_handle *ice_handle = (janus_ice_handle *)plugin_session->gateway_handle;
4085 if(!ice_handle) {
4086 json_decref(event);
4087 return;
4088 }
4089 handle_id = ice_handle->handle_id;
4090 opaque_id = ice_handle->opaque_id;
4091 janus_session *session = (janus_session *)ice_handle->session;
4092 if(!session) {
4093 json_decref(event);
4094 return;
4095 }
4096 session_id = session->session_id;
4097 }
4098 /* Notify event handlers */
4099 if(janus_events_is_enabled()) {
4100 janus_events_notify_handlers(JANUS_EVENT_TYPE_PLUGIN, JANUS_EVENT_SUBTYPE_NONE,
4101 session_id, handle_id, opaque_id, plugin->get_package(), event);
4102 } else {
4103 json_decref(event);
4104 }
4105 }
4106
janus_plugin_auth_is_signed()4107 gboolean janus_plugin_auth_is_signed() {
4108 return janus_auth_is_signed_mode();
4109 }
4110
janus_plugin_auth_is_signature_valid(janus_plugin * plugin,const char * token)4111 gboolean janus_plugin_auth_is_signature_valid(janus_plugin *plugin, const char *token) {
4112 return janus_auth_check_signature(token, plugin->get_package());
4113 }
4114
janus_plugin_auth_signature_contains(janus_plugin * plugin,const char * token,const char * descriptor)4115 gboolean janus_plugin_auth_signature_contains(janus_plugin *plugin, const char *token, const char *descriptor) {
4116 return janus_auth_check_signature_contains(token, plugin->get_package(), descriptor);
4117 }
4118
4119
4120 /* Main */
main(int argc,char * argv[])4121 gint main(int argc, char *argv[])
4122 {
4123 /* Core dumps may be disallowed by parent of this process; change that */
4124 struct rlimit core_limits;
4125 core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY;
4126 setrlimit(RLIMIT_CORE, &core_limits);
4127
4128 g_print("Janus commit: %s\n", janus_build_git_sha);
4129 g_print("Compiled on: %s\n\n", janus_build_git_time);
4130
4131 struct gengetopt_args_info args_info;
4132 /* Let's call our cmdline parser */
4133 if(cmdline_parser(argc, argv, &args_info) != 0)
4134 exit(1);
4135
4136 /* Any configuration to open? */
4137 if(args_info.config_given) {
4138 config_file = g_strdup(args_info.config_arg);
4139 }
4140 if(args_info.configs_folder_given) {
4141 configs_folder = g_strdup(args_info.configs_folder_arg);
4142 } else {
4143 configs_folder = g_strdup(CONFDIR);
4144 }
4145 if(config_file == NULL) {
4146 char file[255];
4147 g_snprintf(file, 255, "%s/janus.jcfg", configs_folder);
4148 config_file = g_strdup(file);
4149 }
4150 if((config = janus_config_parse(config_file)) == NULL) {
4151 /* We failed to load the libconfig configuration file, let's try the INI */
4152 g_print("Failed to load %s, trying the INI instead...\n", config_file);
4153 g_free(config_file);
4154 char file[255];
4155 g_snprintf(file, 255, "%s/janus.cfg", configs_folder);
4156 config_file = g_strdup(file);
4157 if((config = janus_config_parse(config_file)) == NULL) {
4158 if(args_info.config_given) {
4159 /* We only give up if the configuration file was explicitly provided */
4160 g_print("Error reading configuration from %s\n", config_file);
4161 exit(1);
4162 }
4163 g_print("Error reading/parsing the configuration file in %s, going on with the defaults and the command line arguments\n",
4164 configs_folder);
4165 config = janus_config_create("janus.cfg");
4166 if(config == NULL) {
4167 /* If we can't even create an empty configuration, something's definitely wrong */
4168 exit(1);
4169 }
4170 }
4171 }
4172 /* Pre-fetch some categories (creates them if they don't exist) */
4173 janus_config_category *config_general = janus_config_get_create(config, NULL, janus_config_type_category, "general");
4174 janus_config_category *config_certs = janus_config_get_create(config, NULL, janus_config_type_category, "certificates");
4175 janus_config_category *config_nat = janus_config_get_create(config, NULL, janus_config_type_category, "nat");
4176 janus_config_category *config_media = janus_config_get_create(config, NULL, janus_config_type_category, "media");
4177 janus_config_category *config_transports = janus_config_get_create(config, NULL, janus_config_type_category, "transports");
4178 janus_config_category *config_plugins = janus_config_get_create(config, NULL, janus_config_type_category, "plugins");
4179 janus_config_category *config_events = janus_config_get_create(config, NULL, janus_config_type_category, "events");
4180 janus_config_category *config_loggers = janus_config_get_create(config, NULL, janus_config_type_category, "loggers");
4181
4182 /* Any log prefix? */
4183 janus_config_array *lp = janus_config_get(config, config_general, janus_config_type_item, "log_prefix");
4184 if(lp && lp->value)
4185 janus_log_global_prefix = g_strdup(lp->value);
4186
4187 /* Check if there are folders to protect */
4188 janus_config_array *pfs = janus_config_get(config, config_general, janus_config_type_array, "protected_folders");
4189 if(pfs && pfs->list) {
4190 GList *item = pfs->list;
4191 while(item) {
4192 janus_config_item *pf = (janus_config_item *)item->data;
4193 if(pf && pf->type == janus_config_type_item && pf->name == NULL && pf->value != NULL)
4194 janus_protected_folder_add(pf->value);
4195 item = item->next;
4196 }
4197 }
4198
4199 /* Check if we need to log to console and/or file */
4200 gboolean use_stdout = TRUE;
4201 if(args_info.disable_stdout_given) {
4202 use_stdout = FALSE;
4203 janus_config_add(config, config_general, janus_config_item_create("log_to_stdout", "no"));
4204 } else if(!args_info.log_stdout_given) {
4205 /* Check if the configuration file is saying anything about this */
4206 janus_config_item *item = janus_config_get(config, config_general, janus_config_type_item, "log_to_stdout");
4207 if(item && item->value && !janus_is_true(item->value))
4208 use_stdout = FALSE;
4209 }
4210 const char *logfile = NULL;
4211 if(args_info.log_file_given) {
4212 logfile = args_info.log_file_arg;
4213 janus_config_add(config, config_general, janus_config_item_create("log_to_file", "no"));
4214 } else {
4215 /* Check if the configuration file is saying anything about this */
4216 janus_config_item *item = janus_config_get(config, config_general, janus_config_type_item, "log_to_file");
4217 if(item && item->value)
4218 logfile = item->value;
4219 }
4220
4221 /* Check if we're going to daemonize Janus */
4222 if(args_info.daemon_given) {
4223 daemonize = TRUE;
4224 janus_config_add(config, config_general, janus_config_item_create("daemonize", "yes"));
4225 } else {
4226 /* Check if the configuration file is saying anything about this */
4227 janus_config_item *item = janus_config_get(config, config_general, janus_config_type_item, "daemonize");
4228 if(item && item->value && janus_is_true(item->value))
4229 daemonize = TRUE;
4230 }
4231 /* If we're going to daemonize, make sure logging to stdout is disabled and a log file has been specified */
4232 if(daemonize && use_stdout && !args_info.log_stdout_given) {
4233 use_stdout = FALSE;
4234 }
4235 /* Daemonize now, if we need to */
4236 if(daemonize) {
4237 g_print("Running Janus as a daemon\n");
4238
4239 /* Create a pipe for parent<->child communication during the startup phase */
4240 if(pipe(pipefd) == -1) {
4241 g_print("pipe error!\n");
4242 exit(1);
4243 }
4244
4245 /* Fork off the parent process */
4246 pid_t pid = fork();
4247 if(pid < 0) {
4248 g_print("Fork error!\n");
4249 exit(1);
4250 }
4251 if(pid > 0) {
4252 /* Ok, we're the parent: let's wait for the child to tell us everything started fine */
4253 close(pipefd[1]);
4254 int code = -1;
4255 struct pollfd pollfds;
4256
4257 while(code < 0) {
4258 pollfds.fd = pipefd[0];
4259 pollfds.events = POLLIN;
4260 int res = poll(&pollfds, 1, -1);
4261 if(res < 0)
4262 break;
4263 if(res == 0)
4264 continue;
4265 if(pollfds.revents & POLLERR || pollfds.revents & POLLHUP)
4266 break;
4267 if(pollfds.revents & POLLIN) {
4268 res = read(pipefd[0], &code, sizeof(int));
4269 break;
4270 }
4271 }
4272 if(code < 0)
4273 code = 1;
4274
4275 /* Leave the parent and return the exit code we received from the child */
4276 if(code)
4277 g_print("Error launching Janus (error code %d), check the logs for more details\n", code);
4278 exit(code);
4279 }
4280 /* Child here */
4281 close(pipefd[0]);
4282
4283 /* Change the file mode mask */
4284 umask(0);
4285
4286 /* Create a new SID for the child process */
4287 pid_t sid = setsid();
4288 if(sid < 0) {
4289 g_print("Error setting SID!\n");
4290 exit(1);
4291 }
4292 /* Change the current working directory */
4293 const char *cwd = (args_info.cwd_path_given) ? args_info.cwd_path_arg : "/";
4294 if((chdir(cwd)) < 0) {
4295 g_print("Error changing the current working directory!\n");
4296 exit(1);
4297 }
4298 /* We close stdin/stdout/stderr when initializing the logger */
4299 }
4300
4301 /* Was a custom instance name provided? */
4302 if(args_info.server_name_given) {
4303 janus_config_add(config, config_general, janus_config_item_create("server_name", args_info.server_name_arg));
4304 }
4305 janus_config_item *item = janus_config_get(config, config_general, janus_config_type_item, "server_name");
4306 if(item && item->value) {
4307 server_name = g_strdup(item->value);
4308 }
4309
4310 /* Check if we should exit immediately on dlopen or dlsym errors */
4311 item = janus_config_get(config, config_general, janus_config_type_item, "exit_on_dl_error");
4312 if(item && item->value && janus_is_true(item->value))
4313 exit_on_dl_error = TRUE;
4314
4315 /* Initialize logger */
4316 if(janus_log_init(daemonize, use_stdout, logfile) < 0)
4317 exit(1);
4318 /* Check if there are external loggers we need to load as well */
4319 const char *path = NULL;
4320 DIR *dir = NULL;
4321 /* External loggers are usually disabled by default: they need to be enabled in the configuration */
4322 gchar **disabled_loggers = NULL;
4323 path = LOGGERDIR;
4324 item = janus_config_get(config, config_general, janus_config_type_item, "loggers_folder");
4325 if(item && item->value)
4326 path = (char *)item->value;
4327 JANUS_LOG(LOG_INFO, "Logger plugins folder: %s\n", path);
4328 dir = opendir(path);
4329 if(!dir) {
4330 /* Not really fatal, we don't care and go on anyway: loggers are not fundamental */
4331 JANUS_LOG(LOG_WARN, "\tCouldn't access logger plugins folder...\n");
4332 } else {
4333 /* Any loggers to ignore? */
4334 item = janus_config_get(config, config_loggers, janus_config_type_item, "disable");
4335 if(item && item->value)
4336 disabled_loggers = g_strsplit(item->value, ",", -1);
4337 /* Open the shared objects */
4338 struct dirent *eventent = NULL;
4339 char eventpath[1024];
4340 while((eventent = readdir(dir))) {
4341 int len = strlen(eventent->d_name);
4342 if (len < 4) {
4343 continue;
4344 }
4345 if (strcasecmp(eventent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
4346 continue;
4347 }
4348 /* Check if this logger has been disabled in the configuration file */
4349 if(disabled_loggers != NULL) {
4350 gchar *index = disabled_loggers[0];
4351 if(index != NULL) {
4352 int i=0;
4353 gboolean skip = FALSE;
4354 while(index != NULL) {
4355 while(isspace(*index))
4356 index++;
4357 if(strlen(index) && !strcmp(index, eventent->d_name)) {
4358 JANUS_LOG(LOG_WARN, "Logger plugin '%s' has been disabled, skipping...\n", eventent->d_name);
4359 skip = TRUE;
4360 break;
4361 }
4362 i++;
4363 index = disabled_loggers[i];
4364 }
4365 if(skip)
4366 continue;
4367 }
4368 }
4369 JANUS_LOG(LOG_INFO, "Loading logger plugin '%s'...\n", eventent->d_name);
4370 memset(eventpath, 0, 1024);
4371 g_snprintf(eventpath, 1024, "%s/%s", path, eventent->d_name);
4372 void *event = dlopen(eventpath, RTLD_NOW | RTLD_GLOBAL);
4373 if (!event) {
4374 JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load logger plugin '%s': %s\n", eventent->d_name, dlerror());
4375 if (exit_on_dl_error)
4376 exit(1);
4377 } else {
4378 dlerror();
4379 create_l *create = (create_l*) dlsym(event, "create");
4380 const char *dlsym_error = dlerror();
4381 if (dlsym_error) {
4382 JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
4383 if (exit_on_dl_error)
4384 exit(1);
4385 continue;
4386 }
4387 janus_logger *janus_logger = create();
4388 if(!janus_logger) {
4389 JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
4390 continue;
4391 }
4392 /* Are all the mandatory methods and callbacks implemented? */
4393 if(!janus_logger->init || !janus_logger->destroy ||
4394 !janus_logger->get_api_compatibility ||
4395 !janus_logger->get_version ||
4396 !janus_logger->get_version_string ||
4397 !janus_logger->get_description ||
4398 !janus_logger->get_package ||
4399 !janus_logger->get_name ||
4400 !janus_logger->incoming_logline) {
4401 JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this logger plugin...\n");
4402 continue;
4403 }
4404 if(janus_logger->get_api_compatibility() < JANUS_LOGGER_API_VERSION) {
4405 JANUS_LOG(LOG_ERR, "The '%s' logger plugin was compiled against an older version of the API (%d < %d), skipping it: update it to enable it again\n",
4406 janus_logger->get_package(), janus_logger->get_api_compatibility(), JANUS_LOGGER_API_VERSION);
4407 continue;
4408 }
4409 janus_logger->init(server_name ? server_name : JANUS_SERVER_NAME, configs_folder);
4410 JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_logger->get_version(), janus_logger->get_version_string());
4411 JANUS_LOG(LOG_VERB, "\t [%s] %s\n", janus_logger->get_package(), janus_logger->get_name());
4412 JANUS_LOG(LOG_VERB, "\t %s\n", janus_logger->get_description());
4413 JANUS_LOG(LOG_VERB, "\t Plugin API version: %d\n", janus_logger->get_api_compatibility());
4414 if(loggers == NULL)
4415 loggers = g_hash_table_new(g_str_hash, g_str_equal);
4416 g_hash_table_insert(loggers, (gpointer)janus_logger->get_package(), janus_logger);
4417 if(loggers_so == NULL)
4418 loggers_so = g_hash_table_new(g_str_hash, g_str_equal);
4419 g_hash_table_insert(loggers_so, (gpointer)janus_logger->get_package(), event);
4420 }
4421 }
4422 closedir(dir);
4423 }
4424 if(disabled_loggers != NULL)
4425 g_strfreev(disabled_loggers);
4426 disabled_loggers = NULL;
4427 janus_log_set_loggers(loggers);
4428
4429 JANUS_PRINT("---------------------------------------------------\n");
4430 JANUS_PRINT(" Starting Meetecho Janus (WebRTC Server) v%s\n", janus_version_string);
4431 JANUS_PRINT("---------------------------------------------------\n\n");
4432
4433 /* Handle SIGINT (CTRL-C), SIGTERM (from service managers) */
4434 signal(SIGINT, janus_handle_signal);
4435 signal(SIGTERM, janus_handle_signal);
4436 atexit(janus_termination_handler);
4437
4438 /* Setup Glib */
4439 #if !GLIB_CHECK_VERSION(2, 36, 0)
4440 g_type_init();
4441 #endif
4442
4443 /* Logging level: default is info and no timestamps */
4444 janus_log_level = LOG_INFO;
4445 janus_log_timestamps = FALSE;
4446 janus_log_colors = TRUE;
4447 if(args_info.debug_level_given) {
4448 if(args_info.debug_level_arg < LOG_NONE)
4449 args_info.debug_level_arg = 0;
4450 else if(args_info.debug_level_arg > LOG_MAX)
4451 args_info.debug_level_arg = LOG_MAX;
4452 janus_log_level = args_info.debug_level_arg;
4453 }
4454
4455 /* Any PID we need to create? */
4456 const char *pidfile = NULL;
4457 if(args_info.pid_file_given) {
4458 pidfile = args_info.pid_file_arg;
4459 janus_config_add(config, config_general, janus_config_item_create("pid_file", pidfile));
4460 } else {
4461 /* Check if the configuration file is saying anything about this */
4462 item = janus_config_get(config, config_general, janus_config_type_item, "pid_file");
4463 if(item && item->value)
4464 pidfile = item->value;
4465 }
4466 if(janus_pidfile_create(pidfile) < 0)
4467 exit(1);
4468
4469 /* Proceed with the rest of the configuration */
4470 janus_config_print(config);
4471 if(args_info.debug_level_given) {
4472 char debug[5];
4473 g_snprintf(debug, 5, "%d", args_info.debug_level_arg);
4474 janus_config_add(config, config_general, janus_config_item_create("debug_level", debug));
4475 } else {
4476 /* No command line directive on logging, try the configuration file */
4477 item = janus_config_get(config, config_general, janus_config_type_item, "debug_level");
4478 if(item && item->value) {
4479 int temp_level = atoi(item->value);
4480 if(temp_level == 0 && strcmp(item->value, "0")) {
4481 JANUS_PRINT("Invalid debug level %s (configuration), using default (info=4)\n", item->value);
4482 } else {
4483 janus_log_level = temp_level;
4484 if(janus_log_level < LOG_NONE)
4485 janus_log_level = 0;
4486 else if(janus_log_level > LOG_MAX)
4487 janus_log_level = LOG_MAX;
4488 }
4489 }
4490 }
4491 /* Any command line argument that should overwrite the configuration? */
4492 JANUS_PRINT("Checking command line arguments...\n");
4493 if(args_info.debug_timestamps_given) {
4494 janus_config_add(config, config_general, janus_config_item_create("debug_timestamps", "yes"));
4495 }
4496 if(args_info.disable_colors_given) {
4497 janus_config_add(config, config_general, janus_config_item_create("debug_colors", "no"));
4498 }
4499 if(args_info.debug_locks_given) {
4500 janus_config_add(config, config_general, janus_config_item_create("debug_locks", "yes"));
4501 }
4502 if(args_info.session_timeout_given) {
4503 char st[20];
4504 g_snprintf(st, 20, "%d", args_info.session_timeout_arg);
4505 janus_config_add(config, config_general, janus_config_item_create("session_timeout", st));
4506 }
4507 if(args_info.reclaim_session_timeout_given) {
4508 char st[20];
4509 g_snprintf(st, 20, "%d", args_info.reclaim_session_timeout_arg);
4510 janus_config_add(config, config_general, janus_config_item_create("reclaim_session_timeout", st));
4511 }
4512 if(args_info.interface_given) {
4513 janus_config_add(config, config_general, janus_config_item_create("interface", args_info.interface_arg));
4514 }
4515 if(args_info.configs_folder_given) {
4516 janus_config_add(config, config_general, janus_config_item_create("configs_folder", args_info.configs_folder_arg));
4517 }
4518 if(args_info.plugins_folder_given) {
4519 janus_config_add(config, config_general, janus_config_item_create("plugins_folder", args_info.plugins_folder_arg));
4520 }
4521 if(args_info.apisecret_given) {
4522 janus_config_add(config, config_general, janus_config_item_create("api_secret", args_info.apisecret_arg));
4523 }
4524 if(args_info.token_auth_given) {
4525 janus_config_add(config, config_general, janus_config_item_create("token_auth", "yes"));
4526 }
4527 if(args_info.token_auth_secret_given) {
4528 janus_config_add(config, config_general, janus_config_item_create("token_auth_secret", args_info.token_auth_secret_arg));
4529 }
4530 if(args_info.no_webrtc_encryption_given) {
4531 janus_config_add(config, config_general, janus_config_item_create("no_webrtc_encryption", "yes"));
4532 }
4533 if(args_info.cert_pem_given) {
4534 janus_config_add(config, config_certs, janus_config_item_create("cert_pem", args_info.cert_pem_arg));
4535 }
4536 if(args_info.cert_key_given) {
4537 janus_config_add(config, config_certs, janus_config_item_create("cert_key", args_info.cert_key_arg));
4538 }
4539 if(args_info.cert_pwd_given) {
4540 janus_config_add(config, config_certs, janus_config_item_create("cert_pwd", args_info.cert_pwd_arg));
4541 }
4542 if(args_info.stun_server_given) {
4543 /* Split in server and port (if port missing, use 3478 as default) */
4544 char *stunport = strrchr(args_info.stun_server_arg, ':');
4545 if(stunport != NULL) {
4546 *stunport = '\0';
4547 stunport++;
4548 janus_config_add(config, config_nat, janus_config_item_create("stun_server", args_info.stun_server_arg));
4549 janus_config_add(config, config_nat, janus_config_item_create("stun_port", stunport));
4550 } else {
4551 janus_config_add(config, config_nat, janus_config_item_create("stun_server", args_info.stun_server_arg));
4552 janus_config_add(config, config_nat, janus_config_item_create("stun_port", "3478"));
4553 }
4554 }
4555 if(args_info.nat_1_1_given) {
4556 janus_config_add(config, config_nat, janus_config_item_create("nat_1_1_mapping", args_info.nat_1_1_arg));
4557 }
4558 if(args_info.keep_private_host_given) {
4559 janus_config_add(config, config_nat, janus_config_item_create("keep_private_host", "true"));
4560 }
4561 if(args_info.ice_enforce_list_given) {
4562 janus_config_add(config, config_nat, janus_config_item_create("ice_enforce_list", args_info.ice_enforce_list_arg));
4563 }
4564 if(args_info.ice_ignore_list_given) {
4565 janus_config_add(config, config_nat, janus_config_item_create("ice_ignore_list", args_info.ice_ignore_list_arg));
4566 }
4567 if(args_info.libnice_debug_given) {
4568 janus_config_add(config, config_nat, janus_config_item_create("nice_debug", "true"));
4569 }
4570 if(args_info.full_trickle_given) {
4571 janus_config_add(config, config_nat, janus_config_item_create("full_trickle", "true"));
4572 }
4573 if(args_info.ice_lite_given) {
4574 janus_config_add(config, config_nat, janus_config_item_create("ice_lite", "true"));
4575 }
4576 if(args_info.ice_tcp_given) {
4577 janus_config_add(config, config_nat, janus_config_item_create("ice_tcp", "true"));
4578 }
4579 if(args_info.ipv6_candidates_given) {
4580 janus_config_add(config, config_media, janus_config_item_create("ipv6", "true"));
4581 }
4582 if(args_info.ipv6_link_local_given) {
4583 janus_config_add(config, config_media, janus_config_item_create("ipv6_linklocal", "true"));
4584 }
4585 if(args_info.min_nack_queue_given) {
4586 char mnq[20];
4587 g_snprintf(mnq, 20, "%d", args_info.min_nack_queue_arg);
4588 janus_config_add(config, config_media, janus_config_item_create("min_nack_queue", mnq));
4589 }
4590 if(args_info.no_media_timer_given) {
4591 char nmt[20];
4592 g_snprintf(nmt, 20, "%d", args_info.no_media_timer_arg);
4593 janus_config_add(config, config_media, janus_config_item_create("no_media_timer", nmt));
4594 }
4595 if(args_info.slowlink_threshold_given) {
4596 char st[20];
4597 g_snprintf(st, 20, "%d", args_info.slowlink_threshold_arg);
4598 janus_config_add(config, config_media, janus_config_item_create("slowlink_threshold", st));
4599 }
4600 if(args_info.twcc_period_given) {
4601 char tp[20];
4602 g_snprintf(tp, 20, "%d", args_info.twcc_period_arg);
4603 janus_config_add(config, config_media, janus_config_item_create("twcc_period", tp));
4604 }
4605 if(args_info.rtp_port_range_given) {
4606 janus_config_add(config, config_media, janus_config_item_create("rtp_port_range", args_info.rtp_port_range_arg));
4607 }
4608 if(args_info.event_handlers_given) {
4609 janus_config_add(config, config_events, janus_config_item_create("broadcast", "yes"));
4610 }
4611 janus_config_print(config);
4612
4613 /* Logging/debugging */
4614 JANUS_PRINT("Debug/log level is %d\n", janus_log_level);
4615 item = janus_config_get(config, config_general, janus_config_type_item, "debug_timestamps");
4616 if(item && item->value)
4617 janus_log_timestamps = janus_is_true(item->value);
4618 JANUS_PRINT("Debug/log timestamps are %s\n", janus_log_timestamps ? "enabled" : "disabled");
4619 item = janus_config_get(config, config_general, janus_config_type_item, "debug_colors");
4620 if(item && item->value)
4621 janus_log_colors = janus_is_true(item->value);
4622 JANUS_PRINT("Debug/log colors are %s\n", janus_log_colors ? "enabled" : "disabled");
4623 item = janus_config_get(config, config_general, janus_config_type_item, "debug_locks");
4624 if(item && item->value)
4625 lock_debug = janus_is_true(item->value);
4626 if(lock_debug) {
4627 JANUS_PRINT("Lock/mutex debugging is enabled\n");
4628 }
4629
4630 /* First of all, let's check if we're disabling WebRTC encryption for debugging purposes */
4631 item = janus_config_get(config, config_general, janus_config_type_item, "no_webrtc_encryption");
4632 if(item && item->value && janus_is_true(item->value)) {
4633 JANUS_LOG(LOG_WARN, "Disabling WebRTC encryption: *THIS IS ONLY ACCEPTABLE WHEN DEBUGGING!*\n");
4634 webrtc_encryption = FALSE;
4635 }
4636
4637 /* Any IP/interface to enforce/ignore? */
4638 item = janus_config_get(config, config_nat, janus_config_type_item, "ice_enforce_list");
4639 if(item && item->value) {
4640 gchar **list = g_strsplit(item->value, ",", -1);
4641 gchar *index = list[0];
4642 if(index != NULL) {
4643 int i=0;
4644 while(index != NULL) {
4645 if(strlen(index) > 0) {
4646 JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE enforce list...\n", index);
4647 janus_ice_enforce_interface(g_strdup(index));
4648 }
4649 i++;
4650 index = list[i];
4651 }
4652 }
4653 g_clear_pointer(&list, g_strfreev);
4654 }
4655 item = janus_config_get(config, config_nat, janus_config_type_item, "ice_ignore_list");
4656 if(item && item->value) {
4657 gchar **list = g_strsplit(item->value, ",", -1);
4658 gchar *index = list[0];
4659 if(index != NULL) {
4660 int i=0;
4661 while(index != NULL) {
4662 if(strlen(index) > 0) {
4663 JANUS_LOG(LOG_INFO, "Adding '%s' to the ICE ignore list...\n", index);
4664 janus_ice_ignore_interface(g_strdup(index));
4665 }
4666 i++;
4667 index = list[i];
4668 }
4669 }
4670 g_clear_pointer(&list, g_strfreev);
4671 }
4672 /* What is the local IP? */
4673 JANUS_LOG(LOG_VERB, "Selecting local IP address...\n");
4674 item = janus_config_get(config, config_general, janus_config_type_item, "interface");
4675 if(item && item->value) {
4676 JANUS_LOG(LOG_VERB, " -- Will try to use %s\n", item->value);
4677 /* Verify that the address is valid */
4678 struct ifaddrs *ifas = NULL;
4679 janus_network_address iface;
4680 janus_network_address_string_buffer ibuf;
4681 if(getifaddrs(&ifas) == -1) {
4682 JANUS_LOG(LOG_ERR, "Unable to acquire list of network devices/interfaces; some configurations may not work as expected... %d (%s)\n",
4683 errno, g_strerror(errno));
4684 } else {
4685 if(janus_network_lookup_interface(ifas, item->value, &iface) != 0) {
4686 JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value);
4687 } else {
4688 if(janus_network_address_to_string_buffer(&iface, &ibuf) != 0 || janus_network_address_string_buffer_is_null(&ibuf)) {
4689 JANUS_LOG(LOG_WARN, "Error getting local IP address from %s, falling back to detecting IP address...\n", item->value);
4690 } else {
4691 local_ip = g_strdup(janus_network_address_string_from_buffer(&ibuf));
4692 }
4693 }
4694 freeifaddrs(ifas);
4695 }
4696 }
4697 if(local_ip == NULL) {
4698 local_ip = janus_network_detect_local_ip_as_string(janus_network_query_options_any_ip);
4699 if(local_ip == NULL) {
4700 JANUS_LOG(LOG_WARN, "Couldn't find any address! using 127.0.0.1 as the local IP... (which is NOT going to work out of your machine)\n");
4701 local_ip = g_strdup("127.0.0.1");
4702 }
4703 }
4704 JANUS_LOG(LOG_INFO, "Using %s as local IP...\n", local_ip);
4705
4706 /* Check if a custom session timeout value was specified */
4707 item = janus_config_get(config, config_general, janus_config_type_item, "session_timeout");
4708 if(item && item->value) {
4709 int st = atoi(item->value);
4710 if(st < 0) {
4711 JANUS_LOG(LOG_WARN, "Ignoring session_timeout value as it's not a positive integer\n");
4712 } else {
4713 if(st == 0) {
4714 JANUS_LOG(LOG_WARN, "Session timeouts have been disabled (note, may result in orphaned sessions)\n");
4715 }
4716 global_session_timeout = st;
4717 }
4718 }
4719
4720 /* Check if a custom reclaim session timeout value was specified */
4721 item = janus_config_get(config, config_general, janus_config_type_item, "reclaim_session_timeout");
4722 if(item && item->value) {
4723 int rst = atoi(item->value);
4724 if(rst < 0) {
4725 JANUS_LOG(LOG_WARN, "Ignoring reclaim_session_timeout value as it's not a positive integer\n");
4726 } else {
4727 if(rst == 0) {
4728 JANUS_LOG(LOG_WARN, "Reclaim session timeouts have been disabled, will cleanup immediately\n");
4729 }
4730 reclaim_session_timeout = rst;
4731 }
4732 }
4733
4734 /* Check if a custom candidates timeout value was specified */
4735 item = janus_config_get(config, config_general, janus_config_type_item, "candidates_timeout");
4736 if(item && item->value) {
4737 int ct = atoi(item->value);
4738 if(ct <= 0) {
4739 JANUS_LOG(LOG_WARN, "Ignoring candidates_timeout value as it's not a positive integer\n");
4740 } else {
4741 candidates_timeout = ct;
4742 }
4743 }
4744
4745 /* Is there any API secret to consider? */
4746 api_secret = NULL;
4747 item = janus_config_get(config, config_general, janus_config_type_item, "api_secret");
4748 if(item && item->value) {
4749 api_secret = g_strdup(item->value);
4750 }
4751 /* Is there any API secret to consider? */
4752 admin_api_secret = NULL;
4753 item = janus_config_get(config, config_general, janus_config_type_item, "admin_secret");
4754 if(item && item->value) {
4755 admin_api_secret = g_strdup(item->value);
4756 }
4757 /* Also check if the token based authentication mechanism needs to be enabled */
4758 item = janus_config_get(config, config_general, janus_config_type_item, "token_auth");
4759 gboolean auth_enabled = item && item->value && janus_is_true(item->value);
4760 item = janus_config_get(config, config_general, janus_config_type_item, "token_auth_secret");
4761 const char *auth_secret = NULL;
4762 if (item && item->value)
4763 auth_secret = item->value;
4764 janus_auth_init(auth_enabled, auth_secret);
4765
4766 /* Check if opaque IDs should be sent back in the Janus API too */
4767 item = janus_config_get(config, config_general, janus_config_type_item, "opaqueid_in_api");
4768 if(item && item->value && janus_is_true(item->value))
4769 janus_enable_opaqueid_in_api();
4770
4771 /* Initialize the recorder code */
4772 item = janus_config_get(config, config_general, janus_config_type_item, "recordings_tmp_ext");
4773 if(item && item->value) {
4774 janus_recorder_init(TRUE, item->value);
4775 } else {
4776 janus_recorder_init(FALSE, NULL);
4777 }
4778
4779 /* Check if we should hide dependencies in "info" requests */
4780 item = janus_config_get(config, config_general, janus_config_type_item, "hide_dependencies");
4781 if(item && item->value && janus_is_true(item->value))
4782 hide_dependencies = TRUE;
4783
4784 /* Setup ICE stuff (e.g., checking if the provided STUN server is correct) */
4785 char *stun_server = NULL, *turn_server = NULL;
4786 uint16_t stun_port = 0, turn_port = 0;
4787 char *turn_type = NULL, *turn_user = NULL, *turn_pwd = NULL;
4788 char *turn_rest_api = NULL, *turn_rest_api_key = NULL;
4789 #ifdef HAVE_TURNRESTAPI
4790 char *turn_rest_api_method = NULL;
4791 uint turn_rest_api_timeout = 10;
4792 #endif
4793 uint16_t rtp_min_port = 0, rtp_max_port = 0;
4794 gboolean ice_lite = FALSE, ice_tcp = FALSE, full_trickle = FALSE, ipv6 = FALSE,
4795 ipv6_linklocal = FALSE, ignore_mdns = FALSE, ignore_unreachable_ice_server = FALSE;
4796 item = janus_config_get(config, config_media, janus_config_type_item, "ipv6");
4797 ipv6 = (item && item->value) ? janus_is_true(item->value) : FALSE;
4798 if(ipv6) {
4799 item = janus_config_get(config, config_media, janus_config_type_item, "ipv6_linklocal");
4800 ipv6_linklocal = (item && item->value) ? janus_is_true(item->value) : FALSE;
4801 }
4802 item = janus_config_get(config, config_media, janus_config_type_item, "rtp_port_range");
4803 if(item && item->value) {
4804 /* Split in min and max port */
4805 char *maxport = strrchr(item->value, '-');
4806 if(maxport != NULL) {
4807 *maxport = '\0';
4808 maxport++;
4809 if(janus_string_to_uint16(item->value, &rtp_min_port) < 0)
4810 JANUS_LOG(LOG_WARN, "Invalid RTP min port value: %s (assuming 0)\n", item->value);
4811 if(janus_string_to_uint16(maxport, &rtp_max_port) < 0)
4812 JANUS_LOG(LOG_WARN, "Invalid RTP max port value: %s (assuming 0)\n", maxport);
4813 maxport--;
4814 *maxport = '-';
4815 }
4816 if(rtp_min_port > rtp_max_port) {
4817 uint16_t temp_port = rtp_min_port;
4818 rtp_min_port = rtp_max_port;
4819 rtp_max_port = temp_port;
4820 }
4821 if(rtp_max_port == 0)
4822 rtp_max_port = 65535;
4823 JANUS_LOG(LOG_INFO, "RTP port range: %u -- %u\n", rtp_min_port, rtp_max_port);
4824 }
4825 /* Check if we need to enable the ICE Lite mode */
4826 item = janus_config_get(config, config_nat, janus_config_type_item, "ice_lite");
4827 ice_lite = (item && item->value) ? janus_is_true(item->value) : FALSE;
4828 /* Check if we need to enable ICE-TCP support (warning: still broken, for debugging only) */
4829 item = janus_config_get(config, config_nat, janus_config_type_item, "ice_tcp");
4830 ice_tcp = (item && item->value) ? janus_is_true(item->value) : FALSE;
4831 /* Check if we need to do full-trickle instead of half-trickle */
4832 item = janus_config_get(config, config_nat, janus_config_type_item, "full_trickle");
4833 full_trickle = (item && item->value) ? janus_is_true(item->value) : FALSE;
4834 /* Check if we should exit if a STUN or TURN server is unreachable */
4835 item = janus_config_get(config, config_nat, janus_config_type_item, "ignore_unreachable_ice_server");
4836 ignore_unreachable_ice_server = (item && item->value) ? janus_is_true(item->value) : FALSE;
4837 /* Any STUN server to use in Janus? */
4838 item = janus_config_get(config, config_nat, janus_config_type_item, "stun_server");
4839 if(item && item->value)
4840 stun_server = (char *)item->value;
4841 item = janus_config_get(config, config_nat, janus_config_type_item, "stun_port");
4842 if(item && item->value && janus_string_to_uint16(item->value, &stun_port) < 0) {
4843 JANUS_LOG(LOG_WARN, "Invalid STUN port: %s (disabling STUN)\n", item->value);
4844 stun_server = NULL;
4845 }
4846 /* Check if we should drop mDNS candidates */
4847 item = janus_config_get(config, config_nat, janus_config_type_item, "ignore_mdns");
4848 ignore_mdns = (item && item->value) ? janus_is_true(item->value) : FALSE;
4849 /* Any 1:1 NAT mapping to take into account? */
4850 item = janus_config_get(config, config_nat, janus_config_type_item, "nat_1_1_mapping");
4851 if(item && item->value) {
4852 JANUS_LOG(LOG_INFO, "Using nat_1_1_mapping for public IP: %s\n", item->value);
4853 char **list = g_strsplit(item->value, ",", -1);
4854 char *index = list[0];
4855 if(index != NULL) {
4856 int i=0;
4857 while(index != NULL) {
4858 if(strlen(index) > 0) {
4859 if(!janus_network_string_is_valid_address(janus_network_query_options_any_ip, index)) {
4860 JANUS_LOG(LOG_WARN, "Invalid nat_1_1_mapping address %s, skipping...\n", index);
4861 } else {
4862 janus_add_public_ip(index);
4863 }
4864 }
4865 i++;
4866 index = list[i];
4867 }
4868 }
4869 g_strfreev(list);
4870 if(janus_get_public_ip_count() > 0) {
4871 /* Check if we should replace the private host, or advertise both candidates */
4872 gboolean keep_private_host = FALSE;
4873 item = janus_config_get(config, config_nat, janus_config_type_item, "keep_private_host");
4874 if(item && item->value && janus_is_true(item->value)) {
4875 JANUS_LOG(LOG_INFO, " -- Going to keep the private host too (separate candidates)\n");
4876 keep_private_host = TRUE;
4877 }
4878 janus_ice_enable_nat_1_1(keep_private_host);
4879 }
4880 }
4881 /* Any TURN server to use in Janus? */
4882 item = janus_config_get(config, config_nat, janus_config_type_item, "turn_server");
4883 if(item && item->value)
4884 turn_server = (char *)item->value;
4885 item = janus_config_get(config, config_nat, janus_config_type_item, "turn_port");
4886 if(item && item->value && janus_string_to_uint16(item->value, &turn_port) < 0) {
4887 JANUS_LOG(LOG_WARN, "Invalid TURN port: %s (disabling TURN)\n", item->value);
4888 turn_server = NULL;
4889 }
4890 item = janus_config_get(config, config_nat, janus_config_type_item, "turn_type");
4891 if(item && item->value)
4892 turn_type = (char *)item->value;
4893 item = janus_config_get(config, config_nat, janus_config_type_item, "turn_user");
4894 if(item && item->value)
4895 turn_user = (char *)item->value;
4896 item = janus_config_get(config, config_nat, janus_config_type_item, "turn_pwd");
4897 if(item && item->value)
4898 turn_pwd = (char *)item->value;
4899 /* Check if there's any TURN REST API backend to use */
4900 item = janus_config_get(config, config_nat, janus_config_type_item, "turn_rest_api");
4901 if(item && item->value)
4902 turn_rest_api = (char *)item->value;
4903 item = janus_config_get(config, config_nat, janus_config_type_item, "turn_rest_api_key");
4904 if(item && item->value)
4905 turn_rest_api_key = (char *)item->value;
4906 #ifdef HAVE_TURNRESTAPI
4907 item = janus_config_get(config, config_nat, janus_config_type_item, "turn_rest_api_method");
4908 if(item && item->value)
4909 turn_rest_api_method = (char *)item->value;
4910 item = janus_config_get(config, config_nat, janus_config_type_item, "turn_rest_api_timeout");
4911 if(item && item->value) {
4912 int rst = atoi(item->value);
4913 if(rst <= 0) { /* Don't allow user to set 0 seconds i.e., infinite wait */
4914 JANUS_LOG(LOG_WARN, "Ignoring turn_rest_api_timeout as it's not a positive integer, leaving at default (10 seconds)\n");
4915 } else {
4916 turn_rest_api_timeout = rst;
4917 }
4918 }
4919 #endif
4920 item = janus_config_get(config, config_nat, janus_config_type_item, "allow_force_relay");
4921 if(item && item->value && janus_is_true(item->value)) {
4922 JANUS_LOG(LOG_WARN, "Note: applications/users will be allowed to force Janus to use TURN. Make sure you know what you're doing!\n");
4923 janus_ice_allow_force_relay();
4924 }
4925 /* Do we need a limited number of static event loops, or is it ok to have one per handle (the default)? */
4926 item = janus_config_get(config, config_general, janus_config_type_item, "event_loops");
4927 if(item && item->value) {
4928 int loops = atoi(item->value);
4929 /* Check if we should allow API calls to specify which loops to use for new handles */
4930 gboolean loops_api = FALSE;
4931 item = janus_config_get(config, config_general, janus_config_type_item, "allow_loop_indication");
4932 if(item && item->value)
4933 loops_api = janus_is_true(item->value);
4934 janus_ice_set_static_event_loops(loops, loops_api);
4935 }
4936 /* Initialize the ICE stack now */
4937 janus_ice_init(ice_lite, ice_tcp, full_trickle, ignore_mdns, ipv6, ipv6_linklocal, rtp_min_port, rtp_max_port);
4938 if(janus_ice_set_stun_server(stun_server, stun_port) < 0) {
4939 if(!ignore_unreachable_ice_server) {
4940 JANUS_LOG(LOG_FATAL, "Invalid STUN address %s:%u\n", stun_server, stun_port);
4941 exit(1);
4942 } else {
4943 JANUS_LOG(LOG_ERR, "Invalid STUN address %s:%u. STUN will be disabled\n", stun_server, stun_port);
4944 }
4945 }
4946 item = janus_config_get(config, config_nat, janus_config_type_item, "ice_nomination");
4947 if(item && item->value) {
4948 #ifndef HAVE_ICE_NOMINATION
4949 JANUS_LOG(LOG_WARN, "This version of libnice doesn't support setting the ICE nomination mode, ignoring '%s'\n", item->value);
4950 #else
4951 janus_ice_set_nomination_mode(item->value);
4952 #endif
4953 }
4954 item = janus_config_get(config, config_nat, janus_config_type_item, "ice_keepalive_conncheck");
4955 if(item && item->value)
4956 janus_ice_set_keepalive_conncheck_enabled(janus_is_true(item->value));
4957 if(janus_ice_set_turn_server(turn_server, turn_port, turn_type, turn_user, turn_pwd) < 0) {
4958 if(!ignore_unreachable_ice_server) {
4959 JANUS_LOG(LOG_FATAL, "Invalid TURN address %s:%u\n", turn_server, turn_port);
4960 exit(1);
4961 } else {
4962 JANUS_LOG(LOG_ERR, "Invalid TURN address %s:%u. TURN will be disabled\n", turn_server, turn_port);
4963 }
4964 }
4965 #ifndef HAVE_TURNRESTAPI
4966 if(turn_rest_api != NULL || turn_rest_api_key != NULL) {
4967 JANUS_LOG(LOG_WARN, "A TURN REST API backend specified in the settings, but libcurl support has not been built\n");
4968 }
4969 #else
4970 if(janus_ice_set_turn_rest_api(turn_rest_api, turn_rest_api_key, turn_rest_api_method, turn_rest_api_timeout) < 0) {
4971 JANUS_LOG(LOG_FATAL, "Invalid TURN REST API configuration: %s (%s, %s)\n", turn_rest_api, turn_rest_api_key, turn_rest_api_method);
4972 exit(1);
4973 }
4974 #endif
4975 item = janus_config_get(config, config_nat, janus_config_type_item, "nice_debug");
4976 if(item && item->value && janus_is_true(item->value)) {
4977 /* Enable libnice debugging */
4978 janus_ice_debugging_enable();
4979 }
4980 if(stun_server == NULL && turn_server == NULL) {
4981 /* No STUN and TURN server provided for Janus: make sure it isn't on a private address */
4982 int num_ips = janus_get_public_ip_count();
4983 if(num_ips == 0) {
4984 /* If nat_1_1_mapping is off, the first (and only) public IP is the local_ip */
4985 num_ips++;
4986 }
4987 /* Check each public IP */
4988 int i = 0;
4989 for(i = 0; i < num_ips; i++) {
4990 gboolean private_address = FALSE;
4991 const gchar *test_ip = janus_get_public_ip(i);
4992 janus_network_address addr;
4993 if(janus_network_string_to_address(janus_network_query_options_any_ip, test_ip, &addr) != 0) {
4994 JANUS_LOG(LOG_ERR, "Invalid address %s..?\n", test_ip);
4995 } else {
4996 if(addr.family == AF_INET) {
4997 unsigned short int ip[4];
4998 sscanf(test_ip, "%hu.%hu.%hu.%hu", &ip[0], &ip[1], &ip[2], &ip[3]);
4999 if(ip[0] == 10) {
5000 /* Class A private address */
5001 private_address = TRUE;
5002 } else if(ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) {
5003 /* Class B private address */
5004 private_address = TRUE;
5005 } else if(ip[0] == 192 && ip[1] == 168) {
5006 /* Class C private address */
5007 private_address = TRUE;
5008 }
5009 } else {
5010 /* TODO Similar check for IPv6... */
5011 }
5012 }
5013 if(private_address) {
5014 JANUS_LOG(LOG_WARN, "Janus is deployed on a private address (%s) but you didn't specify any STUN server!"
5015 " Expect trouble if this is supposed to work over the internet and not just in a LAN...\n", test_ip);
5016 }
5017 }
5018 }
5019
5020 /* Is there any DSCP TOS to apply? */
5021 item = janus_config_get(config, config_media, janus_config_type_item, "dscp");
5022 if(!item || !item->value) /* Just for backwards compatibility */
5023 item = janus_config_get(config, config_media, janus_config_type_item, "dscp_tos");
5024 if(item && item->value) {
5025 int dscp = atoi(item->value);
5026 if(dscp < 0) {
5027 JANUS_LOG(LOG_WARN, "Ignoring dscp value as it's not a positive integer\n");
5028 } else {
5029 janus_set_dscp(dscp);
5030 }
5031 }
5032
5033 /* NACK related stuff */
5034 item = janus_config_get(config, config_media, janus_config_type_item, "min_nack_queue");
5035 if(item && item->value) {
5036 int mnq = atoi(item->value);
5037 if(mnq < 0) {
5038 JANUS_LOG(LOG_WARN, "Ignoring min_nack_queue value as it's not a positive integer\n");
5039 } else {
5040 janus_set_min_nack_queue(mnq);
5041 }
5042 }
5043 item = janus_config_get(config, config_media, janus_config_type_item, "nack_optimizations");
5044 if(item && item->value) {
5045 gboolean optimize = janus_is_true(item->value);
5046 janus_set_nack_optimizations_enabled(optimize);
5047 }
5048 /* no-media timer */
5049 item = janus_config_get(config, config_media, janus_config_type_item, "no_media_timer");
5050 if(item && item->value) {
5051 int nmt = atoi(item->value);
5052 if(nmt < 0) {
5053 JANUS_LOG(LOG_WARN, "Ignoring no_media_timer value as it's not a positive integer\n");
5054 } else {
5055 janus_set_no_media_timer(nmt);
5056 }
5057 }
5058 /* slowlink-threshold value */
5059 item = janus_config_get(config, config_media, janus_config_type_item, "slowlink_threshold");
5060 if(item && item->value) {
5061 int st = atoi(item->value);
5062 if(st < 0) {
5063 JANUS_LOG(LOG_WARN, "Ignoring slowlink_threshold value as it's not a positive integer\n");
5064 } else {
5065 janus_set_slowlink_threshold(st);
5066 }
5067 }
5068 /* TWCC period */
5069 item = janus_config_get(config, config_media, janus_config_type_item, "twcc_period");
5070 if(item && item->value) {
5071 int tp = atoi(item->value);
5072 if(tp <= 0) {
5073 JANUS_LOG(LOG_WARN, "Ignoring twcc_period value as it's not a positive integer\n");
5074 } else {
5075 janus_set_twcc_period(tp);
5076 }
5077 }
5078
5079 /* Setup OpenSSL stuff */
5080 const char *server_pem;
5081 item = janus_config_get(config, config_certs, janus_config_type_item, "cert_pem");
5082 if(!item || !item->value) {
5083 server_pem = NULL;
5084 } else {
5085 server_pem = item->value;
5086 }
5087 const char *server_key;
5088 item = janus_config_get(config, config_certs, janus_config_type_item, "cert_key");
5089 if(!item || !item->value) {
5090 server_key = NULL;
5091 } else {
5092 server_key = item->value;
5093 }
5094 const char *password;
5095 item = janus_config_get(config, config_certs, janus_config_type_item, "cert_pwd");
5096 if(!item || !item->value) {
5097 password = NULL;
5098 } else {
5099 password = item->value;
5100 }
5101 JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
5102
5103 SSL_library_init();
5104 SSL_load_error_strings();
5105 OpenSSL_add_all_algorithms();
5106 /* Check if random pool looks ok (this does not give any guarantees for later, though) */
5107 if(RAND_status() != 1) {
5108 JANUS_LOG(LOG_FATAL, "Random pool is not properly seeded, cannot generate random numbers\n");
5109 exit(1);
5110 }
5111 /* ... and DTLS-SRTP in particular */
5112 const char *dtls_ciphers = NULL;
5113 item = janus_config_get(config, config_certs, janus_config_type_item, "dtls_ciphers");
5114 if(item && item->value)
5115 dtls_ciphers = item->value;
5116 guint16 dtls_timeout = 1000;
5117 item = janus_config_get(config, config_media, janus_config_type_item, "dtls_timeout");
5118 if(item && item->value && janus_string_to_uint16(item->value, &dtls_timeout) < 0) {
5119 JANUS_LOG(LOG_WARN, "Invalid DTLS timeout: %s (falling back to default)\n", item->value);
5120 dtls_timeout = 1000;
5121 }
5122 gboolean rsa_private_key = FALSE;
5123 item = janus_config_get(config, config_certs, janus_config_type_item, "rsa_private_key");
5124 if(item && item->value)
5125 rsa_private_key = janus_is_true(item->value);
5126 gboolean dtls_accept_selfsigned = TRUE;
5127 item = janus_config_get(config, config_certs, janus_config_type_item, "dtls_accept_selfsigned");
5128 if(item && item->value)
5129 dtls_accept_selfsigned = janus_is_true(item->value);
5130 if(janus_dtls_srtp_init(server_pem, server_key, password, dtls_ciphers, dtls_timeout, rsa_private_key, dtls_accept_selfsigned) < 0) {
5131 exit(1);
5132 }
5133 /* Check if there's any custom value for the starting MTU to use in the BIO filter */
5134 item = janus_config_get(config, config_media, janus_config_type_item, "dtls_mtu");
5135 if(item && item->value)
5136 janus_dtls_bio_agent_set_mtu(atoi(item->value));
5137
5138 #ifdef HAVE_SCTP
5139 /* Initialize SCTP for DataChannels */
5140 if(janus_sctp_init() < 0) {
5141 exit(1);
5142 }
5143 #else
5144 JANUS_LOG(LOG_WARN, "Data Channels support not compiled\n");
5145 #endif
5146
5147 /* Sessions */
5148 sessions = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
5149 janus_mutex_init(&sessions_mutex);
5150 /* Start the sessions timeout watchdog */
5151 sessions_watchdog_context = g_main_context_new();
5152 GMainLoop *watchdog_loop = g_main_loop_new(sessions_watchdog_context, FALSE);
5153 GError *error = NULL;
5154 GThread *watchdog = g_thread_try_new("timeout watchdog", &janus_sessions_watchdog, watchdog_loop, &error);
5155 if(error != NULL) {
5156 JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to start sessions timeout watchdog...\n",
5157 error->code, error->message ? error->message : "??");
5158 g_error_free(error);
5159 exit(1);
5160 }
5161 /* Start the thread that will dispatch incoming requests */
5162 requests = g_async_queue_new_full((GDestroyNotify)janus_request_destroy);
5163 GThread *requests_thread = g_thread_try_new("sessions requests", &janus_transport_requests, NULL, &error);
5164 if(error != NULL) {
5165 JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to start requests thread...\n",
5166 error->code, error->message ? error->message : "??");
5167 g_error_free(error);
5168 exit(1);
5169 }
5170 /* Create a thread pool to handle asynchronous requests, no matter what the transport */
5171 error = NULL;
5172 tasks = g_thread_pool_new(janus_transport_task, NULL, -1, FALSE, &error);
5173 if(error != NULL) {
5174 /* Something went wrong... */
5175 JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to launch the request pool task thread...\n",
5176 error->code, error->message ? error->message : "??");
5177 g_error_free(error);
5178 exit(1);
5179 }
5180 /* Wait 120 seconds before stopping idle threads to avoid the creation of too many threads for AddressSanitizer. */
5181 g_thread_pool_set_max_idle_time(120 * 1000);
5182
5183 /* Load event handlers */
5184 path = NULL;
5185 dir = NULL;
5186 /* Event handlers are disabled by default, though: they need to be enabled in the configuration */
5187 item = janus_config_get(config, config_events, janus_config_type_item, "broadcast");
5188 gboolean enable_events = FALSE;
5189 if(item && item->value)
5190 enable_events = janus_is_true(item->value);
5191 if(!enable_events) {
5192 JANUS_LOG(LOG_INFO, "Event handlers support disabled\n");
5193 } else {
5194 gchar **disabled_eventhandlers = NULL;
5195 path = EVENTDIR;
5196 item = janus_config_get(config, config_general, janus_config_type_item, "events_folder");
5197 if(item && item->value)
5198 path = (char *)item->value;
5199 JANUS_LOG(LOG_INFO, "Event handler plugins folder: %s\n", path);
5200 dir = opendir(path);
5201 if(!dir) {
5202 /* Not really fatal, we don't care and go on anyway: event handlers are not fundamental */
5203 JANUS_LOG(LOG_WARN, "\tCouldn't access event handler plugins folder...\n");
5204 } else {
5205 item = janus_config_get(config, config_events, janus_config_type_item, "stats_period");
5206 if(item && item->value) {
5207 /* Check if we need to use a larger period for pushing statistics to event handlers */
5208 int period = atoi(item->value);
5209 if(period < 0) {
5210 JANUS_LOG(LOG_WARN, "Invalid event handlers statistics period, using default value (1 second)\n");
5211 } else if(period == 0) {
5212 janus_ice_set_event_stats_period(0);
5213 JANUS_LOG(LOG_WARN, "Disabling event handlers statistics period, no media statistics will be pushed to event handlers\n");
5214 } else {
5215 janus_ice_set_event_stats_period(period);
5216 JANUS_LOG(LOG_INFO, "Setting event handlers statistics period to %d seconds\n", period);
5217 }
5218 }
5219 item = janus_config_get(config, config_events, janus_config_type_item, "combine_media_stats");
5220 if(item && item->value) {
5221 gboolean combine = janus_is_true(item->value);
5222 janus_ice_event_set_combine_media_stats(combine);
5223 if(combine)
5224 JANUS_LOG(LOG_INFO, "Event handler configured to send media stats combined in a single event\n");
5225 }
5226 /* Any event handlers to ignore? */
5227 item = janus_config_get(config, config_events, janus_config_type_item, "disable");
5228 if(item && item->value)
5229 disabled_eventhandlers = g_strsplit(item->value, ",", -1);
5230 /* Open the shared objects */
5231 struct dirent *eventent = NULL;
5232 char eventpath[1024];
5233 while((eventent = readdir(dir))) {
5234 int len = strlen(eventent->d_name);
5235 if (len < 4) {
5236 continue;
5237 }
5238 if (strcasecmp(eventent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
5239 continue;
5240 }
5241 /* Check if this event handler has been disabled in the configuration file */
5242 if(disabled_eventhandlers != NULL) {
5243 gchar *index = disabled_eventhandlers[0];
5244 if(index != NULL) {
5245 int i=0;
5246 gboolean skip = FALSE;
5247 while(index != NULL) {
5248 while(isspace(*index))
5249 index++;
5250 if(strlen(index) && !strcmp(index, eventent->d_name)) {
5251 JANUS_LOG(LOG_WARN, "Event handler plugin '%s' has been disabled, skipping...\n", eventent->d_name);
5252 skip = TRUE;
5253 break;
5254 }
5255 i++;
5256 index = disabled_eventhandlers[i];
5257 }
5258 if(skip)
5259 continue;
5260 }
5261 }
5262 JANUS_LOG(LOG_INFO, "Loading event handler plugin '%s'...\n", eventent->d_name);
5263 memset(eventpath, 0, 1024);
5264 g_snprintf(eventpath, 1024, "%s/%s", path, eventent->d_name);
5265 void *event = dlopen(eventpath, RTLD_NOW | RTLD_GLOBAL);
5266 if (!event) {
5267 JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load event handler plugin '%s': %s\n", eventent->d_name, dlerror());
5268 if (exit_on_dl_error)
5269 exit(1);
5270 } else {
5271 create_e *create = (create_e*) dlsym(event, "create");
5272 const char *dlsym_error = dlerror();
5273 if (dlsym_error) {
5274 JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
5275 if (exit_on_dl_error)
5276 exit(1);
5277 continue;
5278 }
5279 janus_eventhandler *janus_eventhandler = create();
5280 if(!janus_eventhandler) {
5281 JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
5282 continue;
5283 }
5284 /* Are all the mandatory methods and callbacks implemented? */
5285 if(!janus_eventhandler->init || !janus_eventhandler->destroy ||
5286 !janus_eventhandler->get_api_compatibility ||
5287 !janus_eventhandler->get_version ||
5288 !janus_eventhandler->get_version_string ||
5289 !janus_eventhandler->get_description ||
5290 !janus_eventhandler->get_package ||
5291 !janus_eventhandler->get_name ||
5292 !janus_eventhandler->incoming_event) {
5293 JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this event handler plugin...\n");
5294 continue;
5295 }
5296 if(janus_eventhandler->get_api_compatibility() < JANUS_EVENTHANDLER_API_VERSION) {
5297 JANUS_LOG(LOG_ERR, "The '%s' event handler plugin was compiled against an older version of the API (%d < %d), skipping it: update it to enable it again\n",
5298 janus_eventhandler->get_package(), janus_eventhandler->get_api_compatibility(), JANUS_EVENTHANDLER_API_VERSION);
5299 continue;
5300 }
5301 janus_eventhandler->init(configs_folder);
5302 JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_eventhandler->get_version(), janus_eventhandler->get_version_string());
5303 JANUS_LOG(LOG_VERB, "\t [%s] %s\n", janus_eventhandler->get_package(), janus_eventhandler->get_name());
5304 JANUS_LOG(LOG_VERB, "\t %s\n", janus_eventhandler->get_description());
5305 JANUS_LOG(LOG_VERB, "\t Plugin API version: %d\n", janus_eventhandler->get_api_compatibility());
5306 JANUS_LOG(LOG_VERB, "\t Subscriptions:");
5307 if(janus_eventhandler->events_mask == 0) {
5308 JANUS_LOG(LOG_VERB, " none");
5309 } else {
5310 if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_SESSION))
5311 JANUS_LOG(LOG_VERB, " sessions");
5312 if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_HANDLE))
5313 JANUS_LOG(LOG_VERB, " handles");
5314 if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_JSEP))
5315 JANUS_LOG(LOG_VERB, " jsep");
5316 if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_WEBRTC))
5317 JANUS_LOG(LOG_VERB, " webrtc");
5318 if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_MEDIA))
5319 JANUS_LOG(LOG_VERB, " media");
5320 if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_PLUGIN))
5321 JANUS_LOG(LOG_VERB, " plugins");
5322 if(janus_flags_is_set(&janus_eventhandler->events_mask, JANUS_EVENT_TYPE_TRANSPORT))
5323 JANUS_LOG(LOG_VERB, " transports");
5324 }
5325 JANUS_LOG(LOG_VERB, "\n");
5326 if(eventhandlers == NULL)
5327 eventhandlers = g_hash_table_new(g_str_hash, g_str_equal);
5328 g_hash_table_insert(eventhandlers, (gpointer)janus_eventhandler->get_package(), janus_eventhandler);
5329 if(eventhandlers_so == NULL)
5330 eventhandlers_so = g_hash_table_new(g_str_hash, g_str_equal);
5331 g_hash_table_insert(eventhandlers_so, (gpointer)janus_eventhandler->get_package(), event);
5332 }
5333 }
5334 closedir(dir);
5335 }
5336 if(disabled_eventhandlers != NULL)
5337 g_strfreev(disabled_eventhandlers);
5338 disabled_eventhandlers = NULL;
5339 /* Initialize the event broadcaster */
5340 if(janus_events_init(enable_events, (server_name ? server_name : (char *)JANUS_SERVER_NAME), eventhandlers) < 0) {
5341 JANUS_LOG(LOG_FATAL, "Error initializing the Event handlers mechanism...\n");
5342 exit(1);
5343 }
5344 }
5345
5346 /* Load plugins */
5347 path = PLUGINDIR;
5348 item = janus_config_get(config, config_general, janus_config_type_item, "plugins_folder");
5349 if(item && item->value)
5350 path = (char *)item->value;
5351 JANUS_LOG(LOG_INFO, "Plugins folder: %s\n", path);
5352 dir = opendir(path);
5353 if(!dir) {
5354 JANUS_LOG(LOG_FATAL, "\tCouldn't access plugins folder...\n");
5355 exit(1);
5356 }
5357 /* Any plugin to ignore? */
5358 gchar **disabled_plugins = NULL;
5359 item = janus_config_get(config, config_plugins, janus_config_type_item, "disable");
5360 if(item && item->value)
5361 disabled_plugins = g_strsplit(item->value, ",", -1);
5362 /* Open the shared objects */
5363 struct dirent *pluginent = NULL;
5364 char pluginpath[1024];
5365 while((pluginent = readdir(dir))) {
5366 int len = strlen(pluginent->d_name);
5367 if (len < 4) {
5368 continue;
5369 }
5370 if (strcasecmp(pluginent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
5371 continue;
5372 }
5373 /* Check if this plugins has been disabled in the configuration file */
5374 if(disabled_plugins != NULL) {
5375 gchar *index = disabled_plugins[0];
5376 if(index != NULL) {
5377 int i=0;
5378 gboolean skip = FALSE;
5379 while(index != NULL) {
5380 while(isspace(*index))
5381 index++;
5382 if(strlen(index) && !strcmp(index, pluginent->d_name)) {
5383 JANUS_LOG(LOG_WARN, "Plugin '%s' has been disabled, skipping...\n", pluginent->d_name);
5384 skip = TRUE;
5385 break;
5386 }
5387 i++;
5388 index = disabled_plugins[i];
5389 }
5390 if(skip)
5391 continue;
5392 }
5393 }
5394 JANUS_LOG(LOG_INFO, "Loading plugin '%s'...\n", pluginent->d_name);
5395 memset(pluginpath, 0, 1024);
5396 g_snprintf(pluginpath, 1024, "%s/%s", path, pluginent->d_name);
5397 void *plugin = dlopen(pluginpath, RTLD_NOW | RTLD_GLOBAL);
5398 if (!plugin) {
5399 JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load plugin '%s': %s\n", pluginent->d_name, dlerror());
5400 if (exit_on_dl_error)
5401 exit(1);
5402 } else {
5403 create_p *create = (create_p*) dlsym(plugin, "create");
5404 const char *dlsym_error = dlerror();
5405 if (dlsym_error) {
5406 JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
5407 if (exit_on_dl_error)
5408 exit(1);
5409 continue;
5410 }
5411 janus_plugin *janus_plugin = create();
5412 if(!janus_plugin) {
5413 JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
5414 continue;
5415 }
5416 /* Are all the mandatory methods and callbacks implemented? */
5417 if(!janus_plugin->init || !janus_plugin->destroy ||
5418 !janus_plugin->get_api_compatibility ||
5419 !janus_plugin->get_version ||
5420 !janus_plugin->get_version_string ||
5421 !janus_plugin->get_description ||
5422 !janus_plugin->get_package ||
5423 !janus_plugin->get_name ||
5424 !janus_plugin->create_session ||
5425 !janus_plugin->query_session ||
5426 !janus_plugin->destroy_session ||
5427 !janus_plugin->handle_message ||
5428 !janus_plugin->setup_media ||
5429 !janus_plugin->hangup_media) {
5430 JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this plugin...\n");
5431 continue;
5432 }
5433 if(janus_plugin->get_api_compatibility() < JANUS_PLUGIN_API_VERSION) {
5434 JANUS_LOG(LOG_ERR, "The '%s' plugin was compiled against an older version of the API (%d < %d), skipping it: update it to enable it again\n",
5435 janus_plugin->get_package(), janus_plugin->get_api_compatibility(), JANUS_PLUGIN_API_VERSION);
5436 continue;
5437 }
5438 if(janus_plugin->init(&janus_handler_plugin, configs_folder) < 0) {
5439 JANUS_LOG(LOG_WARN, "The '%s' plugin could not be initialized\n", janus_plugin->get_package());
5440 dlclose(plugin);
5441 continue;
5442 }
5443 JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_plugin->get_version(), janus_plugin->get_version_string());
5444 JANUS_LOG(LOG_VERB, "\t [%s] %s\n", janus_plugin->get_package(), janus_plugin->get_name());
5445 JANUS_LOG(LOG_VERB, "\t %s\n", janus_plugin->get_description());
5446 JANUS_LOG(LOG_VERB, "\t Plugin API version: %d\n", janus_plugin->get_api_compatibility());
5447 if(!janus_plugin->incoming_rtp && !janus_plugin->incoming_rtcp && !janus_plugin->incoming_data) {
5448 JANUS_LOG(LOG_WARN, "The '%s' plugin doesn't implement any callback for RTP/RTCP/data... is this on purpose?\n",
5449 janus_plugin->get_package());
5450 }
5451 if(!janus_plugin->incoming_rtp && !janus_plugin->incoming_rtcp && janus_plugin->incoming_data) {
5452 JANUS_LOG(LOG_WARN, "The '%s' plugin will only handle data channels (no RTP/RTCP)... is this on purpose?\n",
5453 janus_plugin->get_package());
5454 }
5455 if(plugins == NULL)
5456 plugins = g_hash_table_new(g_str_hash, g_str_equal);
5457 g_hash_table_insert(plugins, (gpointer)janus_plugin->get_package(), janus_plugin);
5458 if(plugins_so == NULL)
5459 plugins_so = g_hash_table_new(g_str_hash, g_str_equal);
5460 g_hash_table_insert(plugins_so, (gpointer)janus_plugin->get_package(), plugin);
5461 }
5462 }
5463 closedir(dir);
5464 if(disabled_plugins != NULL)
5465 g_strfreev(disabled_plugins);
5466 disabled_plugins = NULL;
5467
5468 /* Load transports */
5469 gboolean janus_api_enabled = FALSE, admin_api_enabled = FALSE;
5470 path = TRANSPORTDIR;
5471 item = janus_config_get(config, config_general, janus_config_type_item, "transports_folder");
5472 if(item && item->value)
5473 path = (char *)item->value;
5474 JANUS_LOG(LOG_INFO, "Transport plugins folder: %s\n", path);
5475 dir = opendir(path);
5476 if(!dir) {
5477 JANUS_LOG(LOG_FATAL, "\tCouldn't access transport plugins folder...\n");
5478 exit(1);
5479 }
5480 /* Any transport to ignore? */
5481 gchar **disabled_transports = NULL;
5482 item = janus_config_get(config, config_transports, janus_config_type_item, "disable");
5483 if(item && item->value)
5484 disabled_transports = g_strsplit(item->value, ",", -1);
5485 /* Open the shared objects */
5486 struct dirent *transportent = NULL;
5487 char transportpath[1024];
5488 while((transportent = readdir(dir))) {
5489 int len = strlen(transportent->d_name);
5490 if (len < 4) {
5491 continue;
5492 }
5493 if (strcasecmp(transportent->d_name+len-strlen(SHLIB_EXT), SHLIB_EXT)) {
5494 continue;
5495 }
5496 /* Check if this transports has been disabled in the configuration file */
5497 if(disabled_transports != NULL) {
5498 gchar *index = disabled_transports[0];
5499 if(index != NULL) {
5500 int i=0;
5501 gboolean skip = FALSE;
5502 while(index != NULL) {
5503 while(isspace(*index))
5504 index++;
5505 if(strlen(index) && !strcmp(index, transportent->d_name)) {
5506 JANUS_LOG(LOG_WARN, "Transport plugin '%s' has been disabled, skipping...\n", transportent->d_name);
5507 skip = TRUE;
5508 break;
5509 }
5510 i++;
5511 index = disabled_transports[i];
5512 }
5513 if(skip)
5514 continue;
5515 }
5516 }
5517 JANUS_LOG(LOG_INFO, "Loading transport plugin '%s'...\n", transportent->d_name);
5518 memset(transportpath, 0, 1024);
5519 g_snprintf(transportpath, 1024, "%s/%s", path, transportent->d_name);
5520 void *transport = dlopen(transportpath, RTLD_NOW | RTLD_GLOBAL);
5521 if (!transport) {
5522 JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load transport plugin '%s': %s\n", transportent->d_name, dlerror());
5523 if (exit_on_dl_error)
5524 exit(1);
5525 } else {
5526 create_t *create = (create_t*) dlsym(transport, "create");
5527 const char *dlsym_error = dlerror();
5528 if (dlsym_error) {
5529 JANUS_LOG(exit_on_dl_error ? LOG_FATAL : LOG_ERR, "\tCouldn't load symbol 'create': %s\n", dlsym_error);
5530 if (exit_on_dl_error)
5531 exit(1);
5532 continue;
5533 }
5534 janus_transport *janus_transport = create();
5535 if(!janus_transport) {
5536 JANUS_LOG(LOG_ERR, "\tCouldn't use function 'create'...\n");
5537 continue;
5538 }
5539 /* Are all the mandatory methods and callbacks implemented? */
5540 if(!janus_transport->init || !janus_transport->destroy ||
5541 !janus_transport->get_api_compatibility ||
5542 !janus_transport->get_version ||
5543 !janus_transport->get_version_string ||
5544 !janus_transport->get_description ||
5545 !janus_transport->get_package ||
5546 !janus_transport->get_name ||
5547 !janus_transport->send_message ||
5548 !janus_transport->is_janus_api_enabled ||
5549 !janus_transport->is_admin_api_enabled ||
5550 !janus_transport->session_created ||
5551 !janus_transport->session_over ||
5552 !janus_transport->session_claimed) {
5553 JANUS_LOG(LOG_ERR, "\tMissing some mandatory methods/callbacks, skipping this transport plugin...\n");
5554 continue;
5555 }
5556 if(janus_transport->get_api_compatibility() < JANUS_TRANSPORT_API_VERSION) {
5557 JANUS_LOG(LOG_ERR, "The '%s' transport plugin was compiled against an older version of the API (%d < %d), skipping it: update it to enable it again\n",
5558 janus_transport->get_package(), janus_transport->get_api_compatibility(), JANUS_TRANSPORT_API_VERSION);
5559 continue;
5560 }
5561 if(janus_transport->init(&janus_handler_transport, configs_folder) < 0) {
5562 JANUS_LOG(LOG_WARN, "The '%s' plugin could not be initialized\n", janus_transport->get_package());
5563 dlclose(transport);
5564 continue;
5565 }
5566 JANUS_LOG(LOG_VERB, "\tVersion: %d (%s)\n", janus_transport->get_version(), janus_transport->get_version_string());
5567 JANUS_LOG(LOG_VERB, "\t [%s] %s\n", janus_transport->get_package(), janus_transport->get_name());
5568 JANUS_LOG(LOG_VERB, "\t %s\n", janus_transport->get_description());
5569 JANUS_LOG(LOG_VERB, "\t Plugin API version: %d\n", janus_transport->get_api_compatibility());
5570 JANUS_LOG(LOG_VERB, "\t Janus API: %s\n", janus_transport->is_janus_api_enabled() ? "enabled" : "disabled");
5571 JANUS_LOG(LOG_VERB, "\t Admin API: %s\n", janus_transport->is_admin_api_enabled() ? "enabled" : "disabled");
5572 janus_api_enabled = janus_api_enabled || janus_transport->is_janus_api_enabled();
5573 admin_api_enabled = admin_api_enabled || janus_transport->is_admin_api_enabled();
5574 if(transports == NULL)
5575 transports = g_hash_table_new(g_str_hash, g_str_equal);
5576 g_hash_table_insert(transports, (gpointer)janus_transport->get_package(), janus_transport);
5577 if(transports_so == NULL)
5578 transports_so = g_hash_table_new(g_str_hash, g_str_equal);
5579 g_hash_table_insert(transports_so, (gpointer)janus_transport->get_package(), transport);
5580 }
5581 }
5582 closedir(dir);
5583 if(disabled_transports != NULL)
5584 g_strfreev(disabled_transports);
5585 disabled_transports = NULL;
5586 /* Make sure at least a Janus API transport is available */
5587 if(!janus_api_enabled) {
5588 JANUS_LOG(LOG_FATAL, "No Janus API transport is available... enable at least one and restart Janus\n");
5589 exit(1); /* FIXME Should we really give up? */
5590 }
5591 /* Make sure at least an admin API transport is available, if the auth mechanism is enabled */
5592 if(!admin_api_enabled && janus_auth_is_stored_mode()) {
5593 JANUS_LOG(LOG_FATAL, "No Admin/monitor transport is available, but the stored token based authentication mechanism is enabled... this will cause all requests to fail, giving up! If you want to use tokens, enable the Admin/monitor API or set the token auth secret.\n");
5594 exit(1); /* FIXME Should we really give up? */
5595 }
5596
5597 /* Make sure libnice is recent enough, otherwise print a warning */
5598 int libnice_version = 0;
5599 if(libnice_version_string != NULL && sscanf(libnice_version_string, "%*d.%*d.%d", &libnice_version) == 1) {
5600 if(libnice_version < 16) {
5601 JANUS_LOG(LOG_WARN, "libnice version outdated: %s installed, at least 0.1.16 recommended. Notice the installed version was checked at build time: if you updated libnice in the meanwhile, re-configure and recompile to get rid of this warning\n",
5602 libnice_version_string);
5603 }
5604 }
5605
5606 /* Ok, Janus has started! Let the parent now about this if we're daemonizing */
5607 if(daemonize) {
5608 int code = 0;
5609 ssize_t res = 0;
5610 do {
5611 res = write(pipefd[1], &code, sizeof(int));
5612 } while(res == -1 && errno == EINTR);
5613 }
5614
5615 /* If the Event Handlers mechanism is enabled, notify handlers that Janus just started */
5616 if(janus_events_is_enabled()) {
5617 json_t *info = json_object();
5618 json_object_set_new(info, "status", json_string("started"));
5619 json_object_set_new(info, "info", janus_info(NULL));
5620 janus_events_notify_handlers(JANUS_EVENT_TYPE_CORE, JANUS_EVENT_SUBTYPE_CORE_STARTUP, 0, info);
5621 }
5622
5623 /* Loop until we have to stop */
5624 mainloop = g_main_loop_new (NULL, TRUE);
5625 g_main_loop_run(mainloop);
5626
5627 /* If the Event Handlers mechanism is enabled, notify handlers that Janus is hanging up */
5628 if(janus_events_is_enabled()) {
5629 json_t *info = json_object();
5630 json_object_set_new(info, "status", json_string("shutdown"));
5631 json_object_set_new(info, "signum", json_integer(stop_signal));
5632 janus_events_notify_handlers(JANUS_EVENT_TYPE_CORE, JANUS_EVENT_SUBTYPE_CORE_SHUTDOWN, 0, info);
5633 }
5634
5635 /* Done */
5636 JANUS_LOG(LOG_INFO, "Ending sessions timeout watchdog...\n");
5637 g_main_loop_quit(watchdog_loop);
5638 g_thread_join(watchdog);
5639 watchdog = NULL;
5640 g_main_loop_unref(watchdog_loop);
5641 g_main_context_unref(sessions_watchdog_context);
5642 sessions_watchdog_context = NULL;
5643
5644 if(config)
5645 janus_config_destroy(config);
5646
5647 JANUS_LOG(LOG_INFO, "Closing transport plugins:\n");
5648 if(transports != NULL && g_hash_table_size(transports) > 0) {
5649 g_hash_table_foreach(transports, janus_transport_close, NULL);
5650 g_hash_table_destroy(transports);
5651 }
5652 if(transports_so != NULL && g_hash_table_size(transports_so) > 0) {
5653 g_hash_table_foreach(transports_so, janus_transportso_close, NULL);
5654 g_hash_table_destroy(transports_so);
5655 }
5656 /* Get rid of requests tasks and thread too */
5657 g_thread_pool_free(tasks, FALSE, FALSE);
5658 JANUS_LOG(LOG_INFO, "Ending requests thread...\n");
5659 g_async_queue_push(requests, &exit_message);
5660 g_thread_join(requests_thread);
5661 requests_thread = NULL;
5662 g_async_queue_unref(requests);
5663
5664 JANUS_LOG(LOG_INFO, "Destroying sessions...\n");
5665 g_clear_pointer(&sessions, g_hash_table_destroy);
5666 janus_ice_deinit();
5667 JANUS_LOG(LOG_INFO, "Freeing crypto resources...\n");
5668 janus_dtls_srtp_cleanup();
5669 EVP_cleanup();
5670 ERR_free_strings();
5671 #ifdef HAVE_SCTP
5672 JANUS_LOG(LOG_INFO, "De-initializing SCTP...\n");
5673 janus_sctp_deinit();
5674 #endif
5675 janus_auth_deinit();
5676
5677 JANUS_LOG(LOG_INFO, "Closing plugins:\n");
5678 if(plugins != NULL && g_hash_table_size(plugins) > 0) {
5679 g_hash_table_foreach(plugins, janus_plugin_close, NULL);
5680 g_hash_table_destroy(plugins);
5681 }
5682 if(plugins_so != NULL && g_hash_table_size(plugins_so) > 0) {
5683 g_hash_table_foreach(plugins_so, janus_pluginso_close, NULL);
5684 g_hash_table_destroy(plugins_so);
5685 }
5686
5687 JANUS_LOG(LOG_INFO, "Closing event handlers:\n");
5688 janus_events_deinit();
5689 if(eventhandlers != NULL && g_hash_table_size(eventhandlers) > 0) {
5690 g_hash_table_foreach(eventhandlers, janus_eventhandler_close, NULL);
5691 g_hash_table_destroy(eventhandlers);
5692 }
5693 if(eventhandlers_so != NULL && g_hash_table_size(eventhandlers_so) > 0) {
5694 g_hash_table_foreach(eventhandlers_so, janus_eventhandlerso_close, NULL);
5695 g_hash_table_destroy(eventhandlers_so);
5696 }
5697
5698 janus_recorder_deinit();
5699 g_free(local_ip);
5700 if (public_ips) {
5701 g_list_free(public_ips);
5702 }
5703 if (public_ips_table) {
5704 g_hash_table_destroy(public_ips_table);
5705 }
5706
5707 if(janus_ice_get_static_event_loops() > 0)
5708 janus_ice_stop_static_event_loops();
5709
5710 janus_protected_folders_clear();
5711
5712 #ifdef REFCOUNT_DEBUG
5713 /* Any reference counters that are still up while we're leaving? (debug-mode only) */
5714 janus_mutex_lock(&counters_mutex);
5715 if(counters && g_hash_table_size(counters) > 0) {
5716 JANUS_PRINT("Debugging reference counters: %d still allocated\n", g_hash_table_size(counters));
5717 GHashTableIter iter;
5718 gpointer value;
5719 g_hash_table_iter_init(&iter, counters);
5720 while(g_hash_table_iter_next(&iter, NULL, &value)) {
5721 JANUS_PRINT(" -- %p\n", value);
5722 }
5723 } else {
5724 JANUS_PRINT("Debugging reference counters: 0 still allocated\n");
5725 }
5726 janus_mutex_unlock(&counters_mutex);
5727 #endif
5728 g_clear_pointer(&janus_log_global_prefix, g_free);
5729
5730 JANUS_PRINT("Bye!\n");
5731
5732 exit(0);
5733 }
5734