unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
list.c (16125B)
1 /************************************************************************ 2 * Unreal Internet Relay Chat, src/list.c 3 * Copyright (C) 1990 Jarkko Oikarinen and 4 * University of Oulu, Finland 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 1, or (at your option) 9 * any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 21 #include "unrealircd.h" 22 23 void free_link(Link *); 24 Link *make_link(); 25 26 ID_Copyright 27 ("(C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen"); 28 ID_Notes("2.24 4/20/94"); 29 30 #ifdef DEBUGMODE 31 static struct liststats { 32 int inuse; 33 } cloc, crem, users, servs, links; 34 35 #endif 36 37 MODVAR int flinks = 0; 38 MODVAR int freelinks = 0; 39 MODVAR Link *freelink = NULL; 40 MODVAR Member *freemember = NULL; 41 MODVAR Membership *freemembership = NULL; 42 MODVAR int numclients = 0; 43 44 // TODO: Document whether servers are included or excluded in these lists... 45 46 MODVAR struct list_head unknown_list; /**< Local clients in handshake (may become a user or server later) */ 47 MODVAR struct list_head control_list; /**< Local "control channel" clients */ 48 MODVAR struct list_head lclient_list; /**< Local clients (users only, right?) */ 49 MODVAR struct list_head client_list; /**< All clients - local and remote (not in handshake) */ 50 MODVAR struct list_head server_list; /**< Locally connected servers */ 51 MODVAR struct list_head oper_list; /**< Locally connected IRC Operators */ 52 MODVAR struct list_head global_server_list; /**< All servers (local and remote) */ 53 MODVAR struct list_head dead_list; /**< All dead clients (local and remote) that will soon be freed in the main loop */ 54 MODVAR struct list_head rpc_remote_list; /**< All remote RPC clients (very specific use-case) */ 55 56 static mp_pool_t *client_pool = NULL; 57 static mp_pool_t *local_client_pool = NULL; 58 static mp_pool_t *user_pool = NULL; 59 static mp_pool_t *link_pool = NULL; 60 61 void initlists(void) 62 { 63 #ifdef DEBUGMODE 64 memset(&cloc, 0, sizeof(cloc)); 65 memset(&crem, 0, sizeof(crem)); 66 memset(&users, 0, sizeof(users)); 67 memset(&servs, 0, sizeof(servs)); 68 memset(&links, 0, sizeof(links)); 69 #endif 70 71 INIT_LIST_HEAD(&client_list); 72 INIT_LIST_HEAD(&lclient_list); 73 INIT_LIST_HEAD(&server_list); 74 INIT_LIST_HEAD(&oper_list); 75 INIT_LIST_HEAD(&unknown_list); 76 INIT_LIST_HEAD(&control_list); 77 INIT_LIST_HEAD(&global_server_list); 78 INIT_LIST_HEAD(&dead_list); 79 INIT_LIST_HEAD(&rpc_remote_list); 80 81 client_pool = mp_pool_new(sizeof(Client), 512 * 1024); 82 local_client_pool = mp_pool_new(sizeof(LocalClient), 512 * 1024); 83 user_pool = mp_pool_new(sizeof(User), 512 * 1024); 84 link_pool = mp_pool_new(sizeof(Link), 512 * 1024); 85 } 86 87 /* 88 ** Create a new Client structure and set it to initial state. 89 ** 90 ** from == NULL, create local client (a client connected 91 ** to a socket). 92 ** 93 ** from, create remote client (behind a socket 94 ** associated with the client defined by 95 ** 'from'). ('from' is a local client!!). 96 */ 97 Client *make_client(Client *from, Client *servr) 98 { 99 Client *client = mp_pool_get(client_pool); 100 memset(client, 0, sizeof(Client)); 101 102 #ifdef DEBUGMODE 103 if (!from) 104 cloc.inuse++; 105 else 106 crem.inuse++; 107 #endif 108 109 /* Note: all fields are already NULL/0, no need to set here */ 110 client->direction = from ? from : client; /* 'from' of local client is self! */ 111 client->uplink = servr; 112 client->status = CLIENT_STATUS_UNKNOWN; 113 114 INIT_LIST_HEAD(&client->client_node); 115 INIT_LIST_HEAD(&client->client_hash); 116 INIT_LIST_HEAD(&client->id_hash); 117 118 strlcpy(client->ident, "unknown", sizeof(client->ident)); 119 if (!from) 120 { 121 /* Local client */ 122 const char *id; 123 124 client->local = mp_pool_get(local_client_pool); 125 memset(client->local, 0, sizeof(LocalClient)); 126 127 INIT_LIST_HEAD(&client->lclient_node); 128 INIT_LIST_HEAD(&client->special_node); 129 130 client->local->fake_lag = client->local->last_msg_received = 131 client->lastnick = client->local->creationtime = 132 client->local->idle_since = TStime(); 133 client->local->class = NULL; 134 client->local->passwd = NULL; 135 client->local->sockhost[0] = '\0'; 136 client->local->authfd = -1; 137 client->local->fd = -1; 138 139 dbuf_queue_init(&client->local->recvQ); 140 dbuf_queue_init(&client->local->sendQ); 141 142 while (hash_find_id((id = uid_get()), NULL) != NULL) 143 ; 144 strlcpy(client->id, id, sizeof client->id); 145 add_to_id_hash_table(client->id, client); 146 } 147 return client; 148 } 149 150 /** Free the client->rpc struct. 151 * NOTE: if you want to fully free the entire client, call free_client() 152 */ 153 void free_client_rpc(Client *client) 154 { 155 safe_free(client->rpc->rpc_user); 156 safe_free(client->rpc->issuer); 157 if (client->rpc->rehash_request) 158 json_decref(client->rpc->rehash_request); 159 free_log_sources(client->rpc->log_sources); 160 safe_free(client->rpc); 161 } 162 163 void free_client(Client *client) 164 { 165 if (!list_empty(&client->client_node)) 166 list_del(&client->client_node); 167 168 if (MyConnect(client)) 169 { 170 if (!list_empty(&client->lclient_node)) 171 list_del(&client->lclient_node); 172 if (!list_empty(&client->special_node)) 173 list_del(&client->special_node); 174 175 RunHook(HOOKTYPE_FREE_CLIENT, client); 176 if (client->local) 177 { 178 if (client->local->listener) 179 { 180 if (client->local->listener && !IsOutgoing(client)) 181 { 182 ConfigItem_listen *listener = client->local->listener; 183 listener->clients--; 184 if (listener->flag.temporary && (listener->clients == 0)) 185 { 186 /* Call listen cleanup */ 187 listen_cleanup(); 188 } 189 } 190 } 191 safe_free(client->local->passwd); 192 safe_free(client->local->error_str); 193 if (client->local->hostp) 194 unreal_free_hostent(client->local->hostp); 195 196 mp_pool_release(client->local); 197 } 198 if (*client->id) 199 { 200 /* This is already del'd in exit_one_client, so we 201 * only have it here in case a shortcut was taken, 202 * such as from add_connection() to free_client(). 203 */ 204 del_from_id_hash_table(client->id, client); 205 } 206 } 207 208 if (client->rpc) 209 free_client_rpc(client); 210 211 safe_free(client->ip); 212 213 mp_pool_release(client); 214 } 215 216 /* 217 ** 'make_user' add's an User information block to a client 218 ** if it was not previously allocated. 219 */ 220 User *make_user(Client *client) 221 { 222 User *user; 223 224 user = client->user; 225 if (!user) 226 { 227 user = mp_pool_get(user_pool); 228 memset(user, 0, sizeof(User)); 229 230 #ifdef DEBUGMODE 231 users.inuse++; 232 #endif 233 strlcpy(user->account, "0", sizeof(user->account)); 234 if (client->ip) 235 { 236 /* initially set client->user->realhost to IP */ 237 strlcpy(user->realhost, client->ip, sizeof(user->realhost)); 238 } else { 239 *user->realhost = '\0'; 240 } 241 client->user = user; 242 /* These may change later (eg when using hostname instead of IP), 243 * but we now set it early. 244 */ 245 make_cloakedhost(client, client->user->realhost, client->user->cloakedhost, sizeof(client->user->cloakedhost)); 246 safe_strdup(client->user->virthost, client->user->cloakedhost); 247 } 248 return user; 249 } 250 251 Server *make_server(Client *client) 252 { 253 Server *serv = client->server; 254 255 if (!serv) 256 { 257 serv = safe_alloc(sizeof(Server)); 258 #ifdef DEBUGMODE 259 servs.inuse++; 260 #endif 261 *serv->by = '\0'; 262 serv->users = 0; 263 client->server = serv; 264 } 265 if (strlen(client->id) > 3) 266 { 267 /* Probably the auto-generated UID for a server that 268 * still uses the old protocol (without SID). 269 */ 270 del_from_id_hash_table(client->id, client); 271 *client->id = '\0'; 272 } 273 return client->server; 274 } 275 276 /* 277 ** free_user 278 ** Decrease user reference count by one and realease block, 279 ** if count reaches 0 280 */ 281 void free_user(Client *client) 282 { 283 RunHook(HOOKTYPE_FREE_USER, client); 284 safe_free(client->user->away); 285 if (client->user->swhois) 286 { 287 SWhois *s, *s_next; 288 for (s = client->user->swhois; s; s = s_next) 289 { 290 s_next = s->next; 291 safe_free(s->line); 292 safe_free(s->setby); 293 safe_free(s); 294 } 295 client->user->swhois = NULL; 296 } 297 safe_free(client->user->virthost); 298 safe_free(client->user->operlogin); 299 safe_free(client->user->snomask); 300 mp_pool_release(client->user); 301 #ifdef DEBUGMODE 302 users.inuse--; 303 #endif 304 client->user = NULL; 305 } 306 307 /* 308 * taken the code from ExitOneClient() for this and placed it here. 309 * - avalon 310 */ 311 void remove_client_from_list(Client *client) 312 { 313 list_del(&client->client_node); 314 if (MyConnect(client)) 315 { 316 if (!list_empty(&client->lclient_node)) 317 list_del(&client->lclient_node); 318 if (!list_empty(&client->special_node)) 319 list_del(&client->special_node); 320 } 321 if (IsServer(client)) 322 { 323 irccounts.servers--; 324 } 325 if (IsUser(client)) 326 { 327 if (IsInvisible(client)) 328 { 329 irccounts.invisible--; 330 } 331 if (IsOper(client) && !IsHideOper(client)) 332 { 333 irccounts.operators--; 334 VERIFY_OPERCOUNT(client, "rmvlist"); 335 } 336 irccounts.clients--; 337 if (client->uplink && client->uplink->server) 338 client->uplink->server->users--; 339 } 340 if (IsUnknown(client) || IsConnecting(client) || IsHandshake(client) 341 || IsTLSHandshake(client) 342 ) 343 irccounts.unknown--; 344 345 if (IsUser(client)) /* Only persons can have been added before */ 346 { 347 add_history(client, 0, WHOWAS_EVENT_QUIT); 348 off_history(client); /* Remove all pointers to client */ 349 } 350 351 if (client->user) 352 free_user(client); 353 if (client->server) 354 { 355 safe_free(client->server->features.usermodes); 356 safe_free(client->server->features.chanmodes[0]); 357 safe_free(client->server->features.chanmodes[1]); 358 safe_free(client->server->features.chanmodes[2]); 359 safe_free(client->server->features.chanmodes[3]); 360 safe_free(client->server->features.software); 361 safe_free(client->server->features.nickchars); 362 safe_free(client->server); 363 #ifdef DEBUGMODE 364 servs.inuse--; 365 #endif 366 } 367 #ifdef DEBUGMODE 368 if (client->local && client->local->fd == -2) 369 cloc.inuse--; 370 else 371 crem.inuse--; 372 #endif 373 if (!list_empty(&client->client_node)) 374 abort(); 375 if (!list_empty(&client->client_hash)) 376 abort(); 377 if (!list_empty(&client->id_hash)) 378 abort(); 379 numclients--; 380 /* Add to killed clients list */ 381 list_add(&client->client_node, &dead_list); 382 // THIS IS NOW DONE IN THE MAINLOOP --> free_client(client); 383 SetDead(client); 384 SetDeadSocket(client); 385 return; 386 } 387 388 /* 389 * although only a small routine, it appears in a number of places 390 * as a collection of a few lines...functions like this *should* be 391 * in this file, shouldnt they ? after all, this is list.c, isnt it ? 392 * -avalon 393 */ 394 void add_client_to_list(Client *client) 395 { 396 list_add(&client->client_node, &client_list); 397 } 398 399 /** Make a new link entry. 400 * @note When you no longer need it, call free_link() 401 * NEVER call free() or safe_free() on it. 402 */ 403 Link *make_link(void) 404 { 405 Link *l = mp_pool_get(link_pool); 406 memset(l, 0, sizeof(Link)); 407 #ifdef DEBUGMODE 408 links.inuse++; 409 #endif 410 return l; 411 } 412 413 /** Releases a link that was previously created with make_link() */ 414 void free_link(Link *lp) 415 { 416 mp_pool_release(lp); 417 418 #ifdef DEBUGMODE 419 links.inuse--; 420 #endif 421 } 422 423 /** Returns the length (entry count) of a +beI list */ 424 int link_list_length(Link *lp) 425 { 426 int count = 0; 427 428 for (; lp; lp = lp->next) 429 count++; 430 return count; 431 } 432 433 Ban *make_ban(void) 434 { 435 Ban *lp; 436 437 lp = safe_alloc(sizeof(Ban)); 438 #ifdef DEBUGMODE 439 links.inuse++; 440 #endif 441 return lp; 442 } 443 444 void free_ban(Ban *lp) 445 { 446 safe_free(lp); 447 #ifdef DEBUGMODE 448 links.inuse--; 449 #endif 450 } 451 452 void add_ListItem(ListStruct *item, ListStruct **list) 453 { 454 item->next = *list; 455 item->prev = NULL; 456 if (*list) 457 (*list)->prev = item; 458 *list = item; 459 } 460 461 /* (note that if you end up using this, you should probably 462 * use a circular linked list instead) 463 */ 464 void append_ListItem(ListStruct *item, ListStruct **list) 465 { 466 ListStruct *l; 467 468 if (!*list) 469 { 470 *list = item; 471 return; 472 } 473 474 for (l = *list; l->next; l = l->next); 475 l->next = item; 476 item->prev = l; 477 } 478 479 void del_ListItem(ListStruct *item, ListStruct **list) 480 { 481 if (!item) 482 return; 483 484 if (item->prev) 485 item->prev->next = item->next; 486 if (item->next) 487 item->next->prev = item->prev; 488 if (*list == item) 489 *list = item->next; /* new head */ 490 /* And update 'item', prev/next should point nowhere anymore */ 491 item->prev = item->next = NULL; 492 } 493 494 /** Add item to list with a 'priority'. 495 * If there are multiple items with the same priority then it will be 496 * added as the last item within. 497 */ 498 void add_ListItemPrio(ListStructPrio *new, ListStructPrio **list, int priority) 499 { 500 ListStructPrio *x, *last = NULL; 501 502 if (!*list) 503 { 504 /* We are the only item. Easy. */ 505 *list = new; 506 return; 507 } 508 509 for (x = *list; x; x = x->next) 510 { 511 last = x; 512 if (x->priority >= priority) 513 break; 514 } 515 516 if (x) 517 { 518 if (x->prev) 519 { 520 /* We will insert ourselves just before this item */ 521 new->prev = x->prev; 522 new->next = x; 523 x->prev->next = new; 524 x->prev = new; 525 } else { 526 /* We are the new head */ 527 *list = new; 528 new->next = x; 529 x->prev = new; 530 } 531 } else 532 { 533 /* We are the last item */ 534 last->next = new; 535 new->prev = last; 536 } 537 } 538 539 /* NameList functions */ 540 541 void _add_name_list(NameList **list, const char *name) 542 { 543 NameList *e = safe_alloc(sizeof(NameList)+strlen(name)); 544 strcpy(e->name, name); /* safe, allocated above */ 545 AddListItem(e, *list); 546 } 547 548 void _free_entire_name_list(NameList *n) 549 { 550 NameList *n_next; 551 552 for (; n; n = n_next) 553 { 554 n_next = n->next; 555 safe_free(n); 556 } 557 } 558 559 void _del_name_list(NameList **list, const char *name) 560 { 561 NameList *e = find_name_list(*list, name); 562 if (e) 563 { 564 DelListItem(e, *list); 565 safe_free(e); 566 return; 567 } 568 } 569 570 /** Find an entry in a NameList - case insensitive comparisson. 571 * @ingroup ListFunctions 572 */ 573 NameList *find_name_list(NameList *list, const char *name) 574 { 575 NameList *e; 576 577 for (e = list; e; e = e->next) 578 { 579 if (!strcasecmp(e->name, name)) 580 { 581 return e; 582 } 583 } 584 return NULL; 585 } 586 587 /** Find an entry in a NameList by running match_simple() on it. 588 * @ingroup ListFunctions 589 */ 590 NameList *find_name_list_match(NameList *list, const char *name) 591 { 592 NameList *e; 593 594 for (e = list; e; e = e->next) 595 { 596 if (match_simple(e->name, name)) 597 { 598 return e; 599 } 600 } 601 return NULL; 602 } 603 604 void add_nvplist(NameValuePrioList **lst, int priority, const char *name, const char *value) 605 { 606 va_list vl; 607 NameValuePrioList *e = safe_alloc(sizeof(NameValuePrioList)); 608 safe_strdup(e->name, name); 609 if (value && *value) 610 safe_strdup(e->value, value); 611 AddListItemPrio(e, *lst, priority); 612 } 613 614 NameValuePrioList *find_nvplist(NameValuePrioList *list, const char *name) 615 { 616 NameValuePrioList *e; 617 618 for (e = list; e; e = e->next) 619 { 620 if (!strcasecmp(e->name, name)) 621 { 622 return e; 623 } 624 } 625 return NULL; 626 } 627 628 const char *get_nvplist(NameValuePrioList *list, const char *name) 629 { 630 NameValuePrioList *e = find_nvplist(list, name); 631 return e ? e->value : NULL; 632 } 633 634 void add_fmt_nvplist(NameValuePrioList **lst, int priority, const char *name, FORMAT_STRING(const char *format), ...) 635 { 636 char value[512]; 637 va_list vl; 638 *value = '\0'; 639 if (format) 640 { 641 va_start(vl, format); 642 vsnprintf(value, sizeof(value), format, vl); 643 va_end(vl); 644 } 645 add_nvplist(lst, priority, name, value); 646 } 647 648 void free_nvplist(NameValuePrioList *lst) 649 { 650 NameValuePrioList *e, *e_next; 651 for (e = lst; e; e = e_next) 652 { 653 e_next = e->next; 654 safe_free(e->name); 655 safe_free(e->value); 656 safe_free(e); 657 } 658 } 659 660 #define nv_find_by_name(stru, name) do_nv_find_by_name(stru, name, ARRAY_SIZEOF((stru))) 661 662 long do_nv_find_by_name(NameValue *table, const char *cmd, int numelements) 663 { 664 int start = 0; 665 int stop = numelements-1; 666 int mid; 667 while (start <= stop) { 668 mid = (start+stop)/2; 669 670 if (smycmp(cmd,table[mid].name) < 0) { 671 stop = mid-1; 672 } 673 else if (strcmp(cmd,table[mid].name) == 0) { 674 return table[mid].value; 675 } 676 else 677 start = mid+1; 678 } 679 return 0; 680 } 681 682 #define nv_find_by_value(stru, value) do_nv_find_by_value(stru, value, ARRAY_SIZEOF((stru))) 683 const char *do_nv_find_by_value(NameValue *table, long value, int numelements) 684 { 685 int i; 686 687 for (i=0; i < numelements; i++) 688 if (table[i].value == value) 689 return table[i].name; 690 691 return NULL; 692 }