ws_handshake.c 11 KB

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