Author: Bleep <tomh@inxpress.net>
[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 "s_conf.h"
23
24 #include "IPcheck.h"
25 #include "class.h"
26 #include "client.h"
27 #include "crule.h"
28 #include "fileio.h"
29 #include "gline.h"
30 #include "hash.h"
31 #include "ircd.h"
32 #include "ircd_alloc.h"
33 #include "ircd_chattr.h"
34 #include "ircd_log.h"
35 #include "ircd_string.h"
36 #include "list.h"
37 #include "listener.h"
38 #include "match.h"
39 #include "numeric.h"
40 #include "numnicks.h"
41 #include "opercmds.h"
42 #include "parse.h"
43 #include "res.h"
44 #include "s_bsd.h"
45 #include "s_debug.h"
46 #include "s_misc.h"
47 #include "send.h"
48 #include "sprintf_irc.h"
49 #include "struct.h"
50 #include "support.h"
51 #include "sys.h"
52
53 #include <assert.h>
54 #include <arpa/inet.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <netdb.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <sys/stat.h>
62 #include <unistd.h>
63
64 #ifndef INADDR_NONE
65 #define INADDR_NONE 0xffffffff
66 #endif
67
68 struct ConfItem* GlobalConfList  = 0;
69 int              GlobalConfCount = 0;
70 struct MotdItem* motd = NULL;
71 struct MotdItem* rmotd = NULL;
72 struct TRecord*  tdata = NULL;
73 struct tm        motd_tm;
74
75
76 /*
77  *  is the K line field an interval or a comment? - Mmmm
78  */
79 static int is_comment(const char *comment)
80 {
81   size_t i;
82   size_t len = strlen(comment);
83   for (i = 0; i < len; ++i) {
84     if (!IsKTimeChar(comment[i]))
85       return 1;
86   }
87   return 0;
88 }
89
90 /*
91  *  check against a set of time intervals
92  */
93 static int check_time_interval(char *interval, char *reply)
94 {
95   struct tm* tptr;
96   char*      p;
97   int        perm_min_hours;
98   int        perm_min_minutes;
99   int        perm_max_hours;
100   int        perm_max_minutes;
101   int        nowm;
102   int        perm_min;
103   int        perm_max;
104
105   tptr = localtime(&CurrentTime);
106   nowm = tptr->tm_hour * 60 + tptr->tm_min;
107
108   while (interval) {
109     p = strchr(interval, ',');
110     if (p)
111       *p = '\0';
112     if (sscanf(interval, "%2d%2d-%2d%2d", &perm_min_hours, &perm_min_minutes,
113                &perm_max_hours, &perm_max_minutes) != 4)
114     {
115       if (p)
116         *p = ',';
117       return 0;
118     }
119     if (p)
120       *(p++) = ',';
121     perm_min = 60 * perm_min_hours + perm_min_minutes;
122     perm_max = 60 * perm_max_hours + perm_max_minutes;
123     /*
124      * The following check allows intervals over midnight ...
125      */
126     if ((perm_min < perm_max)
127         ? (perm_min <= nowm && nowm <= perm_max)
128         : (perm_min <= nowm || nowm <= perm_max))
129     {
130       sprintf_irc(reply, ":%%s %%d %%s :%s %d:%02d to %d:%02d.",
131                   "You are not allowed to connect from",
132                   perm_min_hours, perm_min_minutes,
133                   perm_max_hours, perm_max_minutes);
134       return (ERR_YOUREBANNEDCREEP);
135     }
136     if ((perm_min < perm_max)
137         ? (perm_min <= nowm + 5 && nowm + 5 <= perm_max)
138         : (perm_min <= nowm + 5 || nowm + 5 <= perm_max))
139     {
140       sprintf_irc(reply, ":%%s %%d %%s :%d minute%s%s",
141                   perm_min - nowm, (perm_min - nowm) > 1 ? "s " : " ",
142                   "and you will be denied for further access");
143       return (ERR_YOUWILLBEBANNED);
144     }
145     interval = p;
146   }
147   return 0;
148 }
149
150 /*
151  * output the reason for being k lined from a file  - Mmmm
152  * sptr is server
153  * parv is the sender prefix
154  * filename is the file that is to be output to the K lined client
155  */
156 static void killcomment(struct Client *sptr, char *parv, char *filename)
157 {
158   FBFILE*     file = NULL;
159   char        line[80];
160   char*       tmp;
161   struct stat sb;
162   struct tm*  tm;
163
164   if (NULL == (file = fbopen(filename, "r"))) {
165     sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv);
166     sendto_one(sptr,
167         ":%s %d %s :Connection from your host is refused on this server.",
168         me.name, ERR_YOUREBANNEDCREEP, parv);
169     return;
170   }
171   fbstat(&sb, file);
172   tm = localtime((time_t*) &sb.st_mtime);        /* NetBSD needs cast */
173   while (fbgets(line, sizeof(line) - 1, file)) {
174     if ((tmp = strchr(line, '\n')))
175       *tmp = '\0';
176     if ((tmp = strchr(line, '\r')))
177       *tmp = '\0';
178     /* sendto_one(sptr,
179      * ":%s %d %s : %s.",
180      * me.name, ERR_YOUREBANNEDCREEP, parv,line); */
181     sendto_one(sptr, rpl_str(RPL_MOTD), me.name, parv, line);
182   }
183   sendto_one(sptr,
184       ":%s %d %s :Connection from your host is refused on this server.",
185       me.name, ERR_YOUREBANNEDCREEP, parv);
186   fbclose(file);
187 }
188
189 struct ConfItem* make_conf(void)
190 {
191   struct ConfItem* aconf;
192
193   aconf = (struct ConfItem*) MyMalloc(sizeof(struct ConfItem));
194   assert(0 != aconf);
195 #ifdef        DEBUGMODE
196   ++GlobalConfCount;
197 #endif
198   aconf->status       = CONF_ILLEGAL;
199   aconf->clients      = 0;
200   aconf->ipnum.s_addr = INADDR_NONE;
201   aconf->host         = 0;
202   aconf->passwd       = 0;
203   aconf->name         = 0;
204   aconf->port         = 0;
205   aconf->hold         = 0;
206   aconf->dns_pending  = 0;
207   aconf->confClass    = 0;
208   aconf->next         = 0;
209   return aconf;
210 }
211
212 void delist_conf(struct ConfItem *aconf)
213 {
214   if (aconf == GlobalConfList)
215     GlobalConfList = GlobalConfList->next;
216   else {
217     struct ConfItem *bconf;
218
219     for (bconf = GlobalConfList; aconf != bconf->next; bconf = bconf->next)
220       ;
221     bconf->next = aconf->next;
222   }
223   aconf->next = NULL;
224 }
225
226 void free_conf(struct ConfItem *aconf)
227 {
228   Debug((DEBUG_DEBUG, "free_conf: %s %s %d",
229          aconf->host ? aconf->host : "*",
230          aconf->name ? aconf->name : "*",
231          aconf->port));
232   if (aconf->dns_pending)
233     delete_resolver_queries(aconf);
234   MyFree(aconf->host);
235   if (aconf->passwd)
236     memset(aconf->passwd, 0, strlen(aconf->passwd));
237   MyFree(aconf->passwd);
238   MyFree(aconf->name);
239   MyFree(aconf);
240 #ifdef        DEBUGMODE
241   --GlobalConfCount;
242 #endif
243 }
244
245 /*
246  * conf_dns_callback - called when resolver query finishes
247  * if the query resulted in a successful search, hp will contain
248  * a non-null pointer, otherwise hp will be null.
249  * if successful save hp in the conf item it was called with
250  */
251 static void conf_dns_callback(void* vptr, struct DNSReply* reply)
252 {
253   struct ConfItem* aconf = (struct ConfItem*) vptr;
254   aconf->dns_pending = 0;
255   if (reply)
256     memcpy(&aconf->ipnum, reply->hp->h_addr, sizeof(struct in_addr));
257 }
258
259 /*
260  * conf_dns_lookup - do a nameserver lookup of the conf host
261  * if the conf entry is currently doing a ns lookup do nothing, otherwise
262  * if the lookup returns a null pointer, set the conf dns_pending flag
263  */
264 static struct DNSReply* conf_dns_lookup(struct ConfItem* aconf)
265 {
266   struct DNSReply* dns_reply = 0;
267   if (!aconf->dns_pending) {
268     char            buf[HOSTLEN + 1];
269     struct DNSQuery query;
270     query.vptr     = aconf;
271     query.callback = conf_dns_callback;
272     host_from_uh(buf, aconf->host, HOSTLEN);
273     buf[HOSTLEN] = '\0';
274
275     if (0 == (dns_reply = gethost_byname(buf, &query)))
276       aconf->dns_pending = 1;
277   }
278   return dns_reply;
279 }
280
281
282 /*
283  * lookup_confhost
284  *
285  * Do (start) DNS lookups of all hostnames in the conf line and convert
286  * an IP addresses in a.b.c.d number for to IP#s.
287  */
288 static void lookup_confhost(struct ConfItem *aconf)
289 {
290   struct DNSReply* reply;
291
292   if (EmptyString(aconf->host) || EmptyString(aconf->name)) {
293     Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
294            aconf->host, aconf->name));
295     return;
296   }
297   /*
298    * Do name lookup now on hostnames given and store the
299    * ip numbers in conf structure.
300    */
301   if (IsDigit(*aconf->host)) {
302     /*
303      * rfc 1035 sez host names may not start with a digit
304      */
305     aconf->ipnum.s_addr = inet_addr(aconf->host);
306     if (INADDR_NONE == aconf->ipnum.s_addr) {
307       Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
308             aconf->host, aconf->name));
309     }
310   }
311   else if ((reply = conf_dns_lookup(aconf)))
312     memcpy(&aconf->ipnum, reply->hp->h_addr, sizeof(struct in_addr));
313 }
314
315 /*
316  * conf_find_server - find a server by name or hostname
317  * returns a server conf item pointer if found, 0 otherwise
318  *
319  * NOTE: at some point when we don't have to scan the entire
320  * list it may be cheaper to look for server names and host
321  * names in separate loops (original code did it that way)
322  */
323 struct ConfItem* conf_find_server(const char* name)
324 {
325   struct ConfItem* conf;
326   assert(0 != name);
327
328   for (conf = GlobalConfList; conf; conf = conf->next) {
329     if (CONF_SERVER == conf->status) {
330       /*
331        * Check first servernames, then try hostnames.
332        * XXX - match returns 0 if there _is_ a match... guess they
333        * haven't decided what true is yet
334        */
335       if (0 == match(name, conf->name))
336         return conf;
337     }
338   }
339   return 0;
340 }
341
342 /*
343  * conf_eval_crule - evaluate connection rules
344  * returns the name of the rule triggered if found, 0 otherwise
345  *
346  * Evaluate connection rules...  If no rules found, allow the
347  * connect.   Otherwise stop with the first true rule (ie: rules
348  * are ored together.  Oper connects are effected only by D
349  * lines (CRULEALL) not d lines (CRULEAUTO).
350  */
351 const char* conf_eval_crule(struct ConfItem* conf)
352 {
353   struct ConfItem* rule;
354   assert(0 != conf);
355
356   for (rule = GlobalConfList; rule; rule = rule->next) {
357     if ((CONF_CRULEALL == rule->status) && (0 == match(rule->host, conf->name))) {
358       if (crule_eval(rule->passwd))
359         return rule->name;
360     }
361   }
362   return 0;
363 }
364
365
366 /*
367  * field breakup for ircd.conf file.
368  */
369 static char* getfield(char* newline, char fs)
370 {
371   static char* gfline = NULL;
372   char*        end;
373   char*        field;
374
375   if (newline)
376     gfline = newline;
377
378   if (gfline == NULL)
379     return NULL;
380
381   end = field = gfline;
382
383   if (fs != ':') {
384     if (*end == fs)
385       ++end;
386     else
387       fs = ':';
388   }
389   do {
390     while (*end != fs) {
391       if (!*end) {
392         end = NULL;
393         break;
394       }
395       ++end;
396     }
397   } while (end && fs != ':' && *++end != ':' && *end != '\n') ;
398
399   if (end == NULL) {
400     gfline = NULL;
401     if ((end = strchr(field, '\n')) == NULL)
402       end = field + strlen(field);
403   }
404   else
405     gfline = end + 1;
406
407   *end = '\0';
408
409   return field;
410 }
411
412 /*
413  * Remove all conf entries from the client except those which match
414  * the status field mask.
415  */
416 void det_confs_butmask(struct Client *cptr, int mask)
417 {
418   struct SLink *tmp, *tmp2;
419
420   for (tmp = cptr->confs; tmp; tmp = tmp2) {
421     tmp2 = tmp->next;
422     if ((tmp->value.aconf->status & mask) == 0)
423       detach_conf(cptr, tmp->value.aconf);
424   }
425 }
426
427 /*
428  * validate_hostent - make sure hostnames are valid in a hostent struct
429  * XXX - this is terrible, what's worse is it used to be in the inner
430  * loop of scanning all the I:lines --Bleep
431  */
432 static int validate_hostent(struct hostent* hp)
433 {
434   char        fullname[HOSTLEN + 1];
435   int         i     = 0;
436   int         error = 0;
437   const char* hname;
438
439   for (hname = hp->h_name; hname; hname = hp->h_aliases[i++]) {
440     size_t fullnamelen = 0;
441     size_t label_count = 0;
442
443     ircd_strncpy(fullname, hname, HOSTLEN);
444     fullname[HOSTLEN] = '\0';
445     /*
446      * Disallow a hostname label to contain anything but a [-a-zA-Z0-9].
447      * It may not start or end on a '.'.
448      * A label may not end on a '-', the maximum length of a label is
449      * 63 characters.
450      * On top of that (which seems to be the RFC) we demand that the
451      * top domain does not contain any digits.
452      */
453     error = (*hname == '.') ? 1 : 0;        /* May not start with a '.' */
454     if (!error) {
455       char *p;
456       for (p = fullname; *p; ++p, ++fullnamelen) {
457         if (*p == '.') { 
458           /* Label may not end on '-' and May not end on a '.' */
459           if (p[-1] == '-' || p[1] == 0) {
460             error = 1;
461             break;
462           }
463           label_count = 0;
464           error = 0;        /* Was not top domain */
465           continue;
466         }
467         if (++label_count > 63) {
468           /* Label not longer then 63 */
469           error = 1;
470           break;
471         }
472         if (*p >= '0' && *p <= '9') {
473           /* In case this is top domain */
474           error = 1;
475           continue;
476         }
477         if (!(*p >= 'a' && *p <= 'z')
478             && !(*p >= 'A' && *p <= 'Z') && *p != '-') {
479           error = 1;
480           break;
481         }
482       }
483     }
484     if (error)
485       break;
486   }
487   return (0 == error);
488 }
489
490 /*
491  * check_limit_and_attach - check client limits and attach I:line
492  */
493 static int check_limit_and_attach(struct Client* cptr, struct ConfItem* aconf)
494 {
495   if (aconf->passwd) {
496     /* Special case: exactly one digit */
497     if (IsDigit(*aconf->passwd) && !aconf->passwd[1]) {
498       /*
499        * Refuse connections when there are already <digit>
500        * clients connected with the same IP number
501        */
502       unsigned short nr = *aconf->passwd - '0';
503       if (IPcheck_nr(cptr) > nr)
504         return ACR_TOO_MANY_FROM_IP; /* Already got nr with that ip# */
505     }
506 #ifdef USEONE
507     else if (0 == strcmp(aconf->passwd, "ONE")) {
508       int i;
509       for (i = HighestFd; i > -1; --i) {
510         if (LocalClientArray[i] && MyUser(LocalClientArray[i]) &&
511             LocalClientArray[i]->ip.s_addr == cptr->ip.s_addr)
512           return ACR_TOO_MANY_FROM_IP; /* Already got one with that ip# */
513       }
514     }
515 #endif
516   }
517   return attach_conf(cptr, aconf);
518 }
519
520 /*
521  * Find the first (best) I line to attach.
522  */
523 int attach_iline(struct Client*  cptr)
524 {
525   struct ConfItem* aconf;
526   const char*      hname;
527   int              i;
528   static char      uhost[HOSTLEN + USERLEN + 3];
529   static char      fullname[HOSTLEN + 1];
530   struct hostent*  hp = 0;
531
532   if (cptr->dns_reply) {
533     hp = cptr->dns_reply->hp;
534     if (!validate_hostent(hp))
535       hp = 0;
536   }
537   for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
538     if (aconf->status != CONF_CLIENT)
539       continue;
540     if (aconf->port && aconf->port != cptr->listener->port)
541       continue;
542     if (!aconf->host || !aconf->name)
543       continue;
544     if (hp) {
545       for (i = 0, hname = hp->h_name; hname; hname = hp->h_aliases[i++]) {
546         ircd_strncpy(fullname, hname, HOSTLEN);
547         fullname[HOSTLEN] = '\0';
548
549         Debug((DEBUG_DNS, "a_il: %s->%s", cptr->sockhost, fullname));
550
551         if (strchr(aconf->name, '@')) {
552           strcpy(uhost, cptr->username);
553           strcat(uhost, "@");
554         }
555         else
556           *uhost = '\0';
557         strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost));
558         uhost[sizeof(uhost) - 1] = 0;
559         if (0 == match(aconf->name, uhost)) {
560           if (strchr(uhost, '@'))
561             cptr->flags |= FLAGS_DOID;
562           return check_limit_and_attach(cptr, aconf);
563         }
564       }
565     }
566     if (strchr(aconf->host, '@')) {
567       ircd_strncpy(uhost, cptr->username, sizeof(uhost) - 2);
568       uhost[sizeof(uhost) - 2] = 0;
569       strcat(uhost, "@");
570     }
571     else
572       *uhost = '\0';
573     strncat(uhost, cptr->sock_ip, sizeof(uhost) - 1 - strlen(uhost));
574     uhost[sizeof(uhost) - 1] = 0;
575     if (match(aconf->host, uhost))
576       continue;
577     if (strchr(uhost, '@'))
578       cptr->flags |= FLAGS_DOID;
579
580     return check_limit_and_attach(cptr, aconf);
581   }
582   return ACR_NO_AUTHORIZATION;
583 }
584
585 /*
586  * detach_conf - Disassociate configuration from the client.
587  */
588 int detach_conf(struct Client *cptr, struct ConfItem *aconf)
589 {
590   struct SLink** lp;
591   struct SLink*  tmp;
592
593   assert(0 != aconf);
594   assert(0 != cptr);
595   assert(0 < aconf->clients);
596
597   lp = &(cptr->confs);
598
599   while (*lp) {
600     if ((*lp)->value.aconf == aconf) {
601       if (aconf->confClass && (aconf->status & CONF_CLIENT_MASK) && 
602           ConfLinks(aconf) > 0)
603         --ConfLinks(aconf);
604       if (0 == --aconf->clients && IsIllegal(aconf))
605         free_conf(aconf);
606       tmp = *lp;
607       *lp = tmp->next;
608       free_link(tmp);
609       return 0;
610     }
611     else
612       lp = &((*lp)->next);
613   }
614   return -1;
615 }
616
617 static int is_attached(struct ConfItem *aconf, struct Client *cptr)
618 {
619   struct SLink *lp;
620
621   for (lp = cptr->confs; lp; lp = lp->next)
622     if (lp->value.aconf == aconf)
623       break;
624
625   return (lp) ? 1 : 0;
626 }
627
628 /*
629  * attach_conf
630  *
631  * Associate a specific configuration entry to a *local*
632  * client (this is the one which used in accepting the
633  * connection). Note, that this automaticly changes the
634  * attachment if there was an old one...
635  */
636 int attach_conf(struct Client *cptr, struct ConfItem *aconf)
637 {
638   struct SLink *lp;
639
640   if (is_attached(aconf, cptr))
641     return ACR_ALREADY_AUTHORIZED;
642   if (IsIllegal(aconf))
643     return ACR_NO_AUTHORIZATION;
644   if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT)) &&
645       ConfLinks(aconf) >= ConfMaxLinks(aconf) && ConfMaxLinks(aconf) > 0)
646     return ACR_TOO_MANY_IN_CLASS;  /* Use this for printing error message */
647   lp = make_link();
648   lp->next = cptr->confs;
649   lp->value.aconf = aconf;
650   cptr->confs = lp;
651   ++aconf->clients;
652   if (aconf->status & CONF_CLIENT_MASK)
653     ConfLinks(aconf)++;
654   return ACR_OK;
655 }
656
657 struct ConfItem *find_admin(void)
658 {
659   struct ConfItem *aconf;
660
661   for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
662     if (aconf->status & CONF_ADMIN)
663       break;
664   }
665   return aconf;
666 }
667
668 struct ConfItem* find_me(void)
669 {
670   struct ConfItem* aconf;
671   for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
672     if (aconf->status & CONF_ME)
673       break;
674   }
675   return aconf;
676 }
677
678 /*
679  * attach_confs_byname
680  *
681  * Attach a CONF line to a client if the name passed matches that for
682  * the conf file (for non-C/N lines) or is an exact match (C/N lines
683  * only).  The difference in behaviour is to stop C:*::* and N:*::*.
684  */
685 struct ConfItem* attach_confs_byname(struct Client* cptr, const char* name,
686                                      int statmask)
687 {
688   struct ConfItem* tmp;
689   struct ConfItem* first = NULL;
690
691   assert(0 != name);
692
693   if (HOSTLEN < strlen(name))
694     return 0;
695
696   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
697     if (0 != (tmp->status & statmask) && !IsIllegal(tmp)) {
698       assert(0 != tmp->name);
699       if (0 == match(tmp->name, name) || 0 == ircd_strcmp(tmp->name, name)) { 
700         if (ACR_OK == attach_conf(cptr, tmp) && !first)
701           first = tmp;
702       }
703     }
704   }
705   return first;
706 }
707
708 /*
709  * Added for new access check    meLazy
710  */
711 struct ConfItem* attach_confs_byhost(struct Client* cptr, const char* host,
712                                      int statmask)
713 {
714   struct ConfItem* tmp;
715   struct ConfItem* first = 0;
716
717   assert(0 != host);
718   if (HOSTLEN < strlen(host))
719     return 0;
720
721   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
722     if (0 != (tmp->status & statmask) && !IsIllegal(tmp)) {
723       assert(0 != tmp->host);
724       if (0 == match(tmp->host, host) || 0 == ircd_strcmp(tmp->host, host)) { 
725         if (ACR_OK == attach_conf(cptr, tmp) && !first)
726           first = tmp;
727       }
728     }
729   }
730   return first;
731 }
732
733 /*
734  * find a conf entry which matches the hostname and has the same name.
735  */
736 struct ConfItem* find_conf_exact(const char* name, const char* user,
737                                  const char* host, int statmask)
738 {
739   struct ConfItem *tmp;
740   char userhost[USERLEN + HOSTLEN + 3];
741
742   /*
743    * XXX - buffer overflow possible, unchecked variables
744    */
745   if (user)
746     sprintf_irc(userhost, "%s@%s", user, host);
747   else
748     ircd_strncpy(userhost, host, sizeof(userhost) - 1);
749
750   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
751     if (!(tmp->status & statmask) || !tmp->name || !tmp->host ||
752         0 != ircd_strcmp(tmp->name, name))
753       continue;
754     /*
755      * Accept if the *real* hostname (usually sockecthost)
756      * socket host) matches *either* host or name field
757      * of the configuration.
758      */
759     if (match(tmp->host, userhost))
760       continue;
761     if (tmp->status & (CONF_OPERATOR | CONF_LOCOP)) {
762       if (tmp->clients < MaxLinks(tmp->confClass))
763         return tmp;
764       else
765         continue;
766     }
767     else
768       return tmp;
769   }
770   return 0;
771 }
772
773 struct ConfItem* find_conf_byname(struct SLink* lp, const char* name,
774                                   int statmask)
775 {
776   struct ConfItem* tmp;
777   assert(0 != name);
778
779   if (HOSTLEN < strlen(name))
780     return 0;
781
782   for (; lp; lp = lp->next) {
783     tmp = lp->value.aconf;
784     if (0 != (tmp->status & statmask)) {
785       assert(0 != tmp->name);
786       if (0 == ircd_strcmp(tmp->name, name) || 0 == match(tmp->name, name))
787         return tmp;
788     }
789   }
790   return 0;
791 }
792
793 /*
794  * Added for new access check    meLazy
795  */
796 struct ConfItem* find_conf_byhost(struct SLink* lp, const char* host,
797                                   int statmask)
798 {
799   struct ConfItem* tmp;
800   assert(0 != host);
801
802   if (HOSTLEN < strlen(host))
803     return 0;
804
805   for (; lp; lp = lp->next) {
806     tmp = lp->value.aconf;
807     if (0 != (tmp->status & statmask)) {
808       assert(0 != tmp->host);
809       if (0 == match(tmp->host, host))
810         return tmp;
811     }
812   }
813   return 0;
814 }
815
816 /*
817  * find_conf_ip
818  *
819  * Find a conf line using the IP# stored in it to search upon.
820  * Added 1/8/92 by Avalon.
821  */
822 struct ConfItem* find_conf_byip(struct SLink* lp, const char* ip, 
823                                 int statmask)
824 {
825   struct ConfItem* tmp;
826
827   for (; lp; lp = lp->next) {
828     tmp = lp->value.aconf;
829     if (0 != (tmp->status & statmask)) {
830       if (0 == memcmp(&tmp->ipnum, ip, sizeof(struct in_addr)))
831         return tmp;
832     }
833   }
834   return 0;
835 }
836
837 /*
838  * find_conf_entry
839  *
840  * - looks for a match on all given fields.
841  */
842 static struct ConfItem *find_conf_entry(struct ConfItem *aconf,
843                                         unsigned int mask)
844 {
845   struct ConfItem *bconf;
846   assert(0 != aconf);
847
848   mask &= ~CONF_ILLEGAL;
849
850   for (bconf = GlobalConfList; bconf; bconf = bconf->next) {
851     if (!(bconf->status & mask) || (bconf->port != aconf->port))
852       continue;
853
854     if ((EmptyString(bconf->host) && !EmptyString(aconf->host)) ||
855         (EmptyString(aconf->host) && !EmptyString(bconf->host)))
856       continue;
857     if (!EmptyString(bconf->host) && 0 != ircd_strcmp(bconf->host, aconf->host))
858       continue;
859
860     if ((EmptyString(bconf->passwd) && !EmptyString(aconf->passwd)) ||
861         (EmptyString(aconf->passwd) && !EmptyString(bconf->passwd)))
862       continue;
863     if (!EmptyString(bconf->passwd) && (!IsDigit(*bconf->passwd) || bconf->passwd[1])
864 #ifdef USEONE
865         && 0 != ircd_strcmp(bconf->passwd, "ONE")
866 #endif
867         && 0 != ircd_strcmp(bconf->passwd, aconf->passwd))
868       continue;
869
870     if ((EmptyString(bconf->name) && !EmptyString(aconf->name)) ||
871         (EmptyString(aconf->name) && !EmptyString(bconf->name)))
872       continue;
873     if (!EmptyString(bconf->name) && 0 != ircd_strcmp(bconf->name, aconf->name))
874       continue;
875     break;
876   }
877   return bconf;
878 }
879
880 /*
881  * rehash
882  *
883  * Actual REHASH service routine. Called with sig == 0 if it has been called
884  * as a result of an operator issuing this command, else assume it has been
885  * called as a result of the server receiving a HUP signal.
886  */
887 int rehash(struct Client *cptr, int sig)
888 {
889   struct ConfItem** tmp = &GlobalConfList;
890   struct ConfItem*  tmp2;
891   struct ConfClass* cltmp;
892   struct Client*    acptr;
893   struct MotdItem*  temp;
894   int               i;
895   int               ret = 0;
896   int               found_g = 0;
897
898   if (1 == sig)
899     sendto_ops("Got signal SIGHUP, reloading ircd conf. file");
900
901   while ((tmp2 = *tmp)) {
902     if (tmp2->clients) {
903       /*
904        * Configuration entry is still in use by some
905        * local clients, cannot delete it--mark it so
906        * that it will be deleted when the last client
907        * exits...
908        */
909       if (!(tmp2->status & CONF_CLIENT)) {
910         *tmp = tmp2->next;
911         tmp2->next = 0;
912       }
913       else
914         tmp = &tmp2->next;
915       tmp2->status |= CONF_ILLEGAL;
916     }
917     else {
918       *tmp = tmp2->next;
919       /* free expression trees of connect rules */
920       if ((tmp2->status & (CONF_CRULEALL | CONF_CRULEAUTO)) &&
921           (tmp2->passwd != NULL))
922         crule_free(&(tmp2->passwd));
923       free_conf(tmp2);
924     }
925   }
926
927   /*
928    * We don't delete the class table, rather mark all entries
929    * for deletion. The table is cleaned up by check_class(). - avalon
930    */
931   for (cltmp = NextClass(FirstClass()); cltmp; cltmp = NextClass(cltmp))
932     MarkDelete(cltmp);
933
934   /*
935    * delete the juped nicks list
936    */
937   clearNickJupes();
938
939   if (sig != 2)
940     flush_resolver_cache();
941
942   mark_listeners_closing();
943
944   if (initconf(0) == -1)        /* This calls check_class(), */
945     check_class();                /* unless it fails */
946
947   /*
948    * make sure that the server listener is re-added so it doesn't get
949    * closed
950    */
951   close_listeners();
952
953   /*
954    * Flush out deleted I and P lines although still in use.
955    */
956   for (tmp = &GlobalConfList; (tmp2 = *tmp);) {
957     if (!(tmp2->status & CONF_ILLEGAL))
958       tmp = &tmp2->next;
959     else
960     {
961       *tmp = tmp2->next;
962       tmp2->next = NULL;
963       if (!tmp2->clients)
964         free_conf(tmp2);
965     }
966   }
967   for (i = 0; i <= HighestFd; i++) {
968     if ((acptr = LocalClientArray[i])) {
969       assert(!IsMe(acptr));
970       if (IsServer(acptr)) {
971         det_confs_butmask(acptr,
972             ~(CONF_HUB | CONF_LEAF | CONF_UWORLD | CONF_ILLEGAL));
973         attach_confs_byname(acptr, acptr->name,
974                             CONF_HUB | CONF_LEAF | CONF_UWORLD);
975       }
976       if ((found_g = find_kill(acptr))) {
977         sendto_op_mask(found_g == -2 ? SNO_GLINE : SNO_OPERKILL,
978             found_g == -2 ? "G-line active for %s" : "K-line active for %s",
979             get_client_name(acptr, HIDE_IP));
980         if (exit_client(cptr, acptr, &me, found_g == -2 ? "G-lined" :
981             "K-lined") == CPTR_KILLED)
982           ret = CPTR_KILLED;
983       }
984     }
985   }
986   /* 
987    * free old motd structs
988    */
989   while (motd) {
990     temp = motd->next;
991     MyFree(motd);
992     motd = temp;
993   }
994   while (rmotd) {
995     temp = rmotd->next;
996     MyFree(rmotd);
997     rmotd = temp;
998   }
999   /* reload motd files */
1000   read_tlines();
1001   rmotd = read_motd(RPATH);
1002   motd = read_motd(MPATH);
1003   return ret;
1004 }
1005
1006 /*
1007  * initconf
1008  *
1009  * Read configuration file.
1010  *
1011  * returns -1, if file cannot be opened
1012  *          0, if file opened
1013  */
1014
1015 #define MAXCONFLINKS 150
1016
1017
1018 int initconf(int opt)
1019 {
1020   static char quotes[9][2] = {
1021     {'b', '\b'},
1022     {'f', '\f'},
1023     {'n', '\n'},
1024     {'r', '\r'},
1025     {'t', '\t'},
1026     {'v', '\v'},
1027     {'\\', '\\'},
1028     {0, 0}
1029   };
1030   char *tmp, *s;
1031   FBFILE *file;
1032   int i;
1033   char line[512];
1034   int ccount = 0;
1035   struct ConfItem *aconf = 0;
1036
1037   Debug((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile));
1038   if (0 == (file = fbopen(configfile, "r"))) {
1039     return -1;
1040   }
1041   while (fbgets(line, sizeof(line) - 1, file)) {
1042     if ((tmp = strchr(line, '\n')))
1043       *tmp = '\0';
1044     /*
1045      * Do quoting of characters and # detection.
1046      */
1047     for (tmp = line; *tmp; tmp++) {
1048       if (*tmp == '\\') {
1049         for (i = 0; quotes[i][0]; i++) {
1050           if (quotes[i][0] == *(tmp + 1)) {
1051             *tmp = quotes[i][1];
1052             break;
1053           }
1054         }
1055         if (!quotes[i][0])
1056           *tmp = *(tmp + 1);
1057         if (!*(tmp + 1))
1058           break;
1059         else {
1060           for (s = tmp; (*s = *(s + 1)); s++)
1061             ;
1062         }
1063       }
1064       else if (*tmp == '#')
1065         *tmp = '\0';
1066     }
1067     if (!*line || line[0] == '#' || line[0] == '\n' ||
1068         line[0] == ' ' || line[0] == '\t')
1069       continue;
1070     /* Could we test if it's conf line at all?      -Vesa */
1071     if (line[1] != ':') {
1072       Debug((DEBUG_ERROR, "Bad config line: %s", line));
1073       continue;
1074     }
1075     if (aconf)
1076       free_conf(aconf);
1077     aconf = make_conf();
1078
1079     tmp = getfield(line, ':');
1080     if (!tmp)
1081       continue;
1082     switch (*tmp) {
1083     case 'A':                /* Name, e-mail address of administrator */
1084     case 'a':                /* of this server. */
1085       aconf->status = CONF_ADMIN;
1086       break;
1087     case 'C':                /* Server where I should try to connect */
1088     case 'c':                /* in case of lp failures             */
1089       ++ccount;
1090       aconf->status = CONF_SERVER;
1091       break;
1092       /* Connect rule */
1093     case 'D':
1094       aconf->status = CONF_CRULEALL;
1095       break;
1096       /* Connect rule - autos only */
1097     case 'd':
1098       aconf->status = CONF_CRULEAUTO;
1099       break;
1100     case 'H':                /* Hub server line */
1101     case 'h':
1102       aconf->status = CONF_HUB;
1103       break;
1104     case 'I':                /* Just plain normal irc client trying  */
1105     case 'i':                /* to connect me */
1106       aconf->status = CONF_CLIENT;
1107       break;
1108     case 'K':                /* Kill user line on irc.conf           */
1109       aconf->status = CONF_KILL;
1110       break;
1111     case 'k':                /* Kill user line based on IP in ircd.conf */
1112       aconf->status = CONF_IPKILL;
1113       break;
1114       /* Operator. Line should contain at least */
1115       /* password and host where connection is  */
1116     case 'L':                /* guaranteed leaf server */
1117     case 'l':
1118       aconf->status = CONF_LEAF;
1119       break;
1120       /* Me. Host field is name used for this host */
1121       /* and port number is the number of the port */
1122     case 'M':
1123     case 'm':
1124       aconf->status = CONF_ME;
1125       break;
1126     case 'O':
1127       aconf->status = CONF_OPERATOR;
1128       break;
1129       /* Local Operator, (limited privs --SRB) */
1130     case 'o':
1131       aconf->status = CONF_LOCOP;
1132       break;
1133     case 'P':                /* listen port line */
1134     case 'p':
1135       aconf->status = CONF_LISTEN_PORT;
1136       break;
1137     case 'T':                /* print out different motd's */
1138     case 't':                /* based on hostmask */
1139       aconf->status = CONF_TLINES;
1140       break;
1141     case 'U':      /* Underworld server, allowed to hack modes */
1142     case 'u':      /* *Every* server on the net must define the same !!! */
1143       aconf->status = CONF_UWORLD;
1144       break;
1145     case 'Y':
1146     case 'y':
1147       aconf->status = CONF_CLASS;
1148       break;
1149     default:
1150       Debug((DEBUG_ERROR, "Error in config file: %s", line));
1151       break;
1152     }
1153     if (IsIllegal(aconf))
1154       continue;
1155
1156     for (;;) {            /* Fake loop, that I can use break here --msa */
1157       if ((tmp = getfield(NULL, ':')) == NULL)
1158         break;
1159       DupString(aconf->host, tmp);
1160       if ((tmp = getfield(NULL, (aconf->status == CONF_KILL
1161           || aconf->status == CONF_IPKILL) ? '"' : ':')) == NULL)
1162         break;
1163       DupString(aconf->passwd, tmp);
1164       if ((tmp = getfield(NULL, ':')) == NULL)
1165         break;
1166       DupString(aconf->name, tmp);
1167       if ((tmp = getfield(NULL, ':')) == NULL)
1168         break;
1169       aconf->port = atoi(tmp);
1170       tmp = getfield(NULL, ':');
1171       if (aconf->status & CONF_ME) {
1172         if (!tmp) {
1173           Debug((DEBUG_FATAL, "Your M: line must have the Numeric, "
1174               "assigned to you by routing-com, behind the port number!\n"));
1175           ircd_log(L_WARNING, "Your M: line must have the Numeric, "
1176               "assigned to you by routing-com, behind the port number!\n");
1177           exit(-1);
1178         }
1179         SetYXXServerName(&me, atoi(tmp));        /* Our Numeric Nick */
1180       }
1181       else if (tmp)
1182         aconf->confClass = find_class(atoi(tmp));
1183       break;
1184     }
1185     /*
1186      * If conf line is a class definition, create a class entry
1187      * for it and make the conf_line illegal and delete it.
1188      */
1189     if (aconf->status & CONF_CLASS) {
1190       add_class(atoi(aconf->host), atoi(aconf->passwd),
1191           atoi(aconf->name), aconf->port, tmp ? atoi(tmp) : 0);
1192       continue;
1193     }
1194     /*
1195      * Associate each conf line with a class by using a pointer
1196      * to the correct class record. -avalon
1197      */
1198     if (aconf->status & CONF_CLIENT_MASK) {
1199       if (aconf->confClass == 0)
1200         aconf->confClass = find_class(0);
1201     }
1202     if (aconf->status & CONF_LISTEN_PORT) {
1203       int         is_server = 0;
1204       int         is_hidden = 0;
1205       if (!EmptyString(aconf->name)) {
1206         const char* x = aconf->name;
1207         if ('S' == ToUpper(*x))
1208           is_server = 1;
1209         ++x;
1210         if ('H' == ToUpper(*x))
1211           is_hidden = 1;
1212       }
1213       add_listener(aconf->port, aconf->passwd, aconf->host, 
1214                    is_server, is_hidden);
1215       continue;
1216     } 
1217     if (aconf->status & CONF_CLIENT) {
1218       struct ConfItem *bconf;
1219
1220       if ((bconf = find_conf_entry(aconf, aconf->status))) {
1221         delist_conf(bconf);
1222         bconf->status &= ~CONF_ILLEGAL;
1223         if (aconf->status == CONF_CLIENT) {
1224           /*
1225            * copy the password field in case it changed
1226            */
1227           MyFree(bconf->passwd);
1228           bconf->passwd = aconf->passwd;
1229           aconf->passwd = 0;
1230
1231           ConfLinks(bconf) -= bconf->clients;
1232           bconf->confClass = aconf->confClass;
1233           if (bconf->confClass)
1234             ConfLinks(bconf) += bconf->clients;
1235         }
1236         free_conf(aconf);
1237         aconf = bconf;
1238       }
1239     }
1240     if (aconf->status & CONF_SERVER) {
1241       if (ccount > MAXCONFLINKS || !aconf->host || strchr(aconf->host, '*') ||
1242           strchr(aconf->host, '?') || !aconf->name)
1243         continue;
1244     }
1245     if (aconf->status & (CONF_LOCOP | CONF_OPERATOR)) {
1246       if (!strchr(aconf->host, '@')) {
1247         char* newhost;
1248         int len = 3;                /* *@\0 = 3 */
1249
1250         len += strlen(aconf->host);
1251         newhost = (char*) MyMalloc(len);
1252         assert(0 != newhost);
1253         sprintf_irc(newhost, "*@%s", aconf->host);
1254         MyFree(aconf->host);
1255         aconf->host = newhost;
1256       }
1257     }
1258     if (aconf->status & CONF_SERVER) {
1259       if (EmptyString(aconf->passwd))
1260         continue;
1261       else if (!(opt & BOOT_QUICK))
1262         lookup_confhost(aconf);
1263     }
1264
1265     /* Create expression tree from connect rule...
1266      * If there's a parsing error, nuke the conf structure */
1267     if (aconf->status & (CONF_CRULEALL | CONF_CRULEAUTO)) {
1268       MyFree(aconf->passwd);
1269       if ((aconf->passwd = (char *)crule_parse(aconf->name)) == NULL) {
1270         free_conf(aconf);
1271         aconf = NULL;
1272         continue;
1273       }
1274     }
1275
1276     /*
1277      * Own port and name cannot be changed after the startup.
1278      * (or could be allowed, but only if all links are closed first).
1279      * Configuration info does not override the name and port
1280      * if previously defined. Note, that "info"-field can be
1281      * changed by "/rehash".
1282      */
1283     if (aconf->status == CONF_ME) {
1284       ircd_strncpy(me.info, aconf->name, REALLEN);
1285     }
1286
1287     /*
1288      * Juped nicks are listed in the 'password' field of U:lines,
1289      * the list is comma separated and might be empty and/or contain
1290      * empty elements... the only limit is that it MUST be shorter
1291      * than 512 chars, or they will be cutted out :)
1292      */
1293     if ((aconf->status == CONF_UWORLD) && (aconf->passwd) && (*aconf->passwd))
1294       addNickJupes(aconf->passwd);
1295
1296     if (aconf->status & CONF_ADMIN) {
1297       if (!aconf->host || !aconf->passwd || !aconf->name) {
1298         Debug((DEBUG_FATAL, "Your A: line must have 4 fields!\n"));
1299         ircd_log(L_WARNING, "Your A: line must have 4 fields!\n");
1300         exit(-1);
1301       }
1302     }
1303     collapse(aconf->host);
1304     collapse(aconf->name);
1305     Debug((DEBUG_NOTICE,
1306         "Read Init: (%d) (%s) (%s) (%s) (%u) (%p)",
1307         aconf->status, aconf->host, aconf->passwd,
1308         aconf->name, aconf->port, aconf->confClass));
1309     aconf->next = GlobalConfList;
1310     GlobalConfList = aconf;
1311     aconf = NULL;
1312   }
1313   if (aconf)
1314     free_conf(aconf);
1315   fbclose(file);
1316   check_class();
1317   nextping = nextconnect = CurrentTime;
1318   return 0;
1319 }
1320
1321 /* read_tlines 
1322  * Read info from T:lines into TRecords which include the file 
1323  * timestamp, the hostmask, and the contents of the motd file 
1324  * -Ghostwolf 7sep97
1325  */
1326 void read_tlines()
1327 {
1328   struct ConfItem *tmp;
1329   struct TRecord *temp;
1330   struct TRecord *last = NULL;        /* Init. to avoid compiler warning */
1331   struct MotdItem *amotd;
1332
1333   /* Free the old trecords and the associated motd contents first */
1334   while (tdata)
1335   {
1336     last = tdata->next;
1337     while (tdata->tmotd)
1338     {
1339       amotd = tdata->tmotd->next;
1340       MyFree(tdata->tmotd);
1341       tdata->tmotd = amotd;
1342     }
1343     MyFree(tdata);
1344     tdata = last;
1345   }
1346
1347   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
1348     if (tmp->status == CONF_TLINES && tmp->host && tmp->passwd) {
1349       temp = (struct TRecord*) MyMalloc(sizeof(struct TRecord));
1350       assert(0 != temp);
1351
1352       temp->hostmask = tmp->host;
1353       temp->tmotd = read_motd(tmp->passwd);
1354       temp->tmotd_tm = motd_tm;
1355       temp->next = NULL;
1356       if (!tdata)
1357         tdata = temp;
1358       else
1359         last->next = temp;
1360       last = temp;
1361     }
1362   }
1363 }
1364
1365 int find_kill(struct Client *cptr)
1366 {
1367   char             reply[256];
1368   const char*      host;
1369   const char*      name;
1370   struct ConfItem* tmp;
1371   struct Gline*    agline = NULL;
1372
1373   assert(0 != cptr);
1374
1375   if (!cptr->user)
1376     return 0;
1377
1378   host = cptr->sockhost;
1379   name = cptr->user->username;
1380
1381 #if 0
1382   /*
1383    * whee :)
1384    * XXX - if this ever happens, we're already screwed
1385    */
1386   if (strlen(host) > (size_t)HOSTLEN ||
1387       (name ? strlen(name) : 0) > (size_t)HOSTLEN)
1388     return (0);
1389 #endif
1390
1391   reply[0] = '\0';
1392
1393   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
1394     /* Added a check against the user's IP address as well.
1395      * If the line is either CONF_KILL or CONF_IPKILL, check it; if and only
1396      * if it's CONF_IPKILL, check the IP address as well (the && below will
1397      * short circuit and the match won't even get run) -Kev
1398      */
1399     if ((tmp->status & CONF_KLINE) && tmp->host && tmp->name &&
1400         (match(tmp->host, host) == 0 ||
1401         ((tmp->status == CONF_IPKILL) &&
1402         match(tmp->host, ircd_ntoa((const char*) &cptr->ip)) == 0)) &&
1403         (!name || match(tmp->name, name) == 0) &&
1404         (!tmp->port || (tmp->port == cptr->listener->port)))
1405     {
1406       /*
1407        * Can short-circuit evaluation - not taking chances
1408        * because check_time_interval destroys tmp->passwd
1409        * - Mmmm
1410        */
1411       if (EmptyString(tmp->passwd))
1412         break;
1413       else if (is_comment(tmp->passwd))
1414         break;
1415       else if (check_time_interval(tmp->passwd, reply))
1416         break;
1417     }
1418   }
1419   if (reply[0])
1420     sendto_one(cptr, reply, me.name, ERR_YOUREBANNEDCREEP, cptr->name);
1421   else if (tmp) {
1422     if (EmptyString(tmp->passwd))
1423       sendto_one(cptr,
1424           ":%s %d %s :Connection from your host is refused on this server.",
1425           me.name, ERR_YOUREBANNEDCREEP, cptr->name);
1426     else {
1427       if (*tmp->passwd == '"') {
1428         char *sbuf =
1429             sprintf_irc(sendbuf, ":%s %d %s :%s", me.name, ERR_YOUREBANNEDCREEP,
1430             cptr->name, &tmp->passwd[1]);
1431         sbuf[-1] = '.';                /* Overwrite last quote with a dot */
1432         sendbufto_one(cptr);
1433       }
1434       else if (*tmp->passwd == '!')
1435         killcomment(cptr, cptr->name, &tmp->passwd[1]);
1436       else
1437 #ifdef COMMENT_IS_FILE
1438         killcomment(cptr, cptr->name, tmp->passwd);
1439 #else
1440         sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
1441             cptr->name, tmp->passwd);
1442 #endif
1443     }
1444   }
1445
1446   /* find active glines */
1447   /* added a check against the user's IP address to find_gline() -Kev */
1448   else if ((agline = find_gline(cptr, NULL)) && GlineIsActive(agline))
1449     sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
1450         cptr->name, agline->reason);
1451   else
1452     agline = NULL;                /* if a gline was found, it was inactive */
1453
1454   return (tmp ? -1 : (agline ? -2 : 0));
1455 }
1456
1457 struct MotdItem* read_motd(const char* motdfile)
1458 {
1459   FBFILE*          file = NULL;
1460   struct MotdItem* temp;
1461   struct MotdItem* newmotd;
1462   struct MotdItem* last;
1463   struct stat      sb;
1464   char             line[80];
1465   char*            tmp;
1466
1467   if (NULL == (file = fbopen(motdfile, "r"))) {
1468     Debug((DEBUG_ERROR, "Couldn't open \"%s\": %s", motdfile, strerror(errno)));
1469     return NULL;
1470   }
1471   if (-1 == fbstat(&sb, file)) {
1472     fbclose(file);
1473     return NULL;
1474   }
1475   newmotd = last = NULL;
1476   motd_tm = *localtime((time_t *) & sb.st_mtime);        /* NetBSD needs cast */
1477   while (fbgets(line, sizeof(line) - 1, file)) {
1478     if ((tmp = (char *)strchr(line, '\n')))
1479       *tmp = '\0';
1480     if ((tmp = (char *)strchr(line, '\r')))
1481       *tmp = '\0';
1482     temp = (struct MotdItem*) MyMalloc(sizeof(struct MotdItem));
1483     assert(0 != temp);
1484     strcpy(temp->line, line);
1485     temp->next = NULL;
1486     if (!newmotd)
1487       newmotd = temp;
1488     else
1489       last->next = temp;
1490     last = temp;
1491   }
1492   fbclose(file);
1493   return newmotd;
1494 }
1495
1496
1497 /*
1498  * Ordinary client access check. Look for conf lines which have the same
1499  * status as the flags passed.
1500  */
1501 enum AuthorizationCheckResult conf_check_client(struct Client *cptr)
1502 {
1503   int acr;
1504
1505   ClearAccess(cptr);
1506
1507   if ((acr = attach_iline(cptr))) {
1508     Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]", 
1509           cptr->name, cptr->sockhost));
1510     return acr;
1511   }
1512   return ACR_OK;
1513 }
1514
1515 /*
1516  * check_server()
1517  *
1518  * Check access for a server given its name (passed in cptr struct).
1519  * Must check for all C/N lines which have a name which matches the
1520  * name given and a host which matches. A host alias which is the
1521  * same as the server name is also acceptable in the host field of a
1522  * C/N line.
1523  *
1524  * Returns
1525  *  0 = Success
1526  * -1 = Access denied
1527  * -2 = Bad socket.
1528  */
1529 int conf_check_server(struct Client *cptr)
1530 {
1531   struct ConfItem* c_conf = NULL;
1532   struct SLink*    lp;
1533
1534   Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", 
1535         cptr->name, cptr->sockhost));
1536
1537   if (IsUnknown(cptr) && !attach_confs_byname(cptr, cptr->name, CONF_SERVER)) {
1538     Debug((DEBUG_DNS, "No C/N lines for %s", cptr->sockhost));
1539     return -1;
1540   }
1541   lp = cptr->confs;
1542   /*
1543    * We initiated this connection so the client should have a C and N
1544    * line already attached after passing through the connect_server()
1545    * function earlier.
1546    */
1547   if (IsConnecting(cptr) || IsHandshake(cptr)) {
1548     c_conf = find_conf_byname(lp, cptr->name, CONF_SERVER);
1549     if (!c_conf) {
1550       sendto_ops("Connect Error: lost C:line for %s", cptr->name);
1551       det_confs_butmask(cptr, 0);
1552       return -1;
1553     }
1554   }
1555
1556   ClearAccess(cptr);
1557
1558   if (!c_conf) {
1559     if (cptr->dns_reply) {
1560       int             i;
1561       struct hostent* hp = cptr->dns_reply->hp;
1562       const char*     name = hp->h_name;
1563       /*
1564        * If we are missing a C or N line from above, search for
1565        * it under all known hostnames we have for this ip#.
1566        */
1567       for (i = 0; name; name = hp->h_aliases[i++]) {
1568         if ((c_conf = find_conf_byhost(lp, name, CONF_SERVER))) {
1569           ircd_strncpy(cptr->sockhost, name, HOSTLEN);
1570           break;
1571         }
1572       }
1573       if (!c_conf) {
1574         for (i = 0; hp->h_addr_list[i]; i++) {
1575           if ((c_conf = find_conf_byip(lp, hp->h_addr_list[i], CONF_SERVER)))
1576             break;
1577         }
1578       }
1579     }
1580     else {
1581       /*
1582        * Check for C lines with the hostname portion the ip number
1583        * of the host the server runs on. This also checks the case where
1584        * there is a server connecting from 'localhost'.
1585        */
1586       c_conf = find_conf_byhost(lp, cptr->sockhost, CONF_SERVER);
1587     }
1588   }
1589   /*
1590    * Attach by IP# only if all other checks have failed.
1591    * It is quite possible to get here with the strange things that can
1592    * happen when using DNS in the way the irc server does. -avalon
1593    */
1594   if (!c_conf)
1595     c_conf = find_conf_byip(lp, (const char*) &cptr->ip, CONF_SERVER);
1596   /*
1597    * detach all conf lines that got attached by attach_confs()
1598    */
1599   det_confs_butmask(cptr, 0);
1600   /*
1601    * if no C or no N lines, then deny access
1602    */
1603   if (!c_conf) {
1604     Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s]",
1605           cptr->name, cptr->username, cptr->sockhost));
1606     return -1;
1607   }
1608   ircd_strncpy(cptr->name, c_conf->name, HOSTLEN);
1609   /*
1610    * attach the C and N lines to the client structure for later use.
1611    */
1612   attach_conf(cptr, c_conf);
1613   attach_confs_byname(cptr, cptr->name, CONF_HUB | CONF_LEAF | CONF_UWORLD);
1614
1615   if (INADDR_NONE == c_conf->ipnum.s_addr)
1616     c_conf->ipnum.s_addr = cptr->ip.s_addr;
1617
1618   Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", cptr->name, cptr->sockhost));
1619   return 0;
1620 }
1621