ws_handshake.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  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 <openssl/sha.h>
  29. #include "../../basex.h"
  30. #include "../../data_lump_rpl.h"
  31. #include "../../dprint.h"
  32. #include "../../locking.h"
  33. #include "../../str.h"
  34. #include "../../tcp_conn.h"
  35. #include "../../lib/kcore/kstats_wrapper.h"
  36. #include "../../lib/kcore/cmpapi.h"
  37. #include "../../lib/kmi/tree.h"
  38. #include "../../mem/mem.h"
  39. #include "../../parser/msg_parser.h"
  40. #include "../sl/sl.h"
  41. #include "../tls/tls_cfg.h"
  42. #include "ws_conn.h"
  43. #include "ws_handshake.h"
  44. #include "ws_mod.h"
  45. #include "config.h"
  46. #define WS_VERSION (13)
  47. int ws_sub_protocols = DEFAULT_SUB_PROTOCOLS;
  48. int ws_cors_mode = CORS_MODE_NONE;
  49. stat_var *ws_failed_handshakes;
  50. stat_var *ws_successful_handshakes;
  51. stat_var *ws_sip_successful_handshakes;
  52. stat_var *ws_msrp_successful_handshakes;
  53. static str str_sip = str_init("sip");
  54. static str str_msrp = str_init("msrp");
  55. static str str_upgrade = str_init("upgrade");
  56. static str str_websocket = str_init("websocket");
  57. static str str_ws_guid = str_init("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
  58. /* HTTP headers */
  59. static str str_hdr_connection = str_init("Connection");
  60. static str str_hdr_upgrade = str_init("Upgrade");
  61. static str str_hdr_sec_websocket_accept = str_init("Sec-WebSocket-Accept");
  62. static str str_hdr_sec_websocket_key = str_init("Sec-WebSocket-Key");
  63. static str str_hdr_sec_websocket_protocol = str_init("Sec-WebSocket-Protocol");
  64. static str str_hdr_sec_websocket_version = str_init("Sec-WebSocket-Version");
  65. static str str_hdr_origin = str_init("Origin");
  66. static str str_hdr_access_control_allow_origin
  67. = str_init("Access-Control-Allow-Origin");
  68. #define CONNECTION (1<<0)
  69. #define UPGRADE (1<<1)
  70. #define SEC_WEBSOCKET_ACCEPT (1<<2)
  71. #define SEC_WEBSOCKET_KEY (1<<3)
  72. #define SEC_WEBSOCKET_PROTOCOL (1<<4)
  73. #define SEC_WEBSOCKET_VERSION (1<<5)
  74. #define ORIGIN (1<<6)
  75. #define REQUIRED_HEADERS (CONNECTION | UPGRADE | SEC_WEBSOCKET_KEY\
  76. | SEC_WEBSOCKET_PROTOCOL\
  77. | SEC_WEBSOCKET_VERSION)
  78. /* HTTP status text */
  79. static str str_status_switching_protocols = str_init("Switching Protocols");
  80. static str str_status_bad_request = str_init("Bad Request");
  81. static str str_status_upgrade_required = str_init("Upgrade Required");
  82. static str str_status_internal_server_error = str_init("Internal Server Error");
  83. static str str_status_service_unavailable = str_init("Service Unavailable");
  84. #define HDR_BUF_LEN (512)
  85. static char headers_buf[HDR_BUF_LEN];
  86. static char key_buf[base64_enc_len(SHA_DIGEST_LENGTH)];
  87. static int ws_send_reply(sip_msg_t *msg, int code, str *reason, str *hdrs)
  88. {
  89. if (hdrs && hdrs->len > 0)
  90. {
  91. if (add_lump_rpl(msg, hdrs->s, hdrs->len, LUMP_RPL_HDR) == 0)
  92. {
  93. LM_ERR("inserting extra-headers lump\n");
  94. update_stat(ws_failed_handshakes, 1);
  95. return -1;
  96. }
  97. }
  98. if (ws_slb.freply(msg, code, reason) < 0)
  99. {
  100. LM_ERR("sending reply\n");
  101. update_stat(ws_failed_handshakes, 1);
  102. return -1;
  103. }
  104. update_stat(
  105. code == 101 ? ws_successful_handshakes : ws_failed_handshakes,
  106. 1);
  107. return 0;
  108. }
  109. int ws_handle_handshake(struct sip_msg *msg)
  110. {
  111. str key = {0, 0}, headers = {0, 0}, reply_key = {0, 0}, origin = {0, 0};
  112. unsigned char sha1[SHA_DIGEST_LENGTH];
  113. unsigned int hdr_flags = 0, sub_protocol = 0;
  114. int version = 0;
  115. struct hdr_field *hdr = msg->headers;
  116. struct tcp_connection *con;
  117. ws_connection_t *wsc;
  118. /* Make sure that the connection is closed after the response _and_
  119. the existing connection (from the request) is reused for the
  120. response. The close flag will be unset later if the handshake is
  121. successful. */
  122. msg->rpl_send_flags.f |= SND_F_CON_CLOSE;
  123. msg->rpl_send_flags.f |= SND_F_FORCE_CON_REUSE;
  124. if (cfg_get(websocket, ws_cfg, enabled) == 0)
  125. {
  126. LM_INFO("disabled: bouncing handshake\n");
  127. ws_send_reply(msg, 503, &str_status_service_unavailable,
  128. NULL);
  129. return 0;
  130. }
  131. /* Retrieve TCP/TLS connection */
  132. if ((con = tcpconn_get(msg->rcv.proto_reserved1, 0, 0, 0, 0)) == NULL)
  133. {
  134. LM_ERR("retrieving connection\n");
  135. ws_send_reply(msg, 500, &str_status_internal_server_error,
  136. NULL);
  137. return 0;
  138. }
  139. if (con->type != PROTO_TCP && con->type != PROTO_TLS)
  140. {
  141. LM_ERR("unsupported transport: %d", con->type);
  142. goto end;
  143. }
  144. if (parse_headers(msg, HDR_EOH_F, 0) < 0)
  145. {
  146. LM_ERR("error parsing headers\n");
  147. ws_send_reply(msg, 500, &str_status_internal_server_error,
  148. NULL);
  149. goto end;
  150. }
  151. /* Process HTTP headers */
  152. while (hdr != NULL)
  153. {
  154. /* Decode and validate Connection */
  155. if (cmp_hdrname_strzn(&hdr->name,
  156. str_hdr_connection.s,
  157. str_hdr_connection.len) == 0)
  158. {
  159. strlower(&hdr->body);
  160. if (str_search(&hdr->body, &str_upgrade) != NULL)
  161. {
  162. LM_DBG("found %.*s: %.*s\n",
  163. hdr->name.len, hdr->name.s,
  164. hdr->body.len, hdr->body.s);
  165. hdr_flags |= CONNECTION;
  166. }
  167. }
  168. /* Decode and validate Upgrade */
  169. else if (cmp_hdrname_strzn(&hdr->name,
  170. str_hdr_upgrade.s,
  171. str_hdr_upgrade.len) == 0)
  172. {
  173. strlower(&hdr->body);
  174. if (str_search(&hdr->body, &str_websocket) != NULL)
  175. {
  176. LM_DBG("found %.*s: %.*s\n",
  177. hdr->name.len, hdr->name.s,
  178. hdr->body.len, hdr->body.s);
  179. hdr_flags |= UPGRADE;
  180. }
  181. }
  182. /* Decode and validate Sec-WebSocket-Key */
  183. else if (cmp_hdrname_strzn(&hdr->name,
  184. str_hdr_sec_websocket_key.s,
  185. str_hdr_sec_websocket_key.len) == 0)
  186. {
  187. if (hdr_flags & SEC_WEBSOCKET_KEY)
  188. {
  189. LM_WARN("%.*s found multiple times\n",
  190. hdr->name.len, hdr->name.s);
  191. ws_send_reply(msg, 400,
  192. &str_status_bad_request,
  193. NULL);
  194. goto end;
  195. }
  196. LM_DBG("found %.*s: %.*s\n",
  197. hdr->name.len, hdr->name.s,
  198. hdr->body.len, hdr->body.s);
  199. key = hdr->body;
  200. hdr_flags |= SEC_WEBSOCKET_KEY;
  201. }
  202. /* Decode and validate Sec-WebSocket-Protocol */
  203. else if (cmp_hdrname_strzn(&hdr->name,
  204. str_hdr_sec_websocket_protocol.s,
  205. str_hdr_sec_websocket_protocol.len) == 0)
  206. {
  207. strlower(&hdr->body);
  208. if (str_search(&hdr->body, &str_sip) != NULL)
  209. {
  210. LM_DBG("found %.*s: %.*s\n",
  211. hdr->name.len, hdr->name.s,
  212. hdr->body.len, hdr->body.s);
  213. hdr_flags |= SEC_WEBSOCKET_PROTOCOL;
  214. sub_protocol |= SUB_PROTOCOL_SIP;
  215. }
  216. if (str_search(&hdr->body, &str_msrp) != NULL)
  217. {
  218. LM_DBG("found %.*s: %.*s\n",
  219. hdr->name.len, hdr->name.s,
  220. hdr->body.len, hdr->body.s);
  221. hdr_flags |= SEC_WEBSOCKET_PROTOCOL;
  222. sub_protocol |= SUB_PROTOCOL_MSRP;
  223. }
  224. }
  225. /* Decode and validate Sec-WebSocket-Version */
  226. else if (cmp_hdrname_strzn(&hdr->name,
  227. str_hdr_sec_websocket_version.s,
  228. str_hdr_sec_websocket_version.len) == 0)
  229. {
  230. if (hdr_flags & SEC_WEBSOCKET_VERSION)
  231. {
  232. LM_WARN("%.*s found multiple times\n",
  233. hdr->name.len, hdr->name.s);
  234. ws_send_reply(msg, 400,
  235. &str_status_bad_request,
  236. NULL);
  237. goto end;
  238. }
  239. str2sint(&hdr->body, &version);
  240. if (version != WS_VERSION)
  241. {
  242. LM_WARN("Unsupported protocol version %.*s\n",
  243. hdr->body.len, hdr->body.s);
  244. headers.s = headers_buf;
  245. headers.len = snprintf(headers.s, HDR_BUF_LEN,
  246. "%.*s: %d\r\n",
  247. str_hdr_sec_websocket_version.len,
  248. str_hdr_sec_websocket_version.s,
  249. WS_VERSION);
  250. ws_send_reply(msg, 426,
  251. &str_status_upgrade_required,
  252. &headers);
  253. goto end;
  254. }
  255. LM_DBG("found %.*s: %.*s\n",
  256. hdr->name.len, hdr->name.s,
  257. hdr->body.len, hdr->body.s);
  258. hdr_flags |= SEC_WEBSOCKET_VERSION;
  259. }
  260. /* Decode Origin */
  261. else if (cmp_hdrname_strzn(&hdr->name,
  262. str_hdr_origin.s,
  263. str_hdr_origin.len) == 0)
  264. {
  265. if (hdr_flags & ORIGIN)
  266. {
  267. LM_WARN("%.*s found multiple times\n",
  268. hdr->name.len, hdr->name.s);
  269. ws_send_reply(msg, 400,
  270. &str_status_bad_request,
  271. NULL);
  272. goto end;
  273. }
  274. LM_DBG("found %.*s: %.*s\n",
  275. hdr->name.len, hdr->name.s,
  276. hdr->body.len, hdr->body.s);
  277. origin = hdr->body;
  278. hdr_flags |= ORIGIN;
  279. }
  280. hdr = hdr->next;
  281. }
  282. /* Final check that all required headers/values were found */
  283. sub_protocol &= ws_sub_protocols;
  284. if ((hdr_flags & REQUIRED_HEADERS) != REQUIRED_HEADERS
  285. || sub_protocol == 0)
  286. {
  287. LM_WARN("required headers not present\n");
  288. headers.s = headers_buf;
  289. headers.len = 0;
  290. if (ws_sub_protocols & SUB_PROTOCOL_SIP)
  291. headers.len += snprintf(headers.s + headers.len,
  292. HDR_BUF_LEN - headers.len,
  293. "%.*s: %.*s\r\n",
  294. str_hdr_sec_websocket_protocol.len,
  295. str_hdr_sec_websocket_protocol.s,
  296. str_sip.len, str_sip.s);
  297. if (ws_sub_protocols & SUB_PROTOCOL_MSRP)
  298. headers.len += snprintf(headers.s + headers.len,
  299. HDR_BUF_LEN - headers.len,
  300. "%.*s: %.*s\r\n",
  301. str_hdr_sec_websocket_protocol.len,
  302. str_hdr_sec_websocket_protocol.s,
  303. str_msrp.len, str_msrp.s);
  304. headers.len += snprintf(headers.s + headers.len,
  305. HDR_BUF_LEN - headers.len,
  306. "%.*s: %d\r\n",
  307. str_hdr_sec_websocket_version.len,
  308. str_hdr_sec_websocket_version.s,
  309. WS_VERSION);
  310. ws_send_reply(msg, 400, &str_status_bad_request, &headers);
  311. goto end;
  312. }
  313. /* Construct reply_key */
  314. reply_key.s = (char *) pkg_malloc(
  315. (key.len + str_ws_guid.len) * sizeof(char));
  316. if (reply_key.s == NULL)
  317. {
  318. LM_ERR("allocating pkg memory\n");
  319. ws_send_reply(msg, 500, &str_status_internal_server_error,
  320. NULL);
  321. goto end;
  322. }
  323. memcpy(reply_key.s, key.s, key.len);
  324. memcpy(reply_key.s + key.len, str_ws_guid.s, str_ws_guid.len);
  325. reply_key.len = key.len + str_ws_guid.len;
  326. SHA1((const unsigned char *) reply_key.s, reply_key.len, sha1);
  327. pkg_free(reply_key.s);
  328. reply_key.s = key_buf;
  329. reply_key.len = base64_enc(sha1, SHA_DIGEST_LENGTH,
  330. (unsigned char *) reply_key.s,
  331. base64_enc_len(SHA_DIGEST_LENGTH));
  332. /* Add the connection to the WebSocket connection table */
  333. wsconn_add(msg->rcv, sub_protocol);
  334. /* Make sure Kamailio core sends future messages on this connection
  335. directly to this module */
  336. if (con->type == PROTO_TLS)
  337. con->type = con->rcv.proto = PROTO_WSS;
  338. else
  339. con->type = con->rcv.proto = PROTO_WS;
  340. /* Now Kamailio is ready to receive WebSocket frames build and send a
  341. 101 reply */
  342. headers.s = headers_buf;
  343. headers.len = 0;
  344. if (ws_cors_mode == CORS_MODE_ANY)
  345. headers.len += snprintf(headers.s + headers.len,
  346. HDR_BUF_LEN - headers.len,
  347. "%.*s: *\r\n",
  348. str_hdr_access_control_allow_origin.len,
  349. str_hdr_access_control_allow_origin.s);
  350. else if (ws_cors_mode == CORS_MODE_ORIGIN && origin.len > 0)
  351. headers.len += snprintf(headers.s + headers.len,
  352. HDR_BUF_LEN - headers.len,
  353. "%.*s: %.*s\r\n",
  354. str_hdr_access_control_allow_origin.len,
  355. str_hdr_access_control_allow_origin.s,
  356. origin.len,
  357. origin.s);
  358. if (sub_protocol & SUB_PROTOCOL_SIP)
  359. headers.len += snprintf(headers.s + headers.len,
  360. HDR_BUF_LEN - headers.len,
  361. "%.*s: %.*s\r\n",
  362. str_hdr_sec_websocket_protocol.len,
  363. str_hdr_sec_websocket_protocol.s,
  364. str_sip.len, str_sip.s);
  365. else if (sub_protocol & SUB_PROTOCOL_MSRP)
  366. headers.len += snprintf(headers.s + headers.len,
  367. HDR_BUF_LEN - headers.len,
  368. "%.*s: %.*s\r\n",
  369. str_hdr_sec_websocket_protocol.len,
  370. str_hdr_sec_websocket_protocol.s,
  371. str_msrp.len, str_msrp.s);
  372. headers.len += snprintf(headers.s + headers.len,
  373. HDR_BUF_LEN - headers.len,
  374. "%.*s: %.*s\r\n"
  375. "%.*s: %.*s\r\n"
  376. "%.*s: %.*s\r\n",
  377. str_hdr_upgrade.len, str_hdr_upgrade.s,
  378. str_websocket.len, str_websocket.s,
  379. str_hdr_connection.len, str_hdr_connection.s,
  380. str_upgrade.len, str_upgrade.s,
  381. str_hdr_sec_websocket_accept.len,
  382. str_hdr_sec_websocket_accept.s, reply_key.len,
  383. reply_key.s);
  384. msg->rpl_send_flags.f &= ~SND_F_CON_CLOSE;
  385. if (ws_send_reply(msg, 101, &str_status_switching_protocols,
  386. &headers) < 0)
  387. {
  388. if ((wsc = wsconn_get(msg->rcv.proto_reserved1)) != NULL)
  389. {
  390. wsconn_rm(wsc, WSCONN_EVENTROUTE_NO);
  391. wsconn_put(wsc);
  392. }
  393. goto end;
  394. }
  395. else
  396. {
  397. if (sub_protocol & SUB_PROTOCOL_SIP)
  398. update_stat(ws_sip_successful_handshakes, 1);
  399. else if (sub_protocol & SUB_PROTOCOL_MSRP)
  400. update_stat(ws_msrp_successful_handshakes, 1);
  401. }
  402. tcpconn_put(con);
  403. return 1;
  404. end:
  405. if (con)
  406. tcpconn_put(con);
  407. return 0;
  408. }
  409. struct mi_root *ws_mi_disable(struct mi_root *cmd, void *param)
  410. {
  411. cfg_get(websocket, ws_cfg, enabled) = 0;
  412. LM_WARN("disabling websockets - new connections will be dropped\n");
  413. return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
  414. }
  415. struct mi_root *ws_mi_enable(struct mi_root *cmd, void *param)
  416. {
  417. cfg_get(websocket, ws_cfg, enabled) = 1;
  418. LM_WARN("enabling websockets\n");
  419. return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
  420. }