2 * IRC - Internet Relay Chat, ircd/s_conf.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
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)
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.
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.
22 #include <sys/socket.h>
28 # include <sys/wait.h>
31 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
34 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
70 #include "sprintf_irc.h"
77 static int check_time_interval(char *, char *);
78 static int lookup_confhost(aConfItem *);
79 static int is_comment(char *);
80 static void killcomment(aClient *sptr, char *parv, char *filename);
82 aConfItem *conf = NULL;
84 aMotdItem *motd = NULL;
85 aMotdItem *rmotd = NULL;
86 atrecord *tdata = NULL;
90 * field breakup for ircd.conf file.
92 static char *gfline = NULL;
93 char *getfield(char *newline, char fs)
103 end = field = gfline;
124 while (end && fs != ':' && *++end != ':' && *end != '\n');
129 if ((end = strchr(field, '\n')) == NULL)
130 end = field + strlen(field);
141 * Remove all conf entries from the client except those which match
142 * the status field mask.
144 void det_confs_butmask(aClient *cptr, int mask)
146 Reg1 Link *tmp, *tmp2;
148 for (tmp = cptr->confs; tmp; tmp = tmp2)
151 if ((tmp->value.aconf->status & mask) == 0)
152 detach_conf(cptr, tmp->value.aconf);
157 * Find the first (best) I line to attach.
159 enum AuthorizationCheckResult attach_Iline(aClient *cptr, struct hostent *hp,
162 Reg1 aConfItem *aconf;
163 Reg3 const char *hname;
165 static char uhost[HOSTLEN + USERLEN + 3];
166 static char fullname[HOSTLEN + 1];
168 for (aconf = conf; aconf; aconf = aconf->next)
170 if (aconf->status != CONF_CLIENT)
172 if (aconf->port && aconf->port != cptr->acpt->port)
174 if (!aconf->host || !aconf->name)
177 for (i = 0, hname = hp->h_name; hname; hname = hp->h_aliases[i++])
179 size_t fullnamelen = 0;
180 size_t label_count = 0;
183 strncpy(fullname, hname, HOSTLEN);
184 fullname[HOSTLEN] = 0;
187 * Disallow a hostname label to contain anything but a [-a-zA-Z0-9].
188 * It may not start or end on a '.'.
189 * A label may not end on a '-', the maximum length of a label is
191 * On top of that (which seems to be the RFC) we demand that the
192 * top domain does not contain any digits.
194 error = (*hname == '.') ? 1 : 0; /* May not start with a '.' */
198 for (p = fullname; *p; ++p, ++fullnamelen)
202 if (p[-1] == '-' /* Label may not end on '-' */
203 || p[1] == 0) /* May not end on a '.' */
209 error = 0; /* Was not top domain */
212 if (++label_count > 63) /* Label not longer then 63 */
217 if (*p >= '0' && *p <= '9')
219 error = 1; /* In case this is top domain */
222 if (!(*p >= 'a' && *p <= 'z')
223 && !(*p >= 'A' && *p <= 'Z') && *p != '-')
236 add_local_domain(fullname, HOSTLEN - fullnamelen);
237 Debug((DEBUG_DNS, "a_il: %s->%s", sockhost, fullname));
238 if (strchr(aconf->name, '@'))
240 strcpy(uhost, cptr->username);
245 strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost));
246 uhost[sizeof(uhost) - 1] = 0;
247 if (!match(aconf->name, uhost))
249 if (strchr(uhost, '@'))
250 cptr->flags |= FLAGS_DOID;
255 if (strchr(aconf->host, '@'))
257 strncpy(uhost, cptr->username, sizeof(uhost) - 2);
258 uhost[sizeof(uhost) - 2] = 0;
263 strncat(uhost, sockhost, sizeof(uhost) - 1 - strlen(uhost));
264 uhost[sizeof(uhost) - 1] = 0;
265 if (match(aconf->host, uhost))
267 if (strchr(uhost, '@'))
268 cptr->flags |= FLAGS_DOID;
269 if (hp && hp->h_name)
271 strncpy(uhost, hp->h_name, HOSTLEN);
273 add_local_domain(uhost, HOSTLEN - strlen(uhost));
276 get_sockhost(cptr, uhost);
280 if (isDigit(*aconf->passwd) && !aconf->passwd[1]) /* Special case: exactly one digit */
282 /* Refuse connections when there are already <digit> clients connected with the same IP number */
283 unsigned short nr = *aconf->passwd - '0';
284 if (IPcheck_nr(cptr) > nr)
285 return ACR_TOO_MANY_FROM_IP; /* Already got nr with that ip# */
288 else if (!strcmp(aconf->passwd, "ONE"))
290 for (i = highest_fd; i >= 0; i--)
291 if (loc_clients[i] && MyUser(loc_clients[i]) &&
292 loc_clients[i]->ip.s_addr == cptr->ip.s_addr)
293 return ACR_TOO_MANY_FROM_IP; /* Already got one with that ip# */
297 return attach_conf(cptr, aconf);
299 return ACR_NO_AUTHORIZATION;
303 * Find the single N line and return pointer to it (from list).
304 * If more than one then return NULL pointer.
306 aConfItem *count_cnlines(Link *lp)
308 Reg1 aConfItem *aconf, *cline = NULL, *nline = NULL;
310 for (; lp; lp = lp->next)
312 aconf = lp->value.aconf;
313 if (!(aconf->status & CONF_SERVER_MASK))
315 if (aconf->status == CONF_CONNECT_SERVER && !cline)
317 else if (aconf->status == CONF_NOCONNECT_SERVER && !nline)
326 * Disassociate configuration from the client.
328 int detach_conf(aClient *cptr, aConfItem *aconf)
330 Reg1 Link **lp, *tmp;
336 if ((*lp)->value.aconf == aconf)
338 if (aconf && (aconf->confClass)
339 && (aconf->status & CONF_CLIENT_MASK) && ConfLinks(aconf) > 0)
341 if (aconf && !--aconf->clients && IsIllegal(aconf))
354 static int is_attached(aConfItem *aconf, aClient *cptr)
358 for (lp = cptr->confs; lp; lp = lp->next)
359 if (lp->value.aconf == aconf)
368 * Associate a specific configuration entry to a *local*
369 * client (this is the one which used in accepting the
370 * connection). Note, that this automaticly changes the
371 * attachment if there was an old one...
373 enum AuthorizationCheckResult attach_conf(aClient *cptr, aConfItem *aconf)
377 if (is_attached(aconf, cptr))
378 return ACR_ALREADY_AUTHORIZED;
379 if (IsIllegal(aconf))
380 return ACR_NO_AUTHORIZATION;
381 if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT)) &&
382 ConfLinks(aconf) >= ConfMaxLinks(aconf) && ConfMaxLinks(aconf) > 0)
383 return ACR_TOO_MANY_IN_CLASS; /* Use this for printing error message */
385 lp->next = cptr->confs;
386 lp->value.aconf = aconf;
389 if (aconf->status & CONF_CLIENT_MASK)
394 aConfItem *find_admin(void)
396 Reg1 aConfItem *aconf;
398 for (aconf = conf; aconf; aconf = aconf->next)
399 if (aconf->status & CONF_ADMIN)
405 aConfItem *find_me(void)
407 Reg1 aConfItem *aconf;
408 for (aconf = conf; aconf; aconf = aconf->next)
409 if (aconf->status & CONF_ME)
418 * Attach a CONF line to a client if the name passed matches that for
419 * the conf file (for non-C/N lines) or is an exact match (C/N lines
420 * only). The difference in behaviour is to stop C:*::* and N:*::*.
422 aConfItem *attach_confs(aClient *cptr, const char *name, int statmask)
425 aConfItem *first = NULL;
426 int len = strlen(name);
428 if (!name || len > HOSTLEN)
430 for (tmp = conf; tmp; tmp = tmp->next)
432 if ((tmp->status & statmask) && !IsIllegal(tmp) &&
433 ((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) == 0) &&
434 tmp->name && !match(tmp->name, name))
436 if (attach_conf(cptr, tmp) == ACR_OK && !first)
439 else if ((tmp->status & statmask) && !IsIllegal(tmp) &&
440 (tmp->status & (CONF_SERVER_MASK | CONF_HUB)) &&
441 tmp->name && !strCasediff(tmp->name, name))
443 if (attach_conf(cptr, tmp) == ACR_OK && !first)
451 * Added for new access check meLazy
453 aConfItem *attach_confs_host(aClient *cptr, char *host, int statmask)
456 aConfItem *first = NULL;
457 int len = strlen(host);
459 if (!host || len > HOSTLEN)
462 for (tmp = conf; tmp; tmp = tmp->next)
464 if ((tmp->status & statmask) && !IsIllegal(tmp) &&
465 (tmp->status & CONF_SERVER_MASK) == 0 &&
466 (!tmp->host || match(tmp->host, host) == 0))
468 if (attach_conf(cptr, tmp) == ACR_OK && !first)
471 else if ((tmp->status & statmask) && !IsIllegal(tmp) &&
472 (tmp->status & CONF_SERVER_MASK) &&
473 (tmp->host && strCasediff(tmp->host, host) == 0))
475 if (attach_conf(cptr, tmp) == ACR_OK && !first)
483 * find a conf entry which matches the hostname and has the same name.
485 aConfItem *find_conf_exact(char *name, char *user, char *host, int statmask)
488 char userhost[USERLEN + HOSTLEN + 3];
490 sprintf_irc(userhost, "%s@%s", user, host);
492 for (tmp = conf; tmp; tmp = tmp->next)
494 if (!(tmp->status & statmask) || !tmp->name || !tmp->host ||
495 strCasediff(tmp->name, name))
498 * Accept if the *real* hostname (usually sockecthost)
499 * socket host) matches *either* host or name field
500 * of the configuration.
502 if (match(tmp->host, userhost))
504 if (tmp->status & (CONF_OPERATOR | CONF_LOCOP))
506 if (tmp->clients < MaxLinks(tmp->confClass))
517 aConfItem *find_conf(Link *lp, const char *name, int statmask)
520 int namelen = name ? strlen(name) : 0;
522 if (namelen > HOSTLEN)
523 return (aConfItem *)0;
525 for (; lp; lp = lp->next)
527 tmp = lp->value.aconf;
528 if ((tmp->status & statmask) &&
529 (((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) &&
530 tmp->name && !strCasediff(tmp->name, name)) ||
531 ((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) == 0 &&
532 tmp->name && !match(tmp->name, name))))
539 * Added for new access check meLazy
541 aConfItem *find_conf_host(Link *lp, char *host, int statmask)
544 int hostlen = host ? strlen(host) : 0;
546 if (hostlen > HOSTLEN || BadPtr(host))
547 return (aConfItem *)NULL;
548 for (; lp; lp = lp->next)
550 tmp = lp->value.aconf;
551 if (tmp->status & statmask &&
552 (!(tmp->status & CONF_SERVER_MASK || tmp->host) ||
553 (tmp->host && !match(tmp->host, host))))
562 * Find a conf line using the IP# stored in it to search upon.
563 * Added 1/8/92 by Avalon.
565 aConfItem *find_conf_ip(Link *lp, char *ip, char *user, int statmask)
570 for (; lp; lp = lp->next)
572 tmp = lp->value.aconf;
573 if (!(tmp->status & statmask))
575 s = strchr(tmp->host, '@');
577 if (match(tmp->host, user))
583 if (!memcmp(&tmp->ipnum, ip, sizeof(struct in_addr)))
592 * - looks for a match on all given fields.
594 static aConfItem *find_conf_entry(aConfItem *aconf, unsigned int mask)
596 Reg1 aConfItem *bconf;
598 for (bconf = conf, mask &= ~CONF_ILLEGAL; bconf; bconf = bconf->next)
600 if (!(bconf->status & mask) || (bconf->port != aconf->port))
603 if ((BadPtr(bconf->host) && !BadPtr(aconf->host)) ||
604 (BadPtr(aconf->host) && !BadPtr(bconf->host)))
606 if (!BadPtr(bconf->host) && strCasediff(bconf->host, aconf->host))
609 if ((BadPtr(bconf->passwd) && !BadPtr(aconf->passwd)) ||
610 (BadPtr(aconf->passwd) && !BadPtr(bconf->passwd)))
612 if (!BadPtr(bconf->passwd) && (!isDigit(*bconf->passwd) || bconf->passwd[1])
614 && strCasediff(bconf->passwd, "ONE")
616 && strCasediff(bconf->passwd, aconf->passwd))
619 if ((BadPtr(bconf->name) && !BadPtr(aconf->name)) ||
620 (BadPtr(aconf->name) && !BadPtr(bconf->name)))
622 if (!BadPtr(bconf->name) && strCasediff(bconf->name, aconf->name))
632 * Actual REHASH service routine. Called with sig == 0 if it has been called
633 * as a result of an operator issuing this command, else assume it has been
634 * called as a result of the server receiving a HUP signal.
636 int rehash(aClient *cptr, int sig)
638 Reg1 aConfItem **tmp = &conf, *tmp2;
639 Reg2 aConfClass *cltmp;
641 Reg2 aMotdItem *temp;
643 int ret = 0, found_g;
646 sendto_ops("Got signal SIGHUP, reloading ircd conf. file");
648 for (i = 0; i <= highest_fd; i++)
649 if ((acptr = loc_clients[i]) && !IsMe(acptr))
652 * Nullify any references from client structures to
653 * this host structure which is about to be freed.
654 * Could always keep reference counts instead of
660 while ((tmp2 = *tmp))
661 if (tmp2->clients || tmp2->status & CONF_LISTEN_PORT)
664 * Configuration entry is still in use by some
665 * local clients, cannot delete it--mark it so
666 * that it will be deleted when the last client
669 if (!(tmp2->status & (CONF_LISTEN_PORT | CONF_CLIENT)))
676 tmp2->status |= CONF_ILLEGAL;
681 /* free expression trees of connect rules */
682 if ((tmp2->status & (CONF_CRULEALL | CONF_CRULEAUTO)) &&
683 (tmp2->passwd != NULL))
684 crule_free(&(tmp2->passwd));
689 * We don't delete the class table, rather mark all entries
690 * for deletion. The table is cleaned up by check_class(). - avalon
692 for (cltmp = NextClass(FirstClass()); cltmp; cltmp = NextClass(cltmp))
696 * delete the juped nicks list
702 if (initconf(0) == -1) /* This calls check_class(), */
703 check_class(); /* unless it fails */
707 * Flush out deleted I and P lines although still in use.
709 for (tmp = &conf; (tmp2 = *tmp);)
710 if (!(tmp2->status & CONF_ILLEGAL))
720 for (i = 0; i <= highest_fd; i++) {
721 if ((acptr = loc_clients[i]) && !IsMe(acptr)) {
722 if (IsServer(acptr)) {
723 det_confs_butmask(acptr,
724 ~(CONF_HUB | CONF_LEAF | CONF_UWORLD | CONF_ILLEGAL));
725 attach_confs(acptr, acptr->name, CONF_HUB | CONF_LEAF | CONF_UWORLD);
727 if ((found_g = find_kill(acptr))) {
728 sendto_op_mask(found_g == -2 ? SNO_GLINE : SNO_OPERKILL,
729 found_g == -2 ? "G-line active for %s" : "K-line active for %s",
730 get_client_name(acptr, FALSE));
731 if (exit_client(cptr, acptr, &me, found_g == -2 ? "G-lined" :
732 "K-lined") == CPTR_KILLED)
735 #if defined(R_LINES) && defined(R_LINES_REHASH) && !defined(R_LINES_OFTEN)
736 if (find_restrict(acptr)) {
737 sendto_ops("Restricting %s, closing lp", get_client_name(acptr, FALSE));
738 if (exit_client(cptr, acptr, &me, "R-lined") == CPTR_KILLED)
745 /* free old motd structs */
756 /* reload motd files */
758 rmotd = read_motd(RPATH);
759 motd = read_motd(MPATH);
767 * Read configuration file.
769 * returns -1, if file cannot be opened
773 #define MAXCONFLINKS 150
775 unsigned short server_port;
777 int initconf(int opt)
779 static char quotes[9][2] = {
793 int ccount = 0, ncount = 0;
794 aConfItem *aconf = NULL;
796 Debug((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile));
797 if (NULL == (file = fbopen(configfile, "r")))
801 while (fbgets(line, sizeof(line) - 1, file))
803 if ((tmp = strchr(line, '\n')))
806 * Do quoting of characters and # detection.
808 for (tmp = line; *tmp; tmp++)
812 for (i = 0; quotes[i][0]; i++)
813 if (quotes[i][0] == *(tmp + 1))
823 for (s = tmp; (*s = *(s + 1)); s++)
826 else if (*tmp == '#')
829 if (!*line || line[0] == '#' || line[0] == '\n' ||
830 line[0] == ' ' || line[0] == '\t')
832 /* Could we test if it's conf line at all? -Vesa */
835 Debug((DEBUG_ERROR, "Bad config line: %s", line));
842 tmp = getfield(line, ':');
847 case 'A': /* Name, e-mail address of administrator */
848 case 'a': /* of this server. */
849 aconf->status = CONF_ADMIN;
851 case 'C': /* Server where I should try to connect */
852 case 'c': /* in case of lp failures */
854 aconf->status = CONF_CONNECT_SERVER;
858 aconf->status = CONF_CRULEALL;
860 /* Connect rule - autos only */
862 aconf->status = CONF_CRULEAUTO;
864 case 'H': /* Hub server line */
866 aconf->status = CONF_HUB;
868 case 'I': /* Just plain normal irc client trying */
869 case 'i': /* to connect me */
870 aconf->status = CONF_CLIENT;
872 case 'K': /* Kill user line on irc.conf */
873 aconf->status = CONF_KILL;
875 case 'k': /* Kill user line based on IP in ircd.conf */
876 aconf->status = CONF_IPKILL;
878 /* Operator. Line should contain at least */
879 /* password and host where connection is */
880 case 'L': /* guaranteed leaf server */
882 aconf->status = CONF_LEAF;
884 /* Me. Host field is name used for this host */
885 /* and port number is the number of the port */
888 aconf->status = CONF_ME;
890 case 'N': /* Server where I should NOT try to */
891 case 'n': /* connect in case of lp failures */
892 /* but which tries to connect ME */
894 aconf->status = CONF_NOCONNECT_SERVER;
897 aconf->status = CONF_OPERATOR;
899 /* Local Operator, (limited privs --SRB) */
901 aconf->status = CONF_LOCOP;
903 case 'P': /* listen port line */
905 aconf->status = CONF_LISTEN_PORT;
908 case 'R': /* extended K line */
909 case 'r': /* Offers more options of how to restrict */
910 aconf->status = CONF_RESTRICT;
913 case 'T': /* print out different motd's */
914 case 't': /* based on hostmask */
915 aconf->status = CONF_TLINES;
917 case 'U': /* Underworld server, allowed to hack modes */
918 case 'u': /* *Every* server on the net must define the same !!! */
919 aconf->status = CONF_UWORLD;
923 aconf->status = CONF_CLASS;
926 Debug((DEBUG_ERROR, "Error in config file: %s", line));
929 if (IsIllegal(aconf))
932 for (;;) /* Fake loop, that I can use break here --msa */
934 if ((tmp = getfield(NULL, ':')) == NULL)
936 DupString(aconf->host, tmp);
937 if ((tmp = getfield(NULL, (aconf->status == CONF_KILL
938 || aconf->status == CONF_IPKILL) ? '"' : ':')) == NULL)
940 DupString(aconf->passwd, tmp);
941 if ((tmp = getfield(NULL, ':')) == NULL)
943 DupString(aconf->name, tmp);
944 if ((tmp = getfield(NULL, ':')) == NULL)
946 aconf->port = atoi(tmp);
947 tmp = getfield(NULL, ':');
948 if (aconf->status & CONF_ME)
950 server_port = aconf->port;
953 Debug((DEBUG_FATAL, "Your M: line must have the Numeric, "
954 "assigned to you by routing-com, behind the port number!\n"));
956 syslog(LOG_WARNING, "Your M: line must have the Numeric, "
957 "assigned to you by routing-com, behind the port number!\n");
961 SetYXXServerName(&me, atoi(tmp)); /* Our Numeric Nick */
964 aconf->confClass = find_class(atoi(tmp));
968 * If conf line is a class definition, create a class entry
969 * for it and make the conf_line illegal and delete it.
971 if (aconf->status & CONF_CLASS)
973 add_class(atoi(aconf->host), atoi(aconf->passwd),
974 atoi(aconf->name), aconf->port, tmp ? atoi(tmp) : 0);
978 * Associate each conf line with a class by using a pointer
979 * to the correct class record. -avalon
981 if (aconf->status & (CONF_CLIENT_MASK | CONF_LISTEN_PORT))
983 if (aconf->confClass == 0)
984 aconf->confClass = find_class(0);
986 if (aconf->status & (CONF_LISTEN_PORT | CONF_CLIENT))
990 if ((bconf = find_conf_entry(aconf, aconf->status)))
993 bconf->status &= ~CONF_ILLEGAL;
994 if (aconf->status == CONF_CLIENT)
996 char *passwd = bconf->passwd;
997 bconf->passwd = aconf->passwd;
998 aconf->passwd = passwd;
999 ConfLinks(bconf) -= bconf->clients;
1000 bconf->confClass = aconf->confClass;
1001 if (bconf->confClass)
1002 ConfLinks(bconf) += bconf->clients;
1007 else if (aconf->host && aconf->status == CONF_LISTEN_PORT)
1008 add_listener(aconf);
1010 if (aconf->status & CONF_SERVER_MASK)
1011 if (ncount > MAXCONFLINKS || ccount > MAXCONFLINKS ||
1012 !aconf->host || strchr(aconf->host, '*') ||
1013 strchr(aconf->host, '?') || !aconf->name)
1016 if (aconf->status & (CONF_SERVER_MASK | CONF_LOCOP | CONF_OPERATOR))
1017 if (!strchr(aconf->host, '@') && *aconf->host != '/')
1020 int len = 3; /* *@\0 = 3 */
1022 len += strlen(aconf->host);
1023 newhost = (char *)RunMalloc(len);
1024 sprintf_irc(newhost, "*@%s", aconf->host);
1025 RunFree(aconf->host);
1026 aconf->host = newhost;
1028 if (aconf->status & CONF_SERVER_MASK)
1030 if (BadPtr(aconf->passwd))
1032 else if (!(opt & BOOT_QUICK))
1033 lookup_confhost(aconf);
1036 /* Create expression tree from connect rule...
1037 * If there's a parsing error, nuke the conf structure */
1038 if (aconf->status & (CONF_CRULEALL | CONF_CRULEAUTO))
1040 RunFree(aconf->passwd);
1041 if ((aconf->passwd = (char *)crule_parse(aconf->name)) == NULL)
1050 * Own port and name cannot be changed after the startup.
1051 * (or could be allowed, but only if all links are closed first).
1052 * Configuration info does not override the name and port
1053 * if previously defined. Note, that "info"-field can be
1054 * changed by "/rehash".
1056 if (aconf->status == CONF_ME)
1058 strncpy(me.info, aconf->name, sizeof(me.info) - 1);
1059 if (me.name[0] == '\0' && aconf->host[0])
1060 strncpy(me.name, aconf->host, sizeof(me.name) - 1);
1062 portnum = aconf->port;
1066 * Juped nicks are listed in the 'password' field of U:lines,
1067 * the list is comma separated and might be empty and/or contain
1068 * empty elements... the only limit is that it MUST be shorter
1069 * than 512 chars, or they will be cutted out :)
1071 if ((aconf->status == CONF_UWORLD) && (aconf->passwd) && (*aconf->passwd))
1072 addNickJupes(aconf->passwd);
1074 if (aconf->status & CONF_ADMIN)
1075 if (!aconf->host || !aconf->passwd || !aconf->name)
1077 Debug((DEBUG_FATAL, "Your A: line must have 4 fields!\n"));
1079 syslog(LOG_WARNING, "Your A: line must have 4 fields!\n");
1084 collapse(aconf->host);
1085 collapse(aconf->name);
1086 Debug((DEBUG_NOTICE,
1087 "Read Init: (%d) (%s) (%s) (%s) (%u) (%p)",
1088 aconf->status, aconf->host, aconf->passwd,
1089 aconf->name, aconf->port, aconf->confClass));
1098 nextping = nextconnect = now;
1105 * Do (start) DNS lookups of all hostnames in the conf line and convert
1106 * an IP addresses in a.b.c.d number for to IP#s.
1108 static int lookup_confhost(aConfItem *aconf)
1111 Reg3 struct hostent *hp;
1114 if (BadPtr(aconf->host) || BadPtr(aconf->name))
1116 if ((s = strchr(aconf->host, '@')))
1121 * Do name lookup now on hostnames given and store the
1122 * ip numbers in conf structure.
1124 if (!isAlpha(*s) && !isDigit(*s))
1128 * Prepare structure in case we have to wait for a
1129 * reply which we get later and store away.
1131 ln.value.aconf = aconf;
1132 ln.flags = ASYNC_CONF;
1135 aconf->ipnum.s_addr = inet_addr(s);
1136 else if ((hp = gethost_byname(s, &ln)))
1137 memcpy(&(aconf->ipnum), hp->h_addr, sizeof(struct in_addr));
1139 if (aconf->ipnum.s_addr == INADDR_NONE)
1143 if (aconf->ipnum.s_addr == INADDR_NONE)
1144 memset(&aconf->ipnum, 0, sizeof(struct in_addr));
1145 Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
1146 aconf->host, aconf->name));
1151 * Read info from T:lines into trecords which include the file
1152 * timestamp, the hostmask, and the contents of the motd file
1158 atrecord *temp, *last = NULL; /* Init. to avoid compiler warning */
1161 /* Free the old trecords and the associated motd contents first */
1165 while (tdata->tmotd)
1167 amotd = tdata->tmotd->next;
1168 RunFree(tdata->tmotd);
1169 tdata->tmotd = amotd;
1175 for (tmp = conf; tmp; tmp = tmp->next)
1176 if (tmp->status == CONF_TLINES && tmp->host && tmp->passwd)
1178 temp = (atrecord *) RunMalloc(sizeof(atrecord));
1181 temp->hostmask = tmp->host;
1182 temp->tmotd = read_motd(tmp->passwd);
1183 temp->tmotd_tm = motd_tm;
1193 int find_kill(aClient *cptr)
1195 char reply[256], *host, *name;
1197 aGline *agline = NULL;
1202 host = cptr->sockhost;
1203 name = cptr->user->username;
1205 if (strlen(host) > (size_t)HOSTLEN ||
1206 (name ? strlen(name) : 0) > (size_t)HOSTLEN)
1211 for (tmp = conf; tmp; tmp = tmp->next)
1212 /* Added a check against the user's IP address as well.
1213 * If the line is either CONF_KILL or CONF_IPKILL, check it; if and only
1214 * if it's CONF_IPKILL, check the IP address as well (the && below will
1215 * short circuit and the match won't even get run) -Kev
1217 if ((tmp->status & CONF_KLINE) && tmp->host && tmp->name &&
1218 (match(tmp->host, host) == 0 ||
1219 ((tmp->status == CONF_IPKILL) &&
1220 match(tmp->host, inetntoa(cptr->ip)) == 0)) &&
1221 (!name || match(tmp->name, name) == 0) &&
1222 (!tmp->port || (tmp->port == cptr->acpt->port)))
1225 * Can short-circuit evaluation - not taking chances
1226 * because check_time_interval destroys tmp->passwd
1229 if (BadPtr(tmp->passwd))
1231 else if (is_comment(tmp->passwd))
1233 else if (check_time_interval(tmp->passwd, reply))
1238 sendto_one(cptr, reply, me.name, ERR_YOUREBANNEDCREEP, cptr->name);
1241 if (BadPtr(tmp->passwd))
1243 ":%s %d %s :Connection from your host is refused on this server.",
1244 me.name, ERR_YOUREBANNEDCREEP, cptr->name);
1247 if (*tmp->passwd == '"')
1250 sprintf_irc(sendbuf, ":%s %d %s :%s", me.name, ERR_YOUREBANNEDCREEP,
1251 cptr->name, &tmp->passwd[1]);
1252 sbuf[-1] = '.'; /* Overwrite last quote with a dot */
1253 sendbufto_one(cptr);
1255 else if (*tmp->passwd == '!')
1256 killcomment(cptr, cptr->name, &tmp->passwd[1]);
1258 #ifdef COMMENT_IS_FILE
1259 killcomment(cptr, cptr->name, tmp->passwd);
1261 sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
1262 cptr->name, tmp->passwd);
1267 /* find active glines */
1268 /* added a check against the user's IP address to find_gline() -Kev */
1269 else if ((agline = find_gline(cptr, NULL)) && GlineIsActive(agline))
1270 sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
1271 cptr->name, agline->reason);
1273 agline = NULL; /* if a gline was found, it was inactive */
1275 return (tmp ? -1 : (agline ? -2 : 0));
1282 * Works against host/name and calls an outside program
1283 * to determine whether a client is allowed to connect. This allows
1284 * more freedom to determine who is legal and who isn't, for example
1285 * machine load considerations. The outside program is expected to
1286 * return a reply line where the first word is either 'Y' or 'N' meaning
1287 * "Yes Let them in" or "No don't let them in." If the first word
1288 * begins with neither 'Y' or 'N' the default is to let the person on.
1289 * It returns a value of 0 if the user is to be let through -Hoppie
1291 int find_restrict(aClient *cptr)
1294 char reply[80], temprpl[80];
1295 char *rplhold = reply, *host, *name, *s;
1297 int pi[2], rc = 0, n;
1298 FBFILE *file = NULL;
1302 name = cptr->user->username;
1303 host = cptr->sockhost;
1304 Debug((DEBUG_INFO, "R-line check for %s[%s]", name, host));
1306 for (tmp = conf; tmp; tmp = tmp->next)
1308 if (tmp->status != CONF_RESTRICT ||
1309 (tmp->host && host && match(tmp->host, host)) ||
1310 (tmp->name && name && match(tmp->name, name)))
1313 if (BadPtr(tmp->passwd))
1315 sendto_ops("Program missing on R-line %s/%s, ignoring", name, host);
1321 report_error("Error creating pipe for R-line %s: %s", &me);
1324 switch (rc = fork())
1327 report_error("Error forking for R-line %s: %s", &me);
1334 for (i = 2; i < MAXCONNECTIONS; i++)
1340 if (pi[1] != 2 && pi[1] != 1)
1342 execlp(tmp->passwd, tmp->passwd, name, host, 0);
1350 file = fdbopen(pi[0], "r");
1351 while (fbgets(temprpl, sizeof(temprpl) - 1, file))
1353 if ((s = strchr(temprpl, '\n')))
1355 if (strlen(temprpl) + strlen(reply) < sizeof(reply) - 2)
1356 sprintf_irc(rplhold, "%s %s", rplhold, temprpl);
1359 sendto_ops("R-line %s/%s: reply too long!", name, host);
1364 kill(rc, SIGKILL); /* cleanup time */
1368 while (*rplhold == ' ')
1370 rplchar = *rplhold; /* Pull out the yes or no */
1371 while (*rplhold != ' ')
1373 while (*rplhold == ' ')
1375 strcpy(reply, rplhold);
1378 if ((rc = (rplchar == 'n' || rplchar == 'N')))
1383 sendto_one(cptr, ":%s %d %s :Restriction: %s",
1384 me.name, ERR_YOUREBANNEDCREEP, cptr->name, reply);
1392 * output the reason for being k lined from a file - Mmmm
1394 * parv is the sender prefix
1395 * filename is the file that is to be output to the K lined client
1397 static void killcomment(aClient *sptr, char *parv, char *filename)
1399 FBFILE *file = NULL;
1405 if (NULL == (file = fbopen(filename, "r")))
1407 sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv);
1409 ":%s %d %s :Connection from your host is refused on this server.",
1410 me.name, ERR_YOUREBANNEDCREEP, parv);
1414 tm = localtime((time_t *) & sb.st_mtime); /* NetBSD needs cast */
1415 while (fbgets(line, sizeof(line) - 1, file))
1417 if ((tmp = strchr(line, '\n')))
1419 if ((tmp = strchr(line, '\r')))
1422 * ":%s %d %s : %s.",
1423 * me.name, ERR_YOUREBANNEDCREEP, parv,line); */
1424 sendto_one(sptr, rpl_str(RPL_MOTD), me.name, parv, line);
1427 ":%s %d %s :Connection from your host is refused on this server.",
1428 me.name, ERR_YOUREBANNEDCREEP, parv);
1434 * is the K line field an interval or a comment? - Mmmm
1436 static int is_comment(char *comment)
1439 for (i = 0; i < strlen(comment); i++)
1440 if ((comment[i] != ' ') && (comment[i] != '-')
1441 && (comment[i] != ',') && ((comment[i] < '0') || (comment[i] > '9')))
1448 * check against a set of time intervals
1450 static int check_time_interval(char *interval, char *reply)
1454 int perm_min_hours, perm_min_minutes, perm_max_hours, perm_max_minutes;
1455 int nowm, perm_min, perm_max;
1457 tptr = localtime(&now);
1458 nowm = tptr->tm_hour * 60 + tptr->tm_min;
1462 p = strchr(interval, ',');
1465 if (sscanf(interval, "%2d%2d-%2d%2d", &perm_min_hours, &perm_min_minutes,
1466 &perm_max_hours, &perm_max_minutes) != 4)
1474 perm_min = 60 * perm_min_hours + perm_min_minutes;
1475 perm_max = 60 * perm_max_hours + perm_max_minutes;
1477 * The following check allows intervals over midnight ...
1479 if ((perm_min < perm_max)
1480 ? (perm_min <= nowm && nowm <= perm_max)
1481 : (perm_min <= nowm || nowm <= perm_max))
1484 ":%%s %%d %%s :%s %d:%02d to %d:%02d.",
1485 "You are not allowed to connect from",
1486 perm_min_hours, perm_min_minutes, perm_max_hours, perm_max_minutes);
1487 return (ERR_YOUREBANNEDCREEP);
1489 if ((perm_min < perm_max)
1490 ? (perm_min <= nowm + 5 && nowm + 5 <= perm_max)
1491 : (perm_min <= nowm + 5 || nowm + 5 <= perm_max))
1493 sprintf_irc(reply, ":%%s %%d %%s :%d minute%s%s",
1494 perm_min - nowm, (perm_min - nowm) > 1 ? "s " : " ",
1495 "and you will be denied for further access");
1496 return (ERR_YOUWILLBEBANNED);
1503 aMotdItem *read_motd(char *motdfile)
1505 FBFILE *file = NULL;
1506 register aMotdItem *temp, *newmotd, *last;
1511 if (NULL == (file = fbopen(motdfile, "r")))
1513 Debug((DEBUG_ERROR, "Couldn't open \"%s\": %s", motdfile, strerror(errno)));
1516 if (-1 == fbstat(&sb, file))
1520 newmotd = last = NULL;
1521 motd_tm = *localtime((time_t *) & sb.st_mtime); /* NetBSD needs cast */
1522 while (fbgets(line, sizeof(line) - 1, file))
1524 if ((tmp = (char *)strchr(line, '\n')))
1526 if ((tmp = (char *)strchr(line, '\r')))
1528 temp = (aMotdItem *) RunMalloc(sizeof(aMotdItem));
1531 strcpy(temp->line, line);