|
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 } |