src/server/daemon/websocket.c

changeset 127
84e206063b64
child 153
85320d8b5d5c
equal deleted inserted replaced
126:631aaa01b2b5 127:84e206063b64
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2016 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 #include "websocket.h"
33
34 #include "../util/io.h"
35 #include "../util/pblock.h"
36 #include "../util/util.h"
37 #include "../util/strbuf.h"
38 #include <ucx/string.h>
39
40 #define WS_BUFFER_LEN 2048
41
42 NSAPI_PUBLIC int http_handle_websocket(Session *sn, Request *rq, WebSocket *websocket) {
43 char *connection = pblock_findkeyval(pb_key_connection, rq->headers);
44 char *upgrade = pblock_findval("upgrade", rq->headers);
45 char *origin = pblock_findval("origin", rq->headers);
46 char *wskey = pblock_findval("sec-websocket-key", rq->headers);
47 char *wsprot = pblock_findval("sec-websocket-protocol", rq->headers);
48 char *wsv = pblock_findval("sec-websocket-version", rq->headers);
49
50 if(!connection || !upgrade) {
51 return REQ_NOACTION;
52 }
53
54 if(sstrcasecmp(sstr(connection), S("upgrade"))) {
55 return REQ_NOACTION;
56 }
57 if(sstrcasecmp(sstr(upgrade), S("websocket"))) {
58 return REQ_NOACTION;
59 }
60
61 sstr_t wsaccept = sstrcat(2, sstr(wskey), S("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
62 unsigned char hash[20];
63 SHA1((const unsigned char*)wsaccept.ptr, wsaccept.length, hash);
64 char *websocket_accept = util_base64encode((char*)hash, 20);
65
66 sbuf_t *response = sbuf_new(512);
67 sbuf_append(response, S("HTTP/1.1 101 Switching Protocols\r\n"));
68 sbuf_append(response, S("Upgrade: websocket\r\n"));
69 sbuf_append(response, S("Connection: Upgrade\r\n"));
70 sbuf_append(response, S("Sec-WebSocket-Accept: "));
71 sbuf_puts(response, websocket_accept);
72 sbuf_append(response, S("\r\n\r\n"));
73
74 net_write(sn->csd, response->ptr, response->length);
75 sbuf_free(response);
76 free(websocket_accept);
77 free(wsaccept.ptr);
78
79 // start websocket I/O
80 WSParser *parser = websocket_parser(sn);
81
82 WSFrame frame;
83
84 int ret = REQ_PROCEED;
85 char *inbuf = pool_malloc(sn->pool, WS_BUFFER_LEN);
86 ssize_t r = 0;
87 while((r = net_read(sn->csd, inbuf, WS_BUFFER_LEN)) > 0) {
88 websocket_input(parser, inbuf, r);
89 WSMessage *msg;
90 int error;
91 while((msg = websocket_get_message(parser, &error)) != NULL) {
92 websocket->on_message(websocket, msg);
93 }
94 if(error) {
95 log_ereport(LOG_FAILURE, "websocket protocol error");
96 break;
97 }
98 }
99
100 return ret;
101 }
102
103
104 WSParser* websocket_parser(Session *sn) {
105 WSParser *parser = pool_malloc(sn->pool, sizeof(WSParser));
106 if(!parser) {
107 return NULL;
108 }
109 ZERO(parser, sizeof(WSParser));
110 parser->pool = sn->pool;
111 return parser;
112 }
113
114 void websocket_input(WSParser *parser, const char *data, size_t length) {
115 parser->inbuf = data;
116 parser->length = length;
117 parser->pos = 0;
118 }
119
120 WSMessage* websocket_get_message(WSParser *parser, int *error) {
121 WSFrame rframe;
122 WSMessage *retmsg = NULL;
123
124 while(parser->pos < parser->length) {
125 const char *inbuf = parser->inbuf + parser->pos;
126 size_t length = parser->length - parser->pos;
127
128 if(parser->state == 0) {
129 WSFrame frame;
130 ZERO(&frame, sizeof(WSFrame));
131
132 /*
133 * small buffer for a websocket frame without payload data
134 * I know using so many buffers it not zero copy but
135 * it makes things a little bit easier :)
136 */
137 char frame_data[WS_FRAMEHEADER_BUFLEN];
138 size_t flen = 0;
139
140 /*
141 * when the last call of websocket_get_message didn't completed
142 * a frame header, the tmpbuf contains the remaining bytes
143 * in this case we combine tmpbuf and inputbuf
144 */
145 if(parser->tmplen > 0) {
146 memcpy(parser->tmpbuf, frame_data, parser->tmplen);
147 flen = parser->tmplen;
148 }
149 size_t cp_remaining = length < WS_FRAMEHEADER_BUFLEN-flen ?
150 length : WS_FRAMEHEADER_BUFLEN-flen;
151 memcpy(&frame_data[flen], inbuf, cp_remaining);
152 flen += cp_remaining;
153
154 // ready to parse the frame
155 ssize_t frame_hlen = websocket_get_frameheader(
156 &frame,
157 frame_data,
158 flen);
159
160 if(frame_hlen == -1) {
161 // protocol error, abort
162 *error = 1;
163 return NULL;
164 }
165 if(frame_hlen == 0) {
166 memcpy(parser->tmpbuf, frame_data, flen);
167 } else {
168 inbuf += frame_hlen;
169 length -= frame_hlen;
170 parser->pos += frame_hlen;
171
172 // frame complete, create a message object
173 if(frame.payload_length > 0) {
174 WSMessage *msg = pool_malloc(parser->pool, sizeof(WSMessage));
175 msg->data = pool_malloc(parser->pool, frame.payload_length);
176 msg->length = frame.payload_length;
177 msg->next = NULL;
178 msg->type = frame.opcode;
179
180 if(frame.payload_length >= length) {
181 // message complete
182 memcpy(msg->data, inbuf, frame.payload_length);
183 parser->pos += frame.payload_length;
184
185 rframe = frame;
186 retmsg = msg;
187 break;
188 } else {
189 memcpy(msg->data, inbuf, length);
190 parser->state = 1;
191 parser->current = msg;
192 parser->cur_plen = length;
193 parser->frame = frame;
194 return NULL;
195 }
196 }
197 }
198 } else {
199 WSMessage *msg = parser->current;
200 if(msg->length >= parser->cur_plen + length) {
201 // still incomplete message
202 memcpy(msg->data + parser->cur_plen, inbuf, length);
203 parser->cur_plen += length;
204 return NULL;
205 } else {
206 size_t cplen = msg->length - parser->cur_plen;
207 memcpy(msg->data + parser->cur_plen, inbuf, cplen);
208 parser->pos += cplen;
209 parser->state = 0;
210 parser->current = NULL;
211
212 rframe = parser->frame;
213 retmsg = msg;
214 break;
215 }
216 }
217 }
218
219 if(retmsg && rframe.mask) {
220 websocket_mask_data(retmsg->data, retmsg->length, rframe.masking_key);
221 }
222 return retmsg;
223 }
224
225
226 ssize_t websocket_get_frameheader(WSFrame *frame, const char *buf, size_t len) {
227 if(len < 2) {
228 return 0; // too small for anything
229 }
230
231 /*
232 printf("websocket_get_frameheader: ");
233 for(int i=0;i<len;i++) {
234 printf("%x ", buf[i]);
235 if(len > 15) {
236 break;
237 }
238 }
239 printf("\n");
240 */
241
242 size_t msglen = 2; // minimal length
243
244 uint8_t fin = (buf[0] & 0x80) != 0;
245 uint8_t opcode = buf[0] & 0xf;
246
247 uint8_t mask = (buf[1] & 0x80) != 0;
248 uint8_t payload_len = buf[1] & 0x7f;
249
250 uint64_t payload_length = payload_len;
251 if(payload_len == 126) {
252 msglen += 2;
253 if(len < msglen) {
254 return 0;
255 }
256 payload_length = *((uint16_t*)(buf+2));
257 } else if(payload_len == 127) {
258 msglen += 8;
259 if(len < msglen) {
260 return 0;
261 }
262 payload_length = *((uint64_t*)(buf+2));
263 } else if(payload_len > 127) {
264 return -1;
265 }
266
267 uint32_t masking_key = 0;
268 if(mask) {
269 msglen += 4;
270 if(len < msglen) {
271 return 0;
272 }
273 masking_key = *((uint32_t*)(buf+msglen-4));
274 }
275
276 frame->header_complete = TRUE;
277 frame->fin = fin;
278 frame->opcode = opcode;
279 frame->mask = mask;
280 frame->masking_key = masking_key;
281 frame->payload_length = payload_length;
282
283 return msglen;
284 }
285
286 void websocket_mask_data(char *buf, size_t len, uint32_t mask) {
287 size_t m = len % 4;
288 size_t alen = (len - m) / 4;
289
290 uint32_t *data = (uint32_t*)buf;
291 for(int i=0;i<alen;i++) {
292 data[i] = data[i] ^ mask;
293 }
294
295 int j = 0;
296 char *cmask = (char*)&mask;
297 for(int i=len-m;i<len;i++) {
298 buf[i] = buf[i] ^ cmask[j];
299 j++;
300 }
301 }
302
303 /* ------------------------------ public API ------------------------------*/
304
305 NSAPI_PUBLIC int websocket_send_text(SYS_NETFD csd, char *msg, size_t len) {
306 char frame[WS_FRAMEHEADER_BUFLEN];
307 frame[0] = 0b10000001;
308 size_t hlen;
309 if(len < 126) {
310 frame[1] = (char)len;
311 hlen = 2;
312 } else if(len < 65536) {
313 frame[1] = 126;
314 uint16_t plen = htons(len);
315 memcpy(frame + 2, &plen, 2);
316 hlen = 4;
317 } else {
318 frame[1] = 127;
319 // TODO
320 hlen = 10;
321 }
322
323 struct iovec iov[2];
324 iov[0].iov_base = frame;
325 iov[0].iov_len = hlen;
326
327 iov[1].iov_base = msg;
328 iov[1].iov_len = len;
329
330 ssize_t w = net_writev(csd, iov, 2);
331 if(w > 0) {
332 return 0;
333 } else {
334 return 1;
335 }
336 }

mercurial