]> jfr.im git - solanum.git/blob - ircd/s_newconf.c
support RSFNC indicating type of FNC (e.g. FORCE vs REGAIN) (#406)
[solanum.git] / ircd / s_newconf.c
1 /*
2 * ircd-ratbox: an advanced Internet Relay Chat Daemon(ircd).
3 * s_newconf.c - code for dealing with conf stuff
4 *
5 * Copyright (C) 2004 Lee Hardy <lee@leeh.co.uk>
6 * Copyright (C) 2004-2005 ircd-ratbox development team
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * 1.Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * 2.Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3.The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "stdinc.h"
34
35 #ifdef HAVE_LIBCRYPTO
36 #include <openssl/evp.h>
37 #include <openssl/rsa.h>
38 #endif
39
40 #include "ircd_defs.h"
41 #include "s_conf.h"
42 #include "s_newconf.h"
43 #include "client.h"
44 #include "s_serv.h"
45 #include "send.h"
46 #include "hostmask.h"
47 #include "newconf.h"
48 #include "hash.h"
49 #include "rb_dictionary.h"
50 #include "rb_radixtree.h"
51 #include "s_assert.h"
52 #include "logger.h"
53 #include "dns.h"
54
55 rb_dlink_list cluster_conf_list;
56 rb_dlink_list oper_conf_list;
57 rb_dlink_list server_conf_list;
58 rb_dlink_list xline_conf_list;
59 rb_dlink_list resv_conf_list; /* nicks only! */
60 rb_dlink_list nd_list; /* nick delay */
61 rb_dlink_list tgchange_list;
62
63 rb_patricia_tree_t *tgchange_tree;
64
65 static rb_bh *nd_heap = NULL;
66
67 static void expire_temp_rxlines(void *unused);
68 static void expire_nd_entries(void *unused);
69
70 struct ev_entry *expire_nd_entries_ev = NULL;
71 struct ev_entry *expire_temp_rxlines_ev = NULL;
72
73 void
74 init_s_newconf(void)
75 {
76 tgchange_tree = rb_new_patricia(PATRICIA_BITS);
77 nd_heap = rb_bh_create(sizeof(struct nd_entry), ND_HEAP_SIZE, "nd_heap");
78 expire_nd_entries_ev = rb_event_addish("expire_nd_entries", expire_nd_entries, NULL, 30);
79 expire_temp_rxlines_ev = rb_event_addish("expire_temp_rxlines", expire_temp_rxlines, NULL, 60);
80 }
81
82 void
83 clear_s_newconf(void)
84 {
85 struct server_conf *server_p;
86 rb_dlink_node *ptr;
87 rb_dlink_node *next_ptr;
88
89 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, cluster_conf_list.head)
90 {
91 rb_dlinkDelete(ptr, &cluster_conf_list);
92 free_remote_conf(ptr->data);
93 }
94
95 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, oper_conf_list.head)
96 {
97 free_oper_conf(ptr->data);
98 rb_dlinkDestroy(ptr, &oper_conf_list);
99 }
100
101 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, server_conf_list.head)
102 {
103 server_p = ptr->data;
104
105 if(!server_p->servers)
106 {
107 rb_dlinkDelete(ptr, &server_conf_list);
108 free_server_conf(ptr->data);
109 }
110 else
111 server_p->flags |= SERVER_ILLEGAL;
112 }
113 }
114
115 void
116 clear_s_newconf_bans(void)
117 {
118 struct ConfItem *aconf;
119 rb_dlink_node *ptr, *next_ptr;
120
121 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, xline_conf_list.head)
122 {
123 aconf = ptr->data;
124
125 if(aconf->hold)
126 continue;
127
128 free_conf(aconf);
129 rb_dlinkDestroy(ptr, &xline_conf_list);
130 }
131
132 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, resv_conf_list.head)
133 {
134 aconf = ptr->data;
135
136 /* temporary resv */
137 if(aconf->hold)
138 continue;
139
140 free_conf(aconf);
141 rb_dlinkDestroy(ptr, &resv_conf_list);
142 }
143
144 clear_resv_hash();
145 }
146
147 struct remote_conf *
148 make_remote_conf(void)
149 {
150 struct remote_conf *remote_p = rb_malloc(sizeof(struct remote_conf));
151 return remote_p;
152 }
153
154 void
155 free_remote_conf(struct remote_conf *remote_p)
156 {
157 s_assert(remote_p != NULL);
158 if(remote_p == NULL)
159 return;
160
161 rb_free(remote_p->username);
162 rb_free(remote_p->host);
163 rb_free(remote_p->server);
164 rb_free(remote_p);
165 }
166
167 void
168 propagate_generic(struct Client *source_p, const char *command,
169 const char *target, int cap, const char *format, ...)
170 {
171 char buffer[BUFSIZE];
172 va_list args;
173
174 va_start(args, format);
175 vsnprintf(buffer, sizeof(buffer), format, args);
176 va_end(args);
177
178 sendto_match_servs(source_p, target, cap, NOCAPS,
179 "%s %s %s",
180 command, target, buffer);
181 sendto_match_servs(source_p, target, CAP_ENCAP, cap,
182 "ENCAP %s %s %s",
183 target, command, buffer);
184 }
185
186 void
187 cluster_generic(struct Client *source_p, const char *command,
188 int cltype, int cap, const char *format, ...)
189 {
190 char buffer[BUFSIZE];
191 struct remote_conf *shared_p;
192 va_list args;
193 rb_dlink_node *ptr;
194
195 va_start(args, format);
196 vsnprintf(buffer, sizeof(buffer), format, args);
197 va_end(args);
198
199 RB_DLINK_FOREACH(ptr, cluster_conf_list.head)
200 {
201 shared_p = ptr->data;
202
203 if(!(shared_p->flags & cltype))
204 continue;
205
206 sendto_match_servs(source_p, shared_p->server, cap, NOCAPS,
207 "%s %s %s",
208 command, shared_p->server, buffer);
209 sendto_match_servs(source_p, shared_p->server, CAP_ENCAP, cap,
210 "ENCAP %s %s %s",
211 shared_p->server, command, buffer);
212 }
213 }
214
215 struct oper_conf *
216 make_oper_conf(void)
217 {
218 struct oper_conf *oper_p = rb_malloc(sizeof(struct oper_conf));
219 return oper_p;
220 }
221
222 void
223 free_oper_conf(struct oper_conf *oper_p)
224 {
225 s_assert(oper_p != NULL);
226 if(oper_p == NULL)
227 return;
228
229 rb_free(oper_p->username);
230 rb_free(oper_p->host);
231 rb_free(oper_p->name);
232 rb_free(oper_p->certfp);
233
234 if(oper_p->passwd)
235 {
236 memset(oper_p->passwd, 0, strlen(oper_p->passwd));
237 rb_free(oper_p->passwd);
238 }
239
240 #ifdef HAVE_LIBCRYPTO
241 rb_free(oper_p->rsa_pubkey_file);
242
243 if(oper_p->rsa_pubkey)
244 #if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
245 EVP_PKEY_free(oper_p->rsa_pubkey);
246 #else
247 RSA_free(oper_p->rsa_pubkey);
248 #endif
249 #endif
250
251 rb_free(oper_p);
252 }
253
254 struct oper_conf *
255 find_oper_conf(const char *username, const char *host, const char *locip, const char *name)
256 {
257 struct oper_conf *oper_p;
258 struct rb_sockaddr_storage ip, cip;
259 char addr[HOSTLEN+1];
260 int bits, cbits;
261 rb_dlink_node *ptr;
262
263 parse_netmask(locip, &cip, &cbits);
264
265 RB_DLINK_FOREACH(ptr, oper_conf_list.head)
266 {
267 oper_p = ptr->data;
268
269 /* name/username doesnt match.. */
270 if(irccmp(oper_p->name, name) || !match(oper_p->username, username))
271 continue;
272
273 rb_strlcpy(addr, oper_p->host, sizeof(addr));
274
275 if(parse_netmask(addr, &ip, &bits) != HM_HOST)
276 {
277 if(GET_SS_FAMILY(&ip) == GET_SS_FAMILY(&cip) &&
278 comp_with_mask_sock((struct sockaddr *)&ip, (struct sockaddr *)&cip, bits))
279 return oper_p;
280 }
281
282 /* we have to compare against the host as well, because its
283 * valid to set a spoof to an IP, which if we only compare
284 * in ip form to sockhost will not necessarily match --anfl
285 */
286 if(match(oper_p->host, host))
287 return oper_p;
288 }
289
290 return NULL;
291 }
292
293 struct server_conf *
294 make_server_conf(void)
295 {
296 struct server_conf *server_p = rb_malloc(sizeof(struct server_conf));
297
298 SET_SS_FAMILY(&server_p->connect4, AF_UNSPEC);
299 SET_SS_LEN(&server_p->connect4, sizeof(struct sockaddr_in));
300
301 SET_SS_FAMILY(&server_p->bind4, AF_UNSPEC);
302 SET_SS_LEN(&server_p->bind4, sizeof(struct sockaddr_in));
303
304 SET_SS_FAMILY(&server_p->connect6, AF_UNSPEC);
305 SET_SS_LEN(&server_p->connect6, sizeof(struct sockaddr_in6));
306
307 SET_SS_FAMILY(&server_p->bind6, AF_UNSPEC);
308 SET_SS_LEN(&server_p->bind6, sizeof(struct sockaddr_in6));
309
310 server_p->aftype = AF_UNSPEC;
311
312 return server_p;
313 }
314
315 void
316 free_server_conf(struct server_conf *server_p)
317 {
318 s_assert(server_p != NULL);
319 if(server_p == NULL)
320 return;
321
322 if(!EmptyString(server_p->passwd))
323 {
324 memset(server_p->passwd, 0, strlen(server_p->passwd));
325 rb_free(server_p->passwd);
326 }
327
328 if(!EmptyString(server_p->spasswd))
329 {
330 memset(server_p->spasswd, 0, strlen(server_p->spasswd));
331 rb_free(server_p->spasswd);
332 }
333
334 rb_free(server_p->name);
335 rb_free(server_p->connect_host);
336 rb_free(server_p->bind_host);
337 rb_free(server_p->class_name);
338 rb_free(server_p->certfp);
339 rb_free(server_p);
340 }
341
342 /*
343 * conf_connect_dns_callback
344 * inputs - pointer to struct ConfItem
345 * - pointer to adns reply
346 * output - none
347 * side effects - called when resolver query finishes
348 * if the query resulted in a successful search, hp will contain
349 * a non-null pointer, otherwise hp will be null.
350 * if successful save hp in the conf item it was called with
351 */
352 static void
353 conf_connect_dns_callback(const char *result, int status, int aftype, void *data)
354 {
355 struct server_conf *server_p = data;
356
357 if(aftype == AF_INET)
358 {
359 if(status == 1)
360 rb_inet_pton_sock(result, &server_p->connect4);
361
362 server_p->dns_query_connect4 = 0;
363 }
364 else if(aftype == AF_INET6)
365 {
366 if(status == 1)
367 rb_inet_pton_sock(result, &server_p->connect6);
368
369 server_p->dns_query_connect6 = 0;
370 }
371 }
372
373 /*
374 * conf_bind_dns_callback
375 * inputs - pointer to struct ConfItem
376 * - pointer to adns reply
377 * output - none
378 * side effects - called when resolver query finishes
379 * if the query resulted in a successful search, hp will contain
380 * a non-null pointer, otherwise hp will be null.
381 * if successful save hp in the conf item it was called with
382 */
383 static void
384 conf_bind_dns_callback(const char *result, int status, int aftype, void *data)
385 {
386 struct server_conf *server_p = data;
387
388 if(aftype == AF_INET)
389 {
390 if(status == 1)
391 rb_inet_pton_sock(result, &server_p->bind4);
392
393 server_p->dns_query_bind4 = 0;
394 }
395 else if(aftype == AF_INET6)
396 {
397 if(status == 1)
398 rb_inet_pton_sock(result, &server_p->bind6);
399
400 server_p->dns_query_bind6 = 0;
401 }
402 }
403
404 void
405 add_server_conf(struct server_conf *server_p)
406 {
407 if(EmptyString(server_p->class_name))
408 {
409 server_p->class_name = rb_strdup("default");
410 server_p->class = default_class;
411 return;
412 }
413
414 server_p->class = find_class(server_p->class_name);
415
416 if(server_p->class == default_class)
417 {
418 conf_report_error("Warning connect::class invalid for %s",
419 server_p->name);
420
421 rb_free(server_p->class_name);
422 server_p->class_name = rb_strdup("default");
423 }
424
425 if(server_p->connect_host && !strpbrk(server_p->connect_host, "*?"))
426 {
427 server_p->dns_query_connect4 =
428 lookup_hostname(server_p->connect_host, AF_INET, conf_connect_dns_callback, server_p);
429 server_p->dns_query_connect6 =
430 lookup_hostname(server_p->connect_host, AF_INET6, conf_connect_dns_callback, server_p);
431 }
432
433 if(server_p->bind_host)
434 {
435 server_p->dns_query_bind4 =
436 lookup_hostname(server_p->bind_host, AF_INET, conf_bind_dns_callback, server_p);
437 server_p->dns_query_bind6 =
438 lookup_hostname(server_p->bind_host, AF_INET6, conf_bind_dns_callback, server_p);
439 }
440 }
441
442 struct server_conf *
443 find_server_conf(const char *name)
444 {
445 struct server_conf *server_p;
446 rb_dlink_node *ptr;
447
448 RB_DLINK_FOREACH(ptr, server_conf_list.head)
449 {
450 server_p = ptr->data;
451
452 if(ServerConfIllegal(server_p))
453 continue;
454
455 if(match(name, server_p->name))
456 return server_p;
457 }
458
459 return NULL;
460 }
461
462 void
463 attach_server_conf(struct Client *client_p, struct server_conf *server_p)
464 {
465 /* already have an attached conf */
466 if(client_p->localClient->att_sconf)
467 {
468 /* short circuit this special case :) */
469 if(client_p->localClient->att_sconf == server_p)
470 return;
471
472 detach_server_conf(client_p);
473 }
474
475 CurrUsers(server_p->class)++;
476
477 client_p->localClient->att_sconf = server_p;
478 server_p->servers++;
479 }
480
481 void
482 detach_server_conf(struct Client *client_p)
483 {
484 struct server_conf *server_p = client_p->localClient->att_sconf;
485
486 if(server_p == NULL)
487 return;
488
489 client_p->localClient->att_sconf = NULL;
490 server_p->servers--;
491 CurrUsers(server_p->class)--;
492
493 if(ServerConfIllegal(server_p) && !server_p->servers)
494 {
495 /* the class this one is using may need destroying too */
496 if(MaxUsers(server_p->class) < 0 && CurrUsers(server_p->class) <= 0)
497 free_class(server_p->class);
498
499 rb_dlinkDelete(&server_p->node, &server_conf_list);
500 free_server_conf(server_p);
501 }
502 }
503
504 void
505 set_server_conf_autoconn(struct Client *source_p, const char *name, int newval)
506 {
507 struct server_conf *server_p;
508
509 if((server_p = find_server_conf(name)) != NULL)
510 {
511 if(newval)
512 server_p->flags |= SERVER_AUTOCONN;
513 else
514 server_p->flags &= ~SERVER_AUTOCONN;
515
516 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
517 "%s has changed AUTOCONN for %s to %i",
518 get_oper_name(source_p), name, newval);
519 }
520 else
521 sendto_one_notice(source_p, ":Can't find %s", name);
522 }
523
524 void
525 disable_server_conf_autoconn(const char *name)
526 {
527 struct server_conf *server_p;
528
529 server_p = find_server_conf(name);
530 if(server_p != NULL && server_p->flags & SERVER_AUTOCONN)
531 {
532 server_p->flags &= ~SERVER_AUTOCONN;
533
534 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
535 "Disabling AUTOCONN for %s because of error",
536 name);
537 ilog(L_SERVER, "Disabling AUTOCONN for %s because of error",
538 name);
539 }
540 }
541
542 struct ConfItem *
543 find_xline(const char *gecos, int counter)
544 {
545 struct ConfItem *aconf;
546 rb_dlink_node *ptr;
547
548 RB_DLINK_FOREACH(ptr, xline_conf_list.head)
549 {
550 aconf = ptr->data;
551
552 if(match_esc(aconf->host, gecos))
553 {
554 if(counter)
555 aconf->port++;
556 return aconf;
557 }
558 }
559
560 return NULL;
561 }
562
563 struct ConfItem *
564 find_xline_mask(const char *gecos)
565 {
566 struct ConfItem *aconf;
567 rb_dlink_node *ptr;
568
569 RB_DLINK_FOREACH(ptr, xline_conf_list.head)
570 {
571 aconf = ptr->data;
572
573 if(!irccmp(aconf->host, gecos))
574 return aconf;
575 }
576
577 return NULL;
578 }
579
580 struct ConfItem *
581 find_nick_resv(const char *name)
582 {
583 struct ConfItem *aconf;
584 rb_dlink_node *ptr;
585
586 RB_DLINK_FOREACH(ptr, resv_conf_list.head)
587 {
588 aconf = ptr->data;
589
590 if(match_esc(aconf->host, name))
591 {
592 aconf->port++;
593 return aconf;
594 }
595 }
596
597 return NULL;
598 }
599
600 struct ConfItem *
601 find_nick_resv_mask(const char *name)
602 {
603 struct ConfItem *aconf;
604 rb_dlink_node *ptr;
605
606 RB_DLINK_FOREACH(ptr, resv_conf_list.head)
607 {
608 aconf = ptr->data;
609
610 if(!irccmp(aconf->host, name))
611 return aconf;
612 }
613
614 return NULL;
615 }
616
617 /* clean_resv_nick()
618 *
619 * inputs - nick
620 * outputs - 1 if nick is vaild resv, 0 otherwise
621 * side effects -
622 */
623 int
624 clean_resv_nick(const char *nick)
625 {
626 char tmpch;
627 int as = 0;
628 int q = 0;
629 int ch = 0;
630
631 if(*nick == '-' || IsDigit(*nick))
632 return 0;
633
634 while ((tmpch = *nick++))
635 {
636 if(tmpch == '?' || tmpch == '@' || tmpch == '#')
637 q++;
638 else if(tmpch == '*')
639 as++;
640 else if(IsNickChar(tmpch))
641 ch++;
642 else
643 return 0;
644 }
645
646 if(!ch && as)
647 return 0;
648
649 return 1;
650 }
651
652 /* valid_wild_card_simple()
653 *
654 * inputs - "thing" to test
655 * outputs - 1 if enough wildcards, else 0
656 * side effects -
657 */
658 int
659 valid_wild_card_simple(const char *data)
660 {
661 const char *p;
662 char tmpch;
663 int nonwild = 0;
664 int wild = 0;
665
666 /* check the string for minimum number of nonwildcard chars */
667 p = data;
668
669 while((tmpch = *p++))
670 {
671 /* found an escape, p points to the char after it, so skip
672 * that and move on.
673 */
674 if(tmpch == '\\' && *p)
675 {
676 p++;
677 if(++nonwild >= ConfigFileEntry.min_nonwildcard_simple)
678 return 1;
679 }
680 else if(!IsMWildChar(tmpch))
681 {
682 /* if we have enough nonwildchars, return */
683 if(++nonwild >= ConfigFileEntry.min_nonwildcard_simple)
684 return 1;
685 }
686 else
687 wild++;
688 }
689
690 /* strings without wilds are also ok */
691 return wild == 0;
692 }
693
694 time_t
695 valid_temp_time(const char *p)
696 {
697 time_t result = 0;
698 long current = 0;
699
700 while (*p) {
701 char *endp;
702 int mul;
703
704 errno = 0;
705 current = strtol(p, &endp, 10);
706
707 if (endp == p)
708 return -1;
709 if (current < 0)
710 return -1;
711
712 switch (*endp) {
713 case '\0': /* No unit was given so send it back as minutes */
714 case 'm':
715 mul = 60;
716 break;
717 case 'h':
718 mul = 3600;
719 break;
720 case 'd':
721 mul = 86400;
722 break;
723 case 'w':
724 mul = 604800;
725 break;
726 default:
727 return -1;
728 }
729
730 if (current > LONG_MAX / mul)
731 return MAX_TEMP_TIME;
732
733 current *= mul;
734
735 if (current > MAX_TEMP_TIME - result)
736 return MAX_TEMP_TIME;
737
738 result += current;
739
740 if (*endp == '\0')
741 break;
742
743 p = endp + 1;
744 }
745
746 return MIN(result, MAX_TEMP_TIME);
747 }
748
749 /* Propagated bans are expired elsewhere. */
750 static void
751 expire_temp_rxlines(void *unused)
752 {
753 struct ConfItem *aconf;
754 rb_dlink_node *ptr;
755 rb_dlink_node *next_ptr;
756 rb_radixtree_iteration_state state;
757
758 RB_RADIXTREE_FOREACH(aconf, &state, resv_tree)
759 {
760 if(aconf->lifetime != 0)
761 continue;
762 if(aconf->hold && aconf->hold <= rb_current_time())
763 {
764 if(ConfigFileEntry.tkline_expire_notices)
765 sendto_realops_snomask(SNO_GENERAL, L_ALL,
766 "Temporary RESV for [%s] expired",
767 aconf->host);
768
769 rb_radixtree_delete(resv_tree, aconf->host);
770 free_conf(aconf);
771 }
772 }
773
774 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, resv_conf_list.head)
775 {
776 aconf = ptr->data;
777
778 if(aconf->lifetime != 0)
779 continue;
780 if(aconf->hold && aconf->hold <= rb_current_time())
781 {
782 if(ConfigFileEntry.tkline_expire_notices)
783 sendto_realops_snomask(SNO_GENERAL, L_ALL,
784 "Temporary RESV for [%s] expired",
785 aconf->host);
786 free_conf(aconf);
787 rb_dlinkDestroy(ptr, &resv_conf_list);
788 }
789 }
790
791 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, xline_conf_list.head)
792 {
793 aconf = ptr->data;
794
795 if(aconf->lifetime != 0)
796 continue;
797 if(aconf->hold && aconf->hold <= rb_current_time())
798 {
799 if(ConfigFileEntry.tkline_expire_notices)
800 sendto_realops_snomask(SNO_GENERAL, L_ALL,
801 "Temporary X-line for [%s] expired",
802 aconf->host);
803 free_conf(aconf);
804 rb_dlinkDestroy(ptr, &xline_conf_list);
805 }
806 }
807 }
808
809 unsigned long
810 get_nd_count(void)
811 {
812 return(rb_dlink_list_length(&nd_list));
813 }
814
815 void
816 add_nd_entry(const char *name)
817 {
818 struct nd_entry *nd;
819
820 if(rb_dictionary_find(nd_dict, name) != NULL)
821 return;
822
823 nd = rb_bh_alloc(nd_heap);
824
825 rb_strlcpy(nd->name, name, sizeof(nd->name));
826 nd->expire = rb_current_time() + ConfigFileEntry.nick_delay;
827
828 /* this list is ordered */
829 rb_dlinkAddTail(nd, &nd->lnode, &nd_list);
830
831 rb_dictionary_add(nd_dict, nd->name, nd);
832 }
833
834 void
835 free_nd_entry(struct nd_entry *nd)
836 {
837 rb_dictionary_delete(nd_dict, nd->name);
838
839 rb_dlinkDelete(&nd->lnode, &nd_list);
840 rb_bh_free(nd_heap, nd);
841 }
842
843 void
844 expire_nd_entries(void *unused)
845 {
846 struct nd_entry *nd;
847 rb_dlink_node *ptr;
848 rb_dlink_node *next_ptr;
849
850 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, nd_list.head)
851 {
852 nd = ptr->data;
853
854 /* this list is ordered - we can stop when we hit the first
855 * entry that doesnt expire..
856 */
857 if(nd->expire > rb_current_time())
858 return;
859
860 free_nd_entry(nd);
861 }
862 }
863
864 void
865 add_tgchange(const char *host)
866 {
867 tgchange *target;
868 rb_patricia_node_t *pnode;
869
870 if(find_tgchange(host))
871 return;
872
873 target = rb_malloc(sizeof(tgchange));
874 pnode = make_and_lookup(tgchange_tree, host);
875
876 pnode->data = target;
877 target->pnode = pnode;
878
879 target->ip = rb_strdup(host);
880 target->expiry = rb_current_time() + (60*60*12);
881
882 rb_dlinkAdd(target, &target->node, &tgchange_list);
883 }
884
885 tgchange *
886 find_tgchange(const char *host)
887 {
888 rb_patricia_node_t *pnode;
889
890 if((pnode = rb_match_exact_string(tgchange_tree, host)))
891 return pnode->data;
892
893 return NULL;
894 }