mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
344 lines
9.4 KiB
344 lines
9.4 KiB
/*
|
|
* Copyright (C) 2012-2013 Crocodile RCS Ltd
|
|
*
|
|
* This file is part of Kamailio, a free SIP server.
|
|
*
|
|
* Kamailio is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* Kamailio is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Exception: permission to copy, modify, propagate, and distribute a work
|
|
* formed by combining OpenSSL toolkit software and the code in this file,
|
|
* such as linking with software components and libraries released under
|
|
* OpenSSL project license.
|
|
*
|
|
*/
|
|
|
|
#include "../../dprint.h"
|
|
#include "../../events.h"
|
|
#include "../../ip_addr.h"
|
|
#include "../../locking.h"
|
|
#include "../../sr_module.h"
|
|
#include "../../tcp_conn.h"
|
|
#include "../../timer_proc.h"
|
|
#include "../../cfg/cfg.h"
|
|
#include "../../lib/kcore/kstats_wrapper.h"
|
|
#include "../../lib/kmi/mi.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../mod_fix.h"
|
|
#include "../../parser/msg_parser.h"
|
|
#include "ws_conn.h"
|
|
#include "ws_handshake.h"
|
|
#include "ws_frame.h"
|
|
#include "ws_mod.h"
|
|
#include "config.h"
|
|
|
|
MODULE_VERSION
|
|
|
|
/* Maximum number of connections to display when using the ws.dump MI command */
|
|
#define MAX_WS_CONNS_DUMP 50
|
|
|
|
static int mod_init(void);
|
|
static int child_init(int rank);
|
|
static void destroy(void);
|
|
static int ws_close_fixup(void** param, int param_no);
|
|
|
|
sl_api_t ws_slb;
|
|
|
|
#define DEFAULT_KEEPALIVE_INTERVAL 1
|
|
static int ws_keepalive_interval = DEFAULT_KEEPALIVE_INTERVAL;
|
|
|
|
static int ws_keepalive_timeout = DEFAULT_KEEPALIVE_TIMEOUT;
|
|
|
|
#define DEFAULT_KEEPALIVE_PROCESSES 1
|
|
static int ws_keepalive_processes = DEFAULT_KEEPALIVE_PROCESSES;
|
|
|
|
static cmd_export_t cmds[]=
|
|
{
|
|
/* ws_frame.c */
|
|
{ "ws_close", (cmd_function) ws_close,
|
|
0, 0, 0,
|
|
ANY_ROUTE },
|
|
{ "ws_close", (cmd_function) ws_close2,
|
|
2, ws_close_fixup, 0,
|
|
ANY_ROUTE },
|
|
{ "ws_close", (cmd_function) ws_close3,
|
|
3, ws_close_fixup, 0,
|
|
ANY_ROUTE },
|
|
|
|
/* ws_handshake.c */
|
|
{ "ws_handle_handshake", (cmd_function) ws_handle_handshake,
|
|
0, 0, 0,
|
|
ANY_ROUTE },
|
|
|
|
{ 0, 0, 0, 0, 0, 0 }
|
|
};
|
|
|
|
static param_export_t params[]=
|
|
{
|
|
/* ws_frame.c */
|
|
{ "keepalive_mechanism", INT_PARAM, &ws_keepalive_mechanism },
|
|
{ "keepalive_timeout", INT_PARAM, &ws_keepalive_timeout },
|
|
{ "ping_application_data", PARAM_STR, &ws_ping_application_data },
|
|
|
|
/* ws_handshake.c */
|
|
{ "sub_protocols", INT_PARAM, &ws_sub_protocols },
|
|
{ "cors_mode", INT_PARAM, &ws_cors_mode },
|
|
|
|
/* ws_mod.c */
|
|
{ "keepalive_interval", INT_PARAM, &ws_keepalive_interval },
|
|
{ "keepalive_processes", INT_PARAM, &ws_keepalive_processes },
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static stat_export_t stats[] =
|
|
{
|
|
/* ws_conn.c */
|
|
{ "ws_current_connections", 0, &ws_current_connections },
|
|
{ "ws_max_concurrent_connections", 0, &ws_max_concurrent_connections },
|
|
{ "ws_sip_current_connections", 0, &ws_sip_current_connections },
|
|
{ "ws_sip_max_concurrent_connections", 0, &ws_sip_max_concurrent_connections },
|
|
{ "ws_msrp_current_connections", 0, &ws_msrp_current_connections },
|
|
{ "ws_msrp_max_concurrent_connections", 0, &ws_msrp_max_concurrent_connections },
|
|
|
|
/* ws_frame.c */
|
|
{ "ws_failed_connections", 0, &ws_failed_connections },
|
|
{ "ws_local_closed_connections", 0, &ws_local_closed_connections },
|
|
{ "ws_received_frames", 0, &ws_received_frames },
|
|
{ "ws_remote_closed_connections", 0, &ws_remote_closed_connections },
|
|
{ "ws_transmitted_frames", 0, &ws_transmitted_frames },
|
|
{ "ws_sip_failed_connections", 0, &ws_sip_failed_connections },
|
|
{ "ws_sip_local_closed_connections", 0, &ws_sip_local_closed_connections },
|
|
{ "ws_sip_received_frames", 0, &ws_sip_received_frames },
|
|
{ "ws_sip_remote_closed_connections", 0, &ws_sip_remote_closed_connections },
|
|
{ "ws_sip_transmitted_frames", 0, &ws_sip_transmitted_frames },
|
|
{ "ws_msrp_failed_connections", 0, &ws_msrp_failed_connections },
|
|
{ "ws_msrp_local_closed_connections", 0, &ws_msrp_local_closed_connections },
|
|
{ "ws_msrp_received_frames", 0, &ws_msrp_received_frames },
|
|
{ "ws_msrp_remote_closed_connections", 0, &ws_msrp_remote_closed_connections },
|
|
{ "ws_msrp_transmitted_frames", 0, &ws_msrp_transmitted_frames },
|
|
|
|
/* ws_handshake.c */
|
|
{ "ws_failed_handshakes", 0, &ws_failed_handshakes },
|
|
{ "ws_successful_handshakes", 0, &ws_successful_handshakes },
|
|
{ "ws_sip_successful_handshakes", 0, &ws_sip_successful_handshakes },
|
|
{ "ws_msrp_successful_handshakes", 0, &ws_msrp_successful_handshakes },
|
|
|
|
/* legacy typo's, fixed in 4.4 */
|
|
{ "ws_sip_max_concurrent_connectons", 0, &ws_sip_max_concurrent_connections },
|
|
{ "ws_msrp_max_concurrent_connectons", 0, &ws_msrp_max_concurrent_connections },
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static mi_export_t mi_cmds[] =
|
|
{
|
|
/* ws_conn.c */
|
|
{ "ws.dump", ws_mi_dump, 0, 0, 0 },
|
|
|
|
/* ws_frame.c */
|
|
{ "ws.close", ws_mi_close, 0, 0, 0 },
|
|
{ "ws.ping", ws_mi_ping, 0, 0, 0 },
|
|
{ "ws.pong", ws_mi_pong, 0, 0, 0 },
|
|
|
|
/* ws_handshake.c */
|
|
{ "ws.disable", ws_mi_disable, 0, 0, 0 },
|
|
{ "ws.enable", ws_mi_enable, 0, 0, 0 },
|
|
|
|
{ 0, 0, 0, 0, 0 }
|
|
};
|
|
|
|
struct module_exports exports=
|
|
{
|
|
"websocket",
|
|
DEFAULT_DLFLAGS, /* dlopen flags */
|
|
cmds, /* Exported functions */
|
|
params, /* Exported parameters */
|
|
stats, /* exported statistics */
|
|
mi_cmds, /* exported MI functions */
|
|
0, /* exported pseudo-variables */
|
|
0, /* extra processes */
|
|
mod_init, /* module initialization function */
|
|
0, /* response function */
|
|
destroy, /* destroy function */
|
|
child_init /* per-child initialization function */
|
|
};
|
|
|
|
static int mod_init(void)
|
|
{
|
|
if (sl_load_api(&ws_slb) != 0)
|
|
{
|
|
LM_ERR("binding to SL\n");
|
|
goto error;
|
|
}
|
|
|
|
if (sr_event_register_cb(SREV_TCP_WS_FRAME_IN, ws_frame_receive) != 0)
|
|
{
|
|
LM_ERR("registering WebSocket receive call-back\n");
|
|
goto error;
|
|
}
|
|
|
|
if (sr_event_register_cb(SREV_TCP_WS_FRAME_OUT, ws_frame_transmit) != 0)
|
|
{
|
|
LM_ERR("registering WebSocket transmit call-back\n");
|
|
goto error;
|
|
}
|
|
|
|
if (register_module_stats(exports.name, stats) != 0)
|
|
{
|
|
LM_ERR("registering core statistics\n");
|
|
goto error;
|
|
}
|
|
|
|
if (register_mi_mod(exports.name, mi_cmds) != 0)
|
|
{
|
|
LM_ERR("registering MI commands\n");
|
|
goto error;
|
|
}
|
|
|
|
if (wsconn_init() < 0)
|
|
{
|
|
LM_ERR("initialising WebSocket connections table\n");
|
|
goto error;
|
|
}
|
|
|
|
if (ws_ping_application_data.len < 1
|
|
|| ws_ping_application_data.len > 125)
|
|
{
|
|
ws_ping_application_data.s = DEFAULT_PING_APPLICATION_DATA + 8;
|
|
ws_ping_application_data.len =
|
|
DEFAULT_PING_APPLICATION_DATA_LEN - 8;
|
|
}
|
|
|
|
if (ws_keepalive_mechanism != KEEPALIVE_MECHANISM_NONE)
|
|
{
|
|
if (ws_keepalive_timeout < 1 || ws_keepalive_timeout > 3600)
|
|
ws_keepalive_timeout = DEFAULT_KEEPALIVE_TIMEOUT;
|
|
|
|
switch(ws_keepalive_mechanism)
|
|
{
|
|
case KEEPALIVE_MECHANISM_PING:
|
|
case KEEPALIVE_MECHANISM_PONG:
|
|
break;
|
|
default:
|
|
ws_keepalive_mechanism = DEFAULT_KEEPALIVE_MECHANISM;
|
|
break;
|
|
}
|
|
|
|
if (ws_keepalive_interval < 1 || ws_keepalive_interval > 60)
|
|
ws_keepalive_interval = DEFAULT_KEEPALIVE_INTERVAL;
|
|
|
|
if (ws_keepalive_processes < 1 || ws_keepalive_processes > 16)
|
|
ws_keepalive_processes = DEFAULT_KEEPALIVE_PROCESSES;
|
|
|
|
/* Add extra process/timer for the keepalive process */
|
|
register_sync_timers(ws_keepalive_processes);
|
|
}
|
|
|
|
if (ws_sub_protocols & SUB_PROTOCOL_MSRP
|
|
&& !sr_event_enabled(SREV_TCP_MSRP_FRAME))
|
|
ws_sub_protocols &= ~SUB_PROTOCOL_MSRP;
|
|
|
|
if ((ws_sub_protocols & SUB_PROTOCOL_ALL) == 0)
|
|
{
|
|
LM_ERR("no sub-protocols enabled\n");
|
|
goto error;
|
|
}
|
|
|
|
if ((ws_sub_protocols | SUB_PROTOCOL_ALL) != SUB_PROTOCOL_ALL)
|
|
{
|
|
LM_ERR("unrecognised sub-protocols enabled\n");
|
|
goto error;
|
|
}
|
|
|
|
if (ws_cors_mode < 0 || ws_cors_mode > 2)
|
|
{
|
|
LM_ERR("bad value for cors_mode\n");
|
|
goto error;
|
|
}
|
|
|
|
if (cfg_declare("websocket", ws_cfg_def, &default_ws_cfg,
|
|
cfg_sizeof(websocket), &ws_cfg))
|
|
{
|
|
LM_ERR("declaring configuration\n");
|
|
return -1;
|
|
}
|
|
cfg_get(websocket, ws_cfg, keepalive_timeout) = ws_keepalive_timeout;
|
|
|
|
if (!module_loaded("xhttp"))
|
|
{
|
|
LM_ERR("\"xhttp\" must be loaded to use WebSocket.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (((ws_sub_protocols & SUB_PROTOCOL_SIP) == SUB_PROTOCOL_SIP)
|
|
&& !module_loaded("nathelper")
|
|
&& !module_loaded("outbound"))
|
|
{
|
|
LM_WARN("neither \"nathelper\" nor \"outbound\" modules are"
|
|
" loaded. At least one of these is required for correct"
|
|
" routing of SIP over WebSocket.\n");
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
wsconn_destroy();
|
|
return -1;
|
|
}
|
|
|
|
static int child_init(int rank)
|
|
{
|
|
int i;
|
|
|
|
if (rank == PROC_INIT || rank == PROC_TCP_MAIN)
|
|
return 0;
|
|
|
|
if (rank == PROC_MAIN
|
|
&& ws_keepalive_mechanism != KEEPALIVE_MECHANISM_NONE)
|
|
{
|
|
for (i = 0; i < ws_keepalive_processes; i++)
|
|
{
|
|
if (fork_sync_timer(PROC_TIMER, "WEBSOCKET KEEPALIVE",
|
|
1, ws_keepalive, NULL,
|
|
ws_keepalive_interval) < 0)
|
|
{
|
|
LM_ERR("starting keepalive process\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void destroy(void)
|
|
{
|
|
wsconn_destroy();
|
|
}
|
|
|
|
static int ws_close_fixup(void** param, int param_no)
|
|
{
|
|
switch(param_no) {
|
|
case 1:
|
|
case 3:
|
|
return fixup_var_int_1(param, 1);
|
|
case 2:
|
|
return fixup_spve_null(param, 1);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|