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 enum AuthorizationCheckResult
495 check_limit_and_attach(struct Client* cptr, struct ConfItem* aconf)
496 {
497   if (aconf->passwd) {
498     /* Special case: exactly one digit */
499     if (IsDigit(*aconf->passwd) && !aconf->passwd[1]) {
500       /*
501        * Refuse connections when there are already <digit>
502        * clients connected with the same IP number
503        */
504       unsigned short nr = *aconf->passwd - '0';
505       if (IPcheck_nr(cptr) > nr)
506         return ACR_TOO_MANY_FROM_IP; /* Already got nr with that ip# */
507     }
508 #ifdef USEONE
509     else if (0 == strcmp(aconf->passwd, "ONE")) {
510       int i;
511       for (i = HighestFd; i > -1; --i) {
512         if (LocalClientArray[i] && MyUser(LocalClientArray[i]) &&
513             LocalClientArray[i]->ip.s_addr == cptr->ip.s_addr)
514           return ACR_TOO_MANY_FROM_IP; /* Already got one with that ip# */
515       }
516     }
517 #endif
518   }
519   return attach_conf(cptr, aconf);
520 }
521
522 /*
523  * Find the first (best) I line to attach.
524  */
525 enum AuthorizationCheckResult attach_iline(struct Client*  cptr)
526 {
527   struct ConfItem* aconf;
528   const char*      hname;
529   int              i;
530   static char      uhost[HOSTLEN + USERLEN + 3];
531   static char      fullname[HOSTLEN + 1];
532   struct hostent*  hp = 0;
533
534   if (cptr->dns_reply) {
535     hp = cptr->dns_reply->hp;
536     if (!validate_hostent(hp))
537       hp = 0;
538   }
539   for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
540     if (aconf->status != CONF_CLIENT)
541       continue;
542     if (aconf->port && aconf->port != cptr->listener->port)
543       continue;
544     if (!aconf->host || !aconf->name)
545       continue;
546     if (hp) {
547       for (i = 0, hname = hp->h_name; hname; hname = hp->h_aliases[i++]) {
548         ircd_strncpy(fullname, hname, HOSTLEN);
549         fullname[HOSTLEN] = '\0';
550
551         Debug((DEBUG_DNS, "a_il: %s->%s", cptr->sockhost, fullname));
552
553         if (strchr(aconf->name, '@')) {
554           strcpy(uhost, cptr->username);
555           strcat(uhost, "@");
556         }
557         else
558           *uhost = '\0';
559         strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost));
560         uhost[sizeof(uhost) - 1] = 0;
561         if (0 == match(aconf->name, uhost)) {
562           if (strchr(uhost, '@'))
563             cptr->flags |= FLAGS_DOID;
564           return check_limit_and_attach(cptr, aconf);
565         }
566       }
567     }
568     if (strchr(aconf->host, '@')) {
569       ircd_strncpy(uhost, cptr->username, sizeof(uhost) - 2);
570       uhost[sizeof(uhost) - 2] = 0;
571       strcat(uhost, "@");
572     }
573     else
574       *uhost = '\0';
575     strncat(uhost, cptr->sock_ip, sizeof(uhost) - 1 - strlen(uhost));
576     uhost[sizeof(uhost) - 1] = 0;
577     if (match(aconf->host, uhost))
578       continue;
579     if (strchr(uhost, '@'))
580       cptr->flags |= FLAGS_DOID;
581
582     return check_limit_and_attach(cptr, aconf);
583   }
584   return ACR_NO_AUTHORIZATION;
585 }
586
587 /*
588  * detach_conf - Disassociate configuration from the client.
589  */
590 int detach_conf(struct Client *cptr, struct ConfItem *aconf)
591 {
592   struct SLink** lp;
593   struct SLink*  tmp;
594
595   assert(0 != aconf);
596   assert(0 != cptr);
597   assert(0 < aconf->clients);
598
599   lp = &(cptr->confs);
600
601   while (*lp) {
602     if ((*lp)->value.aconf == aconf) {
603       if (aconf->confClass && (aconf->status & CONF_CLIENT_MASK) && 
604           ConfLinks(aconf) > 0)
605         --ConfLinks(aconf);
606       if (0 == --aconf->clients && IsIllegal(aconf))
607         free_conf(aconf);
608       tmp = *lp;
609       *lp = tmp->next;
610       free_link(tmp);
611       return 0;
612     }
613     else
614       lp = &((*lp)->next);
615   }
616   return -1;
617 }
618
619 static int is_attached(struct ConfItem *aconf, struct Client *cptr)
620 {
621   struct SLink *lp;
622
623   for (lp = cptr->confs; lp; lp = lp->next)
624     if (lp->value.aconf == aconf)
625       break;
626
627   return (lp) ? 1 : 0;
628 }
629
630 /*
631  * attach_conf
632  *
633  * Associate a specific configuration entry to a *local*
634  * client (this is the one which used in accepting the
635  * connection). Note, that this automaticly changes the
636  * attachment if there was an old one...
637  */
638 enum AuthorizationCheckResult attach_conf(struct Client *cptr, struct ConfItem *aconf)
639 {
640   struct SLink *lp;
641
642   if (is_attached(aconf, cptr))
643     return ACR_ALREADY_AUTHORIZED;
644   if (IsIllegal(aconf))
645     return ACR_NO_AUTHORIZATION;
646   if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT)) &&
647       ConfLinks(aconf) >= ConfMaxLinks(aconf) && ConfMaxLinks(aconf) > 0)
648     return ACR_TOO_MANY_IN_CLASS;  /* Use this for printing error message */
649   lp = make_link();
650   lp->next = cptr->confs;
651   lp->value.aconf = aconf;
652   cptr->confs = lp;
653   ++aconf->clients;
654   if (aconf->status & CONF_CLIENT_MASK)
655     ConfLinks(aconf)++;
656   return ACR_OK;
657 }
658
659 struct ConfItem *find_admin(void)
660 {
661   struct ConfItem *aconf;
662
663   for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
664     if (aconf->status & CONF_ADMIN)
665       break;
666   }
667   return aconf;
668 }
669
670 struct ConfItem* find_me(void)
671 {
672   struct ConfItem* aconf;
673   for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
674     if (aconf->status & CONF_ME)
675       break;
676   }
677   return aconf;
678 }
679
680 /*
681  * attach_confs_byname
682  *
683  * Attach a CONF line to a client if the name passed matches that for
684  * the conf file (for non-C/N lines) or is an exact match (C/N lines
685  * only).  The difference in behaviour is to stop C:*::* and N:*::*.
686  */
687 struct ConfItem* attach_confs_byname(struct Client* cptr, const char* name,
688                                      int statmask)
689 {
690   struct ConfItem* tmp;
691   struct ConfItem* first = NULL;
692
693   assert(0 != name);
694
695   if (HOSTLEN < strlen(name))
696     return 0;
697
698   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
699     if (0 != (tmp->status & statmask) && !IsIllegal(tmp)) {
700       assert(0 != tmp->name);
701       if (0 == match(tmp->name, name) || 0 == ircd_strcmp(tmp->name, name)) { 
702         if (ACR_OK == attach_conf(cptr, tmp) && !first)
703           first = tmp;
704       }
705     }
706   }
707   return first;
708 }
709
710 /*
711  * Added for new access check    meLazy
712  */
713 struct ConfItem* attach_confs_byhost(struct Client* cptr, const char* host,
714                                      int statmask)
715 {
716   struct ConfItem* tmp;
717   struct ConfItem* first = 0;
718
719   assert(0 != host);
720   if (HOSTLEN < strlen(host))
721     return 0;
722
723   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
724     if (0 != (tmp->status & statmask) && !IsIllegal(tmp)) {
725       assert(0 != tmp->host);
726       if (0 == match(tmp->host, host) || 0 == ircd_strcmp(tmp->host, host)) { 
727         if (ACR_OK == attach_conf(cptr, tmp) && !first)
728           first = tmp;
729       }
730     }
731   }
732   return first;
733 }
734
735 /*
736  * find a conf entry which matches the hostname and has the same name.
737  */
738 struct ConfItem* find_conf_exact(const char* name, const char* user,
739                                  const char* host, int statmask)
740 {
741   struct ConfItem *tmp;
742   char userhost[USERLEN + HOSTLEN + 3];
743
744   /*
745    * XXX - buffer overflow possible, unchecked variables
746    */
747   if (user)
748     sprintf_irc(userhost, "%s@%s", user, host);
749   else
750     ircd_strncpy(userhost, host, sizeof(userhost) - 1);
751
752   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
753     if (!(tmp->status & statmask) || !tmp->name || !tmp->host ||
754         0 != ircd_strcmp(tmp->name, name))
755       continue;
756     /*
757      * Accept if the *real* hostname (usually sockecthost)
758      * socket host) matches *either* host or name field
759      * of the configuration.
760      */
761     if (match(tmp->host, userhost))
762       continue;
763     if (tmp->status & (CONF_OPERATOR | CONF_LOCOP)) {
764       if (tmp->clients < MaxLinks(tmp->confClass))
765         return tmp;
766       else
767         continue;
768     }
769     else
770       return tmp;
771   }
772   return 0;
773 }
774
775 struct ConfItem* find_conf_byname(struct SLink* lp, const char* name,
776                                   int statmask)
777 {
778   struct ConfItem* tmp;
779   assert(0 != name);
780
781   if (HOSTLEN < strlen(name))
782     return 0;
783
784   for (; lp; lp = lp->next) {
785     tmp = lp->value.aconf;
786     if (0 != (tmp->status & statmask)) {
787       assert(0 != tmp->name);
788       if (0 == ircd_strcmp(tmp->name, name) || 0 == match(tmp->name, name))
789         return tmp;
790     }
791   }
792   return 0;
793 }
794
795 /*
796  * Added for new access check    meLazy
797  */
798 struct ConfItem* find_conf_byhost(struct SLink* lp, const char* host,
799                                   int statmask)
800 {
801   struct ConfItem* tmp;
802   assert(0 != host);
803
804   if (HOSTLEN < strlen(host))
805     return 0;
806
807   for (; lp; lp = lp->next) {
808     tmp = lp->value.aconf;
809     if (0 != (tmp->status & statmask)) {
810       assert(0 != tmp->host);
811       if (0 == match(tmp->host, host))
812         return tmp;
813     }
814   }
815   return 0;
816 }
817
818 /*
819  * find_conf_ip
820  *
821  * Find a conf line using the IP# stored in it to search upon.
822  * Added 1/8/92 by Avalon.
823  */
824 struct ConfItem* find_conf_byip(struct SLink* lp, const char* ip, 
825                                 int statmask)
826 {
827   struct ConfItem* tmp;
828
829   for (; lp; lp = lp->next) {
830     tmp = lp->value.aconf;
831     if (0 != (tmp->status & statmask)) {
832       if (0 == memcmp(&tmp->ipnum, ip, sizeof(struct in_addr)))
833         return tmp;
834     }
835   }
836   return 0;
837 }
838
839 /*
840  * find_conf_entry
841  *
842  * - looks for a match on all given fields.
843  */
844 static struct ConfItem *find_conf_entry(struct ConfItem *aconf,
845                                         unsigned int mask)
846 {
847   struct ConfItem *bconf;
848   assert(0 != aconf);
849
850   mask &= ~CONF_ILLEGAL;
851
852   for (bconf = GlobalConfList; bconf; bconf = bconf->next) {
853     if (!(bconf->status & mask) || (bconf->port != aconf->port))
854       continue;
855
856     if ((EmptyString(bconf->host) && !EmptyString(aconf->host)) ||
857         (EmptyString(aconf->host) && !EmptyString(bconf->host)))
858       continue;
859     if (!EmptyString(bconf->host) && 0 != ircd_strcmp(bconf->host, aconf->host))
860       continue;
861
862     if ((EmptyString(bconf->passwd) && !EmptyString(aconf->passwd)) ||
863         (EmptyString(aconf->passwd) && !EmptyString(bconf->passwd)))
864       continue;
865     if (!EmptyString(bconf->passwd) && (!IsDigit(*bconf->passwd) || bconf->passwd[1])
866 #ifdef USEONE
867         && 0 != ircd_strcmp(bconf->passwd, "ONE")
868 #endif
869         && 0 != ircd_strcmp(bconf->passwd, aconf->passwd))
870       continue;
871
872     if ((EmptyString(bconf->name) && !EmptyString(aconf->name)) ||
873         (EmptyString(aconf->name) && !EmptyString(bconf->name)))
874       continue;
875     if (!EmptyString(bconf->name) && 0 != ircd_strcmp(bconf->name, aconf->name))
876       continue;
877     break;
878   }
879   return bconf;
880 }
881
882 /*
883  * rehash
884  *
885  * Actual REHASH service routine. Called with sig == 0 if it has been called
886  * as a result of an operator issuing this command, else assume it has been
887  * called as a result of the server receiving a HUP signal.
888  */
889 int rehash(struct Client *cptr, int sig)
890 {
891   struct ConfItem** tmp = &GlobalConfList;
892   struct ConfItem*  tmp2;
893   struct ConfClass* cltmp;
894   struct Client*    acptr;
895   struct MotdItem*  temp;
896   int               i;
897   int               ret = 0;
898   int               found_g = 0;
899
900   if (1 == sig)
901     sendto_ops("Got signal SIGHUP, reloading ircd conf. file");
902
903   while ((tmp2 = *tmp)) {
904     if (tmp2->clients) {
905       /*
906        * Configuration entry is still in use by some
907        * local clients, cannot delete it--mark it so
908        * that it will be deleted when the last client
909        * exits...
910        */
911       if (!(tmp2->status & CONF_CLIENT)) {
912         *tmp = tmp2->next;
913         tmp2->next = 0;
914       }
915       else
916         tmp = &tmp2->next;
917       tmp2->status |= CONF_ILLEGAL;
918     }
919     else {
920       *tmp = tmp2->next;
921       /* free expression trees of connect rules */
922       if ((tmp2->status & (CONF_CRULEALL | CONF_CRULEAUTO)) &&
923           (tmp2->passwd != NULL))
924         crule_free(&(tmp2->passwd));
925       free_conf(tmp2);
926     }
927   }
928
929   /*
930    * We don't delete the class table, rather mark all entries
931    * for deletion. The table is cleaned up by check_class(). - avalon
932    */
933   for (cltmp = NextClass(FirstClass()); cltmp; cltmp = NextClass(cltmp))
934     MarkDelete(cltmp);
935
936   /*
937    * delete the juped nicks list
938    */
939   clearNickJupes();
940
941   if (sig != 2)
942     flush_resolver_cache();
943
944   mark_listeners_closing();
945
946   if (!conf_init())        /* This calls check_class(), */
947     check_class();         /* unless it fails */
948
949   /*
950    * make sure that the server listener is re-added so it doesn't get
951    * closed
952    */
953   close_listeners();
954
955   /*
956    * Flush out deleted I and P lines although still in use.
957    */
958   for (tmp = &GlobalConfList; (tmp2 = *tmp);) {
959     if (!(tmp2->status & CONF_ILLEGAL))
960       tmp = &tmp2->next;
961     else
962     {
963       *tmp = tmp2->next;
964       tmp2->next = NULL;
965       if (!tmp2->clients)
966         free_conf(tmp2);
967     }
968   }
969   for (i = 0; i <= HighestFd; i++) {
970     if ((acptr = LocalClientArray[i])) {
971       assert(!IsMe(acptr));
972       if (IsServer(acptr)) {
973         det_confs_butmask(acptr,
974             ~(CONF_HUB | CONF_LEAF | CONF_UWORLD | CONF_ILLEGAL));
975         attach_confs_byname(acptr, acptr->name,
976                             CONF_HUB | CONF_LEAF | CONF_UWORLD);
977       }
978       if ((found_g = find_kill(acptr))) {
979         sendto_op_mask(found_g == -2 ? SNO_GLINE : SNO_OPERKILL,
980             found_g == -2 ? "G-line active for %s" : "K-line active for %s",
981             get_client_name(acptr, HIDE_IP));
982         if (exit_client(cptr, acptr, &me, found_g == -2 ? "G-lined" :
983             "K-lined") == CPTR_KILLED)
984           ret = CPTR_KILLED;
985       }
986     }
987   }
988   /* 
989    * free old motd structs
990    */
991   while (motd) {
992     temp = motd->next;
993     MyFree(motd);
994     motd = temp;
995   }
996   while (rmotd) {
997     temp = rmotd->next;
998     MyFree(rmotd);
999     rmotd = temp;
1000   }
1001   /* reload motd files */
1002   read_tlines();
1003   rmotd = read_motd(RPATH);
1004   motd = read_motd(MPATH);
1005   return ret;
1006 }
1007
1008 /*
1009  * conf_init
1010  *
1011  * Read configuration file.
1012  *
1013  * returns 0, if file cannot be opened
1014  *         1, if file read
1015  */
1016
1017 #define MAXCONFLINKS 150
1018
1019
1020 int conf_init(void)
1021 {
1022   static char quotes[9][2] = {
1023     {'b', '\b'},
1024     {'f', '\f'},
1025     {'n', '\n'},
1026     {'r', '\r'},
1027     {'t', '\t'},
1028     {'v', '\v'},
1029     {'\\', '\\'},
1030     {0, 0}
1031   };
1032   char *tmp, *s;
1033   FBFILE *file;
1034   int i;
1035   char line[512];
1036   int ccount = 0;
1037   struct ConfItem *aconf = 0;
1038
1039   Debug((DEBUG_DEBUG, "conf_init: ircd.conf = %s", configfile));
1040   if (0 == (file = fbopen(configfile, "r"))) {
1041     return 0;
1042   }
1043   while (fbgets(line, sizeof(line) - 1, file)) {
1044     if ((tmp = strchr(line, '\n')))
1045       *tmp = '\0';
1046     /*
1047      * Do quoting of characters and # detection.
1048      */
1049     for (tmp = line; *tmp; tmp++) {
1050       if (*tmp == '\\') {
1051         for (i = 0; quotes[i][0]; i++) {
1052           if (quotes[i][0] == *(tmp + 1)) {
1053             *tmp = quotes[i][1];
1054             break;
1055           }
1056         }
1057         if (!quotes[i][0])
1058           *tmp = *(tmp + 1);
1059         if (!*(tmp + 1))
1060           break;
1061         else {
1062           for (s = tmp; (*s = *(s + 1)); s++)
1063             ;
1064         }
1065       }
1066       else if (*tmp == '#')
1067         *tmp = '\0';
1068     }
1069     if (!*line || line[0] == '#' || line[0] == '\n' ||
1070         line[0] == ' ' || line[0] == '\t')
1071       continue;
1072     /* Could we test if it's conf line at all?      -Vesa */
1073     if (line[1] != ':') {
1074       Debug((DEBUG_ERROR, "Bad config line: %s", line));
1075       continue;
1076     }
1077     if (aconf)
1078       free_conf(aconf);
1079     aconf = make_conf();
1080
1081     tmp = getfield(line, ':');
1082     if (!tmp)
1083       continue;
1084     switch (*tmp) {
1085     case 'A':                /* Name, e-mail address of administrator */
1086     case 'a':                /* of this server. */
1087       aconf->status = CONF_ADMIN;
1088       break;
1089     case 'C':                /* Server where I should try to connect */
1090     case 'c':                /* in case of lp failures             */
1091       ++ccount;
1092       aconf->status = CONF_SERVER;
1093       break;
1094       /* Connect rule */
1095     case 'D':
1096       aconf->status = CONF_CRULEALL;
1097       break;
1098       /* Connect rule - autos only */
1099     case 'd':
1100       aconf->status = CONF_CRULEAUTO;
1101       break;
1102     case 'H':                /* Hub server line */
1103     case 'h':
1104       aconf->status = CONF_HUB;
1105       break;
1106     case 'I':                /* Just plain normal irc client trying  */
1107     case 'i':                /* to connect me */
1108       aconf->status = CONF_CLIENT;
1109       break;
1110     case 'K':                /* Kill user line on irc.conf           */
1111       aconf->status = CONF_KILL;
1112       break;
1113     case 'k':                /* Kill user line based on IP in ircd.conf */
1114       aconf->status = CONF_IPKILL;
1115       break;
1116       /* Operator. Line should contain at least */
1117       /* password and host where connection is  */
1118     case 'L':                /* guaranteed leaf server */
1119     case 'l':
1120       aconf->status = CONF_LEAF;
1121       break;
1122       /* Me. Host field is name used for this host */
1123       /* and port number is the number of the port */
1124     case 'M':
1125     case 'm':
1126       aconf->status = CONF_ME;
1127       break;
1128     case 'O':
1129       aconf->status = CONF_OPERATOR;
1130       break;
1131       /* Local Operator, (limited privs --SRB) */
1132     case 'o':
1133       aconf->status = CONF_LOCOP;
1134       break;
1135     case 'P':                /* listen port line */
1136     case 'p':
1137       aconf->status = CONF_LISTEN_PORT;
1138       break;
1139     case 'T':                /* print out different motd's */
1140     case 't':                /* based on hostmask */
1141       aconf->status = CONF_TLINES;
1142       break;
1143     case 'U':      /* Underworld server, allowed to hack modes */
1144     case 'u':      /* *Every* server on the net must define the same !!! */
1145       aconf->status = CONF_UWORLD;
1146       break;
1147     case 'Y':
1148     case 'y':
1149       aconf->status = CONF_CLASS;
1150       break;
1151     default:
1152       Debug((DEBUG_ERROR, "Error in config file: %s", line));
1153       break;
1154     }
1155     if (IsIllegal(aconf))
1156       continue;
1157
1158     for (;;) {            /* Fake loop, that I can use break here --msa */
1159       if ((tmp = getfield(NULL, ':')) == NULL)
1160         break;
1161       DupString(aconf->host, tmp);
1162       if ((tmp = getfield(NULL, (aconf->status == CONF_KILL
1163           || aconf->status == CONF_IPKILL) ? '"' : ':')) == NULL)
1164         break;
1165       DupString(aconf->passwd, tmp);
1166       if ((tmp = getfield(NULL, ':')) == NULL)
1167         break;
1168       DupString(aconf->name, tmp);
1169       if ((tmp = getfield(NULL, ':')) == NULL)
1170         break;
1171       aconf->port = atoi(tmp);
1172       tmp = getfield(NULL, ':');
1173       if (aconf->status & CONF_ME) {
1174         if (!tmp) {
1175           Debug((DEBUG_FATAL, "Your M: line must have the Numeric, "
1176               "assigned to you by routing-com, behind the port number!\n"));
1177           ircd_log(L_WARNING, "Your M: line must have the Numeric, "
1178               "assigned to you by routing-com, behind the port number!\n");
1179           exit(-1);
1180         }
1181         SetYXXServerName(&me, atoi(tmp));        /* Our Numeric Nick */
1182       }
1183       else if (tmp)
1184         aconf->confClass = find_class(atoi(tmp));
1185       break;
1186     }
1187     /*
1188      * If conf line is a class definition, create a class entry
1189      * for it and make the conf_line illegal and delete it.
1190      */
1191     if (aconf->status & CONF_CLASS) {
1192       add_class(atoi(aconf->host), atoi(aconf->passwd),
1193           atoi(aconf->name), aconf->port, tmp ? atoi(tmp) : 0);
1194       continue;
1195     }
1196     /*
1197      * Associate each conf line with a class by using a pointer
1198      * to the correct class record. -avalon
1199      */
1200     if (aconf->status & CONF_CLIENT_MASK) {
1201       if (aconf->confClass == 0)
1202         aconf->confClass = find_class(0);
1203     }
1204     if (aconf->status & CONF_LISTEN_PORT) {
1205       int         is_server = 0;
1206       int         is_hidden = 0;
1207       if (!EmptyString(aconf->name)) {
1208         const char* x = aconf->name;
1209         if ('S' == ToUpper(*x))
1210           is_server = 1;
1211         ++x;
1212         if ('H' == ToUpper(*x))
1213           is_hidden = 1;
1214       }
1215       add_listener(aconf->port, aconf->passwd, aconf->host, 
1216                    is_server, is_hidden);
1217       continue;
1218     } 
1219     if (aconf->status & CONF_CLIENT) {
1220       struct ConfItem *bconf;
1221
1222       if ((bconf = find_conf_entry(aconf, aconf->status))) {
1223         delist_conf(bconf);
1224         bconf->status &= ~CONF_ILLEGAL;
1225         if (aconf->status == CONF_CLIENT) {
1226           /*
1227            * copy the password field in case it changed
1228            */
1229           MyFree(bconf->passwd);
1230           bconf->passwd = aconf->passwd;
1231           aconf->passwd = 0;
1232
1233           ConfLinks(bconf) -= bconf->clients;
1234           bconf->confClass = aconf->confClass;
1235           if (bconf->confClass)
1236             ConfLinks(bconf) += bconf->clients;
1237         }
1238         free_conf(aconf);
1239         aconf = bconf;
1240       }
1241     }
1242     if (aconf->status & CONF_SERVER) {
1243       if (ccount > MAXCONFLINKS || !aconf->host || strchr(aconf->host, '*') ||
1244           strchr(aconf->host, '?') || !aconf->name)
1245         continue;
1246     }
1247     if (aconf->status & (CONF_LOCOP | CONF_OPERATOR)) {
1248       if (!strchr(aconf->host, '@')) {
1249         char* newhost;
1250         int len = 3;                /* *@\0 = 3 */
1251
1252         len += strlen(aconf->host);
1253         newhost = (char*) MyMalloc(len);
1254         assert(0 != newhost);
1255         sprintf_irc(newhost, "*@%s", aconf->host);
1256         MyFree(aconf->host);
1257         aconf->host = newhost;
1258       }
1259     }
1260     if (aconf->status & CONF_SERVER) {
1261       if (EmptyString(aconf->passwd))
1262         continue;
1263       lookup_confhost(aconf);
1264     }
1265
1266     /* Create expression tree from connect rule...
1267      * If there's a parsing error, nuke the conf structure */
1268     if (aconf->status & (CONF_CRULEALL | CONF_CRULEAUTO)) {
1269       MyFree(aconf->passwd);
1270       if ((aconf->passwd = (char *)crule_parse(aconf->name)) == NULL) {
1271         free_conf(aconf);
1272         aconf = NULL;
1273         continue;
1274       }
1275     }
1276
1277     /*
1278      * Own port and name cannot be changed after the startup.
1279      * (or could be allowed, but only if all links are closed first).
1280      * Configuration info does not override the name and port
1281      * if previously defined. Note, that "info"-field can be
1282      * changed by "/rehash".
1283      */
1284     if (aconf->status == CONF_ME) {
1285       ircd_strncpy(me.info, aconf->name, REALLEN);
1286     }
1287
1288     /*
1289      * Juped nicks are listed in the 'password' field of U:lines,
1290      * the list is comma separated and might be empty and/or contain
1291      * empty elements... the only limit is that it MUST be shorter
1292      * than 512 chars, or they will be cutted out :)
1293      */
1294     if ((aconf->status == CONF_UWORLD) && (aconf->passwd) && (*aconf->passwd))
1295       addNickJupes(aconf->passwd);
1296
1297     if (aconf->status & CONF_ADMIN) {
1298       if (!aconf->host || !aconf->passwd || !aconf->name) {
1299         Debug((DEBUG_FATAL, "Your A: line must have 4 fields!\n"));
1300         ircd_log(L_WARNING, "Your A: line must have 4 fields!\n");
1301         exit(-1);
1302       }
1303     }
1304     collapse(aconf->host);
1305     collapse(aconf->name);
1306     Debug((DEBUG_NOTICE,
1307         "Read Init: (%d) (%s) (%s) (%s) (%u) (%p)",
1308         aconf->status, aconf->host, aconf->passwd,
1309         aconf->name, aconf->port, aconf->confClass));
1310     aconf->next = GlobalConfList;
1311     GlobalConfList = aconf;
1312     aconf = NULL;
1313   }
1314   if (aconf)
1315     free_conf(aconf);
1316   fbclose(file);
1317   check_class();
1318   nextping = nextconnect = CurrentTime;
1319   return 1;
1320 }
1321
1322 /* read_tlines 
1323  * Read info from T:lines into TRecords which include the file 
1324  * timestamp, the hostmask, and the contents of the motd file 
1325  * -Ghostwolf 7sep97
1326  */
1327 void read_tlines()
1328 {
1329   struct ConfItem *tmp;
1330   struct TRecord *temp;
1331   struct TRecord *last = NULL;        /* Init. to avoid compiler warning */
1332   struct MotdItem *amotd;
1333
1334   /* Free the old trecords and the associated motd contents first */
1335   while (tdata)
1336   {
1337     last = tdata->next;
1338     while (tdata->tmotd)
1339     {
1340       amotd = tdata->tmotd->next;
1341       MyFree(tdata->tmotd);
1342       tdata->tmotd = amotd;
1343     }
1344     MyFree(tdata);
1345     tdata = last;
1346   }
1347
1348   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
1349     if (tmp->status == CONF_TLINES && tmp->host && tmp->passwd) {
1350       temp = (struct TRecord*) MyMalloc(sizeof(struct TRecord));
1351       assert(0 != temp);
1352
1353       temp->hostmask = tmp->host;
1354       temp->tmotd = read_motd(tmp->passwd);
1355       temp->tmotd_tm = motd_tm;
1356       temp->next = NULL;
1357       if (!tdata)
1358         tdata = temp;
1359       else
1360         last->next = temp;
1361       last = temp;
1362     }
1363   }
1364 }
1365
1366 int find_kill(struct Client *cptr)
1367 {
1368   char             reply[256];
1369   const char*      host;
1370   const char*      name;
1371   struct ConfItem* tmp;
1372   struct Gline*    agline = NULL;
1373
1374   assert(0 != cptr);
1375
1376   if (!cptr->user)
1377     return 0;
1378
1379   host = cptr->sockhost;
1380   name = cptr->user->username;
1381
1382 #if 0
1383   /*
1384    * whee :)
1385    * XXX - if this ever happens, we're already screwed
1386    */
1387   if (strlen(host) > HOSTLEN ||
1388       (name ? strlen(name) : 0) > HOSTLEN)
1389     return (0);
1390 #endif
1391
1392   reply[0] = '\0';
1393
1394   for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
1395     /* Added a check against the user's IP address as well.
1396      * If the line is either CONF_KILL or CONF_IPKILL, check it; if and only
1397      * if it's CONF_IPKILL, check the IP address as well (the && below will
1398      * short circuit and the match won't even get run) -Kev
1399      */
1400     if ((tmp->status & CONF_KLINE) && tmp->host && tmp->name &&
1401         (match(tmp->host, host) == 0 ||
1402         ((tmp->status == CONF_IPKILL) &&
1403         match(tmp->host, ircd_ntoa((const char*) &cptr->ip)) == 0)) &&
1404         (!name || match(tmp->name, name) == 0) &&
1405         (!tmp->port || (tmp->port == cptr->listener->port)))
1406     {
1407       /*
1408        * Can short-circuit evaluation - not taking chances
1409        * because check_time_interval destroys tmp->passwd
1410        * - Mmmm
1411        */
1412       if (EmptyString(tmp->passwd))
1413         break;
1414       else if (is_comment(tmp->passwd))
1415         break;
1416       else if (check_time_interval(tmp->passwd, reply))
1417         break;
1418     }
1419   }
1420   if (reply[0])
1421     sendto_one(cptr, reply, me.name, ERR_YOUREBANNEDCREEP, cptr->name);
1422   else if (tmp) {
1423     if (EmptyString(tmp->passwd))
1424       sendto_one(cptr,
1425           ":%s %d %s :Connection from your host is refused on this server.",
1426           me.name, ERR_YOUREBANNEDCREEP, cptr->name);
1427     else {
1428       if (*tmp->passwd == '"') {
1429         char *sbuf =
1430             sprintf_irc(sendbuf, ":%s %d %s :%s", me.name, ERR_YOUREBANNEDCREEP,
1431             cptr->name, &tmp->passwd[1]);
1432         sbuf[-1] = '.';                /* Overwrite last quote with a dot */
1433         sendbufto_one(cptr);
1434       }
1435       else if (*tmp->passwd == '!')
1436         killcomment(cptr, cptr->name, &tmp->passwd[1]);
1437       else
1438 #ifdef COMMENT_IS_FILE
1439         killcomment(cptr, cptr->name, tmp->passwd);
1440 #else
1441         sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
1442             cptr->name, tmp->passwd);
1443 #endif
1444     }
1445   }
1446
1447   /* find active glines */
1448   /* added a check against the user's IP address to find_gline() -Kev */
1449   else if ((agline = gline_lookup(cptr)) && GlineIsActive(agline))
1450     sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
1451                cptr->name, GlineReason(agline));
1452   else
1453     agline = NULL;                /* if a gline was found, it was inactive */
1454
1455   return (tmp ? -1 : (agline ? -2 : 0));
1456 }
1457
1458 struct MotdItem* read_motd(const char* motdfile)
1459 {
1460   FBFILE*          file = NULL;
1461   struct MotdItem* temp;
1462   struct MotdItem* newmotd;
1463   struct MotdItem* last;
1464   struct stat      sb;
1465   char             line[80];
1466   char*            tmp;
1467
1468   if (NULL == (file = fbopen(motdfile, "r"))) {
1469     Debug((DEBUG_ERROR, "Couldn't open \"%s\": %s", motdfile, strerror(errno)));
1470     return NULL;
1471   }
1472   if (-1 == fbstat(&sb, file)) {
1473     fbclose(file);
1474     return NULL;
1475   }
1476   newmotd = last = NULL;
1477   motd_tm = *localtime((time_t *) & sb.st_mtime);        /* NetBSD needs cast */
1478   while (fbgets(line, sizeof(line) - 1, file)) {
1479     if ((tmp = (char *)strchr(line, '\n')))
1480       *tmp = '\0';
1481     if ((tmp = (char *)strchr(line, '\r')))
1482       *tmp = '\0';
1483     temp = (struct MotdItem*) MyMalloc(sizeof(struct MotdItem));
1484     assert(0 != temp);
1485     strcpy(temp->line, line);
1486     temp->next = NULL;
1487     if (!newmotd)
1488       newmotd = temp;
1489     else
1490       last->next = temp;
1491     last = temp;
1492   }
1493   fbclose(file);
1494   return newmotd;
1495 }
1496
1497
1498 /*
1499  * Ordinary client access check. Look for conf lines which have the same
1500  * status as the flags passed.
1501  */
1502 enum AuthorizationCheckResult conf_check_client(struct Client *cptr)
1503 {
1504   enum AuthorizationCheckResult acr = ACR_OK;
1505
1506   ClearAccess(cptr);
1507
1508   if ((acr = attach_iline(cptr))) {
1509     Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]", 
1510           cptr->name, cptr->sockhost));
1511     return acr;
1512   }
1513   return ACR_OK;
1514 }
1515
1516 /*
1517  * check_server()
1518  *
1519  * Check access for a server given its name (passed in cptr struct).
1520  * Must check for all C/N lines which have a name which matches the
1521  * name given and a host which matches. A host alias which is the
1522  * same as the server name is also acceptable in the host field of a
1523  * C/N line.
1524  *
1525  * Returns
1526  *  0 = Success
1527  * -1 = Access denied
1528  * -2 = Bad socket.
1529  */
1530 int conf_check_server(struct Client *cptr)
1531 {
1532   struct ConfItem* c_conf = NULL;
1533   struct SLink*    lp;
1534
1535   Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", 
1536         cptr->name, cptr->sockhost));
1537
1538   if (IsUnknown(cptr) && !attach_confs_byname(cptr, cptr->name, CONF_SERVER)) {
1539     Debug((DEBUG_DNS, "No C/N lines for %s", cptr->sockhost));
1540     return -1;
1541   }
1542   lp = cptr->confs;
1543   /*
1544    * We initiated this connection so the client should have a C and N
1545    * line already attached after passing through the connect_server()
1546    * function earlier.
1547    */
1548   if (IsConnecting(cptr) || IsHandshake(cptr)) {
1549     c_conf = find_conf_byname(lp, cptr->name, CONF_SERVER);
1550     if (!c_conf) {
1551       sendto_ops("Connect Error: lost C:line for %s", cptr->name);
1552       det_confs_butmask(cptr, 0);
1553       return -1;
1554     }
1555   }
1556
1557   ClearAccess(cptr);
1558
1559   if (!c_conf) {
1560     if (cptr->dns_reply) {
1561       int             i;
1562       struct hostent* hp = cptr->dns_reply->hp;
1563       const char*     name = hp->h_name;
1564       /*
1565        * If we are missing a C or N line from above, search for
1566        * it under all known hostnames we have for this ip#.
1567        */
1568       for (i = 0; name; name = hp->h_aliases[i++]) {
1569         if ((c_conf = find_conf_byhost(lp, name, CONF_SERVER))) {
1570           ircd_strncpy(cptr->sockhost, name, HOSTLEN);
1571           break;
1572         }
1573       }
1574       if (!c_conf) {
1575         for (i = 0; hp->h_addr_list[i]; i++) {
1576           if ((c_conf = find_conf_byip(lp, hp->h_addr_list[i], CONF_SERVER)))
1577             break;
1578         }
1579       }
1580     }
1581     else {
1582       /*
1583        * Check for C lines with the hostname portion the ip number
1584        * of the host the server runs on. This also checks the case where
1585        * there is a server connecting from 'localhost'.
1586        */
1587       c_conf = find_conf_byhost(lp, cptr->sockhost, CONF_SERVER);
1588     }
1589   }
1590   /*
1591    * Attach by IP# only if all other checks have failed.
1592    * It is quite possible to get here with the strange things that can
1593    * happen when using DNS in the way the irc server does. -avalon
1594    */
1595   if (!c_conf)
1596     c_conf = find_conf_byip(lp, (const char*) &cptr->ip, CONF_SERVER);
1597   /*
1598    * detach all conf lines that got attached by attach_confs()
1599    */
1600   det_confs_butmask(cptr, 0);
1601   /*
1602    * if no C or no N lines, then deny access
1603    */
1604   if (!c_conf) {
1605     Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s]",
1606           cptr->name, cptr->username, cptr->sockhost));
1607     return -1;
1608   }
1609   ircd_strncpy(cptr->name, c_conf->name, HOSTLEN);
1610   /*
1611    * attach the C and N lines to the client structure for later use.
1612    */
1613   attach_conf(cptr, c_conf);
1614   attach_confs_byname(cptr, cptr->name, CONF_HUB | CONF_LEAF | CONF_UWORLD);
1615
1616   if (INADDR_NONE == c_conf->ipnum.s_addr)
1617     c_conf->ipnum.s_addr = cptr->ip.s_addr;
1618
1619   Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", cptr->name, cptr->sockhost));
1620   return 0;
1621 }
1622