unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
websocket.c (20463B)
1 /* 2 * websocket - WebSocket support (RFC6455) 3 * (C)Copyright 2016 Bram Matthys and the UnrealIRCd team 4 * License: GPLv2 or later 5 * This module was sponsored by Aberrant Software Inc. 6 */ 7 8 #include "unrealircd.h" 9 #include "dns.h" 10 11 #define WEBSOCKET_VERSION "1.1.0" 12 13 ModuleHeader MOD_HEADER 14 = { 15 "websocket", 16 WEBSOCKET_VERSION, 17 "WebSocket support (RFC6455)", 18 "UnrealIRCd Team", 19 "unrealircd-6", 20 }; 21 22 #if CHAR_MIN < 0 23 #error "In UnrealIRCd char should always be unsigned. Check your compiler" 24 #endif 25 26 #ifndef WEBSOCKET_SEND_BUFFER_SIZE 27 #define WEBSOCKET_SEND_BUFFER_SIZE 16384 28 #endif 29 30 #define WSU(client) ((WebSocketUser *)moddata_client(client, websocket_md).ptr) 31 32 #define WEBSOCKET_PORT(client) ((client->local && client->local->listener) ? client->local->listener->websocket_options : 0) 33 #define WEBSOCKET_TYPE(client) (WSU(client)->type) 34 35 /* used to parse http Forwarded header (RFC 7239) */ 36 #define IPLEN 48 37 #define FHEADER_NAMELEN 20 38 39 struct HTTPForwardedHeader 40 { 41 int secure; 42 char hostname[HOSTLEN+1]; 43 char ip[IPLEN+1]; 44 }; 45 46 /* Forward declarations */ 47 int websocket_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); 48 int websocket_config_posttest(int *); 49 int websocket_config_run_ex(ConfigFile *cf, ConfigEntry *ce, int type, void *ptr); 50 int websocket_packet_out(Client *from, Client *to, Client *intended_to, char **msg, int *length); 51 int websocket_handle_handshake(Client *client, const char *readbuf, int *length); 52 int websocket_handshake_send_response(Client *client); 53 int websocket_handle_body_websocket(Client *client, WebRequest *web, const char *readbuf2, int length2); 54 int websocket_secure_connect(Client *client); 55 struct HTTPForwardedHeader *websocket_parse_forwarded_header(char *input); 56 int websocket_ip_compare(const char *ip1, const char *ip2); 57 int websocket_handle_request(Client *client, WebRequest *web); 58 59 /* Global variables */ 60 ModDataInfo *websocket_md; 61 static int ws_text_mode_available = 1; 62 63 MOD_TEST() 64 { 65 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, websocket_config_test); 66 HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, websocket_config_posttest); 67 68 /* Call MOD_INIT very early, since we manage sockets, but depend on websocket_common */ 69 ModuleSetOptions(modinfo->handle, MOD_OPT_PRIORITY, WEBSOCKET_MODULE_PRIORITY_INIT+1); 70 return MOD_SUCCESS; 71 } 72 73 MOD_INIT() 74 { 75 ModDataInfo mreq; 76 77 MARK_AS_OFFICIAL_MODULE(modinfo); 78 79 websocket_md = findmoddata_byname("websocket", MODDATATYPE_CLIENT); 80 if (!websocket_md) 81 config_warn("The 'websocket_common' module is not loaded, even though it was promised to be ???"); 82 83 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN_EX, 0, websocket_config_run_ex); 84 HookAdd(modinfo->handle, HOOKTYPE_PACKET, INT_MAX, websocket_packet_out); 85 HookAdd(modinfo->handle, HOOKTYPE_SECURE_CONNECT, 0, websocket_secure_connect); 86 87 /* Call MOD_LOAD very late, since we manage sockets, but depend on websocket_common */ 88 ModuleSetOptions(modinfo->handle, MOD_OPT_PRIORITY, WEBSOCKET_MODULE_PRIORITY_UNLOAD-1); 89 return MOD_SUCCESS; 90 } 91 92 MOD_LOAD() 93 { 94 if (non_utf8_nick_chars_in_use || (iConf.allowed_channelchars == ALLOWED_CHANNELCHARS_ANY)) 95 ws_text_mode_available = 0; 96 return MOD_SUCCESS; 97 } 98 99 MOD_UNLOAD() 100 { 101 return MOD_SUCCESS; 102 } 103 104 int websocket_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) 105 { 106 int errors = 0; 107 ConfigEntry *cep; 108 int has_type = 0; 109 static char errored_once_nick = 0; 110 111 if (type != CONFIG_LISTEN_OPTIONS) 112 return 0; 113 114 /* We are only interrested in listen::options::websocket.. */ 115 if (!ce || !ce->name || strcmp(ce->name, "websocket")) 116 return 0; 117 118 for (cep = ce->items; cep; cep = cep->next) 119 { 120 if (!strcmp(cep->name, "type")) 121 { 122 CheckNull(cep); 123 has_type = 1; 124 if (!strcmp(cep->value, "text")) 125 { 126 if (non_utf8_nick_chars_in_use && !errored_once_nick) 127 { 128 /* This one is a hard error, since the consequences are grave */ 129 config_error("You have a websocket listener with type 'text' AND your set::allowed-nickchars contains at least one non-UTF8 character set."); 130 config_error("This is a very BAD idea as this makes your websocket vulnerable to UTF8 conversion attacks. " 131 "This can cause things like unkickable users and 'ghosts' for websocket users."); 132 config_error("You have 4 options: 1) Remove the websocket listener, 2) Use websocket type 'binary', " 133 "3) Remove the non-UTF8 character set from set::allowed-nickchars, 4) Replace the non-UTF8 with an UTF8 character set in set::allowed-nickchars"); 134 config_error("For more details see https://www.unrealircd.org/docs/WebSocket_support#websockets-and-non-utf8"); 135 errored_once_nick = 1; 136 errors++; 137 } 138 } 139 else if (!strcmp(cep->value, "binary")) 140 { 141 } 142 else 143 { 144 config_error("%s:%i: listen::options::websocket::type must be either 'binary' or 'text' (not '%s')", 145 cep->file->filename, cep->line_number, cep->value); 146 errors++; 147 } 148 } else if (!strcmp(cep->name, "forward")) 149 { 150 if (!cep->value) 151 { 152 config_error_empty(cep->file->filename, cep->line_number, "listen::options::websocket::forward", cep->name); 153 errors++; 154 continue; 155 } 156 if (!is_valid_ip(cep->value)) 157 { 158 config_error("%s:%i: invalid IP address '%s' in listen::options::websocket::forward", cep->file->filename, cep->line_number, cep->value); 159 errors++; 160 continue; 161 } 162 } else 163 { 164 config_error("%s:%i: unknown directive listen::options::websocket::%s", 165 cep->file->filename, cep->line_number, cep->name); 166 errors++; 167 continue; 168 } 169 } 170 171 if (!has_type) 172 { 173 config_error("%s:%i: websocket set, but type unspecified. Use something like: listen { ip *; port 443; websocket { type text; } }", 174 ce->file->filename, ce->line_number); 175 errors++; 176 } 177 178 *errs = errors; 179 return errors ? -1 : 1; 180 } 181 182 int websocket_config_run_ex(ConfigFile *cf, ConfigEntry *ce, int type, void *ptr) 183 { 184 ConfigEntry *cep, *cepp; 185 ConfigItem_listen *l; 186 static char warned_once_channel = 0; 187 188 if (type != CONFIG_LISTEN_OPTIONS) 189 return 0; 190 191 /* We are only interrested in listen::options::websocket.. */ 192 if (!ce || !ce->name || strcmp(ce->name, "websocket")) 193 return 0; 194 195 l = (ConfigItem_listen *)ptr; 196 l->webserver = safe_alloc(sizeof(WebServer)); 197 l->webserver->handle_request = websocket_handle_request; 198 l->webserver->handle_body = websocket_handle_body_websocket; 199 200 for (cep = ce->items; cep; cep = cep->next) 201 { 202 if (!strcmp(cep->name, "type")) 203 { 204 if (!strcmp(cep->value, "binary")) 205 l->websocket_options = WEBSOCKET_TYPE_BINARY; 206 else if (!strcmp(cep->value, "text")) 207 { 208 l->websocket_options = WEBSOCKET_TYPE_TEXT; 209 if ((tempiConf.allowed_channelchars == ALLOWED_CHANNELCHARS_ANY) && !warned_once_channel) 210 { 211 /* This one is a warning, since the consequences are less grave than with nicks */ 212 config_warn("You have a websocket listener with type 'text' AND your set::allowed-channelchars is set to 'any'."); 213 config_warn("This is not a recommended combination as this makes your websocket vulnerable to UTF8 conversion attacks. " 214 "This can cause things like unpartable channels for websocket users."); 215 config_warn("It is highly recommended that you use set { allowed-channelchars utf8; }"); 216 config_warn("For more details see https://www.unrealircd.org/docs/WebSocket_support#websockets-and-non-utf8"); 217 warned_once_channel = 1; 218 } 219 } 220 } else if (!strcmp(cep->name, "forward")) 221 { 222 safe_strdup(l->websocket_forward, cep->value); 223 } 224 } 225 return 1; 226 } 227 228 int websocket_config_posttest(int *errs) 229 { 230 int errors = 0; 231 char webserver_module = 1, websocket_common_module = 1; 232 233 if (!is_module_loaded("webserver")) 234 { 235 config_error("The 'websocket' module requires the 'webserver' module to be loaded, otherwise websocket connections will not work!"); 236 webserver_module = 0; 237 errors++; 238 } 239 240 if (!is_module_loaded("websocket_common")) 241 { 242 config_error("The 'websocket' module requires the 'websocket_common' module to be loaded, otherwise websocket connections will not work!"); 243 websocket_common_module = 0; 244 errors++; 245 } 246 247 /* Is nicer for the admin when these are grouped... */ 248 if (!webserver_module) 249 config_error("Add the following line to your config file: loadmodule \"webserver\";"); 250 if (!websocket_common_module) 251 config_error("Add the following line to your config file: loadmodule \"websocket_common\";"); 252 253 *errs = errors; 254 return errors ? -1 : 1; 255 } 256 257 /* Add LF (if needed) to a buffer. Max 4K. */ 258 void add_lf_if_needed(char **buf, int *len) 259 { 260 static char newbuf[MAXLINELENGTH]; 261 char *b = *buf; 262 int l = *len; 263 264 if (l <= 0) 265 return; /* too short */ 266 267 if (b[l - 1] == '\n') 268 return; /* already contains \n */ 269 270 if (l >= sizeof(newbuf)-2) 271 l = sizeof(newbuf)-2; /* cut-off if necessary */ 272 273 memcpy(newbuf, b, l); 274 newbuf[l] = '\n'; 275 newbuf[l + 1] = '\0'; /* not necessary, but I like zero termination */ 276 l++; 277 *buf = newbuf; /* new buffer */ 278 *len = l; /* new length */ 279 } 280 281 /** Called on decoded websocket frame (INPUT). 282 * Should contain exactly 1 IRC line (command) 283 */ 284 int websocket_irc_callback(Client *client, char *buf, int len) 285 { 286 add_lf_if_needed(&buf, &len); 287 if (!process_packet(client, buf, len, 1)) /* Let UnrealIRCd handle this as usual */ 288 return 0; /* client killed */ 289 return 1; 290 } 291 292 int websocket_handle_body_websocket(Client *client, WebRequest *web, const char *readbuf2, int length2) 293 { 294 return websocket_handle_websocket(client, web, readbuf2, length2, websocket_irc_callback); 295 } 296 297 /** Outgoing packet hook. 298 * This transforms the output to be Websocket-compliant, if necessary. 299 */ 300 int websocket_packet_out(Client *from, Client *to, Client *intended_to, char **msg, int *length) 301 { 302 static char utf8buf[510]; 303 304 if (MyConnect(to) && !IsRPC(to) && websocket_md && WSU(to) && WSU(to)->handshake_completed) 305 { 306 if (WEBSOCKET_TYPE(to) == WEBSOCKET_TYPE_BINARY) 307 websocket_create_packet(WSOP_BINARY, msg, length); 308 else if (WEBSOCKET_TYPE(to) == WEBSOCKET_TYPE_TEXT) 309 { 310 /* Some more conversions are needed */ 311 char *safe_msg = unrl_utf8_make_valid(*msg, utf8buf, sizeof(utf8buf), 1); 312 *msg = safe_msg; 313 *length = *msg ? strlen(safe_msg) : 0; 314 websocket_create_packet(WSOP_TEXT, msg, length); 315 } 316 return 0; 317 } 318 return 0; 319 } 320 321 #define FHEADER_STATE_NAME 0 322 #define FHEADER_STATE_VALUE 1 323 #define FHEADER_STATE_VALUE_QUOTED 2 324 325 #define FHEADER_ACTION_APPEND 0 326 #define FHEADER_ACTION_IGNORE 1 327 #define FHEADER_ACTION_PROCESS 2 328 329 /** If a valid Forwarded: http header is received from a trusted source (proxy server), this function will 330 * extract remote IP address and secure (https) status from it. If more than one field with same name is received, 331 * we'll accept the last one. This should work correctly with chained proxies. */ 332 struct HTTPForwardedHeader *websocket_parse_forwarded_header(char *input) 333 { 334 static struct HTTPForwardedHeader forwarded; 335 int i, length; 336 int state = FHEADER_STATE_NAME, action = FHEADER_ACTION_APPEND; 337 char name[FHEADER_NAMELEN+1]; 338 char value[IPLEN+1]; 339 int name_length = 0; 340 int value_length = 0; 341 char c; 342 343 memset(&forwarded, 0, sizeof(struct HTTPForwardedHeader)); 344 345 length = strlen(input); 346 for (i = 0; i < length; i++) 347 { 348 c = input[i]; 349 switch (c) 350 { 351 case '"': 352 switch (state) 353 { 354 case FHEADER_STATE_NAME: 355 action = FHEADER_ACTION_APPEND; 356 break; 357 case FHEADER_STATE_VALUE: 358 action = FHEADER_ACTION_IGNORE; 359 state = FHEADER_STATE_VALUE_QUOTED; 360 break; 361 case FHEADER_STATE_VALUE_QUOTED: 362 action = FHEADER_ACTION_IGNORE; 363 state = FHEADER_STATE_VALUE; 364 break; 365 } 366 break; 367 case ',': case ';': case ' ': 368 switch (state) 369 { 370 case FHEADER_STATE_NAME: /* name without value */ 371 name_length = 0; 372 action = FHEADER_ACTION_IGNORE; 373 break; 374 case FHEADER_STATE_VALUE: /* end of value */ 375 action = FHEADER_ACTION_PROCESS; 376 break; 377 case FHEADER_STATE_VALUE_QUOTED: /* quoted character, process as normal */ 378 action = FHEADER_ACTION_APPEND; 379 break; 380 } 381 break; 382 case '=': 383 switch (state) 384 { 385 case FHEADER_STATE_NAME: /* end of name */ 386 name[name_length] = '\0'; 387 state = FHEADER_STATE_VALUE; 388 action = FHEADER_ACTION_IGNORE; 389 break; 390 case FHEADER_STATE_VALUE: case FHEADER_STATE_VALUE_QUOTED: /* none of the values is expected to contain = but proceed anyway */ 391 action = FHEADER_ACTION_APPEND; 392 break; 393 } 394 break; 395 default: 396 action = FHEADER_ACTION_APPEND; 397 break; 398 } 399 switch (action) 400 { 401 case FHEADER_ACTION_APPEND: 402 if (state == FHEADER_STATE_NAME) 403 { 404 if (name_length < FHEADER_NAMELEN) 405 { 406 name[name_length++] = c; 407 } else 408 { 409 /* truncate */ 410 } 411 } else 412 { 413 if (value_length < IPLEN) 414 { 415 value[value_length++] = c; 416 } else 417 { 418 /* truncate */ 419 } 420 } 421 break; 422 case FHEADER_ACTION_IGNORE: default: 423 break; 424 case FHEADER_ACTION_PROCESS: 425 value[value_length] = '\0'; 426 name[name_length] = '\0'; 427 if (!strcasecmp(name, "for")) 428 { 429 strlcpy(forwarded.ip, value, IPLEN+1); 430 } else if (!strcasecmp(name, "proto")) 431 { 432 if (!strcasecmp(value, "https")) 433 { 434 forwarded.secure = 1; 435 } else if (!strcasecmp(value, "http")) 436 { 437 forwarded.secure = 0; 438 } else 439 { 440 /* ignore unknown value */ 441 } 442 } else 443 { 444 /* ignore unknown field name */ 445 } 446 value_length = 0; 447 name_length = 0; 448 state = FHEADER_STATE_NAME; 449 break; 450 } 451 } 452 453 return &forwarded; 454 } 455 456 /** We got a HTTP(S) request and we need to check if we can upgrade the connection 457 * to a websocket connection. 458 */ 459 int websocket_handle_request(Client *client, WebRequest *web) 460 { 461 NameValuePrioList *r; 462 const char *key, *value; 463 464 /* Allocate a new WebSocketUser struct for this session */ 465 moddata_client(client, websocket_md).ptr = safe_alloc(sizeof(WebSocketUser)); 466 /* ...and set the default protocol (text or binary) */ 467 WSU(client)->type = client->local->listener->websocket_options; 468 469 /** Now step through the lines.. **/ 470 for (r = web->headers; r; r = r->next) 471 { 472 key = r->name; 473 value = r->value; 474 if (!strcasecmp(key, "Sec-WebSocket-Key")) 475 { 476 if (strchr(value, ':')) 477 { 478 /* This would cause unserialization issues. Should be base64 anyway */ 479 webserver_send_response(client, 400, "Invalid characters in Sec-WebSocket-Key"); 480 return -1; 481 } 482 safe_strdup(WSU(client)->handshake_key, value); 483 } else 484 if (!strcasecmp(key, "Sec-WebSocket-Protocol")) 485 { 486 /* Save it here, will be processed later */ 487 safe_strdup(WSU(client)->sec_websocket_protocol, value); 488 } else 489 if (!strcasecmp(key, "Forwarded")) 490 { 491 /* will be processed later too */ 492 safe_strdup(WSU(client)->forwarded, value); 493 } 494 } 495 496 /** Finally, validate the websocket request (handshake) and proceed or reject. */ 497 498 /* Not websocket and webredir loaded? Let that module serve a redirect. */ 499 if (!WSU(client)->handshake_key) 500 { 501 if (is_module_loaded("webredir")) 502 { 503 const char *parx[2] = { NULL, NULL }; 504 do_cmd(client, NULL, "GET", 1, parx); 505 } 506 webserver_send_response(client, 404, "This port is for IRC WebSocket only"); 507 return 0; 508 } 509 510 /* Sec-WebSocket-Protocol (optional) */ 511 if (WSU(client)->sec_websocket_protocol) 512 { 513 char *p = NULL, *name; 514 int negotiated = 0; 515 516 for (name = strtoken(&p, WSU(client)->sec_websocket_protocol, ","); 517 name; 518 name = strtoken(&p, NULL, ",")) 519 { 520 skip_whitespace(&name); 521 if (!strcmp(name, "binary.ircv3.net")) 522 { 523 negotiated = WEBSOCKET_TYPE_BINARY; 524 break; /* First hit wins */ 525 } else 526 if (!strcmp(name, "text.ircv3.net") && ws_text_mode_available) 527 { 528 negotiated = WEBSOCKET_TYPE_TEXT; 529 break; /* First hit wins */ 530 } 531 } 532 if (negotiated == WEBSOCKET_TYPE_BINARY) 533 { 534 WSU(client)->type = WEBSOCKET_TYPE_BINARY; 535 safe_strdup(WSU(client)->sec_websocket_protocol, "binary.ircv3.net"); 536 } else 537 if (negotiated == WEBSOCKET_TYPE_TEXT) 538 { 539 WSU(client)->type = WEBSOCKET_TYPE_TEXT; 540 safe_strdup(WSU(client)->sec_websocket_protocol, "text.ircv3.net"); 541 } else 542 { 543 /* Negotiation failed, fallback to the default (don't set it here) */ 544 safe_free(WSU(client)->sec_websocket_protocol); 545 } 546 } 547 548 /* Check forwarded header (by k4be) */ 549 if (WSU(client)->forwarded) 550 { 551 struct HTTPForwardedHeader *forwarded; 552 char oldip[64]; 553 554 /* check for source ip */ 555 if (BadPtr(client->local->listener->websocket_forward) || !websocket_ip_compare(client->local->listener->websocket_forward, client->ip)) 556 { 557 unreal_log(ULOG_WARNING, "websocket", "UNAUTHORIZED_FORWARDED_HEADER", client, "Received unauthorized Forwarded header from $ip", log_data_string("ip", client->ip)); 558 webserver_send_response(client, 403, "Forwarded: no access"); 559 return 0; 560 } 561 /* parse the header */ 562 forwarded = websocket_parse_forwarded_header(WSU(client)->forwarded); 563 /* check header values */ 564 if (!is_valid_ip(forwarded->ip)) 565 { 566 unreal_log(ULOG_WARNING, "websocket", "INVALID_FORWARDED_IP", client, "Received invalid IP in Forwarded header from $ip", log_data_string("ip", client->ip)); 567 webserver_send_response(client, 400, "Forwarded: invalid IP"); 568 return 0; 569 } 570 /* store data */ 571 WSU(client)->secure = forwarded->secure; 572 strlcpy(oldip, client->ip, sizeof(oldip)); 573 safe_strdup(client->ip, forwarded->ip); 574 /* Update client->local->hostp */ 575 strlcpy(client->local->sockhost, forwarded->ip, sizeof(client->local->sockhost)); /* in case dns lookup fails or is disabled */ 576 /* (free old) */ 577 if (client->local->hostp) 578 { 579 unreal_free_hostent(client->local->hostp); 580 client->local->hostp = NULL; 581 } 582 /* (create new) */ 583 if (!DONT_RESOLVE) 584 { 585 /* taken from socket.c */ 586 struct hostent *he; 587 unrealdns_delreq_bycptr(client); /* in case the proxy ip is still in progress of being looked up */ 588 ClearDNSLookup(client); 589 he = unrealdns_doclient(client); /* call this once more */ 590 if (!client->local->hostp) 591 { 592 if (he) 593 client->local->hostp = he; 594 else 595 SetDNSLookup(client); 596 } else 597 { 598 /* Race condition detected, DNS has been done, continue with auth */ 599 } 600 } 601 RunHook(HOOKTYPE_IP_CHANGE, client, oldip); 602 } 603 604 websocket_handshake_send_response(client); 605 return 1; 606 } 607 608 int websocket_secure_connect(Client *client) 609 { 610 /* Remove secure mode (-z) if the WEBIRC gateway did not ensure 611 * us that their [client]--[webirc gateway] connection is also 612 * secure (eg: using https) 613 */ 614 if (IsSecureConnect(client) && websocket_md && WSU(client) && WSU(client)->forwarded && !WSU(client)->secure) 615 client->umodes &= ~UMODE_SECURE; 616 return 0; 617 } 618 619 /** Complete the handshake by sending the appropriate HTTP 101 response etc. */ 620 int websocket_handshake_send_response(Client *client) 621 { 622 char buf[512], hashbuf[64]; 623 char sha1out[20]; /* 160 bits */ 624 625 WSU(client)->handshake_completed = 1; 626 627 snprintf(buf, sizeof(buf), "%s%s", WSU(client)->handshake_key, WEBSOCKET_MAGIC_KEY); 628 sha1hash_binary(sha1out, buf, strlen(buf)); 629 b64_encode(sha1out, sizeof(sha1out), hashbuf, sizeof(hashbuf)); 630 631 snprintf(buf, sizeof(buf), 632 "HTTP/1.1 101 Switching Protocols\r\n" 633 "Upgrade: websocket\r\n" 634 "Connection: Upgrade\r\n" 635 "Sec-WebSocket-Accept: %s\r\n", 636 hashbuf); 637 638 if (WSU(client)->sec_websocket_protocol) 639 { 640 /* using strlen() is safe here since above buffer will not 641 * cause it to be >=512 and thus we won't get into negatives. 642 */ 643 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), 644 "Sec-WebSocket-Protocol: %s\r\n", 645 WSU(client)->sec_websocket_protocol); 646 } 647 648 strlcat(buf, "\r\n", sizeof(buf)); 649 650 /* Caution: we bypass sendQ flood checking by doing it this way. 651 * Risk is minimal, though, as we only permit limited text only 652 * once per session. 653 */ 654 dbuf_put(&client->local->sendQ, buf, strlen(buf)); 655 send_queued(client); 656 657 return 0; 658 } 659 660 /** Compare IP addresses (for authorization checking) */ 661 int websocket_ip_compare(const char *ip1, const char *ip2) 662 { 663 uint32_t ip4[2]; 664 uint16_t ip6[16]; 665 int i; 666 if (inet_pton(AF_INET, ip1, &ip4[0]) == 1) /* IPv4 */ 667 { 668 if (inet_pton(AF_INET, ip2, &ip4[1]) == 1) /* both are valid, let's compare */ 669 { 670 return ip4[0] == ip4[1]; 671 } 672 return 0; 673 } 674 if (inet_pton(AF_INET6, ip1, &ip6[0]) == 1) /* IPv6 */ 675 { 676 if (inet_pton(AF_INET6, ip2, &ip6[8]) == 1) 677 { 678 for (i = 0; i < 8; i++) 679 { 680 if (ip6[i] != ip6[i+8]) 681 return 0; 682 } 683 return 1; 684 } 685 } 686 return 0; /* neither valid IPv4 nor IPv6 */ 687 } 688