/* Transport implementation */ int janus_websockets_init(janus_transport_callbacks *callback, const char *config_path) { if(g_atomic_int_get(&stopping)) { /* Still stopping from before */ return -1; } if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } /* This is the callback we'll need to invoke to contact the gateway */ gateway = callback; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI JANUS_LOG(LOG_INFO, "libwebsockets >= 1.6 available, using new API\n"); #else JANUS_LOG(LOG_INFO, "libwebsockets < 1.6 available, using old API\n"); #endif /* Read configuration */ char filename[255]; g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_WEBSOCKETS_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) { janus_config_print(config); /* Handle configuration */ janus_config_item *item = janus_config_get_item_drilldown(config, "general", "json"); if(item && item->value) { /* Check how we need to format/serialize the JSON output */ if(!strcasecmp(item->value, "indented")) { /* Default: indented, we use three spaces for that */ json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "plain")) { /* Not indented and no new lines, but still readable */ json_format = JSON_INDENT(0) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "compact")) { /* Compact, so no spaces between separators */ json_format = JSON_COMPACT | JSON_PRESERVE_ORDER; } else { JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", item->value); json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } } /* Check if we need to send events to handlers */ janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events"); if(events != NULL && events->value != NULL) notify_events = janus_is_true(events->value); if(!notify_events && callback->events_is_enabled()) { JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_WEBSOCKETS_NAME); } item = janus_config_get_item_drilldown(config, "general", "ws_logging"); if(item && item->value) { ws_log_level = atoi(item->value); if(ws_log_level < 0) ws_log_level = 0; } JANUS_LOG(LOG_VERB, "libwebsockets logging: %d\n", ws_log_level); lws_set_log_level(ws_log_level, NULL); old_wss = NULL; janus_mutex_init(&old_wss_mutex); /* Any ACL for either the Janus or Admin API? */ item = janus_config_get_item_drilldown(config, "general", "ws_acl"); if(item && item->value) { gchar **list = g_strsplit(item->value, ",", -1); gchar *index = list[0]; if(index != NULL) { int i=0; while(index != NULL) { if(strlen(index) > 0) { JANUS_LOG(LOG_INFO, "Adding '%s' to the Janus API allowed list...\n", index); janus_websockets_allow_address(g_strdup(index), FALSE); } i++; index = list[i]; } } g_strfreev(list); list = NULL; } item = janus_config_get_item_drilldown(config, "admin", "admin_ws_acl"); if(item && item->value) { gchar **list = g_strsplit(item->value, ",", -1); gchar *index = list[0]; if(index != NULL) { int i=0; while(index != NULL) { if(strlen(index) > 0) { JANUS_LOG(LOG_INFO, "Adding '%s' to the Admin/monitor allowed list...\n", index); janus_websockets_allow_address(g_strdup(index), TRUE); } i++; index = list[i]; } } g_strfreev(list); list = NULL; } /* Setup the Janus API WebSockets server(s) */ item = janus_config_get_item_drilldown(config, "general", "ws"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "WebSockets server disabled\n"); } else { int wsport = 8188; item = janus_config_get_item_drilldown(config, "general", "ws_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "general", "ws_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "general", "ws_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } /* Prepare context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = wss_protocols; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI info.extensions = NULL; #else info.extensions = libwebsocket_get_internal_extensions(); #endif info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; info.gid = -1; info.uid = -1; info.options = 0; /* Create the WebSocket context */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI wss = lws_create_context(&info); #else wss = libwebsocket_create_context(&info); #endif if(wss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n"); } else { JANUS_LOG(LOG_INFO, "WebSockets server started (port %d)...\n", wsport); } g_free(ip); } item = janus_config_get_item_drilldown(config, "general", "wss"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Secure WebSockets server disabled\n"); } else { int wsport = 8989; item = janus_config_get_item_drilldown(config, "general", "wss_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "general", "wss_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "general", "wss_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } item = janus_config_get_item_drilldown(config, "certificates", "cert_pem"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n"); } else { char *server_pem = (char *)item->value; char *server_key = (char *)item->value; item = janus_config_get_item_drilldown(config, "certificates", "cert_key"); if(item && item->value) server_key = (char *)item->value; JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key); /* Prepare secure context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = swss_protocols; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI info.extensions = NULL; #else info.extensions = libwebsocket_get_internal_extensions(); #endif info.ssl_cert_filepath = server_pem; info.ssl_private_key_filepath = server_key; info.gid = -1; info.uid = -1; #if LWS_LIBRARY_VERSION_MAJOR >= 2 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; #else info.options = 0; #endif /* Create the secure WebSocket context */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI swss = lws_create_context(&info); #else swss = libwebsocket_create_context(&info); #endif if(swss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n"); } else { JANUS_LOG(LOG_INFO, "Secure WebSockets server started (port %d)...\n", wsport); } g_free(ip); } } /* Do the same for the Admin API, if enabled */ item = janus_config_get_item_drilldown(config, "admin", "admin_ws"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Admin WebSockets server disabled\n"); } else { int wsport = 7188; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } /* Prepare context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = admin_wss_protocols; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI info.extensions = NULL; #else info.extensions = libwebsocket_get_internal_extensions(); #endif info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; info.gid = -1; info.uid = -1; info.options = 0; /* Create the WebSocket context */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI admin_wss = lws_create_context(&info); #else admin_wss = libwebsocket_create_context(&info); #endif if(admin_wss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n"); } else { JANUS_LOG(LOG_INFO, "Admin WebSockets server started (port %d)...\n", wsport); } g_free(ip); } item = janus_config_get_item_drilldown(config, "admin", "admin_wss"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Secure Admin WebSockets server disabled\n"); } else { int wsport = 7989; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } item = janus_config_get_item_drilldown(config, "certificates", "cert_pem"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n"); } else { char *server_pem = (char *)item->value; char *server_key = (char *)item->value; item = janus_config_get_item_drilldown(config, "certificates", "cert_key"); if(item && item->value) server_key = (char *)item->value; JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key); /* Prepare secure context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = admin_swss_protocols; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI info.extensions = NULL; #else info.extensions = libwebsocket_get_internal_extensions(); #endif info.ssl_cert_filepath = server_pem; info.ssl_private_key_filepath = server_key; info.gid = -1; info.uid = -1; #if LWS_LIBRARY_VERSION_MAJOR >= 2 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; #else info.options = 0; #endif /* Create the secure WebSocket context */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI admin_swss = lws_create_context(&info); #else admin_swss = libwebsocket_create_context(&info); #endif if(admin_swss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n"); } else { JANUS_LOG(LOG_INFO, "Secure Admin WebSockets server started (port %d)...\n", wsport); } g_free(ip); } } } janus_config_destroy(config); config = NULL; if(!wss && !swss && !admin_wss && !admin_swss) { JANUS_LOG(LOG_FATAL, "No WebSockets server started, giving up...\n"); return -1; /* No point in keeping the plugin loaded */ } wss_janus_api_enabled = wss || swss; wss_admin_api_enabled = admin_wss || admin_swss; GError *error = NULL; /* Start the WebSocket service threads */ if(wss != NULL) { wss_thread = g_thread_try_new("ws thread", &janus_websockets_thread, wss, &error); if(!wss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } if(swss != NULL) { swss_thread = g_thread_try_new("sws thread", &janus_websockets_thread, swss, &error); if(!swss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } if(admin_wss != NULL) { admin_wss_thread = g_thread_try_new("admin ws thread", &janus_websockets_thread, admin_wss, &error); if(!admin_wss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Admin WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } if(admin_swss != NULL) { admin_swss_thread = g_thread_try_new("admin sws thread", &janus_websockets_thread, admin_swss, &error); if(!admin_swss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure Admin WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } /* Done */ g_atomic_int_set(&initialized, 1); JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_WEBSOCKETS_NAME); return 0; }
/* Transport implementation */ int janus_websockets_init(janus_transport_callbacks *callback, const char *config_path) { if(g_atomic_int_get(&stopping)) { /* Still stopping from before */ return -1; } if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } #ifndef LWS_WITH_IPV6 JANUS_LOG(LOG_WARN, "libwebsockets has been built without IPv6 support, will bind to IPv4 only\n"); #endif /* This is the callback we'll need to invoke to contact the gateway */ gateway = callback; /* Prepare the common context */ struct lws_context_creation_info wscinfo; memset(&wscinfo, 0, sizeof wscinfo); wscinfo.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS; /* We use vhosts on the same context to address both APIs, secure or not */ struct lws_vhost *wss = NULL, *swss = NULL, *admin_wss = NULL, *admin_swss = NULL; /* Read configuration */ char filename[255]; g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_WEBSOCKETS_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) { janus_config_print(config); /* Handle configuration */ janus_config_item *item = janus_config_get_item_drilldown(config, "general", "json"); if(item && item->value) { /* Check how we need to format/serialize the JSON output */ if(!strcasecmp(item->value, "indented")) { /* Default: indented, we use three spaces for that */ json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "plain")) { /* Not indented and no new lines, but still readable */ json_format = JSON_INDENT(0) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "compact")) { /* Compact, so no spaces between separators */ json_format = JSON_COMPACT | JSON_PRESERVE_ORDER; } else { JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", item->value); json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } } /* Check if we need to send events to handlers */ janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events"); if(events != NULL && events->value != NULL) notify_events = janus_is_true(events->value); if(!notify_events && callback->events_is_enabled()) { JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_WEBSOCKETS_NAME); } item = janus_config_get_item_drilldown(config, "general", "ws_logging"); if(item && item->value) { /* libwebsockets uses a mask to set log levels, as documented here: * https://libwebsockets.org/lws-api-doc-master/html/group__log.html */ if(strstr(item->value, "none")) { /* Disable libwebsockets logging completely (the default) */ } else if(strstr(item->value, "all")) { /* Enable all libwebsockets logging */ ws_log_level = LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG | LLL_PARSER | LLL_HEADER | LLL_EXT | LLL_CLIENT | LLL_LATENCY | LLL_USER | LLL_COUNT; } else { /* Only enable some of the properties */ if(strstr(item->value, "err")) ws_log_level |= LLL_ERR; if(strstr(item->value, "warn")) ws_log_level |= LLL_WARN; if(strstr(item->value, "notice")) ws_log_level |= LLL_NOTICE; if(strstr(item->value, "info")) ws_log_level |= LLL_INFO; if(strstr(item->value, "debug")) ws_log_level |= LLL_DEBUG; if(strstr(item->value, "parser")) ws_log_level |= LLL_PARSER; if(strstr(item->value, "header")) ws_log_level |= LLL_HEADER; if(strstr(item->value, "ext")) ws_log_level |= LLL_EXT; if(strstr(item->value, "client")) ws_log_level |= LLL_CLIENT; if(strstr(item->value, "latency")) ws_log_level |= LLL_LATENCY; if(strstr(item->value, "user")) ws_log_level |= LLL_USER; if(strstr(item->value, "count")) ws_log_level |= LLL_COUNT; } } JANUS_LOG(LOG_INFO, "libwebsockets logging: %d\n", ws_log_level); lws_set_log_level(ws_log_level, janus_websockets_log_emit_function); /* Any ACL for either the Janus or Admin API? */ item = janus_config_get_item_drilldown(config, "general", "ws_acl"); if(item && item->value) { gchar **list = g_strsplit(item->value, ",", -1); gchar *index = list[0]; if(index != NULL) { int i=0; while(index != NULL) { if(strlen(index) > 0) { JANUS_LOG(LOG_INFO, "Adding '%s' to the Janus API allowed list...\n", index); janus_websockets_allow_address(g_strdup(index), FALSE); } i++; index = list[i]; } } g_strfreev(list); list = NULL; } item = janus_config_get_item_drilldown(config, "admin", "admin_ws_acl"); if(item && item->value) { gchar **list = g_strsplit(item->value, ",", -1); gchar *index = list[0]; if(index != NULL) { int i=0; while(index != NULL) { if(strlen(index) > 0) { JANUS_LOG(LOG_INFO, "Adding '%s' to the Admin/monitor allowed list...\n", index); janus_websockets_allow_address(g_strdup(index), TRUE); } i++; index = list[i]; } } g_strfreev(list); list = NULL; } /* Check if we need to enable the transport level ping/pong mechanism */ int pingpong_trigger = 0, pingpong_timeout = 0; item = janus_config_get_item_drilldown(config, "general", "pingpong_trigger"); if(item && item->value) { #if LWS_LIBRARY_VERSION_MAJOR >= 2 && LWS_LIBRARY_VERSION_MINOR >= 1 pingpong_trigger = atoi(item->value); if(pingpong_trigger < 0) { JANUS_LOG(LOG_WARN, "Invalid value for pingpong_trigger (%d), ignoring...\n", pingpong_trigger); pingpong_trigger = 0; } #else JANUS_LOG(LOG_WARN, "WebSockets ping/pong only supported in libwebsockets >= 2.1\n"); #endif } item = janus_config_get_item_drilldown(config, "general", "pingpong_timeout"); if(item && item->value) { #if LWS_LIBRARY_VERSION_MAJOR >= 2 && LWS_LIBRARY_VERSION_MINOR >= 1 pingpong_timeout = atoi(item->value); if(pingpong_timeout < 0) { JANUS_LOG(LOG_WARN, "Invalid value for pingpong_timeout (%d), ignoring...\n", pingpong_timeout); pingpong_timeout = 0; } #else JANUS_LOG(LOG_WARN, "WebSockets ping/pong only supported in libwebsockets >= 2.1\n"); #endif } if((pingpong_trigger && !pingpong_timeout) || (!pingpong_trigger && pingpong_timeout)) { JANUS_LOG(LOG_WARN, "pingpong_trigger and pingpong_timeout not both set, ignoring...\n"); } #if LWS_LIBRARY_VERSION_MAJOR >= 2 && LWS_LIBRARY_VERSION_MINOR >= 1 if(pingpong_trigger > 0 && pingpong_timeout > 0) { wscinfo.ws_ping_pong_interval = pingpong_trigger; wscinfo.timeout_secs = pingpong_timeout; } #endif /* Force single-thread server */ wscinfo.count_threads = 1; /* Create the base context */ wsc = lws_create_context(&wscinfo); if(wsc == NULL) { JANUS_LOG(LOG_ERR, "Error creating libwebsockets context...\n"); janus_config_destroy(config); return -1; /* No point in keeping the plugin loaded */ } /* Setup the Janus API WebSockets server(s) */ item = janus_config_get_item_drilldown(config, "general", "ws"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "WebSockets server disabled\n"); } else { int wsport = 8188; item = janus_config_get_item_drilldown(config, "general", "ws_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "general", "ws_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "general", "ws_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } /* Prepare context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = ws_protocols; info.extensions = NULL; info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; info.ssl_private_key_password = NULL; info.gid = -1; info.uid = -1; info.options = 0; /* Create the WebSocket context */ wss = lws_create_vhost(wsc, &info); if(wss == NULL) { JANUS_LOG(LOG_FATAL, "Error creating vhost for WebSockets server...\n"); } else { JANUS_LOG(LOG_INFO, "WebSockets server started (port %d)...\n", wsport); } g_free(ip); } item = janus_config_get_item_drilldown(config, "general", "wss"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Secure WebSockets server disabled\n"); } else { int wsport = 8989; item = janus_config_get_item_drilldown(config, "general", "wss_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "general", "wss_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "general", "wss_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } item = janus_config_get_item_drilldown(config, "certificates", "cert_pem"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n"); } else { char *server_pem = (char *)item->value; char *server_key = (char *)item->value; char *password = NULL; item = janus_config_get_item_drilldown(config, "certificates", "cert_key"); if(item && item->value) server_key = (char *)item->value; item = janus_config_get_item_drilldown(config, "certificates", "cert_pwd"); if(item && item->value) password = (char *)item->value; JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key); /* Prepare secure context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = sws_protocols; info.extensions = NULL; info.ssl_cert_filepath = server_pem; info.ssl_private_key_filepath = server_key; info.ssl_private_key_password = password; info.gid = -1; info.uid = -1; #if LWS_LIBRARY_VERSION_MAJOR >= 2 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; #else info.options = 0; #endif /* Create the secure WebSocket context */ swss = lws_create_vhost(wsc, &info); if(swss == NULL) { JANUS_LOG(LOG_FATAL, "Error creating vhost for Secure WebSockets server...\n"); } else { JANUS_LOG(LOG_INFO, "Secure WebSockets server started (port %d)...\n", wsport); } g_free(ip); } } /* Do the same for the Admin API, if enabled */ item = janus_config_get_item_drilldown(config, "admin", "admin_ws"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Admin WebSockets server disabled\n"); } else { int wsport = 7188; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } /* Prepare context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = admin_ws_protocols; info.extensions = NULL; info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; info.ssl_private_key_password = NULL; info.gid = -1; info.uid = -1; info.options = 0; /* Create the WebSocket context */ admin_wss = lws_create_vhost(wsc, &info); if(admin_wss == NULL) { JANUS_LOG(LOG_FATAL, "Error creating vhost for Admin WebSockets server...\n"); } else { JANUS_LOG(LOG_INFO, "Admin WebSockets server started (port %d)...\n", wsport); } g_free(ip); } item = janus_config_get_item_drilldown(config, "admin", "admin_wss"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Secure Admin WebSockets server disabled\n"); } else { int wsport = 7989; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } item = janus_config_get_item_drilldown(config, "certificates", "cert_pem"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n"); } else { char *server_pem = (char *)item->value; char *server_key = (char *)item->value; char *password = NULL; item = janus_config_get_item_drilldown(config, "certificates", "cert_key"); if(item && item->value) server_key = (char *)item->value; item = janus_config_get_item_drilldown(config, "certificates", "cert_pwd"); if(item && item->value) password = (char *)item->value; JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key); /* Prepare secure context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = admin_sws_protocols; info.extensions = NULL; info.ssl_cert_filepath = server_pem; info.ssl_private_key_filepath = server_key; info.ssl_private_key_password = password; info.gid = -1; info.uid = -1; #if LWS_LIBRARY_VERSION_MAJOR >= 2 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; #else info.options = 0; #endif /* Create the secure WebSocket context */ admin_swss = lws_create_vhost(wsc, &info); if(admin_swss == NULL) { JANUS_LOG(LOG_FATAL, "Error creating vhost for Secure Admin WebSockets server...\n"); } else { JANUS_LOG(LOG_INFO, "Secure Admin WebSockets server started (port %d)...\n", wsport); } g_free(ip); } } } janus_config_destroy(config); config = NULL; if(!wss && !swss && !admin_wss && !admin_swss) { JANUS_LOG(LOG_WARN, "No WebSockets server started, giving up...\n"); lws_context_destroy(wsc); return -1; /* No point in keeping the plugin loaded */ } ws_janus_api_enabled = wss || swss; ws_admin_api_enabled = admin_wss || admin_swss; g_atomic_int_set(&initialized, 1); GError *error = NULL; /* Start the WebSocket service thread */ if(ws_janus_api_enabled || ws_admin_api_enabled) { ws_thread = g_thread_try_new("ws thread", &janus_websockets_thread, wsc, &error); if(!ws_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } /* Done */ JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_WEBSOCKETS_NAME); return 0; }