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   unsigned int i;
82   unsigned int 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      * XXX - this has changed code needs to be updated
305      */
306     aconf->ipnum.s_addr = inet_addr(aconf->host);
307     if (INADDR_NONE == aconf->ipnum.s_addr) {
308       Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
309             aconf->host, aconf->name));
310     }
311   }
312   else if ((reply = conf_dns_lookup(aconf)))
313     memcpy(&aconf->ipnum, reply->hp->h_addr, sizeof(struct in_addr));
314 }
315
316 /*
317  * conf_find_server - find a server by name or hostname
318  * returns a server conf item pointer if found, 0 otherwise
319  *
320  * NOTE: at some point when we don't have to scan the entire
321  * list it may be cheaper to look for server names and host
322  * names in separate loops (original code did it that way)
323  */
324 struct ConfItem* conf_find_server(const char* name)
325 {
326   struct ConfItem* conf;
327   assert(0 != name);
328
329   for (conf = GlobalConfList; conf; conf = conf->next) {
330     if (CONF_SERVER == conf->status) {
331       /*
332        * Check first servernames, then try hostnames.
333        * XXX - match returns 0 if there _is_ a match... guess they
334        * haven't decided what true is yet
335        */
336       if (0 == match(name, conf->name))
337         return conf;
338     }
339   }
340   return 0;
341 }
342
343 /*
344  * conf_eval_crule - evaluate connection rules
345  * returns the name of the rule triggered if found, 0 otherwise
346  *
347  * Evaluate connection rules...  If no rules found, allow the
348  * connect.   Otherwise stop with the first true rule (ie: rules
349  * are ored together.  Oper connects are effected only by D
350  * lines (CRULEALL) not d lines (CRULEAUTO).
351  */
352 const char* conf_eval_crule(struct ConfItem* conf)
353 {
354   struct ConfItem* rule;
355   assert(0 != conf);
356
357   for (rule = GlobalConfList; rule; rule = rule->next) {
358     if ((CONF_CRULEALL == rule->status) && (0 == match(rule->host, conf->name))) {
359       if (crule_eval(rule->passwd))
360         return rule->name;
361     }
362   }
363   return 0;
364 }
365
366
367 /*
368  * field breakup for ircd.conf file.
369  */
370 static char* getfield(char* newline, char fs)
371 {
372   static char* gfline = NULL;
373   char*        end;
374   char*        field;
375
376   if (newline)
377     gfline = newline;
378
379   if (gfline == NULL)
380     return NULL;
381
382   end = field = gfline;
383
384   if (fs != ':') {
385     if (*end == fs)
386       ++end;
387     else
388       fs = ':';
389   }
390   do {
391     while (*end != fs) {
392       if (!*end) {
393         end = NULL;
394         break;
395       }
396       ++end;
397     }
398   } while (end && fs != ':' && *++end != ':' && *end != '\n') ;
399
400   if (end == NULL) {
401     gfline = NULL;
402     if ((end = strchr(field, '\n')) == NULL)
403       end = field + strlen(field);
404   }
405   else
406     gfline = end + 1;
407
408   *end = '\0';
409
410   return field;
411 }
412
413 /*
414  * Remove all conf entries from the client except those which match
415  * the status field mask.
416  */
417 void det_confs_butmask(struct Client *cptr, int mask)
418 {
419   struct SLink *tmp, *tmp2;
420
421   for (tmp = cptr->confs; tmp; tmp = tmp2) {
422     tmp2 = tmp->next;
423     if ((tmp->value.aconf->status & mask) == 0)
424       detach_conf(cptr, tmp->value.aconf);
425   }
426 }
427
428 /*
429  * validate_hostent - make sure hostnames are valid in a hostent struct
430  * XXX - this is terrible, what's worse is it used to be in the inner
431  * loop of scanning all the I:lines --Bleep
432  */
433 static int validate_hostent(struct hostent* hp)
434 {
435   char        fullname[HOSTLEN + 1];
436   int         i     = 0;
437   int         error = 0;
438   const char* hname;
439
440   for (hname = hp->h_name; hname; hname = hp->h_aliases[i++]) {
441     unsigned int fullnamelen = 0;
442     unsigned int label_count = 0;
443
444     ircd_strncpy(fullname, hname, HOSTLEN);
445     fullname[HOSTLEN] = '\0';
446     /*
447      * Disallow a hostname label to contain anything but a [-a-zA-Z0-9].
448      * It may not start or end on a '.'.
449      * A label may not end on a '-', the maximum length of a label is
450      * 63 characters.
451      * On top of that (which seems to be the RFC) we demand that the
452      * top domain does not contain any digits.
453      */
454     error = (*hname == '.') ? 1 : 0;        /* May not start with a '.' */
455     if (!error) {
456       char *p;
457       for (p = fullname; *p; ++p, ++fullnamelen) {
458         if (*p == '.') { 
459           /* Label may not end on '-' and May not end on a '.' */
460           if (p[-1] == '-' || p[1] == 0) {
461             error = 1;
462             break;
463           }
464           label_count = 0;
465           error = 0;        /* Was not top domain */
466           continue;
467         }
468         if (++label_count > 63) {
469           /* Label not longer then 63 */
470           error = 1;
471           break;
472         }
473         if (*p >= '0' && *p <= '9') {
474           /* In case this is top domain */
475           error = 1;
476           continue;
477         }
478         if (!(*p >= 'a' && *p <= 'z')
479             && !(*p >= 'A' && *p <= 'Z') && *p != '-') {
480           error = 1;
481           break;
482         }
483       }
484     }
485     if (error)
486       break;
487   }
488   return (0 == error);
489 }
490
491 /*
492  * check_limit_and_attach - check client limits and attach I:line
493  */
494 static int check_limit_and_attach(struct Client* cptr, struct ConfItem* aconf)
495 {
496   if (aconf->passwd) {
497     /* Special case: exactly one digit */
498     if (IsDigit(*aconf->passwd) && !aconf->passwd[1]) {
499       /*
500        * Refuse connections when there are already <digit>
501        * clients connected with the same IP number
502        */
503       unsigned short nr = *aconf->passwd - '0';
504       if (IPcheck_nr(cptr) > nr)
505         return ACR_TOO_MANY_FROM_IP; /* Already got nr with that ip# */
506     }
507 #ifdef USEONE
508     else if (0 == strcmp(aconf->passwd, "ONE")) {
509       int i;
510       for (i = HighestFd; i > -1; --i) {
511         if (LocalClientArray[i] && MyUser(LocalClientArray[i]) &&
512             LocalClientArray[i]->ip.s_addr == cptr->ip.s_addr)
513           return ACR_TOO_MANY_FROM_IP; /* Already got one with that ip# */
514       }
515     }
516 #endif
517   }
518   return attach_conf(cptr, aconf);
519 }
520
521 /*
522  * Find the first (best) I line to attach.
523  */
524 int attach_iline(struct Client*  cptr)
525 {
526   struct ConfItem* aconf;
527   const char*      hname;
528   int              i;
529   static char      uhost[HOSTLEN + USERLEN + 3];
530   static char      fullname[HOSTLEN + 1];
531   struct hostent*  hp = 0;
532
533   if (cptr->dns_reply) {
534     hp = cptr->dns_reply->hp;
535     if (!validate_hostent(hp))
536       hp = 0;
537   }
538   for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
539     if (aconf->status != CONF_CLIENT)
540       continue;
541     if (aconf->port && aconf->port != cptr->listener->port)
542       continue;
543     if (!aconf->host || !aconf->name)
544       continue;
545     if (hp) {
546       for (i = 0, hname = hp->h_name; hname; hname = hp->h_aliases[i++]) {
547         ircd_strncpy(fullname, hname, HOSTLEN);
548         fullname[HOSTLEN] = '\0';
549
550         Debug((DEBUG_DNS, "a_il: %s->%s", cptr->sockhost, fullname));
551
552         if (strchr(aconf->name, '@')) {
553           strcpy(uhost, cptr->username);
554           strcat(uhost, "@");
555         }
556         else
557           *uhost = '\0';
558         strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost));
559         uhost[sizeof(uhost) - 1] = 0;
560         if (0 == match(aconf->name, uhost)) {
561           if (strchr(uhost, '@'))
562             cptr->flags |= FLAGS_DOID;
563           return check_limit_and_attach(cptr, aconf);
564         }
565       }
566     }
567     if (strchr(aconf->host, '@')) {
568       ircd_strncpy(uhost, cptr->username, sizeof(uhost) - 2);
569       uhost[sizeof(uhost) - 2] = 0;
570       strcat(uhost, "@");
571     }
572     else
573       *uhost = '\0';
574     strncat(uhost, cptr->sock_ip, sizeof(uhost) - 1 - strlen(uhost));
575     uhost[sizeof(uhost) - 1] = 0;
576     if (match(aconf->host, uhost))
577       continue;
578     if (strchr(uhost, '@'))
579       cptr->flags |= FLAGS_DOID;
580
581     return check_limit_and_attach(cptr, aconf);
582   }
583   return ACR_NO_AUTHORIZATION;
584 }
585
586 /*
587  * detach_conf - Disassociate configuration from the client.
588  */
589 int detach_conf(struct Client *cptr, struct ConfItem *aconf)
590 {
591   struct SLink** lp;
592   struct SLink*  tmp;
593
594   assert(0 != aconf);
595   assert(0 != cptr);
596   assert(0 < aconf->clients);
597
598   lp = &(cptr->confs);
599
600   while (*lp) {
601     if ((*lp)->value.aconf == aconf) {
602       if (aconf->confClass && (aconf->status & CONF_CLIENT_MASK) && 
603           ConfLinks(aconf) > 0)
604         --ConfLinks(aconf);
605       if (0 == --aconf->clients && IsIllegal(aconf))
606         free_conf(aconf);
607       tmp = *lp;
608       *lp = tmp->next;
609       free_link(tmp);
610       return 0;
611     }
612     else
613       lp = &((*lp)->next);
614   }
615   return -1;
616 }
617
618 static int is_attached(struct ConfItem *aconf, struct Client *cptr)
619 {
620   struct SLink *lp;
621
622   for (lp = cptr->confs; lp; lp = lp->next)
623     if (lp->value.aconf == aconf)
624       break;
625
626   return (lp) ? 1 : 0;
627 }
628
629 /*
630  * attach_conf
631  *
632  * Associate a specific configuration entry to a *local*
633  * client (this is the one which used in accepting the
634  * connection). Note, that this automaticly changes the
635  * attachment if there was an old one...
636  */
637 int attach_conf(struct Client *cptr, struct ConfItem *aconf)
638 {
639   struct SLink *lp;
640
641   if (is_attached(aconf, cptr))
642     return ACR_ALREADY_AUTHORIZED;
643   if (IsIllegal(aconf))
644     return ACR_NO_AUTHORIZATION;
645   if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT)) &&
646       ConfLinks(aconf) >= ConfMaxLinks(aconf) && ConfMaxLinks(aconf) > 0)
647     return ACR_TOO_MANY_IN_CLASS;  /* Use this for printing error message */
648   lp = make_link();
649   lp->next = cptr->confs;
650   lp->value.aconf = aconf;
651   cptr->confs = lp;
652   ++aconf->clients;
653   if (aconf->status & CONF_CLIENT_MASK)
654     ConfLinks(aconf)++;
655   return ACR_OK;
656 }
657
658 struct ConfItem *find_admin(void)
659 {
660   struct ConfItem *aconf;
661
662   for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
663     if (aconf->status & CONF_ADMIN)
664       break;
665   }
666   return aconf;
667 }
668
669 struct ConfItem* find_me(void)
670 {
671   struct ConfItem* aconf;
672   for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
673     if (aconf->status & CONF_ME)
674       break;
675   }
676   return aconf;
677 }
678
679 /*
680  * attach_confs_byname
681  *
682  * Attach a CONF line to a client if the name passed matches that for
683  * the conf file (for non-C/N lines) or is an exact match (C/N lines
684  * only).  The difference in behaviour is to stop C:*::* and N:*::*.
685  */
686 struct ConfItem* attach_confs_byname(struct Client* cptr, const char* name,
687                                      int statmask)
688 {
689   struct ConfItem* tmp;
690   struct ConfItem* first = NULL;
691
692   assert(0 != name);
693
694   if (HOSTLEN < strlen(name))
695     return 0;
696
697   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
698     if (0 != (tmp->status & statmask) && !IsIllegal(tmp)) {
699       assert(0 != tmp->name);
700       if (0 == match(tmp->name, name) || 0 == ircd_strcmp(tmp->name, name)) { 
701         if (ACR_OK == attach_conf(cptr, tmp) && !first)
702           first = tmp;
703       }
704     }
705   }
706   return first;
707 }
708
709 /*
710  * Added for new access check    meLazy
711  */
712 struct ConfItem* attach_confs_byhost(struct Client* cptr, const char* host,
713                                      int statmask)
714 {
715   struct ConfItem* tmp;
716   struct ConfItem* first = 0;
717
718   assert(0 != host);
719   if (HOSTLEN < strlen(host))
720     return 0;
721
722   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
723     if (0 != (tmp->status & statmask) && !IsIllegal(tmp)) {
724       assert(0 != tmp->host);
725       if (0 == match(tmp->host, host) || 0 == ircd_strcmp(tmp->host, host)) { 
726         if (ACR_OK == attach_conf(cptr, tmp) && !first)
727           first = tmp;
728       }
729     }
730   }
731   return first;
732 }
733
734 /*
735  * find a conf entry which matches the hostname and has the same name.
736  */
737 struct ConfItem* find_conf_exact(const char* name, const char* user,
738                                  const char* host, int statmask)
739 {
740   struct ConfItem *tmp;
741   char userhost[USERLEN + HOSTLEN + 3];
742
743   /*
744    * XXX - buffer overflow possible, unchecked variables
745    */
746   if (user)
747     sprintf_irc(userhost, "%s@%s", user, host);
748   else
749     ircd_strncpy(userhost, host, sizeof(userhost) - 1);
750
751   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
752     if (!(tmp->status & statmask) || !tmp->name || !tmp->host ||
753         0 != ircd_strcmp(tmp->name, name))
754       continue;
755     /*
756      * Accept if the *real* hostname (usually sockecthost)
757      * socket host) matches *either* host or name field
758      * of the configuration.
759      */
760     if (match(tmp->host, userhost))
761       continue;
762     if (tmp->status & (CONF_OPERATOR | CONF_LOCOP)) {
763       if (tmp->clients < MaxLinks(tmp->confClass))
764         return tmp;
765       else
766         continue;
767     }
768     else
769       return tmp;
770   }
771   return 0;
772 }
773
774 struct ConfItem* find_conf_byname(struct SLink* lp, const char* name,
775                                   int statmask)
776 {
777   struct ConfItem* tmp;
778   assert(0 != name);
779
780   if (HOSTLEN < strlen(name))
781     return 0;
782
783   for (; lp; lp = lp->next) {
784     tmp = lp->value.aconf;
785     if (0 != (tmp->status & statmask)) {
786       assert(0 != tmp->name);
787       if (0 == ircd_strcmp(tmp->name, name) || 0 == match(tmp->name, name))
788         return tmp;
789     }
790   }
791   return 0;
792 }
793
794 /*
795  * Added for new access check    meLazy
796  */
797 struct ConfItem* find_conf_byhost(struct SLink* lp, const char* host,
798                                   int statmask)
799 {
800   struct ConfItem* tmp;
801   assert(0 != host);
802
803   if (HOSTLEN < strlen(host))
804     return 0;
805
806   for (; lp; lp = lp->next) {
807     tmp = lp->value.aconf;
808     if (0 != (tmp->status & statmask)) {
809       assert(0 != tmp->host);
810       if (0 == match(tmp->host, host))
811         return tmp;
812     }
813   }
814   return 0;
815 }
816
817 /*
818  * find_conf_ip
819  *
820  * Find a conf line using the IP# stored in it to search upon.
821  * Added 1/8/92 by Avalon.
822  */
823 struct ConfItem* find_conf_byip(struct SLink* lp, const char* ip, 
824                                 int statmask)
825 {
826   struct ConfItem* tmp;
827
828   for (; lp; lp = lp->next) {
829     tmp = lp->value.aconf;
830     if (0 != (tmp->status & statmask)) {
831       if (0 == memcmp(&tmp->ipnum, ip, sizeof(struct in_addr)))
832         return tmp;
833     }
834   }
835   return 0;
836 }
837
838 /*
839  * find_conf_entry
840  *
841  * - looks for a match on all given fields.
842  */
843 static struct ConfItem *find_conf_entry(struct ConfItem *aconf,
844                                         unsigned int mask)
845 {
846   struct ConfItem *bconf;
847   assert(0 != aconf);
848
849   mask &= ~CONF_ILLEGAL;
850
851   for (bconf = GlobalConfList; bconf; bconf = bconf->next) {
852     if (!(bconf->status & mask) || (bconf->port != aconf->port))
853       continue;
854
855     if ((EmptyString(bconf->host) && !EmptyString(aconf->host)) ||
856         (EmptyString(aconf->host) && !EmptyString(bconf->host)))
857       continue;
858     if (!EmptyString(bconf->host) && 0 != ircd_strcmp(bconf->host, aconf->host))
859       continue;
860
861     if ((EmptyString(bconf->passwd) && !EmptyString(aconf->passwd)) ||
862         (EmptyString(aconf->passwd) && !EmptyString(bconf->passwd)))
863       continue;
864     if (!EmptyString(bconf->passwd) && (!IsDigit(*bconf->passwd) || bconf->passwd[1])
865 #ifdef USEONE
866         && 0 != ircd_strcmp(bconf->passwd, "ONE")
867 #endif
868         && 0 != ircd_strcmp(bconf->passwd, aconf->passwd))
869       continue;
870
871     if ((EmptyString(bconf->name) && !EmptyString(aconf->name)) ||
872         (EmptyString(aconf->name) && !EmptyString(bconf->name)))
873       continue;
874     if (!EmptyString(bconf->name) && 0 != ircd_strcmp(bconf->name, aconf->name))
875       continue;
876     break;
877   }
878   return bconf;
879 }
880
881 /*
882  * rehash
883  *
884  * Actual REHASH service routine. Called with sig == 0 if it has been called
885  * as a result of an operator issuing this command, else assume it has been
886  * called as a result of the server receiving a HUP signal.
887  */
888 int rehash(struct Client *cptr, int sig)
889 {
890   struct ConfItem** tmp = &GlobalConfList;
891   struct ConfItem*  tmp2;
892   struct ConfClass* cltmp;
893   struct Client*    acptr;
894   struct MotdItem*  temp;
895   int               i;
896   int               ret = 0;
897   int               found_g = 0;
898
899   if (1 == sig)
900     sendto_ops("Got signal SIGHUP, reloading ircd conf. file");
901
902   while ((tmp2 = *tmp)) {
903     if (tmp2->clients) {
904       /*
905        * Configuration entry is still in use by some
906        * local clients, cannot delete it--mark it so
907        * that it will be deleted when the last client
908        * exits...
909        */
910       if (!(tmp2->status & CONF_CLIENT)) {
911         *tmp = tmp2->next;
912         tmp2->next = 0;
913       }
914       else
915         tmp = &tmp2->next;
916       tmp2->status |= CONF_ILLEGAL;
917     }
918     else {
919       *tmp = tmp2->next;
920       /* free expression trees of connect rules */
921       if ((tmp2->status & (CONF_CRULEALL | CONF_CRULEAUTO)) &&
922           (tmp2->passwd != NULL))
923         crule_free(&(tmp2->passwd));
924       free_conf(tmp2);
925     }
926   }
927
928   /*
929    * We don't delete the class table, rather mark all entries
930    * for deletion. The table is cleaned up by check_class(). - avalon
931    */
932   for (cltmp = NextClass(FirstClass()); cltmp; cltmp = NextClass(cltmp))
933     MarkDelete(cltmp);
934
935   /*
936    * delete the juped nicks list
937    */
938   clearNickJupes();
939
940   if (sig != 2)
941     flush_resolver_cache();
942
943   mark_listeners_closing();
944
945   if (!conf_init())        /* This calls check_class(), */
946     check_class();         /* unless it fails */
947
948   /*
949    * make sure that the server listener is re-added so it doesn't get
950    * closed
951    */
952   close_listeners();
953
954   /*
955    * Flush out deleted I and P lines although still in use.
956    */
957   for (tmp = &GlobalConfList; (tmp2 = *tmp);) {
958     if (!(tmp2->status & CONF_ILLEGAL))
959       tmp = &tmp2->next;
960     else
961     {
962       *tmp = tmp2->next;
963       tmp2->next = NULL;
964       if (!tmp2->clients)
965         free_conf(tmp2);
966     }
967   }
968   for (i = 0; i <= HighestFd; i++) {
969     if ((acptr = LocalClientArray[i])) {
970       assert(!IsMe(acptr));
971       if (IsServer(acptr)) {
972         det_confs_butmask(acptr,
973             ~(CONF_HUB | CONF_LEAF | CONF_UWORLD | CONF_ILLEGAL));
974         attach_confs_byname(acptr, acptr->name,
975                             CONF_HUB | CONF_LEAF | CONF_UWORLD);
976       }
977       if ((found_g = find_kill(acptr))) {
978         sendto_op_mask(found_g == -2 ? SNO_GLINE : SNO_OPERKILL,
979             found_g == -2 ? "G-line active for %s" : "K-line active for %s",
980             get_client_name(acptr, HIDE_IP));
981         if (exit_client(cptr, acptr, &me, found_g == -2 ? "G-lined" :
982             "K-lined") == CPTR_KILLED)
983           ret = CPTR_KILLED;
984       }
985     }
986   }
987   /* 
988    * free old motd structs
989    */
990   while (motd) {
991     temp = motd->next;
992     MyFree(motd);
993     motd = temp;
994   }
995   while (rmotd) {
996     temp = rmotd->next;
997     MyFree(rmotd);
998     rmotd = temp;
999   }
1000   /* reload motd files */
1001   read_tlines();
1002   rmotd = read_motd(RPATH);
1003   motd = read_motd(MPATH);
1004   return ret;
1005 }
1006
1007 /*
1008  * conf_init
1009  *
1010  * Read configuration file.
1011  *
1012  * returns 0, if file cannot be opened
1013  *         1, if file read
1014  */
1015
1016 #define MAXCONFLINKS 150
1017
1018
1019 int conf_init(void)
1020 {
1021   static char quotes[9][2] = {
1022     {'b', '\b'},
1023     {'f', '\f'},
1024     {'n', '\n'},
1025     {'r', '\r'},
1026     {'t', '\t'},
1027     {'v', '\v'},
1028     {'\\', '\\'},
1029     {0, 0}
1030   };
1031   char *tmp, *s;
1032   FBFILE *file;
1033   int i;
1034   char line[512];
1035   int ccount = 0;
1036   struct ConfItem *aconf = 0;
1037
1038   Debug((DEBUG_DEBUG, "conf_init: ircd.conf = %s", configfile));
1039   if (0 == (file = fbopen(configfile, "r"))) {
1040     return 0;
1041   }
1042   while (fbgets(line, sizeof(line) - 1, file)) {
1043     if ((tmp = strchr(line, '\n')))
1044       *tmp = '\0';
1045     /*
1046      * Do quoting of characters and # detection.
1047      */
1048     for (tmp = line; *tmp; tmp++) {
1049       if (*tmp == '\\') {
1050         for (i = 0; quotes[i][0]; i++) {
1051           if (quotes[i][0] == *(tmp + 1)) {
1052             *tmp = quotes[i][1];
1053             break;
1054           }
1055         }
1056         if (!quotes[i][0])
1057           *tmp = *(tmp + 1);
1058         if (!*(tmp + 1))
1059           break;
1060         else {
1061           for (s = tmp; (*s = *(s + 1)); s++)
1062             ;
1063         }
1064       }
1065       else if (*tmp == '#')
1066         *tmp = '\0';
1067     }
1068     if (!*line || line[0] == '#' || line[0] == '\n' ||
1069         line[0] == ' ' || line[0] == '\t')
1070       continue;
1071     /* Could we test if it's conf line at all?      -Vesa */
1072     if (line[1] != ':') {
1073       Debug((DEBUG_ERROR, "Bad config line: %s", line));
1074       continue;
1075     }
1076     if (aconf)
1077       free_conf(aconf);
1078     aconf = make_conf();
1079
1080     tmp = getfield(line, ':');
1081     if (!tmp)
1082       continue;
1083     switch (*tmp) {
1084     case 'A':                /* Name, e-mail address of administrator */
1085     case 'a':                /* of this server. */
1086       aconf->status = CONF_ADMIN;
1087       break;
1088     case 'C':                /* Server where I should try to connect */
1089     case 'c':                /* in case of lp failures             */
1090       ++ccount;
1091       aconf->status = CONF_SERVER;
1092       break;
1093       /* Connect rule */
1094     case 'D':
1095       aconf->status = CONF_CRULEALL;
1096       break;
1097       /* Connect rule - autos only */
1098     case 'd':
1099       aconf->status = CONF_CRULEAUTO;
1100       break;
1101     case 'H':                /* Hub server line */
1102     case 'h':
1103       aconf->status = CONF_HUB;
1104       break;
1105     case 'I':                /* Just plain normal irc client trying  */
1106     case 'i':                /* to connect me */
1107       aconf->status = CONF_CLIENT;
1108       break;
1109     case 'K':                /* Kill user line on irc.conf           */
1110       aconf->status = CONF_KILL;
1111       break;
1112     case 'k':                /* Kill user line based on IP in ircd.conf */
1113       aconf->status = CONF_IPKILL;
1114       break;
1115       /* Operator. Line should contain at least */
1116       /* password and host where connection is  */
1117     case 'L':                /* guaranteed leaf server */
1118     case 'l':
1119       aconf->status = CONF_LEAF;
1120       break;
1121       /* Me. Host field is name used for this host */
1122       /* and port number is the number of the port */
1123     case 'M':
1124     case 'm':
1125       aconf->status = CONF_ME;
1126       break;
1127     case 'O':
1128       aconf->status = CONF_OPERATOR;
1129       break;
1130       /* Local Operator, (limited privs --SRB) */
1131     case 'o':
1132       aconf->status = CONF_LOCOP;
1133       break;
1134     case 'P':                /* listen port line */
1135     case 'p':
1136       aconf->status = CONF_LISTEN_PORT;
1137       break;
1138     case 'T':                /* print out different motd's */
1139     case 't':                /* based on hostmask */
1140       aconf->status = CONF_TLINES;
1141       break;
1142     case 'U':      /* Underworld server, allowed to hack modes */
1143     case 'u':      /* *Every* server on the net must define the same !!! */
1144       aconf->status = CONF_UWORLD;
1145       break;
1146     case 'Y':
1147     case 'y':
1148       aconf->status = CONF_CLASS;
1149       break;
1150     default:
1151       Debug((DEBUG_ERROR, "Error in config file: %s", line));
1152       break;
1153     }
1154     if (IsIllegal(aconf))
1155       continue;
1156
1157     for (;;) {            /* Fake loop, that I can use break here --msa */
1158       if ((tmp = getfield(NULL, ':')) == NULL)
1159         break;
1160       DupString(aconf->host, tmp);
1161       if ((tmp = getfield(NULL, (aconf->status == CONF_KILL
1162           || aconf->status == CONF_IPKILL) ? '"' : ':')) == NULL)
1163         break;
1164       DupString(aconf->passwd, tmp);
1165       if ((tmp = getfield(NULL, ':')) == NULL)
1166         break;
1167       DupString(aconf->name, tmp);
1168       if ((tmp = getfield(NULL, ':')) == NULL)
1169         break;
1170       aconf->port = atoi(tmp);
1171       tmp = getfield(NULL, ':');
1172       if (aconf->status & CONF_ME) {
1173         if (!tmp) {
1174           Debug((DEBUG_FATAL, "Your M: line must have the Numeric, "
1175               "assigned to you by routing-com, behind the port number!\n"));
1176           ircd_log(L_WARNING, "Your M: line must have the Numeric, "
1177               "assigned to you by routing-com, behind the port number!\n");
1178           exit(-1);
1179         }
1180         SetYXXServerName(&me, atoi(tmp));        /* Our Numeric Nick */
1181       }
1182       else if (tmp)
1183         aconf->confClass = find_class(atoi(tmp));
1184       break;
1185     }
1186     /*
1187      * If conf line is a class definition, create a class entry
1188      * for it and make the conf_line illegal and delete it.
1189      */
1190     if (aconf->status & CONF_CLASS) {
1191       add_class(atoi(aconf->host), atoi(aconf->passwd),
1192           atoi(aconf->name), aconf->port, tmp ? atoi(tmp) : 0);
1193       continue;
1194     }
1195     /*
1196      * Associate each conf line with a class by using a pointer
1197      * to the correct class record. -avalon
1198      */
1199     if (aconf->status & CONF_CLIENT_MASK) {
1200       if (aconf->confClass == 0)
1201         aconf->confClass = find_class(0);
1202     }
1203     if (aconf->status & CONF_LISTEN_PORT) {
1204       int         is_server = 0;
1205       int         is_hidden = 0;
1206       if (!EmptyString(aconf->name)) {
1207         const char* x = aconf->name;
1208         if ('S' == ToUpper(*x))
1209           is_server = 1;
1210         ++x;
1211         if ('H' == ToUpper(*x))
1212           is_hidden = 1;
1213       }
1214       add_listener(aconf->port, aconf->passwd, aconf->host, 
1215                    is_server, is_hidden);
1216       continue;
1217     } 
1218     if (aconf->status & CONF_CLIENT) {
1219       struct ConfItem *bconf;
1220
1221       if ((bconf = find_conf_entry(aconf, aconf->status))) {
1222         delist_conf(bconf);
1223         bconf->status &= ~CONF_ILLEGAL;
1224         if (aconf->status == CONF_CLIENT) {
1225           /*
1226            * copy the password field in case it changed
1227            */
1228           MyFree(bconf->passwd);
1229           bconf->passwd = aconf->passwd;
1230           aconf->passwd = 0;
1231
1232           ConfLinks(bconf) -= bconf->clients;
1233           bconf->confClass = aconf->confClass;
1234           if (bconf->confClass)
1235             ConfLinks(bconf) += bconf->clients;
1236         }
1237         free_conf(aconf);
1238         aconf = bconf;
1239       }
1240     }
1241     if (aconf->status & CONF_SERVER) {
1242       if (ccount > MAXCONFLINKS || !aconf->host || strchr(aconf->host, '*') ||
1243           strchr(aconf->host, '?') || !aconf->name)
1244         continue;
1245     }
1246     if (aconf->status & (CONF_LOCOP | CONF_OPERATOR)) {
1247       if (!strchr(aconf->host, '@')) {
1248         char* newhost;
1249         int len = 3;                /* *@\0 = 3 */
1250
1251         len += strlen(aconf->host);
1252         newhost = (char*) MyMalloc(len);
1253         assert(0 != newhost);
1254         sprintf_irc(newhost, "*@%s", aconf->host);
1255         MyFree(aconf->host);
1256         aconf->host = newhost;
1257       }
1258     }
1259     if (aconf->status & CONF_SERVER) {
1260       if (EmptyString(aconf->passwd))
1261         continue;
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 1;
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) > HOSTLEN ||
1387       (name ? strlen(name) : 0) > 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