ws_handshake.c 13 KB

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