Make default virtual host work for .12, and make IPv4-only
[ircu2.10.12-pk.git] / ircd / s_conf.c
1 /*
2  * IRC - Internet Relay Chat, ircd/s_conf.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
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  * $Id$
21  */
22 #include "config.h"
23
24 #include "s_conf.h"
25 #include "IPcheck.h"
26 #include "class.h"
27 #include "client.h"
28 #include "crule.h"
29 #include "ircd_features.h"
30 #include "fileio.h"
31 #include "gline.h"
32 #include "hash.h"
33 #include "ircd.h"
34 #include "ircd_alloc.h"
35 #include "ircd_auth.h"
36 #include "ircd_chattr.h"
37 #include "ircd_log.h"
38 #include "ircd_reply.h"
39 #include "ircd_snprintf.h"
40 #include "ircd_string.h"
41 #include "list.h"
42 #include "listener.h"
43 #include "match.h"
44 #include "motd.h"
45 #include "numeric.h"
46 #include "numnicks.h"
47 #include "opercmds.h"
48 #include "parse.h"
49 #include "res.h"
50 #include "s_bsd.h"
51 #include "s_debug.h"
52 #include "s_misc.h"
53 #include "send.h"
54 #include "struct.h"
55 #include "support.h"
56 #include "sys.h"
57
58 #include <assert.h>
59 #include <arpa/inet.h>
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <netdb.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <sys/stat.h>
67 #include <unistd.h>
68
69 struct ConfItem  *GlobalConfList  = 0;
70 int              GlobalConfCount = 0;
71 struct s_map     *GlobalServiceMapList = 0;
72 struct qline     *GlobalQuarantineList = 0;
73
74 void yyparse(void);
75 int conf_fd, lineno;
76
77 struct LocalConf   localConf;
78 struct CRuleConf*  cruleConfList;
79 /* struct ServerConf* serverConfList; */
80 struct DenyConf*   denyConfList;
81
82 /*
83  * output the reason for being k lined from a file  - Mmmm
84  * sptr is client being dumped
85  * filename is the file that is to be output to the K lined client
86  */
87 static void killcomment(struct Client* sptr, const char* filename)
88 {
89   FBFILE*     file = 0;
90   char        line[80];
91   struct stat sb;
92   struct tm*  tm;
93
94   if (NULL == (file = fbopen(filename, "r"))) {
95     send_reply(sptr, ERR_NOMOTD);
96     send_reply(sptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP,
97                ":Connection from your host is refused on this server.");
98     return;
99   }
100   fbstat(&sb, file);
101   tm = localtime((time_t*) &sb.st_mtime);        /* NetBSD needs cast */
102   while (fbgets(line, sizeof(line) - 1, file)) {
103     char* end = line + strlen(line);
104     while (end > line) {
105       --end;
106       if ('\n' == *end || '\r' == *end)
107         *end = '\0';
108       else
109         break;
110     }
111     send_reply(sptr, RPL_MOTD, line);
112   }
113   send_reply(sptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP,
114              ":Connection from your host is refused on this server.");
115   fbclose(file);
116 }
117
118 struct ConfItem* make_conf(void)
119 {
120   struct ConfItem* aconf;
121
122   aconf = (struct ConfItem*) MyMalloc(sizeof(struct ConfItem));
123   assert(0 != aconf);
124 #ifdef        DEBUGMODE
125   ++GlobalConfCount;
126 #endif
127   memset(aconf, 0, sizeof(struct ConfItem));
128   aconf->status       = CONF_ILLEGAL;
129   return aconf;
130 }
131
132 void delist_conf(struct ConfItem *aconf)
133 {
134   if (aconf == GlobalConfList)
135     GlobalConfList = GlobalConfList->next;
136   else {
137     struct ConfItem *bconf;
138
139     for (bconf = GlobalConfList; aconf != bconf->next; bconf = bconf->next)
140       ;
141     bconf->next = aconf->next;
142   }
143   aconf->next = 0;
144 }
145
146 void free_conf(struct ConfItem *aconf)
147 {
148   Debug((DEBUG_DEBUG, "free_conf: %s %s %d",
149          aconf->host ? aconf->host : "*",
150          aconf->name ? aconf->name : "*",
151          aconf->address.port));
152   if (aconf->dns_pending)
153     delete_resolver_queries(aconf);
154   MyFree(aconf->host);
155   if (aconf->passwd)
156     memset(aconf->passwd, 0, strlen(aconf->passwd));
157   MyFree(aconf->passwd);
158   MyFree(aconf->name);
159   MyFree(aconf);
160 #ifdef        DEBUGMODE
161   --GlobalConfCount;
162 #endif
163 }
164
165 /*
166  * detach_conf - Disassociate configuration from the client.
167  */
168 static void detach_conf(struct Client* cptr, struct ConfItem* aconf)
169 {
170   struct SLink** lp;
171   struct SLink*  tmp;
172
173   assert(0 != aconf);
174   assert(0 != cptr);
175   assert(0 < aconf->clients);
176
177   lp = &(cli_confs(cptr));
178
179   while (*lp) {
180     if ((*lp)->value.aconf == aconf) {
181       if (aconf->conn_class && (aconf->status & CONF_CLIENT_MASK) && ConfLinks(aconf) > 0)
182         --ConfLinks(aconf);
183
184       assert(0 < aconf->clients);
185       if (0 == --aconf->clients && IsIllegal(aconf))
186         free_conf(aconf);
187       tmp = *lp;
188       *lp = tmp->next;
189       free_link(tmp);
190       return;
191     }
192     lp = &((*lp)->next);
193   }
194 }
195
196 /*
197  * conf_dns_callback - called when resolver query finishes
198  * if the query resulted in a successful search, hp will contain
199  * a non-null pointer, otherwise hp will be null.
200  * if successful save hp in the conf item it was called with
201  */
202 static void conf_dns_callback(void* vptr, struct DNSReply* hp)
203 {
204   struct ConfItem* aconf = (struct ConfItem*) vptr;
205   assert(aconf);
206   aconf->dns_pending = 0;
207   if (hp) {
208     memcpy(&aconf->address.addr, &hp->addr, sizeof(aconf->address.addr));
209     MyFree(hp);
210   }
211 }
212
213 /*
214  * conf_dns_lookup - do a nameserver lookup of the conf host
215  * if the conf entry is currently doing a ns lookup do nothing, otherwise
216  * if the lookup returns a null pointer, set the conf dns_pending flag
217  */
218 static void conf_dns_lookup(struct ConfItem* aconf)
219 {
220   if (!aconf->dns_pending) {
221     char            buf[HOSTLEN + 1];
222     struct DNSQuery query;
223     query.vptr     = aconf;
224     query.callback = conf_dns_callback;
225     host_from_uh(buf, aconf->host, HOSTLEN);
226     buf[HOSTLEN] = '\0';
227
228     gethost_byname(buf, &query);
229     aconf->dns_pending = 1;
230   }
231 }
232
233
234 /*
235  * lookup_confhost
236  *
237  * Do (start) DNS lookups of all hostnames in the conf line and convert
238  * an IP addresses in a.b.c.d number for to IP#s.
239  */
240 void
241 lookup_confhost(struct ConfItem *aconf)
242 {
243   if (EmptyString(aconf->host) || EmptyString(aconf->name)) {
244     Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
245            aconf->host, aconf->name));
246     return;
247   }
248   if (aconf->origin_name
249       && !ircd_aton(&aconf->origin.addr, aconf->origin_name)) {
250     Debug((DEBUG_ERROR, "Origin name error: (%s) (%s)",
251         aconf->origin_name, aconf->name));
252   }
253   /*
254    * Do name lookup now on hostnames given and store the
255    * ip numbers in conf structure.
256    */
257   if (IsIP6Char(*aconf->host)) {
258     if (!ircd_aton(&aconf->address.addr, aconf->host)) {
259       Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
260           aconf->host, aconf->name));
261     }
262   }
263   else
264     conf_dns_lookup(aconf);
265 }
266
267 /*
268  * conf_find_server - find a server by name or hostname
269  * returns a server conf item pointer if found, 0 otherwise
270  *
271  * NOTE: at some point when we don't have to scan the entire
272  * list it may be cheaper to look for server names and host
273  * names in separate loops (original code did it that way)
274  */
275 struct ConfItem* conf_find_server(const char* name)
276 {
277   struct ConfItem* conf;
278   assert(0 != name);
279
280   for (conf = GlobalConfList; conf; conf = conf->next) {
281     if (CONF_SERVER == conf->status) {
282       /*
283        * Check first servernames, then try hostnames.
284        * XXX - match returns 0 if there _is_ a match... guess they
285        * haven't decided what true is yet
286        */
287       if (0 == match(name, conf->name))
288         return conf;
289     }
290   }
291   return 0;
292 }
293
294 /*
295  * conf_eval_crule - evaluate connection rules
296  * returns the name of the rule triggered if found, 0 otherwise
297  *
298  * Evaluate connection rules...  If no rules found, allow the
299  * connect.   Otherwise stop with the first true rule (ie: rules
300  * are ored together.  Oper connects are effected only by D
301  * lines (CRULE_ALL) not d lines (CRULE_AUTO).
302  */
303 const char* conf_eval_crule(const char* name, int mask)
304 {
305   struct CRuleConf* p = cruleConfList;
306   assert(0 != name);
307
308   for ( ; p; p = p->next) {
309     if (0 != (p->type & mask) && 0 == match(p->hostmask, name)) {
310       if (crule_eval(p->node))
311         return p->rule;
312     }
313   }
314   return 0;
315 }
316
317 /*
318  * Remove all conf entries from the client except those which match
319  * the status field mask.
320  */
321 void det_confs_butmask(struct Client* cptr, int mask)
322 {
323   struct SLink* link;
324   struct SLink* next;
325   assert(0 != cptr);
326
327   for (link = cli_confs(cptr); link; link = next) {
328     next = link->next;
329     if ((link->value.aconf->status & mask) == 0)
330       detach_conf(cptr, link->value.aconf);
331   }
332 }
333
334 /*
335  * check_limit_and_attach - check client limits and attach I:line
336  *
337  * Made it accept 1 charactor, and 2 charactor limits (0->99 now), 
338  * and dislallow more than 255 people here as well as in ipcheck.
339  * removed the old "ONE" scheme too.
340  *  -- Isomer 2000-06-22
341  */
342 static enum AuthorizationCheckResult
343 check_limit_and_attach(struct Client* cptr, struct ConfItem* aconf)
344 {
345   int number = 255;
346   
347   if (aconf->passwd) {
348     if (IsDigit(*aconf->passwd) && !aconf->passwd[1])
349       number = *aconf->passwd-'0';
350     else if (IsDigit(*aconf->passwd) && IsDigit(aconf->passwd[1]) && 
351              !aconf->passwd[2])
352       number = (*aconf->passwd-'0')*10+(aconf->passwd[1]-'0');
353   }
354   if (IPcheck_nr(cptr) > number)
355     return ACR_TOO_MANY_FROM_IP;
356   return attach_conf(cptr, aconf);
357 }
358
359 /*
360  * Find the first (best) I line to attach.
361  */
362 enum AuthorizationCheckResult attach_iline(struct Client*  cptr)
363 {
364   struct ConfItem* aconf;
365   static char      uhost[HOSTLEN + USERLEN + 3];
366   static char      fullname[HOSTLEN + 1];
367   struct DNSReply* hp = 0;
368
369   assert(0 != cptr);
370
371   if (cli_dns_reply(cptr))
372     hp = cli_dns_reply(cptr);
373
374   for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
375     if (aconf->status != CONF_CLIENT)
376       continue;
377     if (aconf->address.port && aconf->address.port != cli_listener(cptr)->addr.port)
378       continue;
379     if (!aconf->host || !aconf->name)
380       continue;
381     if (hp) {
382       ircd_strncpy(fullname, hp->h_name, HOSTLEN);
383       fullname[HOSTLEN] = '\0';
384
385       Debug((DEBUG_DNS, "a_il: %s->%s", cli_sockhost(cptr), fullname));
386
387       if (strchr(aconf->name, '@')) {
388         strcpy(uhost, cli_username(cptr));
389         strcat(uhost, "@");
390       }
391       else
392         *uhost = '\0';
393       strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost));
394       uhost[sizeof(uhost) - 1] = 0;
395       if (0 == match(aconf->name, uhost)) {
396         if (strchr(uhost, '@'))
397           SetFlag(cptr, FLAG_DOID);
398         return check_limit_and_attach(cptr, aconf);
399       }
400     }
401     if (strchr(aconf->host, '@')) {
402       ircd_strncpy(uhost, cli_username(cptr), sizeof(uhost) - 2);
403       uhost[sizeof(uhost) - 2] = 0;
404       strcat(uhost, "@");
405     }
406     else
407       *uhost = '\0';
408     strncat(uhost, cli_sock_ip(cptr), sizeof(uhost) - 1 - strlen(uhost));
409     uhost[sizeof(uhost) - 1] = 0;
410     if (match(aconf->host, uhost))
411       continue;
412     if (strchr(uhost, '@'))
413       SetFlag(cptr, FLAG_DOID);
414
415     return check_limit_and_attach(cptr, aconf);
416   }
417   return ACR_NO_AUTHORIZATION;
418 }
419
420 static int is_attached(struct ConfItem *aconf, struct Client *cptr)
421 {
422   struct SLink *lp;
423
424   for (lp = cli_confs(cptr); lp; lp = lp->next) {
425     if (lp->value.aconf == aconf)
426       return 1;
427   }
428   return 0;
429 }
430
431 /*
432  * attach_conf
433  *
434  * Associate a specific configuration entry to a *local*
435  * client (this is the one which used in accepting the
436  * connection). Note, that this automaticly changes the
437  * attachment if there was an old one...
438  */
439 enum AuthorizationCheckResult attach_conf(struct Client *cptr, struct ConfItem *aconf)
440 {
441   struct SLink *lp;
442
443   if (is_attached(aconf, cptr))
444     return ACR_ALREADY_AUTHORIZED;
445   if (IsIllegal(aconf))
446     return ACR_NO_AUTHORIZATION;
447   if ((aconf->status & (CONF_OPERATOR | CONF_CLIENT)) &&
448       ConfLinks(aconf) >= ConfMaxLinks(aconf) && ConfMaxLinks(aconf) > 0)
449     return ACR_TOO_MANY_IN_CLASS;  /* Use this for printing error message */
450   lp = make_link();
451   lp->next = cli_confs(cptr);
452   lp->value.aconf = aconf;
453   cli_confs(cptr) = lp;
454   ++aconf->clients;
455   if (aconf->status & CONF_CLIENT_MASK)
456     ConfLinks(aconf)++;
457   return ACR_OK;
458 }
459
460 const struct LocalConf* conf_get_local(void)
461 {
462   return &localConf;
463 }
464
465 /*
466  * attach_confs_byname
467  *
468  * Attach a CONF line to a client if the name passed matches that for
469  * the conf file (for non-C/N lines) or is an exact match (C/N lines
470  * only).  The difference in behaviour is to stop C:*::* and N:*::*.
471  */
472 struct ConfItem* attach_confs_byname(struct Client* cptr, const char* name,
473                                      int statmask)
474 {
475   struct ConfItem* tmp;
476   struct ConfItem* first = NULL;
477
478   assert(0 != name);
479
480   if (HOSTLEN < strlen(name))
481     return 0;
482
483   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
484     if (0 != (tmp->status & statmask) && !IsIllegal(tmp)) {
485       assert(0 != tmp->name);
486       if (0 == match(tmp->name, name) || 0 == ircd_strcmp(tmp->name, name)) { 
487         if (ACR_OK == attach_conf(cptr, tmp) && !first)
488           first = tmp;
489       }
490     }
491   }
492   return first;
493 }
494
495 /*
496  * Added for new access check    meLazy
497  */
498 struct ConfItem* attach_confs_byhost(struct Client* cptr, const char* host,
499                                      int statmask)
500 {
501   struct ConfItem* tmp;
502   struct ConfItem* first = 0;
503
504   assert(0 != host);
505   if (HOSTLEN < strlen(host))
506     return 0;
507
508   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
509     if (0 != (tmp->status & statmask) && !IsIllegal(tmp)) {
510       assert(0 != tmp->host);
511       if (0 == match(tmp->host, host) || 0 == ircd_strcmp(tmp->host, host)) { 
512         if (ACR_OK == attach_conf(cptr, tmp) && !first)
513           first = tmp;
514       }
515     }
516   }
517   return first;
518 }
519
520 /*
521  * find a conf entry which matches the hostname and has the same name.
522  */
523 struct ConfItem* find_conf_exact(const char* name, const char* user,
524                                  const char* host, int statmask)
525 {
526   struct ConfItem *tmp;
527   char userhost[USERLEN + HOSTLEN + 3];
528
529   if (user)
530     ircd_snprintf(0, userhost, sizeof(userhost), "%s@%s", user, host);
531   else
532     ircd_strncpy(userhost, host, sizeof(userhost) - 1);
533
534   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
535     if (!(tmp->status & statmask) || !tmp->name || !tmp->host ||
536         0 != ircd_strcmp(tmp->name, name))
537       continue;
538     /*
539      * Accept if the *real* hostname (usually sockecthost)
540      * socket host) matches *either* host or name field
541      * of the configuration.
542      */
543     if (match(tmp->host, userhost))
544       continue;
545     if (tmp->status & CONF_OPERATOR) {
546       if (tmp->clients < MaxLinks(tmp->conn_class))
547         return tmp;
548       else
549         continue;
550     }
551     else
552       return tmp;
553   }
554   return 0;
555 }
556
557 struct ConfItem* find_conf_byname(struct SLink* lp, const char* name,
558                                   int statmask)
559 {
560   struct ConfItem* tmp;
561   assert(0 != name);
562
563   if (HOSTLEN < strlen(name))
564     return 0;
565
566   for (; lp; lp = lp->next) {
567     tmp = lp->value.aconf;
568     if (0 != (tmp->status & statmask)) {
569       assert(0 != tmp->name);
570       if (0 == ircd_strcmp(tmp->name, name) || 0 == match(tmp->name, name))
571         return tmp;
572     }
573   }
574   return 0;
575 }
576
577 /*
578  * Added for new access check    meLazy
579  */
580 struct ConfItem* find_conf_byhost(struct SLink* lp, const char* host,
581                                   int statmask)
582 {
583   struct ConfItem* tmp = NULL;
584   assert(0 != host);
585
586   if (HOSTLEN < strlen(host))
587     return 0;
588
589   for (; lp; lp = lp->next) {
590     tmp = lp->value.aconf;
591     if (0 != (tmp->status & statmask)) {
592       assert(0 != tmp->host);
593       if (0 == match(tmp->host, host))
594         return tmp;
595     }
596   }
597   return 0;
598 }
599
600 /*
601  * find_conf_ip
602  *
603  * Find a conf line using the IP# stored in it to search upon.
604  * Added 1/8/92 by Avalon.
605  */
606 struct ConfItem* find_conf_byip(struct SLink* lp, const struct irc_in_addr* ip,
607                                 int statmask)
608 {
609   struct ConfItem* tmp;
610
611   for (; lp; lp = lp->next) {
612     tmp = lp->value.aconf;
613     if (0 != (tmp->status & statmask)
614         && !irc_in_addr_cmp(&tmp->address.addr, ip))
615       return tmp;
616   }
617   return 0;
618 }
619
620 /*
621  * find_conf_entry
622  *
623  * - looks for a match on all given fields.
624  */
625 #if 0
626 static struct ConfItem *find_conf_entry(struct ConfItem *aconf,
627                                         unsigned int mask)
628 {
629   struct ConfItem *bconf;
630   assert(0 != aconf);
631
632   mask &= ~CONF_ILLEGAL;
633
634   for (bconf = GlobalConfList; bconf; bconf = bconf->next) {
635     if (!(bconf->status & mask) || (bconf->port != aconf->port))
636       continue;
637
638     if ((EmptyString(bconf->host) && !EmptyString(aconf->host)) ||
639         (EmptyString(aconf->host) && !EmptyString(bconf->host)))
640       continue;
641     if (!EmptyString(bconf->host) && 0 != ircd_strcmp(bconf->host, aconf->host))
642       continue;
643
644     if ((EmptyString(bconf->passwd) && !EmptyString(aconf->passwd)) ||
645         (EmptyString(aconf->passwd) && !EmptyString(bconf->passwd)))
646       continue;
647     if (!EmptyString(bconf->passwd) && (!IsDigit(*bconf->passwd) || bconf->passwd[1])
648         && 0 != ircd_strcmp(bconf->passwd, aconf->passwd))
649       continue;
650
651     if ((EmptyString(bconf->name) && !EmptyString(aconf->name)) ||
652         (EmptyString(aconf->name) && !EmptyString(bconf->name)))
653       continue;
654     if (!EmptyString(bconf->name) && 0 != ircd_strcmp(bconf->name, aconf->name))
655       continue;
656     break;
657   }
658   return bconf;
659 }
660
661 /*
662  * If conf line is a class definition, create a class entry
663  * for it and make the conf_line illegal and delete it.
664  */
665 void conf_add_class(const char* const* fields, int count)
666 {
667   if (count < 6)
668     return;
669   add_class(atoi(fields[1]), atoi(fields[2]), atoi(fields[3]),
670             atoi(fields[4]), atoi(fields[5]));
671 }
672
673 void conf_add_listener(const char* const* fields, int count)
674 {
675   int is_server = 0;
676   int is_hidden = 0;
677
678   /*
679    * need a port
680    */
681   if (count < 5 || EmptyString(fields[4]))
682     return;
683
684   if (!EmptyString(fields[3])) {
685     const char* x = fields[3];
686     if ('S' == ToUpper(*x))
687       is_server = 1;
688     ++x;
689     if ('H' == ToUpper(*x))
690       is_hidden = 1;
691   }
692   /*           port             vhost      mask  */
693   add_listener(atoi(fields[4]), fields[2], fields[1], is_server, is_hidden);
694 }
695
696 void conf_add_admin(const char* const* fields, int count)
697 {
698   /*
699    * if you have one, it MUST have 3 lines
700    */
701   if (count < 4) {
702     log_write(LS_CONFIG, L_CRIT, 0, "Your A: line must have 4 fields!");
703     return;
704   }
705   MyFree(localConf.location1);
706   DupString(localConf.location1, fields[1]);
707
708   MyFree(localConf.location2);
709   DupString(localConf.location2, fields[2]);
710
711   MyFree(localConf.contact);
712   DupString(localConf.contact, fields[3]);
713 }
714
715 /*
716  * conf_add_crule - Create expression tree from connect rule and add it
717  * to the crule list
718  */
719 void conf_add_crule(const char* const* fields, int count, int type)
720 {
721   struct CRuleNode* node;
722   assert(0 != fields);
723   
724   if (count < 4 || EmptyString(fields[1]) || EmptyString(fields[3]))
725     return;
726   
727   if ((node = crule_parse(fields[3]))) {
728     struct CRuleConf* p = (struct CRuleConf*) MyMalloc(sizeof(struct CRuleConf));
729     assert(0 != p);
730
731     DupString(p->hostmask, fields[1]);
732     collapse(p->hostmask);
733
734     DupString(p->rule, fields[3]);
735
736     p->type = type;
737     p->node = node;
738     p->next = cruleConfList;
739     cruleConfList = p;
740   } 
741 }
742 #endif
743
744 void conf_erase_crule_list(void)
745 {
746   struct CRuleConf* next;
747   struct CRuleConf* p = cruleConfList;
748
749   for ( ; p; p = next) {
750     next = p->next;
751     crule_free(&p->node);
752     MyFree(p->hostmask);
753     MyFree(p->rule);
754     MyFree(p);
755   }
756   cruleConfList = 0;
757 }
758
759 const struct CRuleConf* conf_get_crule_list(void)
760 {
761   return cruleConfList;
762 }
763
764 void conf_erase_deny_list(void)
765 {
766   struct DenyConf* next;
767   struct DenyConf* p = denyConfList;
768   for ( ; p; p = next) {
769     next = p->next;
770     MyFree(p->hostmask);
771     MyFree(p->usermask);
772     MyFree(p->message);
773     MyFree(p);
774   }
775   denyConfList = 0;
776 }
777
778 const struct DenyConf* conf_get_deny_list(void)
779 {
780   return denyConfList;
781 }
782
783 const char*
784 find_quarantine(const char *chname)
785 {
786   struct qline *qline;
787
788   for (qline = GlobalQuarantineList; qline; qline = qline->next)
789     if (!ircd_strcmp(qline->chname, chname))
790       return qline->reason;
791   return NULL;
792 }
793
794 void clear_quarantines(void)
795 {
796   struct qline *qline;
797   while ((qline = GlobalQuarantineList))
798   {
799     GlobalQuarantineList = qline->next;
800     MyFree(qline->reason);
801     MyFree(qline->chname);
802     MyFree(qline);
803   }
804 }
805
806
807 /*
808  * read_configuration_file
809  *
810  * Read configuration file.
811  *
812  * returns 0, if file cannot be opened
813  *         1, if file read
814  */
815
816 #define MAXCONFLINKS 150
817
818 static int conf_error;
819 static int conf_already_read;
820 extern FILE *yyin;
821 void init_lexer(void);
822
823 int read_configuration_file(void)
824 {
825   conf_error = 0;
826   feature_unmark(); /* unmark all features for resetting later */
827   /* Now just open an fd. The buffering isn't really needed... */
828   init_lexer();
829   yyparse();
830   fclose(yyin);
831   yyin = NULL;
832   feature_mark(); /* reset unmarked features */
833   conf_already_read = 1;
834   return 1;
835 }
836
837 void
838 yyerror(const char *msg)
839 {
840  sendto_opmask_butone(0, SNO_ALL, "Config file parse error line %d: %s",
841                       lineno, msg);
842  log_write(LS_CONFIG, L_ERROR, 0, "Config file parse error line %d: %s",
843            lineno, msg);
844  if (!conf_already_read)
845    fprintf(stderr, "Config file parse error line %d: %s\n", lineno, msg);
846  conf_error = 1;
847 }
848
849 /*
850  * rehash
851  *
852  * Actual REHASH service routine. Called with sig == 0 if it has been called
853  * as a result of an operator issuing this command, else assume it has been
854  * called as a result of the server receiving a HUP signal.
855  */
856 int rehash(struct Client *cptr, int sig)
857 {
858   struct ConfItem** tmp = &GlobalConfList;
859   struct ConfItem*  tmp2;
860   struct Client*    acptr;
861   int               i;
862   int               ret = 0;
863   int               found_g = 0;
864
865   if (1 == sig)
866     sendto_opmask_butone(0, SNO_OLDSNO,
867                          "Got signal SIGHUP, reloading ircd conf. file");
868
869   while ((tmp2 = *tmp)) {
870     if (tmp2->clients) {
871       /*
872        * Configuration entry is still in use by some
873        * local clients, cannot delete it--mark it so
874        * that it will be deleted when the last client
875        * exits...
876        */
877       if (CONF_CLIENT == (tmp2->status & CONF_CLIENT))
878         tmp = &tmp2->next;
879       else {
880         *tmp = tmp2->next;
881         tmp2->next = 0;
882       }
883       tmp2->status |= CONF_ILLEGAL;
884     }
885     else {
886       *tmp = tmp2->next;
887       free_conf(tmp2);
888     }
889   }
890   conf_erase_crule_list();
891   conf_erase_deny_list();
892   motd_clear();
893
894   /*
895    * delete the juped nicks list
896    */
897   clearNickJupes();
898
899   clear_quarantines();
900
901   if (sig != 2)
902     restart_resolver();
903
904   class_mark_delete();
905   mark_listeners_closing();
906   iauth_mark_closing();
907
908   read_configuration_file();
909
910   log_reopen(); /* reopen log files */
911
912   iauth_close_unused();
913   close_listeners();
914   class_delete_marked();         /* unless it fails */
915
916   /*
917    * Flush out deleted I and P lines although still in use.
918    */
919   for (tmp = &GlobalConfList; (tmp2 = *tmp);) {
920     if (CONF_ILLEGAL == (tmp2->status & CONF_ILLEGAL)) {
921       *tmp = tmp2->next;
922       tmp2->next = NULL;
923       if (!tmp2->clients)
924         free_conf(tmp2);
925     }
926     else
927       tmp = &tmp2->next;
928   }
929
930   for (i = 0; i <= HighestFd; i++) {
931     if ((acptr = LocalClientArray[i])) {
932       assert(!IsMe(acptr));
933       if (IsServer(acptr)) {
934         det_confs_butmask(acptr,
935             ~(CONF_HUB | CONF_LEAF | CONF_UWORLD | CONF_ILLEGAL));
936         attach_confs_byname(acptr, cli_name(acptr),
937                             CONF_HUB | CONF_LEAF | CONF_UWORLD);
938       }
939       /* Because admin's are getting so uppity about people managing to
940        * get past K/G's etc, we'll "fix" the bug by actually explaining
941        * whats going on.
942        */
943       if ((found_g = find_kill(acptr))) {
944         sendto_opmask_butone(0, found_g == -2 ? SNO_GLINE : SNO_OPERKILL,
945                              found_g == -2 ? "G-line active for %s%s" :
946                              "K-line active for %s%s",
947                              IsUnknown(acptr) ? "Unregistered Client ":"",
948                              get_client_name(acptr, SHOW_IP));
949         if (exit_client(cptr, acptr, &me, found_g == -2 ? "G-lined" :
950             "K-lined") == CPTR_KILLED)
951           ret = CPTR_KILLED;
952       }
953     }
954   }
955
956   return ret;
957 }
958
959 /*
960  * init_conf
961  *
962  * Read configuration file.
963  *
964  * returns 0, if file cannot be opened
965  *         1, if file read
966  */
967
968 int init_conf(void)
969 {
970   if (read_configuration_file()) {
971     /*
972      * make sure we're sane to start if the config
973      * file read didn't get everything we need.
974      * XXX - should any of these abort the server?
975      * TODO: add warning messages
976      */
977     if (0 == localConf.name || 0 == localConf.numeric)
978       return 0;
979     if (conf_error)
980       return 0;
981
982     if (0 == localConf.location1)
983       DupString(localConf.location1, "");
984     if (0 == localConf.location2)
985       DupString(localConf.location2, "");
986     if (0 == localConf.contact)
987       DupString(localConf.contact, "");
988
989     return 1;
990   }
991   return 0;
992 }
993
994 /*
995  * find_kill
996  * input:
997  *  client pointer
998  * returns:
999  *  0: Client may continue to try and connect
1000  * -1: Client was K/k:'d - sideeffect: reason was sent.
1001  * -2: Client was G/g:'d - sideeffect: reason was sent.
1002  * sideeffects:
1003  *  Client may have been sent a reason why they are denied, as above.
1004  */
1005 int find_kill(struct Client *cptr)
1006 {
1007   const char*      host;
1008   const char*      name;
1009   const char*      realname;
1010   struct DenyConf* deny;
1011   struct Gline*    agline = NULL;
1012
1013   assert(0 != cptr);
1014
1015   if (!cli_user(cptr))
1016     return 0;
1017
1018   host = cli_sockhost(cptr);
1019   name = cli_user(cptr)->username;
1020   realname = cli_info(cptr);
1021
1022   assert(strlen(host) <= HOSTLEN);
1023   assert((name ? strlen(name) : 0) <= HOSTLEN);
1024   assert((realname ? strlen(realname) : 0) <= REALLEN);
1025
1026   /* 2000-07-14: Rewrote this loop for massive speed increases.
1027    *             -- Isomer
1028    */
1029   for (deny = denyConfList; deny; deny = deny->next) {
1030     if (0 != match(deny->usermask, name))
1031       continue;
1032
1033     if (EmptyString(deny->hostmask))
1034       break;
1035
1036     if (deny->flags & DENY_FLAGS_REALNAME) { /* K: by real name */
1037       if (0 == match(deny->hostmask + 2, realname))
1038         break;
1039     } else if (deny->flags & DENY_FLAGS_IP) { /* k: by IP */
1040 #ifdef DEBUGMODE
1041       char tbuf1[SOCKIPLEN], tbuf2[SOCKIPLEN];
1042       Debug((DEBUG_DEBUG, "ip: %s network: %s/%u",
1043              ircd_ntoa_r(tbuf1, &cli_ip(cptr)), ircd_ntoa_r(tbuf2, &deny->address), deny->bits));
1044 #endif
1045       if (ipmask_check(&cli_ip(cptr), &deny->address, deny->bits))
1046         break;
1047     }
1048     else if (0 == match(deny->hostmask, host))
1049       break;
1050   }
1051   if (deny) {
1052     if (EmptyString(deny->message))
1053       send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP,
1054                  ":Connection from your host is refused on this server.");
1055     else {
1056       if (deny->flags & DENY_FLAGS_FILE)
1057         killcomment(cptr, deny->message);
1058       else
1059         send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", deny->message);
1060     }
1061   }
1062   else if ((agline = gline_lookup(cptr, 0))) {
1063     /*
1064      * find active glines
1065      * added a check against the user's IP address to find_gline() -Kev
1066      */
1067     send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", GlineReason(agline));
1068   }
1069
1070   if (deny)
1071     return -1;
1072   if (agline)
1073     return -2;
1074     
1075   return 0;
1076 }
1077
1078 /*
1079  * Ordinary client access check. Look for conf lines which have the same
1080  * status as the flags passed.
1081  */
1082 enum AuthorizationCheckResult conf_check_client(struct Client *cptr)
1083 {
1084   enum AuthorizationCheckResult acr = ACR_OK;
1085
1086   ClearAccess(cptr);
1087
1088   if ((acr = attach_iline(cptr))) {
1089     Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]", 
1090           cli_name(cptr), cli_sockhost(cptr)));
1091     return acr;
1092   }
1093   return ACR_OK;
1094 }
1095
1096 /*
1097  * check_server()
1098  *
1099  * Check access for a server given its name (passed in cptr struct).
1100  * Must check for all C/N lines which have a name which matches the
1101  * name given and a host which matches. A host alias which is the
1102  * same as the server name is also acceptable in the host field of a
1103  * C/N line.
1104  *
1105  * Returns
1106  *  0 = Success
1107  * -1 = Access denied
1108  * -2 = Bad socket.
1109  */
1110 int conf_check_server(struct Client *cptr)
1111 {
1112   struct ConfItem* c_conf = NULL;
1113   struct SLink*    lp;
1114
1115   Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", 
1116         cli_name(cptr), cli_sockhost(cptr)));
1117
1118   if (IsUnknown(cptr) && !attach_confs_byname(cptr, cli_name(cptr), CONF_SERVER)) {
1119     Debug((DEBUG_DNS, "No C/N lines for %s", cli_sockhost(cptr)));
1120     return -1;
1121   }
1122   lp = cli_confs(cptr);
1123   /*
1124    * We initiated this connection so the client should have a C and N
1125    * line already attached after passing through the connect_server()
1126    * function earlier.
1127    */
1128   if (IsConnecting(cptr) || IsHandshake(cptr)) {
1129     c_conf = find_conf_byname(lp, cli_name(cptr), CONF_SERVER);
1130     if (!c_conf) {
1131       sendto_opmask_butone(0, SNO_OLDSNO, "Connect Error: lost C:line for %s",
1132                            cli_name(cptr));
1133       det_confs_butmask(cptr, 0);
1134       return -1;
1135     }
1136   }
1137
1138   ClearAccess(cptr);
1139
1140   if (!c_conf) {
1141     if (cli_dns_reply(cptr)) {
1142       struct DNSReply* hp = cli_dns_reply(cptr);
1143       const char*     name = hp->h_name;
1144       /*
1145        * If we are missing a C or N line from above, search for
1146        * it under all known hostnames we have for this ip#.
1147        */
1148       if ((c_conf = find_conf_byhost(lp, hp->h_name, CONF_SERVER)))
1149         ircd_strncpy(cli_sockhost(cptr), name, HOSTLEN);
1150       else
1151         c_conf = find_conf_byip(lp, &hp->addr, CONF_SERVER);
1152     }
1153     else {
1154       /*
1155        * Check for C lines with the hostname portion the ip number
1156        * of the host the server runs on. This also checks the case where
1157        * there is a server connecting from 'localhost'.
1158        */
1159       c_conf = find_conf_byhost(lp, cli_sockhost(cptr), CONF_SERVER);
1160     }
1161   }
1162   /*
1163    * Attach by IP# only if all other checks have failed.
1164    * It is quite possible to get here with the strange things that can
1165    * happen when using DNS in the way the irc server does. -avalon
1166    */
1167   if (!c_conf)
1168     c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER);
1169   /*
1170    * detach all conf lines that got attached by attach_confs()
1171    */
1172   det_confs_butmask(cptr, 0);
1173   /*
1174    * if no C or no N lines, then deny access
1175    */
1176   if (!c_conf) {
1177     Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s]",
1178           cli_name(cptr), cli_username(cptr), cli_sockhost(cptr)));
1179     return -1;
1180   }
1181   ircd_strncpy(cli_name(cptr), c_conf->name, HOSTLEN);
1182   /*
1183    * attach the C and N lines to the client structure for later use.
1184    */
1185   attach_conf(cptr, c_conf);
1186   attach_confs_byname(cptr, cli_name(cptr), CONF_HUB | CONF_LEAF | CONF_UWORLD);
1187
1188   if (!irc_in_addr_valid(&c_conf->address.addr))
1189     memcpy(&c_conf->address.addr, &cli_ip(cptr), sizeof(c_conf->address.addr));
1190
1191   Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]",
1192          cli_name(cptr), cli_sockhost(cptr)));
1193   return 0;
1194 }
1195