1 #include "event2/event-config.h"
2 #include "evconfig-private.h"
4 #include "event2/buffer.h"
5 #include "event2/bufferevent.h"
6 #include "event2/event.h"
7 #include "event2/http.h"
9 #include "util-internal.h"
10 #include "mm-internal.h"
12 #include "event2/bufferevent.h"
13 #include "sys/queue.h"
14 #include "http-internal.h"
21 #include <sys/socket.h>
28 #ifdef EVENT__HAVE_ARPA_INET_H
29 #include <arpa/inet.h>
31 #ifdef EVENT__HAVE_NETINET_IN_H
32 #include <netinet/in.h>
34 #ifdef EVENT__HAVE_NETINET_IN6_H
35 #include <netinet/in6.h>
38 #define WS_UUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
40 struct evws_connection {
41 TAILQ_ENTRY(evws_connection) next;
43 struct bufferevent *bufev;
48 ws_on_close_cb cbclose;
51 /* for server connections, the http server they are connected with */
52 struct evhttp *http_server;
54 struct evbuffer *incomplete_frames;
58 enum WebSocketFrameType {
60 INCOMPLETE_DATA = 0xFE,
64 INCOMPLETE_FRAME = 0x81,
74 * Clean up a WebSockets connection object
78 evws_connection_free(struct evws_connection *evws)
80 /* notify interested parties that this connection is going down */
81 if (evws->cbclose != NULL)
82 (*evws->cbclose)(evws, evws->cbclose_arg);
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--;
90 if (evws->bufev != NULL) {
91 bufferevent_free(evws->bufev);
93 if (evws->incomplete_frames != NULL) {
94 evbuffer_free(evws->incomplete_frames);
100 static const char basis_64[] =
101 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
104 Base64encode(char *encoded, const char *string, int len)
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];
119 *p++ = basis_64[(string[i] >> 2) & 0x3F];
120 if (i == (len - 1)) {
121 *p++ = basis_64[((string[i] & 0x3) << 4)];
124 *p++ = basis_64[((string[i] & 0x3) << 4) |
125 ((int)(string[i + 1] & 0xF0) >> 4)];
126 *p++ = basis_64[((string[i + 1] & 0xF) << 2)];
136 ws_gen_accept_key(const char *ws_key, char out[32])
141 snprintf(buf, sizeof(buf), "%s" WS_UUID, ws_key);
143 builtin_SHA1(digest, buf, strlen(buf));
144 Base64encode(out, digest, sizeof(digest));
149 close_after_write_cb(struct bufferevent *bev, void *ctx)
151 if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) {
152 evws_connection_free(ctx);
157 close_event_cb(struct bufferevent *bev, short what, void *ctx)
159 evws_connection_free(ctx);
163 evws_close(struct evws_connection *evws, uint16_t reason)
165 uint8_t fr[4] = {0x8 | 0x80, 2, 0};
166 struct evbuffer *output;
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);
178 /* wait for close frame writing complete and close connection */
180 evws->bufev, NULL, close_after_write_cb, close_event_cb, evws);
184 evws_force_disconnect_(struct evws_connection *evws)
186 evws_close(evws, WS_CR_NONE);
189 /* parse base frame according to
190 * https://www.rfc-editor.org/rfc/rfc6455#section-5.2
192 static enum WebSocketFrameType
193 get_ws_frame(unsigned char *in_buffer, int buf_len, unsigned char **payload_ptr,
196 unsigned char opcode;
198 unsigned char masked;
205 return INCOMPLETE_DATA;
208 opcode = in_buffer[0] & 0x0F;
209 fin = (in_buffer[0] >> 7) & 0x01;
210 masked = (in_buffer[1] >> 7) & 0x01;
214 length_field = in_buffer[1] & (~0x80);
216 if (length_field <= 125) {
217 payload_len = length_field;
218 } else if (length_field == 126) { /* msglen is 16bit */
220 return INCOMPLETE_DATA;
221 payload_len = ntohs(*(uint16_t *)(in_buffer + 2));
223 } else if (length_field == 127) { /* msglen is 64bit */
225 return INCOMPLETE_DATA;
226 payload_len = ntohs(*(uint64_t *)(in_buffer + 2));
229 if (buf_len < payload_len + pos + (masked ? 4 : 0)) {
230 return INCOMPLETE_DATA;
233 /* According to RFC it seems that unmasked data should be prohibited
234 * but we support it for nonconformant clients
239 mask = *((unsigned int *)(in_buffer + pos));
244 for (int i = 0; i < payload_len; i++) {
245 c[i] = c[i] ^ ((unsigned char *)(&mask))[i % 4];
249 *payload_ptr = in_buffer + pos;
250 *out_len = payload_len;
252 /* are reserved for further frames */
253 if ((opcode >= 3 && opcode <= 7) || (opcode >= 0xb))
256 if (opcode <= 0x3 && !fin) {
257 return INCOMPLETE_FRAME;
264 ws_evhttp_read_cb(struct bufferevent *bufev, void *arg)
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);
272 while ((in_len = evbuffer_get_length(input))) {
273 unsigned char *data = evbuffer_pullup(input, in_len);
278 type = get_ws_frame(data, in_len, &payload, &msg_len);
279 if (type == INCOMPLETE_DATA) {
280 /* incomplete data received, wait for next chunk */
283 header_sz = payload - data;
284 evbuffer_drain(input, header_sz);
285 data = evbuffer_pullup(input, -1);
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);
295 data = evbuffer_pullup(evws->incomplete_frames, -1);
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;
302 evws->cb(evws, type, data, msg_len, evws->cb_arg);
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();
311 evbuffer_remove_buffer(input, evws->incomplete_frames, msg_len);
315 evws_force_disconnect_(evws);
319 /* ping or pong frame */
322 event_warn("%s: unexpected frame type %d\n", __func__, type);
323 evws_force_disconnect_(evws);
325 evbuffer_drain(input, msg_len);
330 ws_evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
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);
338 struct evws_connection *
339 evws_new_session(struct evhttp_request *req, ws_on_msg_cb cb, void *arg)
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;
347 in_hdrs = evhttp_request_get_input_headers(req);
348 upgrade = evhttp_find_header(in_hdrs, "Upgrade");
349 if (upgrade == NULL || strcmp(upgrade, "websocket"))
352 connection = evhttp_find_header(in_hdrs, "Connection");
353 if (connection == NULL || strcmp(connection, "Upgrade"))
356 ws_key = evhttp_find_header(in_hdrs, "Sec-WebSocket-Key");
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");
364 evhttp_add_header(out_hdrs, "Sec-WebSocket-Accept",
365 ws_gen_accept_key(ws_key, (char[32]){0}));
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);
371 if ((evws = mm_calloc(1, sizeof(struct evws_connection))) == NULL) {
372 event_warn("%s: calloc failed", __func__);
379 evcon = evhttp_request_get_connection(req);
380 evws->http_server = evcon->http_server;
382 evws->bufev = evhttp_start_ws_(req);
384 evws->bufev, ws_evhttp_read_cb, NULL, ws_evhttp_error_cb, evws);
386 TAILQ_INSERT_TAIL(&evws->http_server->ws_sessions, evws, next);
387 evws->http_server->connection_cnt++;
392 evhttp_send_reply(req, HTTP_BADREQUEST, NULL, NULL);
397 make_ws_frame(struct evbuffer *output, enum WebSocketFrameType frame_type,
398 unsigned char *msg, int len)
401 unsigned char header[16] = {0};
403 header[pos++] = (unsigned char)frame_type | 0x80; /* fin */
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 */
415 evbuffer_add(output, header, pos);
416 evbuffer_add(output, msg, len);
420 evws_send(struct evws_connection *evws, const char *packet_str, size_t str_len)
422 struct evbuffer *output = bufferevent_get_output(evws->bufev);
424 make_ws_frame(output, TEXT_FRAME, (unsigned char *)packet_str, str_len);
428 evws_connection_set_closecb(
429 struct evws_connection *evws, ws_on_close_cb cb, void *cbarg)
432 evws->cbclose_arg = cbarg;
436 evws_connection_get_bufferevent(struct evws_connection *evws)