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