ws_mod.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /*
  2. * $Id$
  3. *
  4. * Copyright (C) 2012-2013 Crocodile RCS Ltd
  5. *
  6. * This file is part of Kamailio, a free SIP server.
  7. *
  8. * Kamailio is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version
  12. *
  13. * Kamailio is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. *
  22. * Exception: permission to copy, modify, propagate, and distribute a work
  23. * formed by combining OpenSSL toolkit software and the code in this file,
  24. * such as linking with software components and libraries released under
  25. * OpenSSL project license.
  26. *
  27. */
  28. #include "../../dprint.h"
  29. #include "../../events.h"
  30. #include "../../ip_addr.h"
  31. #include "../../locking.h"
  32. #include "../../sr_module.h"
  33. #include "../../tcp_conn.h"
  34. #include "../../timer_proc.h"
  35. #include "../../cfg/cfg.h"
  36. #include "../../lib/kcore/kstats_wrapper.h"
  37. #include "../../lib/kmi/mi.h"
  38. #include "../../mem/mem.h"
  39. #include "../../mod_fix.h"
  40. #include "../../parser/msg_parser.h"
  41. #include "ws_conn.h"
  42. #include "ws_handshake.h"
  43. #include "ws_frame.h"
  44. #include "ws_mod.h"
  45. #include "config.h"
  46. MODULE_VERSION
  47. /* Maximum number of connections to display when using the ws.dump MI command */
  48. #define MAX_WS_CONNS_DUMP 50
  49. static int mod_init(void);
  50. static int child_init(int rank);
  51. static void destroy(void);
  52. static int ws_close_fixup(void** param, int param_no);
  53. sl_api_t ws_slb;
  54. #define DEFAULT_KEEPALIVE_INTERVAL 1
  55. static int ws_keepalive_interval = DEFAULT_KEEPALIVE_INTERVAL;
  56. static int ws_keepalive_timeout = DEFAULT_KEEPALIVE_TIMEOUT;
  57. #define DEFAULT_KEEPALIVE_PROCESSES 1
  58. static int ws_keepalive_processes = DEFAULT_KEEPALIVE_PROCESSES;
  59. static cmd_export_t cmds[]=
  60. {
  61. /* ws_frame.c */
  62. { "ws_close", (cmd_function) ws_close,
  63. 0, 0, 0,
  64. ANY_ROUTE },
  65. { "ws_close", (cmd_function) ws_close2,
  66. 2, ws_close_fixup, 0,
  67. ANY_ROUTE },
  68. { "ws_close", (cmd_function) ws_close3,
  69. 3, ws_close_fixup, 0,
  70. ANY_ROUTE },
  71. /* ws_handshake.c */
  72. { "ws_handle_handshake", (cmd_function) ws_handle_handshake,
  73. 0, 0, 0,
  74. ANY_ROUTE },
  75. { 0, 0, 0, 0, 0, 0 }
  76. };
  77. static param_export_t params[]=
  78. {
  79. /* ws_frame.c */
  80. { "keepalive_mechanism", INT_PARAM, &ws_keepalive_mechanism },
  81. { "keepalive_timeout", INT_PARAM, &ws_keepalive_timeout },
  82. { "ping_application_data", PARAM_STR, &ws_ping_application_data },
  83. /* ws_handshake.c */
  84. { "sub_protocols", INT_PARAM, &ws_sub_protocols },
  85. { "cors_mode", INT_PARAM, &ws_cors_mode },
  86. /* ws_mod.c */
  87. { "keepalive_interval", INT_PARAM, &ws_keepalive_interval },
  88. { "keepalive_processes", INT_PARAM, &ws_keepalive_processes },
  89. { 0, 0, 0 }
  90. };
  91. static stat_export_t stats[] =
  92. {
  93. /* ws_conn.c */
  94. { "ws_current_connections", 0, &ws_current_connections },
  95. { "ws_max_concurrent_connections", 0, &ws_max_concurrent_connections },
  96. { "ws_sip_current_connections", 0, &ws_sip_current_connections },
  97. { "ws_sip_max_concurrent_connectons", 0, &ws_sip_max_concurrent_connections },
  98. { "ws_msrp_current_connections", 0, &ws_msrp_current_connections },
  99. { "ws_msrp_max_concurrent_connectons", 0, &ws_msrp_max_concurrent_connections },
  100. /* ws_frame.c */
  101. { "ws_failed_connections", 0, &ws_failed_connections },
  102. { "ws_local_closed_connections", 0, &ws_local_closed_connections },
  103. { "ws_received_frames", 0, &ws_received_frames },
  104. { "ws_remote_closed_connections", 0, &ws_remote_closed_connections },
  105. { "ws_transmitted_frames", 0, &ws_transmitted_frames },
  106. { "ws_sip_failed_connections", 0, &ws_sip_failed_connections },
  107. { "ws_sip_local_closed_connections", 0, &ws_sip_local_closed_connections },
  108. { "ws_sip_received_frames", 0, &ws_sip_received_frames },
  109. { "ws_sip_remote_closed_connections", 0, &ws_sip_remote_closed_connections },
  110. { "ws_sip_transmitted_frames", 0, &ws_sip_transmitted_frames },
  111. { "ws_msrp_failed_connections", 0, &ws_msrp_failed_connections },
  112. { "ws_msrp_local_closed_connections", 0, &ws_msrp_local_closed_connections },
  113. { "ws_msrp_received_frames", 0, &ws_msrp_received_frames },
  114. { "ws_msrp_remote_closed_connections", 0, &ws_msrp_remote_closed_connections },
  115. { "ws_msrp_transmitted_frames", 0, &ws_msrp_transmitted_frames },
  116. /* ws_handshake.c */
  117. { "ws_failed_handshakes", 0, &ws_failed_handshakes },
  118. { "ws_successful_handshakes", 0, &ws_successful_handshakes },
  119. { "ws_sip_successful_handshakes", 0, &ws_sip_successful_handshakes },
  120. { "ws_msrp_successful_handshakes", 0, &ws_msrp_successful_handshakes },
  121. { 0, 0, 0 }
  122. };
  123. static mi_export_t mi_cmds[] =
  124. {
  125. /* ws_conn.c */
  126. { "ws.dump", ws_mi_dump, 0, 0, 0 },
  127. /* ws_frame.c */
  128. { "ws.close", ws_mi_close, 0, 0, 0 },
  129. { "ws.ping", ws_mi_ping, 0, 0, 0 },
  130. { "ws.pong", ws_mi_pong, 0, 0, 0 },
  131. /* ws_handshake.c */
  132. { "ws.disable", ws_mi_disable, 0, 0, 0 },
  133. { "ws.enable", ws_mi_enable, 0, 0, 0 },
  134. { 0, 0, 0, 0, 0 }
  135. };
  136. struct module_exports exports=
  137. {
  138. "websocket",
  139. DEFAULT_DLFLAGS, /* dlopen flags */
  140. cmds, /* Exported functions */
  141. params, /* Exported parameters */
  142. stats, /* exported statistics */
  143. mi_cmds, /* exported MI functions */
  144. 0, /* exported pseudo-variables */
  145. 0, /* extra processes */
  146. mod_init, /* module initialization function */
  147. 0, /* response function */
  148. destroy, /* destroy function */
  149. child_init /* per-child initialization function */
  150. };
  151. static int mod_init(void)
  152. {
  153. if (sl_load_api(&ws_slb) != 0)
  154. {
  155. LM_ERR("binding to SL\n");
  156. goto error;
  157. }
  158. if (sr_event_register_cb(SREV_TCP_WS_FRAME_IN, ws_frame_receive) != 0)
  159. {
  160. LM_ERR("registering WebSocket receive call-back\n");
  161. goto error;
  162. }
  163. if (sr_event_register_cb(SREV_TCP_WS_FRAME_OUT, ws_frame_transmit) != 0)
  164. {
  165. LM_ERR("registering WebSocket transmit call-back\n");
  166. goto error;
  167. }
  168. if (register_module_stats(exports.name, stats) != 0)
  169. {
  170. LM_ERR("registering core statistics\n");
  171. goto error;
  172. }
  173. if (register_mi_mod(exports.name, mi_cmds) != 0)
  174. {
  175. LM_ERR("registering MI commands\n");
  176. goto error;
  177. }
  178. if (wsconn_init() < 0)
  179. {
  180. LM_ERR("initialising WebSocket connections table\n");
  181. goto error;
  182. }
  183. if (ws_ping_application_data.len < 1
  184. || ws_ping_application_data.len > 125)
  185. {
  186. ws_ping_application_data.s = DEFAULT_PING_APPLICATION_DATA + 8;
  187. ws_ping_application_data.len =
  188. DEFAULT_PING_APPLICATION_DATA_LEN - 8;
  189. }
  190. if (ws_keepalive_mechanism != KEEPALIVE_MECHANISM_NONE)
  191. {
  192. if (ws_keepalive_timeout < 1 || ws_keepalive_timeout > 3600)
  193. ws_keepalive_timeout = DEFAULT_KEEPALIVE_TIMEOUT;
  194. switch(ws_keepalive_mechanism)
  195. {
  196. case KEEPALIVE_MECHANISM_PING:
  197. case KEEPALIVE_MECHANISM_PONG:
  198. break;
  199. default:
  200. ws_keepalive_mechanism = DEFAULT_KEEPALIVE_MECHANISM;
  201. break;
  202. }
  203. if (ws_keepalive_interval < 1 || ws_keepalive_interval > 60)
  204. ws_keepalive_interval = DEFAULT_KEEPALIVE_INTERVAL;
  205. if (ws_keepalive_processes < 1 || ws_keepalive_processes > 16)
  206. ws_keepalive_processes = DEFAULT_KEEPALIVE_PROCESSES;
  207. /* Add extra process/timer for the keepalive process */
  208. register_sync_timers(ws_keepalive_processes);
  209. }
  210. if (ws_sub_protocols & SUB_PROTOCOL_MSRP
  211. && !sr_event_enabled(SREV_TCP_MSRP_FRAME))
  212. ws_sub_protocols &= ~SUB_PROTOCOL_MSRP;
  213. if ((ws_sub_protocols & SUB_PROTOCOL_ALL) == 0)
  214. {
  215. LM_ERR("no sub-protocols enabled\n");
  216. goto error;
  217. }
  218. if ((ws_sub_protocols | SUB_PROTOCOL_ALL) != SUB_PROTOCOL_ALL)
  219. {
  220. LM_ERR("unrecognised sub-protocols enabled\n");
  221. goto error;
  222. }
  223. if (ws_cors_mode < 0 || ws_cors_mode > 2)
  224. {
  225. LM_ERR("bad value for cors_mode\n");
  226. goto error;
  227. }
  228. if (cfg_declare("websocket", ws_cfg_def, &default_ws_cfg,
  229. cfg_sizeof(websocket), &ws_cfg))
  230. {
  231. LM_ERR("declaring configuration\n");
  232. return -1;
  233. }
  234. cfg_get(websocket, ws_cfg, keepalive_timeout) = ws_keepalive_timeout;
  235. if (!module_loaded("xhttp"))
  236. {
  237. LM_ERR("\"xhttp\" must be loaded to use WebSocket.\n");
  238. return -1;
  239. }
  240. if (((ws_sub_protocols & SUB_PROTOCOL_SIP) == SUB_PROTOCOL_SIP)
  241. && !module_loaded("nathelper")
  242. && !module_loaded("outbound"))
  243. {
  244. LM_WARN("neither \"nathelper\" nor \"outbound\" modules are"
  245. " loaded. At least one of these is required for correct"
  246. " routing of SIP over WebSocket.\n");
  247. }
  248. return 0;
  249. error:
  250. wsconn_destroy();
  251. return -1;
  252. }
  253. static int child_init(int rank)
  254. {
  255. int i;
  256. if (rank == PROC_INIT || rank == PROC_TCP_MAIN)
  257. return 0;
  258. if (rank == PROC_MAIN
  259. && ws_keepalive_mechanism != KEEPALIVE_MECHANISM_NONE)
  260. {
  261. for (i = 0; i < ws_keepalive_processes; i++)
  262. {
  263. if (fork_sync_timer(PROC_TIMER, "WEBSOCKET KEEPALIVE",
  264. 1, ws_keepalive, NULL,
  265. ws_keepalive_interval) < 0)
  266. {
  267. LM_ERR("starting keepalive process\n");
  268. return -1;
  269. }
  270. }
  271. }
  272. return 0;
  273. }
  274. static void destroy(void)
  275. {
  276. wsconn_destroy();
  277. }
  278. static int ws_close_fixup(void** param, int param_no)
  279. {
  280. switch(param_no) {
  281. case 1:
  282. case 3:
  283. return fixup_var_int_1(param, 1);
  284. case 2:
  285. return fixup_spve_null(param, 1);
  286. default:
  287. return 0;
  288. }
  289. }