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 }