]> granicus.if.org Git - libevent/blob - ws.c
epoll: use epoll_pwait2() if available
[libevent] / ws.c
1 #include "event2/event-config.h"
2 #include "evconfig-private.h"
3
4 #include "event2/buffer.h"
5 #include "event2/bufferevent.h"
6 #include "event2/event.h"
7 #include "event2/http.h"
8 #include "event2/ws.h"
9 #include "util-internal.h"
10 #include "mm-internal.h"
11 #include "sha1.h"
12 #include "event2/bufferevent.h"
13 #include "sys/queue.h"
14 #include "http-internal.h"
15
16 #include <assert.h>
17 #include <string.h>
18 #include <stdbool.h>
19
20 #ifndef _WIN32
21 #include <sys/socket.h>
22 #include <sys/stat.h>
23 #else /* _WIN32 */
24 #include <winsock2.h>
25 #include <ws2tcpip.h>
26 #endif /* _WIN32 */
27
28 #ifdef EVENT__HAVE_ARPA_INET_H
29 #include <arpa/inet.h>
30 #endif
31 #ifdef EVENT__HAVE_NETINET_IN_H
32 #include <netinet/in.h>
33 #endif
34 #ifdef EVENT__HAVE_NETINET_IN6_H
35 #include <netinet/in6.h>
36 #endif
37
38 #define WS_UUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
39
40 struct evws_connection {
41         TAILQ_ENTRY(evws_connection) next;
42
43         struct bufferevent *bufev;
44
45         ws_on_msg_cb cb;
46         void *cb_arg;
47
48         ws_on_close_cb cbclose;
49         void *cbclose_arg;
50
51         /* for server connections, the http server they are connected with */
52         struct evhttp *http_server;
53
54         struct evbuffer *incomplete_frames;
55         bool closed;
56 };
57
58 enum WebSocketFrameType {
59         ERROR_FRAME = 0xFF,
60         INCOMPLETE_DATA = 0xFE,
61
62         CLOSING_FRAME = 0x8,
63
64         INCOMPLETE_FRAME = 0x81,
65
66         TEXT_FRAME = 0x1,
67         BINARY_FRAME = 0x2,
68
69         PING_FRAME = 0x9,
70         PONG_FRAME = 0xA
71 };
72
73 /*
74  * Clean up a WebSockets connection object
75  */
76
77 void
78 evws_connection_free(struct evws_connection *evws)
79 {
80         /* notify interested parties that this connection is going down */
81         if (evws->cbclose != NULL)
82                 (*evws->cbclose)(evws, evws->cbclose_arg);
83
84         if (evws->http_server != NULL) {
85                 struct evhttp *http = evws->http_server;
86                 TAILQ_REMOVE(&http->ws_sessions, evws, next);
87                 http->connection_cnt--;
88         }
89
90         if (evws->bufev != NULL) {
91                 bufferevent_free(evws->bufev);
92         }
93         if (evws->incomplete_frames != NULL) {
94                 evbuffer_free(evws->incomplete_frames);
95         }
96
97         mm_free(evws);
98 }
99
100 static const char basis_64[] =
101         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
102
103 static int
104 Base64encode(char *encoded, const char *string, int len)
105 {
106         int i;
107         char *p;
108
109         p = encoded;
110         for (i = 0; i < len - 2; i += 3) {
111                 *p++ = basis_64[(string[i] >> 2) & 0x3F];
112                 *p++ = basis_64[((string[i] & 0x3) << 4) |
113                                                 ((int)(string[i + 1] & 0xF0) >> 4)];
114                 *p++ = basis_64[((string[i + 1] & 0xF) << 2) |
115                                                 ((int)(string[i + 2] & 0xC0) >> 6)];
116                 *p++ = basis_64[string[i + 2] & 0x3F];
117         }
118         if (i < len) {
119                 *p++ = basis_64[(string[i] >> 2) & 0x3F];
120                 if (i == (len - 1)) {
121                         *p++ = basis_64[((string[i] & 0x3) << 4)];
122                         *p++ = '=';
123                 } else {
124                         *p++ = basis_64[((string[i] & 0x3) << 4) |
125                                                         ((int)(string[i + 1] & 0xF0) >> 4)];
126                         *p++ = basis_64[((string[i + 1] & 0xF) << 2)];
127                 }
128                 *p++ = '=';
129         }
130
131         *p++ = '\0';
132         return p - encoded;
133 }
134
135 static char *
136 ws_gen_accept_key(const char *ws_key, char out[32])
137 {
138         char buf[1024];
139         char digest[20];
140
141         snprintf(buf, sizeof(buf), "%s" WS_UUID, ws_key);
142
143         builtin_SHA1(digest, buf, strlen(buf));
144         Base64encode(out, digest, sizeof(digest));
145         return out;
146 }
147
148 static void
149 close_after_write_cb(struct bufferevent *bev, void *ctx)
150 {
151         if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) {
152                 evws_connection_free(ctx);
153         }
154 }
155
156 static void
157 close_event_cb(struct bufferevent *bev, short what, void *ctx)
158 {
159         evws_connection_free(ctx);
160 }
161
162 void
163 evws_close(struct evws_connection *evws, uint16_t reason)
164 {
165         uint8_t fr[4] = {0x8 | 0x80, 2, 0};
166         struct evbuffer *output;
167         uint16_t *u16;
168
169         if (evws->closed)
170                 return;
171         evws->closed = true;
172
173         u16 = (uint16_t *)&fr[2];
174         *u16 = htons((int16_t)reason);
175         output = bufferevent_get_output(evws->bufev);
176         evbuffer_add(output, fr, 4);
177
178         /* wait for close frame writing complete and close connection */
179         bufferevent_setcb(
180                 evws->bufev, NULL, close_after_write_cb, close_event_cb, evws);
181 }
182
183 static void
184 evws_force_disconnect_(struct evws_connection *evws)
185 {
186         evws_close(evws, WS_CR_NONE);
187 }
188
189 /* parse base frame according to
190  * https://www.rfc-editor.org/rfc/rfc6455#section-5.2
191  */
192 static enum WebSocketFrameType
193 get_ws_frame(unsigned char *in_buffer, int buf_len, unsigned char **payload_ptr,
194         int *out_len)
195 {
196         unsigned char opcode;
197         unsigned char fin;
198         unsigned char masked;
199         int payload_len;
200         int pos;
201         int length_field;
202         unsigned int mask;
203
204         if (buf_len < 2) {
205                 return INCOMPLETE_DATA;
206         }
207
208         opcode = in_buffer[0] & 0x0F;
209         fin = (in_buffer[0] >> 7) & 0x01;
210         masked = (in_buffer[1] >> 7) & 0x01;
211
212         payload_len = 0;
213         pos = 2;
214         length_field = in_buffer[1] & (~0x80);
215
216         if (length_field <= 125) {
217                 payload_len = length_field;
218         } else if (length_field == 126) { /* msglen is 16bit */
219                 if (buf_len < 4)
220                         return INCOMPLETE_DATA;
221                 payload_len = ntohs(*(uint16_t *)(in_buffer + 2));
222                 pos += 2;
223         } else if (length_field == 127) { /* msglen is 64bit */
224                 if (buf_len < 10)
225                         return INCOMPLETE_DATA;
226                 payload_len = ntohs(*(uint64_t *)(in_buffer + 2));
227                 pos += 8;
228         }
229         if (buf_len < payload_len + pos + (masked ? 4 : 0)) {
230                 return INCOMPLETE_DATA;
231         }
232
233         /* According to RFC it seems that unmasked data should be prohibited
234          * but we support it for nonconformant clients
235          */
236         if (masked) {
237                 unsigned char *c;
238
239                 mask = *((unsigned int *)(in_buffer + pos));
240                 pos += 4;
241
242                 /* unmask data */
243                 c = in_buffer + pos;
244                 for (int i = 0; i < payload_len; i++) {
245                         c[i] = c[i] ^ ((unsigned char *)(&mask))[i % 4];
246                 }
247         }
248
249         *payload_ptr = in_buffer + pos;
250         *out_len = payload_len;
251
252         /* are reserved for further frames */
253         if ((opcode >= 3 && opcode <= 7) || (opcode >= 0xb))
254                 return ERROR_FRAME;
255
256         if (opcode <= 0x3 && !fin) {
257                 return INCOMPLETE_FRAME;
258         }
259         return opcode;
260 }
261
262
263 static void
264 ws_evhttp_read_cb(struct bufferevent *bufev, void *arg)
265 {
266         struct evws_connection *evws = arg;
267         unsigned char *payload;
268         enum WebSocketFrameType type;
269         int msg_len, in_len, header_sz;
270         struct evbuffer *input = bufferevent_get_input(evws->bufev);
271
272         while ((in_len = evbuffer_get_length(input))) {
273                 unsigned char *data = evbuffer_pullup(input, in_len);
274                 if (data == NULL) {
275                         return;
276                 }
277
278                 type = get_ws_frame(data, in_len, &payload, &msg_len);
279                 if (type == INCOMPLETE_DATA) {
280                         /* incomplete data received, wait for next chunk */
281                         return;
282                 }
283                 header_sz = payload - data;
284                 evbuffer_drain(input, header_sz);
285                 data = evbuffer_pullup(input, -1);
286
287                 switch (type) {
288                 case TEXT_FRAME:
289                 case BINARY_FRAME:
290                         if (evws->incomplete_frames != NULL) {
291                                 /* we already have incomplete frames in internal buffer
292                                  * and need to concatenate them with final one */
293                                 evbuffer_add(evws->incomplete_frames, data, msg_len);
294
295                                 data = evbuffer_pullup(evws->incomplete_frames, -1);
296
297                                 evws->cb(evws, type, data,
298                                         evbuffer_get_length(evws->incomplete_frames), evws->cb_arg);
299                                 evbuffer_free(evws->incomplete_frames);
300                                 evws->incomplete_frames = NULL;
301                         } else {
302                                 evws->cb(evws, type, data, msg_len, evws->cb_arg);
303                         }
304                         break;
305                 case INCOMPLETE_FRAME:
306                         /* we received full frame until get fin and need to
307                          * postpone callback until all data arrives */
308                         if (evws->incomplete_frames == NULL) {
309                                 evws->incomplete_frames = evbuffer_new();
310                         }
311                         evbuffer_remove_buffer(input, evws->incomplete_frames, msg_len);
312                         continue;
313                 case CLOSING_FRAME:
314                 case ERROR_FRAME:
315                         evws_force_disconnect_(evws);
316                         break;
317                 case PING_FRAME:
318                 case PONG_FRAME:
319                         /* ping or pong frame */
320                         break;
321                 default:
322                         event_warn("%s: unexpected frame type %d\n", __func__, type);
323                         evws_force_disconnect_(evws);
324                 }
325                 evbuffer_drain(input, msg_len);
326         }
327 }
328
329 static void
330 ws_evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
331 {
332         /* when client just disappears after connection (wscat closed by Cmd+Q) */
333         if (what & BEV_EVENT_EOF) {
334                 close_after_write_cb(bufev, arg);
335         }
336 }
337
338 struct evws_connection *
339 evws_new_session(struct evhttp_request *req, ws_on_msg_cb cb, void *arg)
340 {
341         struct evws_connection *evws = NULL;
342         struct evkeyvalq *in_hdrs;
343         const char *upgrade, *connection, *ws_key, *ws_protocol;
344         struct evkeyvalq *out_hdrs;
345         struct evhttp_connection *evcon;
346
347         in_hdrs = evhttp_request_get_input_headers(req);
348         upgrade = evhttp_find_header(in_hdrs, "Upgrade");
349         if (upgrade == NULL || strcmp(upgrade, "websocket"))
350                 goto error;
351
352         connection = evhttp_find_header(in_hdrs, "Connection");
353         if (connection == NULL || strcmp(connection, "Upgrade"))
354                 goto error;
355
356         ws_key = evhttp_find_header(in_hdrs, "Sec-WebSocket-Key");
357         if (ws_key == NULL)
358                 goto error;
359
360         out_hdrs = evhttp_request_get_output_headers(req);
361         evhttp_add_header(out_hdrs, "Upgrade", "websocket");
362         evhttp_add_header(out_hdrs, "Connection", "Upgrade");
363
364         evhttp_add_header(out_hdrs, "Sec-WebSocket-Accept",
365                 ws_gen_accept_key(ws_key, (char[32]){0}));
366
367         ws_protocol = evhttp_find_header(in_hdrs, "Sec-WebSocket-Protocol");
368         if (ws_protocol != NULL)
369                 evhttp_add_header(out_hdrs, "Sec-WebSocket-Protocol", ws_protocol);
370
371         if ((evws = mm_calloc(1, sizeof(struct evws_connection))) == NULL) {
372                 event_warn("%s: calloc failed", __func__);
373                 goto error;
374         }
375
376         evws->cb = cb;
377         evws->cb_arg = arg;
378
379         evcon = evhttp_request_get_connection(req);
380         evws->http_server = evcon->http_server;
381
382         evws->bufev = evhttp_start_ws_(req);
383         bufferevent_setcb(
384                 evws->bufev, ws_evhttp_read_cb, NULL, ws_evhttp_error_cb, evws);
385
386         TAILQ_INSERT_TAIL(&evws->http_server->ws_sessions, evws, next);
387         evws->http_server->connection_cnt++;
388
389         return evws;
390
391 error:
392         evhttp_send_reply(req, HTTP_BADREQUEST, NULL, NULL);
393         return NULL;
394 }
395
396 static void
397 make_ws_frame(struct evbuffer *output, enum WebSocketFrameType frame_type,
398         unsigned char *msg, int len)
399 {
400         int pos = 0;
401         unsigned char header[16] = {0};
402
403         header[pos++] = (unsigned char)frame_type | 0x80; /* fin */
404         if (len <= 125) {
405                 header[pos++] = len;
406         } else if (len <= 65535) {
407                 header[pos++] = 126;                       /* 16 bit length */
408                 header[pos++] = (len >> 8) & 0xFF; /* rightmost first */
409                 header[pos++] = len & 0xFF;
410         } else {                                 /* >2^16-1 */
411                 header[pos++] = 127; /* 64 bit length */
412
413                 pos += 8;
414         }
415         evbuffer_add(output, header, pos);
416         evbuffer_add(output, msg, len);
417 }
418
419 void
420 evws_send(struct evws_connection *evws, const char *packet_str, size_t str_len)
421 {
422         struct evbuffer *output = bufferevent_get_output(evws->bufev);
423
424         make_ws_frame(output, TEXT_FRAME, (unsigned char *)packet_str, str_len);
425 }
426
427 void
428 evws_connection_set_closecb(
429         struct evws_connection *evws, ws_on_close_cb cb, void *cbarg)
430 {
431         evws->cbclose = cb;
432         evws->cbclose_arg = cbarg;
433 }
434
435 struct bufferevent *
436 evws_connection_get_bufferevent(struct evws_connection *evws)
437 {
438         return evws->bufev;
439 }