Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 1 | #include <stdio.h> |
| 2 | #include <stdlib.h> |
| 3 | #include <string.h> |
| 4 | #include <ctype.h> |
| 5 | #include <unistd.h> |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 6 | #include <errno.h> |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 7 | |
| 8 | #include <sys/types.h> |
| 9 | #include <sys/socket.h> |
| 10 | #include <netinet/in.h> |
| 11 | |
| 12 | #include <poll.h> |
| 13 | #include <sys/mman.h> |
| 14 | |
| 15 | #include "libwebsockets.h" |
| 16 | |
| 17 | void md5(const unsigned char *input, int ilen, unsigned char output[16]); |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 18 | static void libwebsocket_service(struct libwebsocket *wsi, int sock); |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 19 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 20 | #define LWS_MAX_HEADER_NAME_LENGTH 64 |
| 21 | #define LWS_MAX_HEADER_LEN 4096 |
| 22 | #define LWS_INITIAL_HDR_ALLOC 256 |
| 23 | #define LWS_ADDITIONAL_HDR_ALLOC 64 |
| 24 | |
| 25 | |
| 26 | enum lws_connection_states { |
| 27 | WSI_STATE_CLOSED, |
| 28 | WSI_STATE_HANDSHAKE_RX, |
| 29 | WSI_STATE_DEAD_SOCKET, |
| 30 | WSI_STATE_ESTABLISHED |
| 31 | }; |
| 32 | |
| 33 | enum lws_token_indexes { |
| 34 | WSI_TOKEN_GET_URI, |
| 35 | WSI_TOKEN_HOST, |
| 36 | WSI_TOKEN_CONNECTION, |
| 37 | WSI_TOKEN_KEY1, |
| 38 | WSI_TOKEN_KEY2, |
| 39 | WSI_TOKEN_PROTOCOL, |
| 40 | WSI_TOKEN_UPGRADE, |
| 41 | WSI_TOKEN_ORIGIN, |
| 42 | WSI_TOKEN_CHALLENGE, |
| 43 | |
| 44 | /* always last real token index*/ |
| 45 | WSI_TOKEN_COUNT, |
| 46 | /* parser state additions */ |
| 47 | WSI_TOKEN_NAME_PART, |
| 48 | WSI_TOKEN_SKIPPING, |
| 49 | WSI_TOKEN_SKIPPING_SAW_CR, |
| 50 | WSI_PARSING_COMPLETE |
| 51 | }; |
| 52 | |
| 53 | |
| 54 | struct lws_tokens { |
| 55 | char * token; |
| 56 | int token_len; |
| 57 | }; |
| 58 | |
| 59 | |
| 60 | /* |
| 61 | * This is totally opaque to code using the library. It's exported as a |
| 62 | * forward-reference pointer-only declaration. |
| 63 | */ |
| 64 | |
| 65 | struct libwebsocket { |
| 66 | int (*callback)(struct libwebsocket *, |
| 67 | enum libwebsocket_callback_reasons reason); |
| 68 | |
| 69 | enum lws_connection_states state; |
| 70 | |
| 71 | char name_buffer[LWS_MAX_HEADER_NAME_LENGTH]; |
| 72 | int name_buffer_pos; |
| 73 | int current_alloc_len; |
| 74 | enum lws_token_indexes parser_state; |
| 75 | struct lws_tokens utf8_token[WSI_TOKEN_COUNT]; |
| 76 | int ietf_spec_revision; |
| 77 | |
| 78 | int sock; |
| 79 | }; |
| 80 | |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 81 | |
| 82 | const struct lws_tokens lws_tokens[WSI_TOKEN_COUNT] = { |
| 83 | { "GET ", 4 }, |
| 84 | { "Host:", 5 }, |
| 85 | { "Connection:", 11 }, |
| 86 | { "Sec-WebSocket-Key1:", 19 }, |
| 87 | { "Sec-WebSocket-Key2:", 19 }, |
| 88 | { "Sec-WebSocket-Protocol:", 23 }, |
| 89 | { "Upgrade:", 8 }, |
| 90 | { "Origin:", 7 }, |
| 91 | { "\x0d\x0a", 2 }, |
| 92 | }; |
| 93 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 94 | int libwebsocket_create_server(int port, int (*callback)(struct libwebsocket *, enum libwebsocket_callback_reasons)) |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 95 | { |
| 96 | int n; |
| 97 | int sockfd, newsockfd; |
| 98 | unsigned int clilen; |
| 99 | struct sockaddr_in serv_addr, cli_addr; |
| 100 | int pid; |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 101 | struct libwebsocket *wsi = malloc(sizeof(struct libwebsocket)); |
| 102 | |
| 103 | if (!wsi) |
| 104 | return -1; |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 105 | |
| 106 | wsi->state = WSI_STATE_CLOSED; |
| 107 | wsi->name_buffer_pos = 0; |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 108 | |
| 109 | for (n = 0; n < WSI_TOKEN_COUNT; n++) { |
| 110 | wsi->utf8_token[n].token = NULL; |
| 111 | wsi->utf8_token[n].token_len = 0; |
| 112 | } |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 113 | |
| 114 | wsi->callback = callback; |
| 115 | wsi->ietf_spec_revision = 0; |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 116 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 117 | /* sit there listening for connects, accept and spawn session servers */ |
| 118 | |
| 119 | sockfd = socket(AF_INET, SOCK_STREAM, 0); |
| 120 | if (sockfd < 0) { |
| 121 | fprintf(stderr, "ERROR opening socket"); |
| 122 | } |
| 123 | bzero((char *) &serv_addr, sizeof(serv_addr)); |
| 124 | |
| 125 | serv_addr.sin_family = AF_INET; |
| 126 | serv_addr.sin_addr.s_addr = INADDR_ANY; |
| 127 | serv_addr.sin_port = htons(port); |
| 128 | n = bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); |
| 129 | if (n < 0) { |
| 130 | fprintf(stderr, "ERROR on binding %d %d\n", n, errno); |
| 131 | return -1; |
| 132 | } |
| 133 | |
| 134 | /* fork off a master server for this websocket server */ |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 135 | |
| 136 | n = fork(); |
| 137 | if (n < 0) { |
| 138 | fprintf(stderr, "Failed on forking server thread: %d\n", n); |
| 139 | exit(1); |
| 140 | } |
| 141 | |
| 142 | /* we are done as far as the caller is concerned */ |
| 143 | |
| 144 | if (n) |
| 145 | return 0; |
| 146 | |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 147 | |
| 148 | listen(sockfd, 5); |
| 149 | |
| 150 | while (1) { |
| 151 | clilen = sizeof(cli_addr); |
| 152 | |
| 153 | newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 154 | if (newsockfd < 0) { |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 155 | fprintf(stderr, "ERROR on accept"); |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 156 | continue; |
| 157 | } |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 158 | |
| 159 | /* fork off a new server instance */ |
| 160 | |
| 161 | pid = fork(); |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 162 | if (pid < 0) { |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 163 | fprintf(stderr, "ERROR on fork"); |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 164 | continue; |
| 165 | } |
| 166 | |
| 167 | if (pid) { |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 168 | close(newsockfd); |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 169 | continue; |
| 170 | } |
| 171 | |
| 172 | /* we are the session process */ |
| 173 | |
| 174 | close(sockfd); |
| 175 | |
| 176 | /* sit in libwebsocket_service() until session socket closed */ |
| 177 | |
| 178 | libwebsocket_service(wsi, newsockfd); |
| 179 | exit(0); |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 180 | } |
| 181 | } |
| 182 | |
| 183 | void libwebsocket_close(struct libwebsocket *wsi) |
| 184 | { |
| 185 | int n; |
| 186 | |
| 187 | wsi->state = WSI_STATE_DEAD_SOCKET; |
| 188 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 189 | if (wsi->callback) |
| 190 | wsi->callback(wsi, LWS_CALLBACK_CLOSED); |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 191 | |
| 192 | for (n = 0; n < WSI_TOKEN_COUNT; n++) |
| 193 | if (wsi->utf8_token[n].token) |
| 194 | free(wsi->utf8_token[n].token); |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | |
| 198 | static int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) |
| 199 | { |
| 200 | int n; |
| 201 | |
| 202 | switch (wsi->parser_state) { |
| 203 | case WSI_TOKEN_GET_URI: |
| 204 | case WSI_TOKEN_HOST: |
| 205 | case WSI_TOKEN_CONNECTION: |
| 206 | case WSI_TOKEN_KEY1: |
| 207 | case WSI_TOKEN_KEY2: |
| 208 | case WSI_TOKEN_PROTOCOL: |
| 209 | case WSI_TOKEN_UPGRADE: |
| 210 | case WSI_TOKEN_ORIGIN: |
| 211 | case WSI_TOKEN_CHALLENGE: |
| 212 | |
| 213 | // fprintf(stderr, "WSI_TOKEN_(body %d) '%c'\n", wsi->parser_state, c); |
| 214 | |
| 215 | /* collect into malloc'd buffers */ |
| 216 | /* optional space swallow */ |
| 217 | if (!wsi->utf8_token[wsi->parser_state].token_len && c == ' ') |
| 218 | break; |
| 219 | |
| 220 | /* special case space terminator for get-uri */ |
| 221 | if (wsi->parser_state == WSI_TOKEN_GET_URI && c == ' ') { |
| 222 | wsi->utf8_token[wsi->parser_state].token[ |
| 223 | wsi->utf8_token[wsi->parser_state].token_len] = '\0'; |
| 224 | wsi->parser_state = WSI_TOKEN_SKIPPING; |
| 225 | break; |
| 226 | } |
| 227 | |
| 228 | /* allocate appropriate memory */ |
| 229 | if (wsi->utf8_token[wsi->parser_state].token_len == wsi->current_alloc_len - 1) { |
| 230 | /* need to extend */ |
| 231 | wsi->current_alloc_len += LWS_ADDITIONAL_HDR_ALLOC; |
| 232 | if (wsi->current_alloc_len >= LWS_MAX_HEADER_LEN) { |
| 233 | /* it's waaay to much payload, fail it */ |
| 234 | strcpy(wsi->utf8_token[wsi->parser_state].token, |
| 235 | "!!! Length exceeded maximum supported !!!"); |
| 236 | wsi->parser_state = WSI_TOKEN_SKIPPING; |
| 237 | break; |
| 238 | } |
| 239 | wsi->utf8_token[wsi->parser_state].token = |
| 240 | realloc(wsi->utf8_token[wsi->parser_state].token, |
| 241 | wsi->current_alloc_len); |
| 242 | } |
| 243 | |
| 244 | /* bail at EOL */ |
| 245 | if (wsi->parser_state != WSI_TOKEN_CHALLENGE && c == '\x0d') { |
| 246 | wsi->utf8_token[wsi->parser_state].token[ |
| 247 | wsi->utf8_token[wsi->parser_state].token_len] = '\0'; |
| 248 | wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR; |
| 249 | break; |
| 250 | } |
| 251 | |
| 252 | wsi->utf8_token[wsi->parser_state].token[ |
| 253 | wsi->utf8_token[wsi->parser_state].token_len++] = c; |
| 254 | |
| 255 | /* special payload limiting */ |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 256 | if (wsi->parser_state == WSI_TOKEN_CHALLENGE && |
| 257 | wsi->utf8_token[wsi->parser_state].token_len == 8) { |
| 258 | // fprintf(stderr, "Setting WSI_PARSING_COMPLETE\n"); |
| 259 | wsi->parser_state = WSI_PARSING_COMPLETE; |
| 260 | break; |
| 261 | } |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 262 | |
| 263 | break; |
| 264 | |
| 265 | /* collecting and checking a name part */ |
| 266 | case WSI_TOKEN_NAME_PART: |
| 267 | // fprintf(stderr, "WSI_TOKEN_NAME_PART '%c'\n", c); |
| 268 | |
| 269 | if (wsi->name_buffer_pos == sizeof(wsi->name_buffer) - 1) { |
| 270 | /* name bigger than we can handle, skip until next */ |
| 271 | wsi->parser_state = WSI_TOKEN_SKIPPING; |
| 272 | break; |
| 273 | } |
| 274 | wsi->name_buffer[wsi->name_buffer_pos++] = c; |
| 275 | wsi->name_buffer[wsi->name_buffer_pos] = '\0'; |
| 276 | |
| 277 | for (n = 0; n < WSI_TOKEN_COUNT; n++) { |
| 278 | if (wsi->name_buffer_pos != lws_tokens[n].token_len) |
| 279 | continue; |
| 280 | if (strcmp(lws_tokens[n].token, wsi->name_buffer)) |
| 281 | continue; |
| 282 | wsi->parser_state = WSI_TOKEN_GET_URI + n; |
| 283 | wsi->current_alloc_len = LWS_INITIAL_HDR_ALLOC; |
| 284 | wsi->utf8_token[wsi->parser_state].token = |
| 285 | malloc(wsi->current_alloc_len); |
| 286 | wsi->utf8_token[wsi->parser_state].token_len = 0; |
| 287 | n = WSI_TOKEN_COUNT; |
| 288 | } |
| 289 | if (wsi->parser_state != WSI_TOKEN_NAME_PART) |
| 290 | break; |
| 291 | break; |
| 292 | |
| 293 | /* skipping arg part of a name we didn't recognize */ |
| 294 | case WSI_TOKEN_SKIPPING: |
| 295 | // fprintf(stderr, "WSI_TOKEN_SKIPPING '%c'\n", c); |
| 296 | if (c == '\x0d') |
| 297 | wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR; |
| 298 | break; |
| 299 | case WSI_TOKEN_SKIPPING_SAW_CR: |
| 300 | // fprintf(stderr, "WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c); |
| 301 | if (c == '\x0a') |
| 302 | wsi->parser_state = WSI_TOKEN_NAME_PART; |
| 303 | else |
| 304 | wsi->parser_state = WSI_TOKEN_SKIPPING; |
| 305 | wsi->name_buffer_pos = 0; |
| 306 | break; |
| 307 | /* we're done, ignore anything else */ |
| 308 | case WSI_PARSING_COMPLETE: |
| 309 | // fprintf(stderr, "WSI_PARSING_COMPLETE '%c'\n", c); |
| 310 | break; |
| 311 | |
| 312 | default: /* keep gcc happy */ |
| 313 | break; |
| 314 | } |
| 315 | |
| 316 | return 0; |
| 317 | } |
| 318 | |
| 319 | static int interpret_key(const char *key, unsigned int *result) |
| 320 | { |
| 321 | char digits[20]; |
| 322 | int digit_pos = 0; |
| 323 | const char *p = key; |
| 324 | int spaces = 0; |
| 325 | |
| 326 | while (*p) { |
| 327 | if (isdigit(*p)) { |
| 328 | if (digit_pos == sizeof(digits) - 1) |
| 329 | return -1; |
| 330 | digits[digit_pos++] = *p; |
| 331 | } |
| 332 | p++; |
| 333 | } |
| 334 | digits[digit_pos] = '\0'; |
| 335 | if (!digit_pos) |
| 336 | return -2; |
| 337 | |
| 338 | while (*key) { |
| 339 | if (*key == ' ') |
| 340 | spaces++; |
| 341 | key++; |
| 342 | } |
| 343 | |
| 344 | if (!spaces) |
| 345 | return -3; |
| 346 | |
| 347 | *result = atol(digits) / spaces; |
| 348 | |
| 349 | return 0; |
| 350 | } |
| 351 | |
| 352 | |
| 353 | /* |
| 354 | * We have to take care about parsing because the headers may be split |
| 355 | * into multiple fragments. They may contain unknown headers with arbitrary |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 356 | * argument lengths. So, we parse using a single-character at a time state |
| 357 | * machine that is completely independent of packet size. |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 358 | */ |
| 359 | |
| 360 | int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len) |
| 361 | { |
| 362 | size_t n; |
| 363 | char *p; |
| 364 | unsigned int key1, key2; |
| 365 | unsigned char sum[16]; |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 366 | char *response; |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 367 | |
| 368 | switch (wsi->state) { |
| 369 | case WSI_STATE_CLOSED: |
| 370 | wsi->state = WSI_STATE_HANDSHAKE_RX; |
| 371 | wsi->parser_state = WSI_TOKEN_NAME_PART; |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 372 | /* fallthru */ |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 373 | case WSI_STATE_HANDSHAKE_RX: |
| 374 | |
| 375 | fprintf(stderr, "issuing %ld bytes to parser\n", len); |
| 376 | |
| 377 | |
| 378 | fwrite(buf, 1, len, stderr); |
| 379 | for (n = 0; n< len; n++) |
| 380 | libwebsocket_parse(wsi, *buf++); |
| 381 | |
| 382 | if (wsi->parser_state != WSI_PARSING_COMPLETE) |
| 383 | break; |
| 384 | |
| 385 | fprintf(stderr, "Preparing return packet\n"); |
| 386 | |
| 387 | |
| 388 | /* Confirm we have all the necessary pieces */ |
| 389 | |
| 390 | if ( |
| 391 | !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len || |
| 392 | !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len || |
| 393 | !wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len || |
| 394 | !wsi->utf8_token[WSI_TOKEN_HOST].token_len || |
| 395 | !wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len || |
| 396 | !wsi->utf8_token[WSI_TOKEN_KEY1].token_len || |
| 397 | !wsi->utf8_token[WSI_TOKEN_KEY2].token_len) { |
| 398 | |
| 399 | /* completed header processing, but missing some bits */ |
| 400 | goto bail; |
| 401 | } |
| 402 | |
| 403 | /* create the response packet */ |
| 404 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 405 | response = malloc(256 + |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 406 | wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len + |
| 407 | wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len + |
| 408 | wsi->utf8_token[WSI_TOKEN_HOST].token_len + |
| 409 | wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len + |
| 410 | wsi->utf8_token[WSI_TOKEN_GET_URI].token_len + |
| 411 | wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len); |
| 412 | |
| 413 | |
| 414 | fprintf(stderr, "'%s;\n", wsi->utf8_token[WSI_TOKEN_HOST].token); |
| 415 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 416 | p = response; |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 417 | strcpy(p, "HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0aUpgrade: WebSocket\x0d\x0a"); |
| 418 | p += strlen("HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0aUpgrade: WebSocket\x0d\x0a"); |
| 419 | strcpy(p, "Connection: Upgrade\x0d\x0aSec-WebSocket-Origin: "); |
| 420 | p += strlen("Connection: Upgrade\x0d\x0aSec-WebSocket-Origin: "); |
| 421 | strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token); |
| 422 | p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len; |
| 423 | strcpy(p, "\x0d\x0aSec-WebSocket-Location: ws://"); |
| 424 | p += strlen("\x0d\x0aSec-WebSocket-Location: ws://"); |
| 425 | strcpy(p, wsi->utf8_token[WSI_TOKEN_HOST].token); |
| 426 | p += wsi->utf8_token[WSI_TOKEN_HOST].token_len; |
| 427 | strcpy(p, wsi->utf8_token[WSI_TOKEN_GET_URI].token); |
| 428 | p += wsi->utf8_token[WSI_TOKEN_GET_URI].token_len; |
| 429 | strcpy(p, "\x0d\x0aSec-WebSocket-Protocol: "); |
| 430 | p += strlen("\x0d\x0aSec-WebSocket-Protocol: "); |
| 431 | if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) { |
| 432 | strcpy(p, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token); |
| 433 | p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len; |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 434 | } else { |
| 435 | strcpy(p, "none"); |
| 436 | p += strlen("none"); |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 437 | } |
| 438 | strcpy(p, "\x0d\x0a\x0d\x0a"); |
| 439 | p += strlen("\x0d\x0a\x0d\x0a"); |
| 440 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 441 | /* convert the two keys into 32-bit integers */ |
| 442 | |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 443 | if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY1].token, &key1)) |
| 444 | goto bail; |
| 445 | |
| 446 | if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY2].token, &key2)) |
| 447 | goto bail; |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 448 | |
| 449 | /* lay them out in network byte order (MSB first */ |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 450 | |
| 451 | sum[0] = key1 >> 24; |
| 452 | sum[1] = key1 >> 16; |
| 453 | sum[2] = key1 >> 8; |
| 454 | sum[3] = key1; |
| 455 | sum[4] = key2 >> 24; |
| 456 | sum[5] = key2 >> 16; |
| 457 | sum[6] = key2 >> 8; |
| 458 | sum[7] = key2; |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 459 | |
| 460 | /* follow them with the challenge token we were sent */ |
| 461 | |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 462 | memcpy(&sum[8], wsi->utf8_token[WSI_TOKEN_CHALLENGE].token, 8); |
| 463 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 464 | /* |
| 465 | * compute the md5sum of that 16-byte series and use as our |
| 466 | * payload after our headers |
| 467 | */ |
| 468 | |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 469 | md5(sum, 16, (unsigned char *)p); |
| 470 | p += 16; |
| 471 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 472 | /* it's complete, go ahead and send it */ |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 473 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 474 | fprintf(stderr, "issuing response packet %d len\n", |
| 475 | (int)(p - response)); |
| 476 | fwrite(response, 1, p - response, stderr); |
| 477 | |
| 478 | n = write(wsi->sock, response, p - response); |
| 479 | if (n < 0) { |
| 480 | fprintf(stderr, "ERROR writing to socket"); |
| 481 | goto bail; |
| 482 | } |
| 483 | |
| 484 | free(response); |
| 485 | wsi->state = WSI_STATE_ESTABLISHED; |
| 486 | |
| 487 | /* notify user code that we're ready to roll */ |
| 488 | |
| 489 | if (wsi->callback) |
| 490 | wsi->callback(wsi, LWS_CALLBACK_ESTABLISHED); |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 491 | break; |
| 492 | |
| 493 | case WSI_STATE_ESTABLISHED: |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 494 | fprintf(stderr, "received %ld byte packet\n", len); |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 495 | break; |
| 496 | default: |
| 497 | break; |
| 498 | } |
| 499 | |
| 500 | return 0; |
| 501 | |
| 502 | bail: |
| 503 | libwebsocket_close(wsi); |
| 504 | return -1; |
| 505 | } |
| 506 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 507 | int libwebsocket_write(struct libwebsocket * wsi, void *buf, size_t len) |
| 508 | { |
| 509 | int n; |
| 510 | unsigned char hdr[9]; |
| 511 | |
| 512 | if (wsi->state != WSI_STATE_ESTABLISHED) |
| 513 | return -1; |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 514 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 515 | switch (wsi->ietf_spec_revision) { |
| 516 | /* chrome */ |
| 517 | case 0: |
| 518 | hdr[0] = 0xff; |
| 519 | hdr[1] = len >> 56; |
| 520 | hdr[2] = len >> 48; |
| 521 | hdr[3] = len >> 40; |
| 522 | hdr[4] = len >> 32; |
| 523 | hdr[5] = len >> 24; |
| 524 | hdr[6] = len >> 16; |
| 525 | hdr[7] = len >> 8; |
| 526 | hdr[8] = len; |
| 527 | |
| 528 | n = write(wsi->sock, hdr, sizeof hdr); |
| 529 | if (n < 0) { |
| 530 | fprintf(stderr, "ERROR writing to socket"); |
| 531 | return -1; |
| 532 | } |
| 533 | break; |
| 534 | /* just an unimplemented spec right now apparently */ |
| 535 | case 2: |
| 536 | n = 0; |
| 537 | if (len < 126) { |
| 538 | hdr[n++] = 0x04; |
| 539 | hdr[n++] = len; |
| 540 | } else { |
| 541 | if (len < 65536) { |
| 542 | hdr[n++] = 0x04; /* text frame */ |
| 543 | hdr[n++] = 126; |
| 544 | hdr[n++] = len >> 8; |
| 545 | hdr[n++] = len; |
| 546 | } else { |
| 547 | hdr[n++] = 0x04; |
| 548 | hdr[n++] = 127; |
| 549 | hdr[n++] = len >> 24; |
| 550 | hdr[n++] = len >> 16; |
| 551 | hdr[n++] = len >> 8; |
| 552 | hdr[n++] = len; |
| 553 | } |
| 554 | } |
| 555 | n = write(wsi->sock, hdr, n); |
| 556 | if (n < 0) { |
| 557 | fprintf(stderr, "ERROR writing to socket"); |
| 558 | return -1; |
| 559 | } |
| 560 | break; |
| 561 | } |
| 562 | |
| 563 | n = write(wsi->sock, buf, len); |
| 564 | if (n < 0) { |
| 565 | fprintf(stderr, "ERROR writing to socket"); |
| 566 | return -1; |
| 567 | } |
| 568 | |
| 569 | fprintf(stderr, "written %d bytes to websocket\n", (int)len); |
| 570 | |
| 571 | return 0; |
| 572 | } |
| 573 | |
| 574 | static void libwebsocket_service(struct libwebsocket *wsi, int sock) |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 575 | { |
| 576 | int n; |
| 577 | unsigned char buf[256]; |
| 578 | struct pollfd fds; |
| 579 | |
| 580 | wsi->sock = sock; |
| 581 | |
| 582 | fds.fd = sock; |
| 583 | fds.events = POLLIN | POLLOUT; |
| 584 | |
| 585 | while (1) { |
| 586 | |
| 587 | n = poll(&fds, 1, 10); |
| 588 | if (n < 0) { |
| 589 | fprintf(stderr, "Socket dead (poll = %d)\n", n); |
| 590 | return; |
| 591 | } |
| 592 | |
| 593 | if (fds.revents & (POLLERR | POLLHUP)) { |
| 594 | fprintf(stderr, "Socket dead\n"); |
| 595 | return; |
| 596 | } |
| 597 | |
| 598 | if (wsi->state == WSI_STATE_DEAD_SOCKET) |
| 599 | return; |
| 600 | |
| 601 | |
| 602 | if (fds.revents & POLLIN) { |
| 603 | |
| 604 | // fprintf(stderr, "POLLIN\n"); |
| 605 | |
| 606 | n = read(sock, buf, sizeof(buf)); |
| 607 | if (n < 0) { |
| 608 | fprintf(stderr, "Socket read returned %d\n", n); |
| 609 | continue; |
| 610 | } |
| 611 | if (n) |
| 612 | libwebsocket_read(wsi, buf, n); |
| 613 | } |
| 614 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 615 | if (wsi->state != WSI_STATE_ESTABLISHED) |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 616 | continue; |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 617 | |
Andy Green | 775c0dd | 2010-10-29 14:15:22 +0100 | [diff] [blame^] | 618 | if (wsi->callback) |
| 619 | wsi->callback(wsi, LWS_CALLBACK_SEND); |
Andy Green | ff95d7a | 2010-10-28 22:36:01 +0100 | [diff] [blame] | 620 | } |
| 621 | } |
| 622 | |