unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
websocket_common.c (14403B)
1 /* 2 * websocket_common - Common WebSocket functions (RFC6455) 3 * (C)Copyright 2016 Bram Matthys and the UnrealIRCd team 4 * License: GPLv2 or later 5 * The websocket module was sponsored by Aberrant Software Inc. 6 */ 7 8 #include "unrealircd.h" 9 10 ModuleHeader MOD_HEADER 11 = { 12 "websocket_common", 13 "6.0.0", 14 "WebSocket support (RFC6455)", 15 "UnrealIRCd Team", 16 "unrealircd-6", 17 }; 18 19 #if CHAR_MIN < 0 20 #error "In UnrealIRCd char should always be unsigned. Check your compiler" 21 #endif 22 23 #ifndef WEBSOCKET_SEND_BUFFER_SIZE 24 #define WEBSOCKET_SEND_BUFFER_SIZE 16384 25 #endif 26 27 #define WSU(client) ((WebSocketUser *)moddata_client(client, websocket_md).ptr) 28 29 /* used to parse http Forwarded header (RFC 7239) */ 30 #define IPLEN 48 31 #define FHEADER_NAMELEN 20 32 33 struct HTTPForwardedHeader 34 { 35 int secure; 36 char hostname[HOSTLEN+1]; 37 char ip[IPLEN+1]; 38 }; 39 40 /* Forward declarations - public functions */ 41 int _websocket_handle_websocket(Client *client, WebRequest *web, const char *readbuf2, int length2, int callback(Client *client, char *buf, int len)); 42 int _websocket_create_packet(int opcode, char **buf, int *len); 43 int _websocket_create_packet_ex(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize); 44 int _websocket_create_packet_simple(int opcode, const char **buf, int *len); 45 /* Forward declarations - other */ 46 int websocket_handle_packet(Client *client, const char *readbuf, int length, int callback(Client *client, char *buf, int len)); 47 int websocket_handle_packet_ping(Client *client, const char *buf, int len); 48 int websocket_handle_packet_pong(Client *client, const char *buf, int len); 49 int websocket_send_pong(Client *client, const char *buf, int len); 50 const char *websocket_mdata_serialize(ModData *m); 51 void websocket_mdata_unserialize(const char *str, ModData *m); 52 void websocket_mdata_free(ModData *m); 53 54 /* Global variables */ 55 ModDataInfo *websocket_md; 56 static int ws_text_mode_available = 1; 57 58 MOD_TEST() 59 { 60 MARK_AS_OFFICIAL_MODULE(modinfo); 61 EfunctionAdd(modinfo->handle, EFUNC_WEBSOCKET_HANDLE_WEBSOCKET, _websocket_handle_websocket); 62 EfunctionAdd(modinfo->handle, EFUNC_WEBSOCKET_CREATE_PACKET, _websocket_create_packet); 63 EfunctionAdd(modinfo->handle, EFUNC_WEBSOCKET_CREATE_PACKET_EX, _websocket_create_packet_ex); 64 EfunctionAdd(modinfo->handle, EFUNC_WEBSOCKET_CREATE_PACKET_SIMPLE, _websocket_create_packet_simple); 65 66 /* Init first, since we manage sockets */ 67 ModuleSetOptions(modinfo->handle, MOD_OPT_PRIORITY, WEBSOCKET_MODULE_PRIORITY_INIT); 68 69 return MOD_SUCCESS; 70 } 71 72 MOD_INIT() 73 { 74 ModDataInfo mreq; 75 76 MARK_AS_OFFICIAL_MODULE(modinfo); 77 78 memset(&mreq, 0, sizeof(mreq)); 79 mreq.name = "websocket"; 80 mreq.serialize = websocket_mdata_serialize; 81 mreq.unserialize = websocket_mdata_unserialize; 82 mreq.free = websocket_mdata_free; 83 mreq.sync = MODDATA_SYNC_EARLY; 84 mreq.type = MODDATATYPE_CLIENT; 85 websocket_md = ModDataAdd(modinfo->handle, mreq); 86 87 /* Unload last, since we manage sockets */ 88 ModuleSetOptions(modinfo->handle, MOD_OPT_PRIORITY, WEBSOCKET_MODULE_PRIORITY_UNLOAD); 89 90 return MOD_SUCCESS; 91 } 92 93 MOD_LOAD() 94 { 95 return MOD_SUCCESS; 96 } 97 98 MOD_UNLOAD() 99 { 100 return MOD_SUCCESS; 101 } 102 103 int _websocket_handle_websocket(Client *client, WebRequest *web, const char *readbuf2, int length2, int callback(Client *client, char *buf, int len)) 104 { 105 int n; 106 char *ptr; 107 int length; 108 int length1 = WSU(client)->lefttoparselen; 109 char readbuf[MAXLINELENGTH]; 110 111 length = length1 + length2; 112 if (length > sizeof(readbuf)-1) 113 { 114 dead_socket(client, "Illegal buffer stacking/Excess flood"); 115 return 0; 116 } 117 118 if (length1 > 0) 119 memcpy(readbuf, WSU(client)->lefttoparse, length1); 120 memcpy(readbuf+length1, readbuf2, length2); 121 122 safe_free(WSU(client)->lefttoparse); 123 WSU(client)->lefttoparselen = 0; 124 125 ptr = readbuf; 126 do { 127 n = websocket_handle_packet(client, ptr, length, callback); 128 if (n < 0) 129 return -1; /* killed -- STOP processing */ 130 if (n == 0) 131 { 132 /* Short read. Stop processing for now, but save data for next time */ 133 safe_free(WSU(client)->lefttoparse); 134 WSU(client)->lefttoparse = safe_alloc(length); 135 WSU(client)->lefttoparselen = length; 136 memcpy(WSU(client)->lefttoparse, ptr, length); 137 return 0; 138 } 139 length -= n; 140 ptr += n; 141 if (length < 0) 142 abort(); /* less than 0 is impossible */ 143 } while(length > 0); 144 145 return 0; 146 } 147 148 /** WebSocket packet handler. 149 * For more information on the format, check out page 28 of RFC6455. 150 * @returns The number of bytes processed (the size of the frame) 151 * OR 0 to indicate a possible short read (want more data) 152 * OR -1 in case of an error. 153 */ 154 int websocket_handle_packet(Client *client, const char *readbuf, int length, int callback(Client *client, char *buf, int len)) 155 { 156 char opcode; /**< Opcode */ 157 char masked; /**< Masked */ 158 int len; /**< Length of the packet */ 159 char maskkey[4]; /**< Key used for masking */ 160 const char *p; 161 int total_packet_size; 162 char *payload = NULL; 163 static char payloadbuf[READBUF_SIZE]; 164 int maskkeylen = 4; 165 166 if (length < 4) 167 { 168 /* WebSocket packet too short */ 169 return 0; 170 } 171 172 /* fin = readbuf[0] & 0x80; -- unused */ 173 opcode = readbuf[0] & 0x7F; 174 masked = readbuf[1] & 0x80; 175 len = readbuf[1] & 0x7F; 176 p = &readbuf[2]; /* point to next element */ 177 178 /* actually 'fin' is unused.. we don't care. */ 179 180 /* Masked. According to RFC6455 page 29: 181 * "All frames sent from client to server have this bit set to 1." 182 * But in practice i see that for PONG this may not always be 183 * true, so let's make an exception for that... 184 */ 185 if (!masked && (opcode != WSOP_PONG)) 186 { 187 dead_socket(client, "WebSocket packet not masked"); 188 return -1; /* Having the masked bit set is required (RFC6455 p29) */ 189 } 190 191 if (!masked) 192 maskkeylen = 0; 193 194 if (len == 127) 195 { 196 dead_socket(client, "WebSocket packet with insane size"); 197 return -1; /* Packets requiring 64bit lengths are not supported. Would be insane. */ 198 } 199 200 total_packet_size = len + 2 + maskkeylen; /* 2 for header, 4 for mask key, rest for payload */ 201 202 /* Early (minimal) length check */ 203 if (length < total_packet_size) 204 { 205 /* WebSocket frame too short */ 206 return 0; 207 } 208 209 /* Len=126 is special. It indicates the data length is actually "126 or more" */ 210 if (len == 126) 211 { 212 /* Extended payload length (16 bit). For packets of >=126 bytes */ 213 len = (readbuf[2] << 8) + readbuf[3]; 214 if (len < 126) 215 { 216 dead_socket(client, "WebSocket protocol violation (extended payload length too short)"); 217 return -1; /* This is a violation (not a short read), see page 29 */ 218 } 219 p += 2; /* advance pointer 2 bytes */ 220 221 /* Need to check the length again, now it has changed: */ 222 if (length < len + 4 + maskkeylen) 223 { 224 /* WebSocket frame too short */ 225 return 0; 226 } 227 /* And update the packet size */ 228 total_packet_size = len + 4 + maskkeylen; /* 4 for header, 4 for mask key, rest for payload */ 229 } 230 231 if (masked) 232 { 233 memcpy(maskkey, p, maskkeylen); 234 p+= maskkeylen; 235 } 236 237 if (len > 0) 238 { 239 memcpy(payloadbuf, p, len); 240 payload = payloadbuf; 241 } /* else payload is NULL */ 242 243 if (masked && (len > 0)) 244 { 245 /* Unmask this thing (page 33, section 5.3) */ 246 int n; 247 char v; 248 char *p; 249 for (p = payload, n = 0; n < len; n++) 250 { 251 v = *p; 252 *p++ = v ^ maskkey[n % 4]; 253 } 254 } 255 256 switch(opcode) 257 { 258 case WSOP_CONTINUATION: 259 case WSOP_TEXT: 260 case WSOP_BINARY: 261 if (len > 0) 262 { 263 if (!callback(client, payload, len)) 264 return -1; /* fatal error occured (such as flood kill) */ 265 } 266 return total_packet_size; 267 268 case WSOP_CLOSE: 269 dead_socket(client, "Connection closed"); /* TODO: Improve I guess */ 270 return -1; 271 272 case WSOP_PING: 273 if (websocket_handle_packet_ping(client, payload, len) < 0) 274 return -1; 275 return total_packet_size; 276 277 case WSOP_PONG: 278 if (websocket_handle_packet_pong(client, payload, len) < 0) 279 return -1; 280 return total_packet_size; 281 282 default: 283 dead_socket(client, "WebSocket: Unknown opcode"); 284 return -1; 285 } 286 287 return -1; /* NOTREACHED */ 288 } 289 290 int websocket_handle_packet_ping(Client *client, const char *buf, int len) 291 { 292 if (len > 500) 293 { 294 dead_socket(client, "WebSocket: oversized PING request"); 295 return -1; 296 } 297 websocket_send_pong(client, buf, len); 298 add_fake_lag(client, 1000); /* lag penalty of 1 second */ 299 return 0; 300 } 301 302 int websocket_handle_packet_pong(Client *client, const char *buf, int len) 303 { 304 /* We only care about pongs for RPC websocket connections. 305 * Also, we don't verify the content, actually, 306 * so don't use this for security like a pingpong cookie. 307 */ 308 if (IsRPC(client)) 309 { 310 client->local->last_msg_received = TStime(); 311 ClearPingSent(client); 312 } 313 return 0; 314 } 315 316 /** Create a simple websocket packet that is ready to be sent. 317 * This is the simple version that is used ONLY for WSOP_PONG, 318 * as it does not take \r\n into account. 319 */ 320 int _websocket_create_packet_simple(int opcode, const char **buf, int *len) 321 { 322 static char sendbuf[8192]; 323 324 sendbuf[0] = opcode | 0x80; /* opcode & final */ 325 326 if (*len > sizeof(sendbuf) - 8) 327 return -1; /* should never happen (safety) */ 328 329 if (*len < 126) 330 { 331 /* Short payload */ 332 sendbuf[1] = (char)*len; 333 memcpy(&sendbuf[2], *buf, *len); 334 *buf = sendbuf; 335 *len += 2; 336 } else { 337 /* Long payload */ 338 sendbuf[1] = 126; 339 sendbuf[2] = (char)((*len >> 8) & 0xFF); 340 sendbuf[3] = (char)(*len & 0xFF); 341 memcpy(&sendbuf[4], *buf, *len); 342 *buf = sendbuf; 343 *len += 4; 344 } 345 return 0; 346 } 347 348 /** Create a websocket packet that is ready to be send. 349 * This version takes into account stripping off \r and \n, 350 * and possibly multi line due to labeled-response. 351 * It is used for WSOP_TEXT and WSOP_BINARY. 352 * The end result is one or more websocket frames, 353 * all in a single packet *buf with size *len. 354 * 355 * This is the version that uses the specified buffer, 356 * it is used from the JSON-RPC code, 357 * and indirectly from websocket_create_packet(). 358 */ 359 int _websocket_create_packet_ex(int opcode, char **buf, int *len, char *sendbuf, size_t sendbufsize) 360 { 361 char *s = *buf; /* points to start of current line */ 362 char *s2; /* used for searching of end of current line */ 363 char *lastbyte = *buf + *len - 1; /* points to last byte in *buf that can be safely read */ 364 int bytes_to_copy; 365 char newline; 366 char *o = sendbuf; /* points to current byte within 'sendbuf' of output buffer */ 367 int bytes_in_sendbuf = 0; 368 int bytes_single_frame; 369 370 /* Sending 0 bytes makes no sense, and the code below may assume >0, so reject this. */ 371 if (*len == 0) 372 return -1; 373 374 do { 375 /* Find next \r or \n */ 376 for (s2 = s; *s2 && (s2 <= lastbyte); s2++) 377 { 378 if ((*s2 == '\n') || (*s2 == '\r')) 379 break; 380 } 381 382 /* Now 's' points to start of line and 's2' points to beyond end of the line 383 * (either at \r, \n or beyond the buffer). 384 */ 385 bytes_to_copy = s2 - s; 386 387 if (bytes_to_copy < 126) 388 bytes_single_frame = 2 + bytes_to_copy; 389 else if (bytes_to_copy < 65536) 390 bytes_single_frame = 4 + bytes_to_copy; 391 else 392 bytes_single_frame = 10 + bytes_to_copy; 393 394 if (bytes_in_sendbuf + bytes_single_frame > sendbufsize) 395 { 396 /* Overflow. This should never happen. */ 397 unreal_log(ULOG_WARNING, "websocket", "BUG_WEBSOCKET_OVERFLOW", NULL, 398 "[BUG] [websocket] Overflow prevented in _websocket_create_packet(): " 399 "$bytes_in_sendbuf + $bytes_single_frame > $sendbuf_size", 400 log_data_integer("bytes_in_sendbuf", bytes_in_sendbuf), 401 log_data_integer("bytes_single_frame", bytes_single_frame), 402 log_data_integer("sendbuf_size", sendbufsize)); 403 return -1; 404 } 405 406 /* Create the new frame */ 407 o[0] = opcode | 0x80; /* opcode & final */ 408 409 if (bytes_to_copy < 126) 410 { 411 /* Short payload */ 412 o[1] = (char)bytes_to_copy; 413 memcpy(&o[2], s, bytes_to_copy); 414 } else 415 if (bytes_to_copy < 65536) 416 { 417 /* Long payload */ 418 o[1] = 126; 419 o[2] = (char)((bytes_to_copy >> 8) & 0xFF); 420 o[3] = (char)(bytes_to_copy & 0xFF); 421 memcpy(&o[4], s, bytes_to_copy); 422 } else { 423 /* Longest payload */ 424 // XXX: yeah we don't support sending more than 4GB. 425 o[1] = 127; 426 o[2] = 0; 427 o[3] = 0; 428 o[4] = 0; 429 o[5] = 0; 430 o[6] = (char)((bytes_to_copy >> 24) & 0xFF); 431 o[7] = (char)((bytes_to_copy >> 16) & 0xFF); 432 o[8] = (char)((bytes_to_copy >> 8) & 0xFF); 433 o[9] = (char)(bytes_to_copy & 0xFF); 434 memcpy(&o[10], s, bytes_to_copy); 435 } 436 437 /* Advance destination pointer and counter */ 438 o += bytes_single_frame; 439 bytes_in_sendbuf += bytes_single_frame; 440 441 /* Advance source pointer and skip all trailing \n and \r */ 442 for (s = s2; *s && (s <= lastbyte) && ((*s == '\n') || (*s == '\r')); s++); 443 } while(s <= lastbyte); 444 445 *buf = sendbuf; 446 *len = bytes_in_sendbuf; 447 return 0; 448 } 449 450 /** Create a websocket packet that is ready to be send. 451 * This version takes into account stripping off \r and \n, 452 * and possibly multi line due to labeled-response. 453 * It is used for WSOP_TEXT and WSOP_BINARY. 454 * The end result is one or more websocket frames, 455 * all in a single packet *buf with size *len. 456 * 457 * This is the version that uses a static sendbuf buffer, 458 * it is used from IRC websockets. 459 */ 460 int _websocket_create_packet(int opcode, char **buf, int *len) 461 { 462 static char sendbuf[WEBSOCKET_SEND_BUFFER_SIZE]; 463 return _websocket_create_packet_ex(opcode, buf, len, sendbuf, sizeof(sendbuf)); 464 } 465 466 /** Create and send a WSOP_PONG frame */ 467 int websocket_send_pong(Client *client, const char *buf, int len) 468 { 469 const char *b = buf; 470 int l = len; 471 472 if (_websocket_create_packet_simple(WSOP_PONG, &b, &l) < 0) 473 return -1; 474 475 if (DBufLength(&client->local->sendQ) > get_sendq(client)) 476 { 477 dead_socket(client, "Max SendQ exceeded"); 478 return -1; 479 } 480 481 dbuf_put(&client->local->sendQ, b, l); 482 send_queued(client); 483 return 0; 484 } 485 486 /** UnrealIRCd internals: free WebSocketUser object. */ 487 void websocket_mdata_free(ModData *m) 488 { 489 WebSocketUser *wsu = (WebSocketUser *)m->ptr; 490 if (wsu) 491 { 492 safe_free(wsu->handshake_key); 493 safe_free(wsu->lefttoparse); 494 safe_free(wsu->sec_websocket_protocol); 495 safe_free(wsu->forwarded); 496 safe_free(m->ptr); 497 } 498 } 499 500 /** This only serializes wsu->type atm */ 501 const char *websocket_mdata_serialize(ModData *m) 502 { 503 static char buf[32]; 504 WebSocketUser *wsu = m->ptr; 505 506 if (!wsu) 507 return NULL; /* not set */ 508 509 snprintf(buf, sizeof(buf), "%d", wsu->type); 510 return buf; 511 } 512 513 /** This only sets wsu->type atm */ 514 void websocket_mdata_unserialize(const char *str, ModData *m) 515 { 516 WebSocketUser *wsu; 517 if (m->ptr) 518 websocket_mdata_free(m); 519 if (BadPtr(str)) 520 return; /* empty/freed */ 521 m->ptr = wsu = safe_alloc(sizeof(WebSocketUser)); 522 wsu->type = atoi(str); 523 }