bcc498bd6e2a00bd8fd9921d771a658c8c53c261
[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_local(const char* const* fields, int count)
697 {
698   if (count < 6 || EmptyString(fields[1]) || EmptyString(fields[5])) {
699     log_write(LS_CONFIG, L_CRIT, 0, "Your M: line must have 6 fields!");
700     return;
701   }
702   /*
703    * these two can only be set the first time
704    */
705   if (0 == localConf.name) {
706     if (string_is_hostname(fields[1]))
707       DupString(localConf.name, fields[1]);
708   }
709   if (0 == localConf.numeric) {
710     localConf.numeric = atoi(fields[5]);
711     if (0 == localConf.numeric)
712       log_write(LS_CONFIG, L_WARNING, 0,
713                 "Your M: line must have a Numeric value greater than 0");
714   }
715   /*
716    * these two can be changed while the server is running
717    */
718   if (string_is_address(fields[2])) {
719     if (INADDR_NONE == (localConf.vhost_address.s_addr = inet_addr(fields[2])))
720       localConf.vhost_address.s_addr = INADDR_ANY;
721   }
722   MyFree(localConf.description);
723   DupString(localConf.description, fields[3]);
724   /*
725    * XXX - shouldn't be setting these directly here
726    */
727   ircd_strncpy(cli_info(&me), fields[3], REALLEN);
728   set_virtual_host(localConf.vhost_address);
729 }
730
731 void conf_add_admin(const char* const* fields, int count)
732 {
733   /*
734    * if you have one, it MUST have 3 lines
735    */
736   if (count < 4) {
737     log_write(LS_CONFIG, L_CRIT, 0, "Your A: line must have 4 fields!");
738     return;
739   }
740   MyFree(localConf.location1);
741   DupString(localConf.location1, fields[1]);
742
743   MyFree(localConf.location2);
744   DupString(localConf.location2, fields[2]);
745
746   MyFree(localConf.contact);
747   DupString(localConf.contact, fields[3]);
748 }
749
750 /*
751  * conf_add_crule - Create expression tree from connect rule and add it
752  * to the crule list
753  */
754 void conf_add_crule(const char* const* fields, int count, int type)
755 {
756   struct CRuleNode* node;
757   assert(0 != fields);
758   
759   if (count < 4 || EmptyString(fields[1]) || EmptyString(fields[3]))
760     return;
761   
762   if ((node = crule_parse(fields[3]))) {
763     struct CRuleConf* p = (struct CRuleConf*) MyMalloc(sizeof(struct CRuleConf));
764     assert(0 != p);
765
766     DupString(p->hostmask, fields[1]);
767     collapse(p->hostmask);
768
769     DupString(p->rule, fields[3]);
770
771     p->type = type;
772     p->node = node;
773     p->next = cruleConfList;
774     cruleConfList = p;
775   } 
776 }
777 #endif
778
779 void conf_erase_crule_list(void)
780 {
781   struct CRuleConf* next;
782   struct CRuleConf* p = cruleConfList;
783
784   for ( ; p; p = next) {
785     next = p->next;
786     crule_free(&p->node);
787     MyFree(p->hostmask);
788     MyFree(p->rule);
789     MyFree(p);
790   }
791   cruleConfList = 0;
792 }
793
794 const struct CRuleConf* conf_get_crule_list(void)
795 {
796   return cruleConfList;
797 }
798
799 void conf_erase_deny_list(void)
800 {
801   struct DenyConf* next;
802   struct DenyConf* p = denyConfList;
803   for ( ; p; p = next) {
804     next = p->next;
805     MyFree(p->hostmask);
806     MyFree(p->usermask);
807     MyFree(p->message);
808     MyFree(p);
809   }
810   denyConfList = 0;
811 }
812
813 const struct DenyConf* conf_get_deny_list(void)
814 {
815   return denyConfList;
816 }
817
818 const char*
819 find_quarantine(const char *chname)
820 {
821   struct qline *qline;
822
823   for (qline = GlobalQuarantineList; qline; qline = qline->next)
824     if (!ircd_strcmp(qline->chname, chname))
825       return qline->reason;
826   return NULL;
827 }
828
829 void clear_quarantines(void)
830 {
831   struct qline *qline;
832   while ((qline = GlobalQuarantineList))
833   {
834     GlobalQuarantineList = qline->next;
835     MyFree(qline->reason);
836     MyFree(qline->chname);
837     MyFree(qline);
838   }
839 }
840
841
842 /*
843  * read_configuration_file
844  *
845  * Read configuration file.
846  *
847  * returns 0, if file cannot be opened
848  *         1, if file read
849  */
850
851 #define MAXCONFLINKS 150
852
853 static int conf_error;
854 static int conf_already_read;
855 extern FILE *yyin;
856 void init_lexer(void);
857
858 int read_configuration_file(void)
859 {
860   conf_error = 0;
861   feature_unmark(); /* unmark all features for resetting later */
862   /* Now just open an fd. The buffering isn't really needed... */
863   init_lexer();
864   yyparse();
865   fclose(yyin);
866   yyin = NULL;
867   feature_mark(); /* reset unmarked features */
868   conf_already_read = 1;
869   return 1;
870 }
871
872 void
873 yyerror(const char *msg)
874 {
875  sendto_opmask_butone(0, SNO_ALL, "Config file parse error line %d: %s",
876                       lineno, msg);
877  log_write(LS_CONFIG, L_ERROR, 0, "Config file parse error line %d: %s",
878            lineno, msg);
879  if (!conf_already_read)
880    fprintf(stderr, "Config file parse error line %d: %s\n", lineno, msg);
881  conf_error = 1;
882 }
883
884 /*
885  * rehash
886  *
887  * Actual REHASH service routine. Called with sig == 0 if it has been called
888  * as a result of an operator issuing this command, else assume it has been
889  * called as a result of the server receiving a HUP signal.
890  */
891 int rehash(struct Client *cptr, int sig)
892 {
893   struct ConfItem** tmp = &GlobalConfList;
894   struct ConfItem*  tmp2;
895   struct Client*    acptr;
896   int               i;
897   int               ret = 0;
898   int               found_g = 0;
899
900   if (1 == sig)
901     sendto_opmask_butone(0, SNO_OLDSNO,
902                          "Got signal SIGHUP, reloading ircd conf. file");
903
904   while ((tmp2 = *tmp)) {
905     if (tmp2->clients) {
906       /*
907        * Configuration entry is still in use by some
908        * local clients, cannot delete it--mark it so
909        * that it will be deleted when the last client
910        * exits...
911        */
912       if (CONF_CLIENT == (tmp2->status & CONF_CLIENT))
913         tmp = &tmp2->next;
914       else {
915         *tmp = tmp2->next;
916         tmp2->next = 0;
917       }
918       tmp2->status |= CONF_ILLEGAL;
919     }
920     else {
921       *tmp = tmp2->next;
922       free_conf(tmp2);
923     }
924   }
925   conf_erase_crule_list();
926   conf_erase_deny_list();
927   motd_clear();
928
929   /*
930    * delete the juped nicks list
931    */
932   clearNickJupes();
933
934   clear_quarantines();
935
936   if (sig != 2)
937     restart_resolver();
938
939   class_mark_delete();
940   mark_listeners_closing();
941   iauth_mark_closing();
942
943   read_configuration_file();
944
945   log_reopen(); /* reopen log files */
946
947   iauth_close_unused();
948   close_listeners();
949   class_delete_marked();         /* unless it fails */
950
951   /*
952    * Flush out deleted I and P lines although still in use.
953    */
954   for (tmp = &GlobalConfList; (tmp2 = *tmp);) {
955     if (CONF_ILLEGAL == (tmp2->status & CONF_ILLEGAL)) {
956       *tmp = tmp2->next;
957       tmp2->next = NULL;
958       if (!tmp2->clients)
959         free_conf(tmp2);
960     }
961     else
962       tmp = &tmp2->next;
963   }
964
965   for (i = 0; i <= HighestFd; i++) {
966     if ((acptr = LocalClientArray[i])) {
967       assert(!IsMe(acptr));
968       if (IsServer(acptr)) {
969         det_confs_butmask(acptr,
970             ~(CONF_HUB | CONF_LEAF | CONF_UWORLD | CONF_ILLEGAL));
971         attach_confs_byname(acptr, cli_name(acptr),
972                             CONF_HUB | CONF_LEAF | CONF_UWORLD);
973       }
974       /* Because admin's are getting so uppity about people managing to
975        * get past K/G's etc, we'll "fix" the bug by actually explaining
976        * whats going on.
977        */
978       if ((found_g = find_kill(acptr))) {
979         sendto_opmask_butone(0, found_g == -2 ? SNO_GLINE : SNO_OPERKILL,
980                              found_g == -2 ? "G-line active for %s%s" :
981                              "K-line active for %s%s",
982                              IsUnknown(acptr) ? "Unregistered Client ":"",
983                              get_client_name(acptr, SHOW_IP));
984         if (exit_client(cptr, acptr, &me, found_g == -2 ? "G-lined" :
985             "K-lined") == CPTR_KILLED)
986           ret = CPTR_KILLED;
987       }
988     }
989   }
990
991   return ret;
992 }
993
994 /*
995  * init_conf
996  *
997  * Read configuration file.
998  *
999  * returns 0, if file cannot be opened
1000  *         1, if file read
1001  */
1002
1003 int init_conf(void)
1004 {
1005   if (read_configuration_file()) {
1006     /*
1007      * make sure we're sane to start if the config
1008      * file read didn't get everything we need.
1009      * XXX - should any of these abort the server?
1010      * TODO: add warning messages
1011      */
1012     if (0 == localConf.name || 0 == localConf.numeric)
1013       return 0;
1014     if (conf_error)
1015       return 0;
1016
1017     if (0 == localConf.location1)
1018       DupString(localConf.location1, "");
1019     if (0 == localConf.location2)
1020       DupString(localConf.location2, "");
1021     if (0 == localConf.contact)
1022       DupString(localConf.contact, "");
1023
1024     return 1;
1025   }
1026   return 0;
1027 }
1028
1029 /*
1030  * find_kill
1031  * input:
1032  *  client pointer
1033  * returns:
1034  *  0: Client may continue to try and connect
1035  * -1: Client was K/k:'d - sideeffect: reason was sent.
1036  * -2: Client was G/g:'d - sideeffect: reason was sent.
1037  * sideeffects:
1038  *  Client may have been sent a reason why they are denied, as above.
1039  */
1040 int find_kill(struct Client *cptr)
1041 {
1042   const char*      host;
1043   const char*      name;
1044   const char*      realname;
1045   struct DenyConf* deny;
1046   struct Gline*    agline = NULL;
1047
1048   assert(0 != cptr);
1049
1050   if (!cli_user(cptr))
1051     return 0;
1052
1053   host = cli_sockhost(cptr);
1054   name = cli_user(cptr)->username;
1055   realname = cli_info(cptr);
1056
1057   assert(strlen(host) <= HOSTLEN);
1058   assert((name ? strlen(name) : 0) <= HOSTLEN);
1059   assert((realname ? strlen(realname) : 0) <= REALLEN);
1060
1061   /* 2000-07-14: Rewrote this loop for massive speed increases.
1062    *             -- Isomer
1063    */
1064   for (deny = denyConfList; deny; deny = deny->next) {
1065     if (0 != match(deny->usermask, name))
1066       continue;
1067
1068     if (EmptyString(deny->hostmask))
1069       break;
1070
1071     if (deny->flags & DENY_FLAGS_REALNAME) { /* K: by real name */
1072       if (0 == match(deny->hostmask + 2, realname))
1073         break;
1074     } else if (deny->flags & DENY_FLAGS_IP) { /* k: by IP */
1075 #ifdef DEBUGMODE
1076       char tbuf1[SOCKIPLEN], tbuf2[SOCKIPLEN];
1077       Debug((DEBUG_DEBUG, "ip: %s network: %s/%u",
1078              ircd_ntoa_r(tbuf1, &cli_ip(cptr)), ircd_ntoa_r(tbuf2, &deny->address), deny->bits));
1079 #endif
1080       if (ipmask_check(&cli_ip(cptr), &deny->address, deny->bits))
1081         break;
1082     }
1083     else if (0 == match(deny->hostmask, host))
1084       break;
1085   }
1086   if (deny) {
1087     if (EmptyString(deny->message))
1088       send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP,
1089                  ":Connection from your host is refused on this server.");
1090     else {
1091       if (deny->flags & DENY_FLAGS_FILE)
1092         killcomment(cptr, deny->message);
1093       else
1094         send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", deny->message);
1095     }
1096   }
1097   else if ((agline = gline_lookup(cptr, 0))) {
1098     /*
1099      * find active glines
1100      * added a check against the user's IP address to find_gline() -Kev
1101      */
1102     send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", GlineReason(agline));
1103   }
1104
1105   if (deny)
1106     return -1;
1107   if (agline)
1108     return -2;
1109     
1110   return 0;
1111 }
1112
1113 /*
1114  * Ordinary client access check. Look for conf lines which have the same
1115  * status as the flags passed.
1116  */
1117 enum AuthorizationCheckResult conf_check_client(struct Client *cptr)
1118 {
1119   enum AuthorizationCheckResult acr = ACR_OK;
1120
1121   ClearAccess(cptr);
1122
1123   if ((acr = attach_iline(cptr))) {
1124     Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]", 
1125           cli_name(cptr), cli_sockhost(cptr)));
1126     return acr;
1127   }
1128   return ACR_OK;
1129 }
1130
1131 /*
1132  * check_server()
1133  *
1134  * Check access for a server given its name (passed in cptr struct).
1135  * Must check for all C/N lines which have a name which matches the
1136  * name given and a host which matches. A host alias which is the
1137  * same as the server name is also acceptable in the host field of a
1138  * C/N line.
1139  *
1140  * Returns
1141  *  0 = Success
1142  * -1 = Access denied
1143  * -2 = Bad socket.
1144  */
1145 int conf_check_server(struct Client *cptr)
1146 {
1147   struct ConfItem* c_conf = NULL;
1148   struct SLink*    lp;
1149
1150   Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", 
1151         cli_name(cptr), cli_sockhost(cptr)));
1152
1153   if (IsUnknown(cptr) && !attach_confs_byname(cptr, cli_name(cptr), CONF_SERVER)) {
1154     Debug((DEBUG_DNS, "No C/N lines for %s", cli_sockhost(cptr)));
1155     return -1;
1156   }
1157   lp = cli_confs(cptr);
1158   /*
1159    * We initiated this connection so the client should have a C and N
1160    * line already attached after passing through the connect_server()
1161    * function earlier.
1162    */
1163   if (IsConnecting(cptr) || IsHandshake(cptr)) {
1164     c_conf = find_conf_byname(lp, cli_name(cptr), CONF_SERVER);
1165     if (!c_conf) {
1166       sendto_opmask_butone(0, SNO_OLDSNO, "Connect Error: lost C:line for %s",
1167                            cli_name(cptr));
1168       det_confs_butmask(cptr, 0);
1169       return -1;
1170     }
1171   }
1172
1173   ClearAccess(cptr);
1174
1175   if (!c_conf) {
1176     if (cli_dns_reply(cptr)) {
1177       struct DNSReply* hp = cli_dns_reply(cptr);
1178       const char*     name = hp->h_name;
1179       /*
1180        * If we are missing a C or N line from above, search for
1181        * it under all known hostnames we have for this ip#.
1182        */
1183       if ((c_conf = find_conf_byhost(lp, hp->h_name, CONF_SERVER)))
1184         ircd_strncpy(cli_sockhost(cptr), name, HOSTLEN);
1185       else
1186         c_conf = find_conf_byip(lp, &hp->addr, CONF_SERVER);
1187     }
1188     else {
1189       /*
1190        * Check for C lines with the hostname portion the ip number
1191        * of the host the server runs on. This also checks the case where
1192        * there is a server connecting from 'localhost'.
1193        */
1194       c_conf = find_conf_byhost(lp, cli_sockhost(cptr), CONF_SERVER);
1195     }
1196   }
1197   /*
1198    * Attach by IP# only if all other checks have failed.
1199    * It is quite possible to get here with the strange things that can
1200    * happen when using DNS in the way the irc server does. -avalon
1201    */
1202   if (!c_conf)
1203     c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER);
1204   /*
1205    * detach all conf lines that got attached by attach_confs()
1206    */
1207   det_confs_butmask(cptr, 0);
1208   /*
1209    * if no C or no N lines, then deny access
1210    */
1211   if (!c_conf) {
1212     Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s]",
1213           cli_name(cptr), cli_username(cptr), cli_sockhost(cptr)));
1214     return -1;
1215   }
1216   ircd_strncpy(cli_name(cptr), c_conf->name, HOSTLEN);
1217   /*
1218    * attach the C and N lines to the client structure for later use.
1219    */
1220   attach_conf(cptr, c_conf);
1221   attach_confs_byname(cptr, cli_name(cptr), CONF_HUB | CONF_LEAF | CONF_UWORLD);
1222
1223   if (!irc_in_addr_valid(&c_conf->address.addr))
1224     memcpy(&c_conf->address.addr, &cli_ip(cptr), sizeof(c_conf->address.addr));
1225
1226   Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]",
1227          cli_name(cptr), cli_sockhost(cptr)));
1228   return 0;
1229 }
1230