1 /*
2 * Copyright (c) 2014-2021 Joris Vink <joris@coders.se>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <sys/param.h>
18 #include <sys/types.h>
19
20 #include <openssl/sha.h>
21
22 #include <limits.h>
23 #include <string.h>
24
25 #include "kore.h"
26 #include "http.h"
27
28 #define WEBSOCKET_FRAME_HDR 2
29 #define WEBSOCKET_MASK_LEN 4
30 #define WEBSOCKET_FRAME_MAXLEN 16384
31 #define WEBSOCKET_PAYLOAD_SINGLE 125
32 #define WEBSOCKET_PAYLOAD_EXTEND_1 126
33 #define WEBSOCKET_PAYLOAD_EXTEND_2 127
34 #define WEBSOCKET_OPCODE_MASK 0x0f
35 #define WEBSOCKET_FRAME_LENGTH(x) ((x) & ~(1 << 7))
36 #define WEBSOCKET_HAS_MASK(x) ((x) & (1 << 7))
37 #define WEBSOCKET_HAS_FINFLAG(x) ((x) & (1 << 7))
38 #define WEBSOCKET_RSV(x, i) ((x) & (1 << (7 - i)))
39
40 #define WEBSOCKET_SERVER_RESPONSE "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
41
42
43 u_int64_t kore_websocket_timeout = 120000;
44 u_int64_t kore_websocket_maxframe = 16384;
45
46 static int websocket_recv_frame(struct netbuf *);
47 static int websocket_recv_opcode(struct netbuf *);
48 static void websocket_disconnect(struct connection *);
49 static void websocket_frame_build(struct kore_buf *, u_int8_t,
50 const void *, size_t);
51
52 void
kore_websocket_handshake(struct http_request * req,const char * onconnect,const char * onmessage,const char * ondisconnect)53 kore_websocket_handshake(struct http_request *req, const char *onconnect,
54 const char *onmessage, const char *ondisconnect)
55 {
56 SHA_CTX sctx;
57 struct kore_buf *buf;
58 char *base64;
59 const char *key, *version;
60 u_int8_t digest[SHA_DIGEST_LENGTH];
61
62 if (!http_request_header(req, "sec-websocket-key", &key)) {
63 http_response(req, HTTP_STATUS_BAD_REQUEST, NULL, 0);
64 return;
65 }
66
67 if (!http_request_header(req, "sec-websocket-version", &version)) {
68 http_response_header(req, "sec-websocket-version", "13");
69 http_response(req, HTTP_STATUS_BAD_REQUEST, NULL, 0);
70 return;
71 }
72
73 if (strcmp(version, "13")) {
74 http_response_header(req, "sec-websocket-version", "13");
75 http_response(req, HTTP_STATUS_BAD_REQUEST, NULL, 0);
76 return;
77 }
78
79 buf = kore_buf_alloc(128);
80 kore_buf_appendf(buf, "%s%s", key, WEBSOCKET_SERVER_RESPONSE);
81
82 (void)SHA1_Init(&sctx);
83 (void)SHA1_Update(&sctx, buf->data, buf->offset);
84 (void)SHA1_Final(digest, &sctx);
85
86 kore_buf_free(buf);
87
88 if (!kore_base64_encode(digest, sizeof(digest), &base64)) {
89 kore_debug("failed to base64 encode digest");
90 http_response(req, HTTP_STATUS_INTERNAL_ERROR, NULL, 0);
91 return;
92 }
93
94 http_response_header(req, "upgrade", "websocket");
95 http_response_header(req, "connection", "upgrade");
96 http_response_header(req, "sec-websocket-accept", base64);
97 kore_free(base64);
98
99 kore_debug("%p: new websocket connection", req->owner);
100
101 req->owner->proto = CONN_PROTO_WEBSOCKET;
102 http_response(req, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, 0);
103 net_recv_reset(req->owner, WEBSOCKET_FRAME_HDR, websocket_recv_opcode);
104
105 req->owner->disconnect = websocket_disconnect;
106 req->owner->rnb->flags &= ~NETBUF_CALL_CB_ALWAYS;
107
108 req->owner->http_timeout = 0;
109 req->owner->idle_timer.start = kore_time_ms();
110 req->owner->idle_timer.length = kore_websocket_timeout;
111
112 if (onconnect != NULL) {
113 req->owner->ws_connect = kore_runtime_getcall(onconnect);
114 if (req->owner->ws_connect == NULL)
115 fatal("no symbol '%s' for ws_connect", onconnect);
116 } else {
117 req->owner->ws_connect = NULL;
118 }
119
120 if (onmessage != NULL) {
121 req->owner->ws_message = kore_runtime_getcall(onmessage);
122 if (req->owner->ws_message == NULL)
123 fatal("no symbol '%s' for ws_message", onmessage);
124 } else {
125 req->owner->ws_message = NULL;
126 }
127
128 if (ondisconnect != NULL) {
129 req->owner->ws_disconnect = kore_runtime_getcall(ondisconnect);
130 if (req->owner->ws_disconnect == NULL)
131 fatal("no symbol '%s' for ws_disconnect", ondisconnect);
132 } else {
133 req->owner->ws_disconnect = NULL;
134 }
135
136 if (req->owner->ws_connect != NULL)
137 kore_runtime_wsconnect(req->owner->ws_connect, req->owner);
138 }
139
140 int
kore_websocket_send_clean(struct netbuf * nb)141 kore_websocket_send_clean(struct netbuf *nb)
142 {
143 kore_free(nb->buf);
144 return (0);
145 }
146
147 void
kore_websocket_send(struct connection * c,u_int8_t op,const void * data,size_t len)148 kore_websocket_send(struct connection *c, u_int8_t op, const void *data,
149 size_t len)
150 {
151 struct kore_buf frame;
152
153 kore_buf_init(&frame, len);
154 websocket_frame_build(&frame, op, data, len);
155 net_send_stream(c, frame.data, frame.offset,
156 kore_websocket_send_clean, NULL);
157
158 /* net_send_stream() takes over the buffer data pointer. */
159 frame.data = NULL;
160 kore_buf_cleanup(&frame);
161
162 net_send_flush(c);
163 }
164
165 void
kore_websocket_broadcast(struct connection * src,u_int8_t op,const void * data,size_t len,int scope)166 kore_websocket_broadcast(struct connection *src, u_int8_t op, const void *data,
167 size_t len, int scope)
168 {
169 struct connection *c;
170 struct kore_buf *frame;
171
172 frame = kore_buf_alloc(len);
173 websocket_frame_build(frame, op, data, len);
174
175 TAILQ_FOREACH(c, &connections, list) {
176 if (c != src && c->proto == CONN_PROTO_WEBSOCKET) {
177 net_send_queue(c, frame->data, frame->offset);
178 net_send_flush(c);
179 }
180 }
181
182 if (scope == WEBSOCKET_BROADCAST_GLOBAL) {
183 kore_msg_send(KORE_MSG_WORKER_ALL,
184 KORE_MSG_WEBSOCKET, frame->data, frame->offset);
185 }
186
187 kore_buf_free(frame);
188 }
189
190 static void
websocket_frame_build(struct kore_buf * frame,u_int8_t op,const void * data,size_t len)191 websocket_frame_build(struct kore_buf *frame, u_int8_t op, const void *data,
192 size_t len)
193 {
194 u_int8_t len_1;
195 u_int16_t len16;
196 u_int64_t len64;
197
198 if (len > WEBSOCKET_PAYLOAD_SINGLE) {
199 if (len <= USHRT_MAX)
200 len_1 = WEBSOCKET_PAYLOAD_EXTEND_1;
201 else
202 len_1 = WEBSOCKET_PAYLOAD_EXTEND_2;
203 } else {
204 len_1 = len;
205 }
206
207 op |= (1 << 7);
208 kore_buf_append(frame, &op, sizeof(op));
209
210 len_1 &= ~(1 << 7);
211 kore_buf_append(frame, &len_1, sizeof(len_1));
212
213 if (len_1 > WEBSOCKET_PAYLOAD_SINGLE) {
214 switch (len_1) {
215 case WEBSOCKET_PAYLOAD_EXTEND_1:
216 net_write16((u_int8_t *)&len16, len);
217 kore_buf_append(frame, &len16, sizeof(len16));
218 break;
219 case WEBSOCKET_PAYLOAD_EXTEND_2:
220 net_write64((u_int8_t *)&len64, len);
221 kore_buf_append(frame, &len64, sizeof(len64));
222 break;
223 }
224 }
225
226 if (data != NULL && len > 0)
227 kore_buf_append(frame, data, len);
228 }
229
230 static int
websocket_recv_opcode(struct netbuf * nb)231 websocket_recv_opcode(struct netbuf *nb)
232 {
233 u_int8_t op, len;
234 struct connection *c = nb->owner;
235
236 if (!WEBSOCKET_HAS_MASK(nb->buf[1])) {
237 kore_debug("%p: frame did not have a mask set", c);
238 return (KORE_RESULT_ERROR);
239 }
240
241 if (WEBSOCKET_RSV(nb->buf[0], 1) || WEBSOCKET_RSV(nb->buf[0], 2) ||
242 WEBSOCKET_RSV(nb->buf[0], 3)) {
243 kore_debug("%p: RSV bits are not zero", c);
244 return (KORE_RESULT_ERROR);
245 }
246
247 len = WEBSOCKET_FRAME_LENGTH(nb->buf[1]);
248
249 op = nb->buf[0] & WEBSOCKET_OPCODE_MASK;
250 switch (op) {
251 case WEBSOCKET_OP_CONT:
252 case WEBSOCKET_OP_TEXT:
253 case WEBSOCKET_OP_BINARY:
254 break;
255 case WEBSOCKET_OP_CLOSE:
256 case WEBSOCKET_OP_PING:
257 case WEBSOCKET_OP_PONG:
258 if (len > WEBSOCKET_PAYLOAD_SINGLE ||
259 !WEBSOCKET_HAS_FINFLAG(nb->buf[0])) {
260 kore_debug("%p: large or fragmented control frame", c);
261 return (KORE_RESULT_ERROR);
262 }
263 break;
264 default:
265 kore_debug("%p: bad websocket op %d", c, op);
266 return (KORE_RESULT_ERROR);
267 }
268
269 switch (len) {
270 case WEBSOCKET_PAYLOAD_EXTEND_1:
271 len += sizeof(u_int16_t);
272 break;
273 case WEBSOCKET_PAYLOAD_EXTEND_2:
274 len += sizeof(u_int64_t);
275 break;
276 }
277
278 len += WEBSOCKET_MASK_LEN;
279 net_recv_expand(c, len, websocket_recv_frame);
280
281 return (KORE_RESULT_OK);
282 }
283
284 static int
websocket_recv_frame(struct netbuf * nb)285 websocket_recv_frame(struct netbuf *nb)
286 {
287 struct connection *c;
288 int ret;
289 u_int64_t len, i, total;
290 u_int8_t op, moff, extra;
291
292 c = nb->owner;
293 op = nb->buf[0] & WEBSOCKET_OPCODE_MASK;
294 len = WEBSOCKET_FRAME_LENGTH(nb->buf[1]);
295
296 switch (len) {
297 case WEBSOCKET_PAYLOAD_EXTEND_1:
298 moff = 4;
299 extra = sizeof(u_int16_t);
300 len = net_read16(&nb->buf[2]);
301 break;
302 case WEBSOCKET_PAYLOAD_EXTEND_2:
303 moff = 10;
304 extra = sizeof(u_int64_t);
305 len = net_read64(&nb->buf[2]);
306 break;
307 default:
308 extra = 0;
309 moff = 2;
310 break;
311 }
312
313 if (len > kore_websocket_maxframe) {
314 kore_debug("%p: frame too big", c);
315 return (KORE_RESULT_ERROR);
316 }
317
318 extra += WEBSOCKET_FRAME_HDR;
319 total = len + extra + WEBSOCKET_MASK_LEN;
320 if (total > nb->b_len) {
321 total -= nb->b_len;
322 net_recv_expand(c, total, websocket_recv_frame);
323 return (KORE_RESULT_OK);
324 }
325
326 if (total != nb->b_len)
327 return (KORE_RESULT_ERROR);
328
329 for (i = 0; i < len; i++)
330 nb->buf[moff + 4 + i] ^= nb->buf[moff + (i % 4)];
331
332 ret = KORE_RESULT_OK;
333 switch (op) {
334 case WEBSOCKET_OP_PONG:
335 break;
336 case WEBSOCKET_OP_CONT:
337 ret = KORE_RESULT_ERROR;
338 kore_log(LOG_ERR,
339 "%p: we do not support op 0x%02x yet", (void *)c, op);
340 break;
341 case WEBSOCKET_OP_TEXT:
342 case WEBSOCKET_OP_BINARY:
343 if (c->ws_message != NULL) {
344 kore_runtime_wsmessage(c->ws_message,
345 c, op, &nb->buf[moff + 4], len);
346 }
347 break;
348 case WEBSOCKET_OP_CLOSE:
349 c->evt.flags &= ~KORE_EVENT_READ;
350 if (!(c->flags & CONN_WS_CLOSE_SENT)) {
351 c->flags |= CONN_WS_CLOSE_SENT;
352 kore_websocket_send(c, WEBSOCKET_OP_CLOSE, NULL, 0);
353 }
354 kore_connection_disconnect(c);
355 break;
356 case WEBSOCKET_OP_PING:
357 kore_websocket_send(c, WEBSOCKET_OP_PONG,
358 &nb->buf[moff + 4], len);
359 break;
360 default:
361 kore_debug("%p: bad websocket op %d", c, op);
362 return (KORE_RESULT_ERROR);
363 }
364
365 net_recv_reset(c, WEBSOCKET_FRAME_HDR, websocket_recv_opcode);
366
367 return (ret);
368 }
369
370 static void
websocket_disconnect(struct connection * c)371 websocket_disconnect(struct connection *c)
372 {
373 if (c->ws_disconnect != NULL)
374 kore_runtime_wsdisconnect(c->ws_disconnect, c);
375
376 if (!(c->flags & CONN_WS_CLOSE_SENT)) {
377 c->flags |= CONN_WS_CLOSE_SENT;
378 c->evt.flags &= ~KORE_EVENT_READ;
379 kore_websocket_send(c, WEBSOCKET_OP_CLOSE, NULL, 0);
380 }
381 }
382