Rewrite DNS lookup API to remove a memory leak and keep it from coming back.
[ircu2.10.12-pk.git] / ircd / list.c
1 /*
2  * IRC - Internet Relay Chat, ircd/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 /** @file
21  * @brief Singly and doubly linked list manipulation implementation.
22  * @version $Id$
23  */
24 #include "config.h"
25
26 #include "list.h"
27 #include "client.h"
28 #include "ircd.h"
29 #include "ircd_alloc.h"
30 #include "ircd_events.h"
31 #include "ircd_log.h"
32 #include "ircd_reply.h"
33 #include "ircd_string.h"
34 #include "listener.h"
35 #include "match.h"
36 #include "numeric.h"
37 #include "res.h"
38 #include "s_auth.h"
39 #include "s_bsd.h"
40 #include "s_conf.h"
41 #include "s_debug.h"
42 #include "s_misc.h"
43 #include "s_user.h"
44 #include "send.h"
45 #include "struct.h"
46 #include "whowas.h"
47
48 /* #include <assert.h> -- Now using assert in ircd_log.h */
49 #include <stddef.h>  /* offsetof */
50 #include <unistd.h>  /* close */
51 #include <string.h>
52
53 /** Stores linked list statistics for various types of lists. */
54 static struct liststats {
55   size_t alloc; /**< Number of structures ever allocated. */
56   size_t inuse; /**< Number of structures currently in use. */
57   size_t mem;   /**< Memory used by in-use structures. */
58 } clients, connections, servs, links;
59
60 /** Linked list of currently unused Client structures. */
61 static struct Client* clientFreeList;
62
63 /** Linked list of currently unused Connection structures. */
64 static struct Connection* connectionFreeList;
65
66 /** Linked list of currently unused SLink structures. */
67 static struct SLink* slinkFreeList;
68
69 /** Initialize the list manipulation support system.
70  * Pre-allocate MAXCONNECTIONS Client and Connection structures.
71  */
72 void init_list(void)
73 {
74   struct Client* cptr;
75   struct Connection* con;
76   int i;
77   /*
78    * pre-allocate MAXCONNECTIONS clients and connections
79    */
80   for (i = 0; i < MAXCONNECTIONS; ++i) {
81     cptr = (struct Client*) MyMalloc(sizeof(struct Client));
82     cli_next(cptr) = clientFreeList;
83     clientFreeList = cptr;
84     clients.alloc++;
85
86     con = (struct Connection*) MyMalloc(sizeof(struct Connection));
87     con_next(con) = connectionFreeList;
88     connectionFreeList = con;
89     connections.alloc++;
90   }
91 }
92
93 /** Allocate a new Client structure.
94  * If #clientFreeList != NULL, use the head of that list.
95  * Otherwise, allocate a new structure.
96  * @return Newly allocated Client.
97  */
98 static struct Client* alloc_client(void)
99 {
100   struct Client* cptr = clientFreeList;
101
102   if (!cptr) {
103     cptr = (struct Client*) MyMalloc(sizeof(struct Client));
104     clients.alloc++;
105   } else
106     clientFreeList = cli_next(cptr);
107
108   clients.inuse++;
109
110   memset(cptr, 0, sizeof(struct Client));
111
112   return cptr;
113 }
114
115 /** Release a Client structure by prepending it to #clientFreeList.
116  * @param[in] cptr Client that is no longer being used.
117  */
118 static void dealloc_client(struct Client* cptr)
119 {
120   assert(cli_verify(cptr));
121   assert(0 == cli_connect(cptr));
122
123   --clients.inuse;
124
125   cli_next(cptr) = clientFreeList;
126   clientFreeList = cptr;
127
128   cli_magic(cptr) = 0;
129 }
130
131 /** Allocate a new Connection structure.
132  * If #connectionFreeList != NULL, use the head of that list.
133  * Otherwise, allocate a new structure.
134  * @return Newly allocated Connection.
135  */
136 static struct Connection* alloc_connection(void)
137 {
138   struct Connection* con = connectionFreeList;
139
140   if (!con) {
141     con = (struct Connection*) MyMalloc(sizeof(struct Connection));
142     connections.alloc++;
143   } else
144     connectionFreeList = con_next(con);
145
146   connections.inuse++;
147
148   memset(con, 0, sizeof(struct Connection));
149   timer_init(&(con_proc(con)));
150
151   return con;
152 }
153
154 /** Release a Connection and all memory associated with it.
155  * The connection's DNS reply field is freed, its file descriptor is
156  * closed, its msgq and sendq are cleared, and its associated Listener
157  * is dereferenced.  Then it is prepended to #connectionFreeList.
158  * @param[in] con Connection to free.
159  */
160 static void dealloc_connection(struct Connection* con)
161 {
162   assert(con_verify(con));
163   assert(!t_active(&(con_proc(con))));
164   assert(!t_onqueue(&(con_proc(con))));
165
166   Debug((DEBUG_LIST, "Deallocating connection %p", con));
167
168   if (-1 < con_fd(con))
169     close(con_fd(con));
170   MsgQClear(&(con_sendQ(con)));
171   client_drop_sendq(con);
172   DBufClear(&(con_recvQ(con)));
173   if (con_listener(con))
174     release_listener(con_listener(con));
175
176   --connections.inuse;
177
178   con_next(con) = connectionFreeList;
179   connectionFreeList = con;
180
181   con_magic(con) = 0;
182 }
183
184 /** Allocate a new client and initialize it.
185  * If \a from == NULL, initialize the fields for a local client,
186  * including allocating a Connection for him; otherwise initialize the
187  * fields for a remote client..
188  * @param[in] from Server connection that introduced the client (or
189  * NULL).
190  * @param[in] status Initial Client::cli_status value.
191  * @return Newly allocated and initialized Client.
192  */
193 struct Client* make_client(struct Client *from, int status)
194 {
195   struct Client* cptr = 0;
196
197   assert(!from || cli_verify(from));
198
199   cptr = alloc_client();
200
201   assert(0 != cptr);
202   assert(!cli_magic(cptr));
203   assert(0 == from || 0 != cli_connect(from));
204
205   if (!from) { /* local client, allocate a struct Connection */
206     struct Connection *con = alloc_connection();
207
208     assert(0 != con);
209     assert(!con_magic(con));
210
211     con_magic(con) = CONNECTION_MAGIC;
212     con_fd(con) = -1; /* initialize struct Connection */
213     con_freeflag(con) = 0;
214     con_nextnick(con) = CurrentTime - NICK_DELAY;
215     con_nexttarget(con) = CurrentTime - (TARGET_DELAY * (STARTTARGETS - 1));
216     con_handler(con) = UNREGISTERED_HANDLER;
217     con_client(con) = cptr;
218
219     cli_connect(cptr) = con; /* set the connection and other fields */
220     cli_since(cptr) = cli_lasttime(cptr) = cli_firsttime(cptr) = CurrentTime;
221     cli_lastnick(cptr) = TStime();
222     cli_unreg(cptr) = CLIREG_INIT;
223   } else
224     cli_connect(cptr) = cli_connect(from); /* use 'from's connection */
225
226   assert(con_verify(cli_connect(cptr)));
227
228   cli_magic(cptr) = CLIENT_MAGIC;
229   cli_status(cptr) = status;
230   cli_hnext(cptr) = cptr;
231   strcpy(cli_username(cptr), "unknown");
232
233   return cptr;
234 }
235
236 /** Release a Connection.
237  * @param[in] con Connection to free.
238  */
239 void free_connection(struct Connection* con)
240 {
241   if (!con)
242     return;
243
244   assert(con_verify(con));
245   assert(0 == con_client(con));
246
247   dealloc_connection(con); /* deallocate the connection */
248 }
249
250 /** Release a Client.
251  * In addition to the cleanup done by dealloc_client(), this will free
252  * any pending auth request, free the connection for local clients,
253  * and delete the processing timer for the client.
254  * @param[in] cptr Client to free.
255  */
256 void free_client(struct Client* cptr)
257 {
258   if (!cptr)
259     return;
260   /*
261    * forget to remove the client from the hash table?
262    */
263   assert(cli_verify(cptr));
264   assert(cli_hnext(cptr) == cptr);
265   /* or from linked list? */
266   assert(cli_next(cptr) == 0);
267   assert(cli_prev(cptr) == 0);
268
269   Debug((DEBUG_LIST, "Freeing client %s [%p], connection %p", cli_name(cptr),
270          cptr, cli_connect(cptr)));
271
272   if (cli_auth(cptr))
273     destroy_auth_request(cli_auth(cptr), 0);
274
275   /* Make sure we didn't magically get re-added to the list */
276   assert(cli_next(cptr) == 0);
277   assert(cli_prev(cptr) == 0);
278
279   if (cli_from(cptr) == cptr) { /* in other words, we're local */
280     cli_from(cptr) = 0;
281     /* timer must be marked as not active */
282     if (!cli_freeflag(cptr) && !t_active(&(cli_proc(cptr))))
283       dealloc_connection(cli_connect(cptr)); /* connection not open anymore */
284     else {
285       if (-1 < cli_fd(cptr) && cli_freeflag(cptr) & FREEFLAG_SOCKET)
286         socket_del(&(cli_socket(cptr))); /* queue a socket delete */
287       if (cli_freeflag(cptr) & FREEFLAG_TIMER)
288         timer_del(&(cli_proc(cptr))); /* queue a timer delete */
289     }
290   }
291
292   cli_connect(cptr) = 0;
293
294   dealloc_client(cptr); /* actually destroy the client */
295 }
296
297 /** Allocate a new Server object for a client.
298  * If Client::cli_serv == NULL, allocate a Server structure for it and
299  * initialize it.
300  * @param[in] cptr %Client to make into a server.
301  * @return The value of cli_serv(\a cptr).
302  */
303 struct Server *make_server(struct Client *cptr)
304 {
305   struct Server *serv = cli_serv(cptr);
306
307   assert(cli_verify(cptr));
308
309   if (!serv)
310   {
311     serv = (struct Server*) MyMalloc(sizeof(struct Server));
312     assert(0 != serv);
313     memset(serv, 0, sizeof(struct Server)); /* All variables are 0 by default */
314     servs.inuse++;
315     servs.alloc++;
316     cli_serv(cptr) = serv;
317     cli_serv(cptr)->lag = 60000;
318     *serv->by = '\0';
319     DupString(serv->last_error_msg, "<>");      /* String must be non-empty */
320   }
321   return cli_serv(cptr);
322 }
323
324 /** Remove \a cptr from lists that it is a member of.
325  * Specifically, this delinks \a cptr from #GlobalClientList, updates
326  * the whowas history list, frees its Client::cli_user and
327  * Client::cli_serv fields, and finally calls free_client() on it.
328  * @param[in] cptr Client to remove from lists and free.
329  */
330 void remove_client_from_list(struct Client *cptr)
331 {
332   assert(cli_verify(cptr));
333   assert(con_verify(cli_connect(cptr)));
334   assert(!cli_prev(cptr) || cli_verify(cli_prev(cptr)));
335   assert(!cli_next(cptr) || cli_verify(cli_next(cptr)));
336   assert(!IsMe(cptr));
337
338   /* Only try remove cptr from the list if it IS in the list.
339    * cli_next(cptr) cannot be NULL here, as &me is always the end
340    * the list, and we never remove &me.    -GW 
341    */
342   if(cli_next(cptr))
343   {
344     if (cli_prev(cptr))
345       cli_next(cli_prev(cptr)) = cli_next(cptr);
346     else {
347       GlobalClientList = cli_next(cptr);
348       cli_prev(GlobalClientList) = 0;
349     }
350     cli_prev(cli_next(cptr)) = cli_prev(cptr);
351   }
352   cli_next(cptr) = cli_prev(cptr) = 0;
353
354   if (IsUser(cptr) && cli_user(cptr)) {
355     add_history(cptr, 0);
356     off_history(cptr);
357   }
358   if (cli_user(cptr)) {
359     free_user(cli_user(cptr));
360     cli_user(cptr) = 0;
361   }
362
363   if (cli_serv(cptr)) {
364     if (cli_serv(cptr)->user) {
365       free_user(cli_serv(cptr)->user);
366       cli_serv(cptr)->user = 0;
367     }
368     if (cli_serv(cptr)->client_list)
369       MyFree(cli_serv(cptr)->client_list);
370     MyFree(cli_serv(cptr)->last_error_msg);
371     MyFree(cli_serv(cptr));
372     --servs.inuse;
373     --servs.alloc;
374   }
375   free_client(cptr);
376 }
377
378 /** Link \a cptr into #GlobalClientList.
379  * @param[in] cptr Client to link into the global list.
380  */
381 void add_client_to_list(struct Client *cptr)
382 {
383   assert(cli_verify(cptr));
384   assert(cli_next(cptr) == 0);
385   assert(cli_prev(cptr) == 0);
386
387   /*
388    * Since we always insert new clients to the top of the list,
389    * this should mean the "me" is the bottom most item in the list.
390    * XXX - don't always count on the above, things change
391    */
392   cli_prev(cptr) = 0;
393   cli_next(cptr) = GlobalClientList;
394   GlobalClientList = cptr;
395   if (cli_next(cptr))
396     cli_prev(cli_next(cptr)) = cptr;
397 }
398
399 #if 0
400 /** Perform a very CPU-intensive verification of %GlobalClientList.
401  * This checks the Client::cli_magic and Client::cli_prev field for
402  * each element in the list, and also checks that there are no loops.
403  * Any detected error will lead to an assertion failure.
404  */
405 void verify_client_list(void)
406 {
407   struct Client *client, *prev = 0;
408   unsigned int visited = 0;
409
410   for (client = GlobalClientList; client; client = cli_next(client), ++visited) {
411     /* Verify that this is a valid client, not a free'd one */
412     assert(cli_verify(client));
413     /* Verify that the list hasn't suddenly jumped around */
414     assert(cli_prev(client) == prev);
415     /* Verify that the list hasn't become circular */
416     assert(cli_next(client) != GlobalClientList);
417     assert(visited <= clients.alloc);
418     /* Remember what should precede us */
419     prev = client;
420   }
421 }
422 #endif /* DEBUGMODE */
423
424 /** Allocate a new SLink element.
425  * Pulls from #slinkFreeList if it contains anything, else it
426  * allocates a new one from the heap.
427  * @return Newly allocated list element.
428  */
429 struct SLink* make_link(void)
430 {
431   struct SLink* lp = slinkFreeList;
432   if (lp)
433     slinkFreeList = lp->next;
434   else {
435     lp = (struct SLink*) MyMalloc(sizeof(struct SLink));
436     links.alloc++;
437   }
438   assert(0 != lp);
439   links.inuse++;
440   return lp;
441 }
442
443 /** Release a singly linked list element.
444  * @param[in] lp List element to mark as unused.
445  */
446 void free_link(struct SLink* lp)
447 {
448   if (lp) {
449     lp->next = slinkFreeList;
450     slinkFreeList = lp;
451   }
452   links.inuse--;
453 }
454
455 /** Add an element to a doubly linked list.
456  * If \a lpp points to a non-NULL pointer, its DLink::prev field is
457  * updated to point to the newly allocated element.  Regardless,
458  * \a lpp is overwritten with the pointer to the new link.
459  * @param[in,out] lpp Pointer to insertion location.
460  * @param[in] cp %Client to put in newly allocated element.
461  * @return Allocated link structure (same as \a lpp on output).
462  */
463 struct DLink *add_dlink(struct DLink **lpp, struct Client *cp)
464 {
465   struct DLink* lp = (struct DLink*) MyMalloc(sizeof(struct DLink));
466   assert(0 != lp);
467   lp->value.cptr = cp;
468   lp->prev = 0;
469   if ((lp->next = *lpp))
470     lp->next->prev = lp;
471   *lpp = lp;
472   return lp;
473 }
474
475 /** Remove a node from a doubly linked list.
476  * @param[out] lpp Pointer to next list element.
477  * @param[in] lp List node to unlink.
478  */
479 void remove_dlink(struct DLink **lpp, struct DLink *lp)
480 {
481   assert(0 != lpp);
482   assert(0 != lp);
483
484   if (lp->prev) {
485     if ((lp->prev->next = lp->next))
486       lp->next->prev = lp->prev;
487   }
488   else if ((*lpp = lp->next))
489     lp->next->prev = NULL;
490   MyFree(lp);
491 }
492
493 /** Report memory usage of a list to \a cptr.
494  * @param[in] cptr Client requesting information.
495  * @param[in] lstats List statistics descriptor.
496  * @param[in] itemname Plural name of item type.
497  * @param[in,out] totals If non-null, accumulates item counts and memory usage.
498  */
499 void send_liststats(struct Client *cptr, const struct liststats *lstats,
500                     const char *itemname, struct liststats *totals)
501 {
502   send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":%s: inuse %zu(%zu) alloc %zu",
503              itemname, lstats->inuse, lstats->mem, lstats->alloc);
504   if (totals)
505   {
506     totals->inuse += lstats->inuse;
507     totals->alloc += lstats->alloc;
508     totals->mem += lstats->mem;
509   }
510 }
511
512 /** Report memory usage of list elements to \a cptr.
513  * @param[in] cptr Client requesting information.
514  * @param[in] name Unused pointer.
515  */
516 void send_listinfo(struct Client *cptr, char *name)
517 {
518   struct liststats total;
519   struct liststats confs;
520   struct ConfItem *conf;
521
522   memset(&total, 0, sizeof(total));
523
524   clients.mem = clients.inuse * sizeof(struct Client);
525   send_liststats(cptr, &clients, "Clients", &total);
526
527   connections.mem = connections.inuse * sizeof(struct Connection);
528   send_liststats(cptr, &connections, "Connections", &total);
529
530   servs.mem = servs.inuse * sizeof(struct Server);
531   send_liststats(cptr, &servs, "Servers", &total);
532
533   links.mem = links.inuse * sizeof(struct SLink);
534   send_liststats(cptr, &links, "Links", &total);
535
536   confs.alloc = GlobalConfCount;
537   confs.mem = confs.alloc * sizeof(GlobalConfCount);
538   for (confs.inuse = 0, conf = GlobalConfList; conf; conf = conf->next)
539     confs.inuse++;
540   send_liststats(cptr, &confs, "Confs", &total);
541
542   send_liststats(cptr, &total, "Totals", NULL);
543 }