UNIXworkcode

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] = 129; // 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 } 337