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 aGline *badchan = NULL;
85 aMotdItem *motd = NULL;
86 aMotdItem *rmotd = NULL;
87 atrecord *tdata = NULL;
91 * field breakup for ircd.conf file.
93 static char *gfline = NULL;
94 char *getfield(char *newline, char fs)
104 end = field = gfline;
125 while (end && fs != ':' && *++end != ':' && *end != '\n');
130 if ((end = strchr(field, '\n')) == NULL)
131 end = field + strlen(field);
142 * Remove all conf entries from the client except those which match
143 * the status field mask.
145 void det_confs_butmask(aClient *cptr, int mask)
147 Reg1 Link *tmp, *tmp2;
149 for (tmp = cptr->confs; tmp; tmp = tmp2)
152 if ((tmp->value.aconf->status & mask) == 0)
153 detach_conf(cptr, tmp->value.aconf);
158 * Find the first (best) I line to attach.
160 enum AuthorizationCheckResult attach_Iline(aClient *cptr, struct hostent *hp,
163 Reg1 aConfItem *aconf;
164 Reg3 const char *hname;
166 static char uhost[HOSTLEN + USERLEN + 3];
167 static char fullname[HOSTLEN + 1];
169 for (aconf = conf; aconf; aconf = aconf->next)
171 if (aconf->status != CONF_CLIENT)
173 if (aconf->port && aconf->port != cptr->acpt->port)
175 if (!aconf->host || !aconf->name)
178 for (i = 0, hname = hp->h_name; hname; hname = hp->h_aliases[i++])
180 size_t fullnamelen = 0;
181 size_t label_count = 0;
184 strncpy(fullname, hname, HOSTLEN);
185 fullname[HOSTLEN] = 0;
188 * Disallow a hostname label to contain anything but a [-a-zA-Z0-9].
189 * It may not start or end on a '.'.
190 * A label may not end on a '-', the maximum length of a label is
192 * On top of that (which seems to be the RFC) we demand that the
193 * top domain does not contain any digits.
195 error = (*hname == '.') ? 1 : 0; /* May not start with a '.' */
199 for (p = fullname; *p; ++p, ++fullnamelen)
203 if (p[-1] == '-' /* Label may not end on '-' */
204 || p[1] == 0) /* May not end on a '.' */
210 error = 0; /* Was not top domain */
213 if (++label_count > 63) /* Label not longer then 63 */
218 if (*p >= '0' && *p <= '9')
220 error = 1; /* In case this is top domain */
223 if (!(*p >= 'a' && *p <= 'z')
224 && !(*p >= 'A' && *p <= 'Z') && *p != '-')
237 add_local_domain(fullname, HOSTLEN - fullnamelen);
238 Debug((DEBUG_DNS, "a_il: %s->%s", sockhost, fullname));
239 if (strchr(aconf->name, '@'))
241 strcpy(uhost, cptr->username);
246 strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost));
247 uhost[sizeof(uhost) - 1] = 0;
248 if (!match(aconf->name, uhost))
250 if (strchr(uhost, '@'))
251 cptr->flags |= FLAGS_DOID;
256 if (strchr(aconf->host, '@'))
258 strncpy(uhost, cptr->username, sizeof(uhost) - 2);
259 uhost[sizeof(uhost) - 2] = 0;
264 strncat(uhost, sockhost, sizeof(uhost) - 1 - strlen(uhost));
265 uhost[sizeof(uhost) - 1] = 0;
266 if (match(aconf->host, uhost))
268 if (strchr(uhost, '@'))
269 cptr->flags |= FLAGS_DOID;
270 if (hp && hp->h_name)
272 strncpy(uhost, hp->h_name, HOSTLEN);
274 add_local_domain(uhost, HOSTLEN - strlen(uhost));
277 get_sockhost(cptr, uhost);
281 if (isDigit(*aconf->passwd) && !aconf->passwd[1]) /* Special case: exactly one digit */
283 /* Refuse connections when there are already <digit> clients connected with the same IP number */
284 unsigned short nr = *aconf->passwd - '0';
285 if (IPcheck_nr(cptr) > nr)
286 return ACR_TOO_MANY_FROM_IP; /* Already got nr with that ip# */
289 else if (!strcmp(aconf->passwd, "ONE"))
291 for (i = highest_fd; i >= 0; i--)
292 if (loc_clients[i] && MyUser(loc_clients[i]) &&
293 loc_clients[i]->ip.s_addr == cptr->ip.s_addr)
294 return ACR_TOO_MANY_FROM_IP; /* Already got one with that ip# */
298 return attach_conf(cptr, aconf);
300 return ACR_NO_AUTHORIZATION;
304 * Find the single N line and return pointer to it (from list).
305 * If more than one then return NULL pointer.
307 aConfItem *count_cnlines(Link *lp)
309 Reg1 aConfItem *aconf, *cline = NULL, *nline = NULL;
311 for (; lp; lp = lp->next)
313 aconf = lp->value.aconf;
314 if (!(aconf->status & CONF_SERVER_MASK))
316 if (aconf->status == CONF_CONNECT_SERVER && !cline)
318 else if (aconf->status == CONF_NOCONNECT_SERVER && !nline)
327 * Disassociate configuration from the client.
329 int detach_conf(aClient *cptr, aConfItem *aconf)
331 Reg1 Link **lp, *tmp;
337 if ((*lp)->value.aconf == aconf)
339 if (aconf && (aconf->confClass)
340 && (aconf->status & CONF_CLIENT_MASK) && ConfLinks(aconf) > 0)
342 if (aconf && !--aconf->clients && IsIllegal(aconf))
355 static int is_attached(aConfItem *aconf, aClient *cptr)
359 for (lp = cptr->confs; lp; lp = lp->next)
360 if (lp->value.aconf == aconf)
369 * Associate a specific configuration entry to a *local*
370 * client (this is the one which used in accepting the
371 * connection). Note, that this automaticly changes the
372 * attachment if there was an old one...
374 enum AuthorizationCheckResult attach_conf(aClient *cptr, aConfItem *aconf)
378 if (is_attached(aconf, cptr))
379 return ACR_ALREADY_AUTHORIZED;
380 if (IsIllegal(aconf))
381 return ACR_NO_AUTHORIZATION;
382 if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT)) &&
383 ConfLinks(aconf) >= ConfMaxLinks(aconf) && ConfMaxLinks(aconf) > 0)
384 return ACR_TOO_MANY_IN_CLASS; /* Use this for printing error message */
386 lp->next = cptr->confs;
387 lp->value.aconf = aconf;
390 if (aconf->status & CONF_CLIENT_MASK)
395 aConfItem *find_admin(void)
397 Reg1 aConfItem *aconf;
399 for (aconf = conf; aconf; aconf = aconf->next)
400 if (aconf->status & CONF_ADMIN)
406 aConfItem *find_me(void)
408 Reg1 aConfItem *aconf;
409 for (aconf = conf; aconf; aconf = aconf->next)
410 if (aconf->status & CONF_ME)
419 * Attach a CONF line to a client if the name passed matches that for
420 * the conf file (for non-C/N lines) or is an exact match (C/N lines
421 * only). The difference in behaviour is to stop C:*::* and N:*::*.
423 aConfItem *attach_confs(aClient *cptr, const char *name, int statmask)
426 aConfItem *first = NULL;
427 int len = strlen(name);
429 if (!name || len > HOSTLEN)
431 for (tmp = conf; tmp; tmp = tmp->next)
433 if ((tmp->status & statmask) && !IsIllegal(tmp) &&
434 ((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) == 0) &&
435 tmp->name && !match(tmp->name, name))
437 if (attach_conf(cptr, tmp) == ACR_OK && !first)
440 else if ((tmp->status & statmask) && !IsIllegal(tmp) &&
441 (tmp->status & (CONF_SERVER_MASK | CONF_HUB)) &&
442 tmp->name && !strCasediff(tmp->name, name))
444 if (attach_conf(cptr, tmp) == ACR_OK && !first)
452 * Added for new access check meLazy
454 aConfItem *attach_confs_host(aClient *cptr, char *host, int statmask)
457 aConfItem *first = NULL;
458 int len = strlen(host);
460 if (!host || len > HOSTLEN)
463 for (tmp = conf; tmp; tmp = tmp->next)
465 if ((tmp->status & statmask) && !IsIllegal(tmp) &&
466 (tmp->status & CONF_SERVER_MASK) == 0 &&
467 (!tmp->host || match(tmp->host, host) == 0))
469 if (attach_conf(cptr, tmp) == ACR_OK && !first)
472 else if ((tmp->status & statmask) && !IsIllegal(tmp) &&
473 (tmp->status & CONF_SERVER_MASK) &&
474 (tmp->host && strCasediff(tmp->host, host) == 0))
476 if (attach_conf(cptr, tmp) == ACR_OK && !first)
484 * find a conf entry which matches the hostname and has the same name.
486 aConfItem *find_conf_exact(char *name, char *user, char *host, int statmask)
489 char userhost[USERLEN + HOSTLEN + 3];
491 sprintf_irc(userhost, "%s@%s", user, host);
493 for (tmp = conf; tmp; tmp = tmp->next)
495 if (!(tmp->status & statmask) || !tmp->name || !tmp->host ||
496 strCasediff(tmp->name, name))
499 * Accept if the *real* hostname (usually sockecthost)
500 * socket host) matches *either* host or name field
501 * of the configuration.
503 if (match(tmp->host, userhost))
505 if (tmp->status & (CONF_OPERATOR | CONF_LOCOP))
507 if (tmp->clients < MaxLinks(tmp->confClass))
518 aConfItem *find_conf(Link *lp, const char *name, int statmask)
521 int namelen = name ? strlen(name) : 0;
523 if (namelen > HOSTLEN)
524 return (aConfItem *)0;
526 for (; lp; lp = lp->next)
528 tmp = lp->value.aconf;
529 if ((tmp->status & statmask) &&
530 (((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) &&
531 tmp->name && !strCasediff(tmp->name, name)) ||
532 ((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) == 0 &&
533 tmp->name && !match(tmp->name, name))))
540 * Added for new access check meLazy
542 aConfItem *find_conf_host(Link *lp, char *host, int statmask)
545 int hostlen = host ? strlen(host) : 0;
547 if (hostlen > HOSTLEN || BadPtr(host))
548 return (aConfItem *)NULL;
549 for (; lp; lp = lp->next)
551 tmp = lp->value.aconf;
552 if (tmp->status & statmask &&
553 (!(tmp->status & CONF_SERVER_MASK || tmp->host) ||
554 (tmp->host && !match(tmp->host, host))))
563 * Find a conf line using the IP# stored in it to search upon.
564 * Added 1/8/92 by Avalon.
566 aConfItem *find_conf_ip(Link *lp, char *ip, char *user, int statmask)
571 for (; lp; lp = lp->next)
573 tmp = lp->value.aconf;
574 if (!(tmp->status & statmask))
576 s = strchr(tmp->host, '@');
578 if (match(tmp->host, user))
584 if (!memcmp(&tmp->ipnum, ip, sizeof(struct in_addr)))
593 * - looks for a match on all given fields.
595 static aConfItem *find_conf_entry(aConfItem *aconf, unsigned int mask)
597 Reg1 aConfItem *bconf;
599 for (bconf = conf, mask &= ~CONF_ILLEGAL; bconf; bconf = bconf->next)
601 if (!(bconf->status & mask) || (bconf->port != aconf->port))
604 if ((BadPtr(bconf->host) && !BadPtr(aconf->host)) ||
605 (BadPtr(aconf->host) && !BadPtr(bconf->host)))
607 if (!BadPtr(bconf->host) && strCasediff(bconf->host, aconf->host))
610 if ((BadPtr(bconf->passwd) && !BadPtr(aconf->passwd)) ||
611 (BadPtr(aconf->passwd) && !BadPtr(bconf->passwd)))
613 if (!BadPtr(bconf->passwd) && (!isDigit(*bconf->passwd) || bconf->passwd[1])
615 && strCasediff(bconf->passwd, "ONE")
617 && strCasediff(bconf->passwd, aconf->passwd))
620 if ((BadPtr(bconf->name) && !BadPtr(aconf->name)) ||
621 (BadPtr(aconf->name) && !BadPtr(bconf->name)))
623 if (!BadPtr(bconf->name) && strCasediff(bconf->name, aconf->name))
633 * Actual REHASH service routine. Called with sig == 0 if it has been called
634 * as a result of an operator issuing this command, else assume it has been
635 * called as a result of the server receiving a HUP signal.
637 int rehash(aClient *cptr, int sig)
639 Reg1 aConfItem **tmp = &conf, *tmp2;
640 Reg2 aConfClass *cltmp;
642 Reg2 aMotdItem *temp;
644 int ret = 0, found_g;
647 sendto_ops("Got signal SIGHUP, reloading ircd conf. file");
649 for (i = 0; i <= highest_fd; i++)
650 if ((acptr = loc_clients[i]) && !IsMe(acptr))
653 * Nullify any references from client structures to
654 * this host structure which is about to be freed.
655 * Could always keep reference counts instead of
661 while ((tmp2 = *tmp))
662 if (tmp2->clients || tmp2->status & CONF_LISTEN_PORT)
665 * Configuration entry is still in use by some
666 * local clients, cannot delete it--mark it so
667 * that it will be deleted when the last client
670 if (!(tmp2->status & (CONF_LISTEN_PORT | CONF_CLIENT)))
677 tmp2->status |= CONF_ILLEGAL;
682 /* free expression trees of connect rules */
683 if ((tmp2->status & (CONF_CRULEALL | CONF_CRULEAUTO)) &&
684 (tmp2->passwd != NULL))
685 crule_free(&(tmp2->passwd));
690 * We don't delete the class table, rather mark all entries
691 * for deletion. The table is cleaned up by check_class(). - avalon
693 for (cltmp = NextClass(FirstClass()); cltmp; cltmp = NextClass(cltmp))
697 * delete the juped nicks list
703 if (initconf(0) == -1) /* This calls check_class(), */
704 check_class(); /* unless it fails */
708 * Flush out deleted I and P lines although still in use.
710 for (tmp = &conf; (tmp2 = *tmp);)
711 if (!(tmp2->status & CONF_ILLEGAL))
721 for (i = 0; i <= highest_fd; i++) {
722 if ((acptr = loc_clients[i]) && !IsMe(acptr)) {
723 if (IsServer(acptr)) {
724 det_confs_butmask(acptr,
725 ~(CONF_HUB | CONF_LEAF | CONF_UWORLD | CONF_ILLEGAL));
726 attach_confs(acptr, acptr->name, CONF_HUB | CONF_LEAF | CONF_UWORLD);
728 if ((found_g = find_kill(acptr))) {
729 sendto_op_mask(found_g == -2 ? SNO_GLINE : SNO_OPERKILL,
730 found_g == -2 ? "G-line active for %s" : "K-line active for %s",
731 get_client_name(acptr, FALSE));
732 if (exit_client(cptr, acptr, &me, found_g == -2 ? "G-lined" :
733 "K-lined") == CPTR_KILLED)
736 #if defined(R_LINES) && defined(R_LINES_REHASH) && !defined(R_LINES_OFTEN)
737 if (find_restrict(acptr)) {
738 sendto_ops("Restricting %s, closing lp", get_client_name(acptr, FALSE));
739 if (exit_client(cptr, acptr, &me, "R-lined") == CPTR_KILLED)
746 /* free old motd structs */
757 /* reload motd files */
759 rmotd = read_motd(RPATH);
760 motd = read_motd(MPATH);
768 * Read configuration file.
770 * returns -1, if file cannot be opened
774 #define MAXCONFLINKS 150
776 unsigned short server_port;
778 int initconf(int opt)
780 static char quotes[9][2] = {
794 int ccount = 0, ncount = 0;
795 aConfItem *aconf = NULL;
797 Debug((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile));
798 if (NULL == (file = fbopen(configfile, "r")))
802 while (fbgets(line, sizeof(line) - 1, file))
804 if ((tmp = strchr(line, '\n')))
807 * Do quoting of characters and # detection.
809 for (tmp = line; *tmp; tmp++)
813 for (i = 0; quotes[i][0]; i++)
814 if (quotes[i][0] == *(tmp + 1))
824 for (s = tmp; (*s = *(s + 1)); s++)
827 else if (*tmp == '#')
830 if (!*line || line[0] == '#' || line[0] == '\n' ||
831 line[0] == ' ' || line[0] == '\t')
833 /* Could we test if it's conf line at all? -Vesa */
836 Debug((DEBUG_ERROR, "Bad config line: %s", line));
843 tmp = getfield(line, ':');
848 case 'A': /* Name, e-mail address of administrator */
849 case 'a': /* of this server. */
850 aconf->status = CONF_ADMIN;
852 case 'C': /* Server where I should try to connect */
853 case 'c': /* in case of lp failures */
855 aconf->status = CONF_CONNECT_SERVER;
859 aconf->status = CONF_CRULEALL;
861 /* Connect rule - autos only */
863 aconf->status = CONF_CRULEAUTO;
865 case 'H': /* Hub server line */
867 aconf->status = CONF_HUB;
869 case 'I': /* Just plain normal irc client trying */
870 case 'i': /* to connect me */
871 aconf->status = CONF_CLIENT;
873 case 'K': /* Kill user line on irc.conf */
874 aconf->status = CONF_KILL;
876 case 'k': /* Kill user line based on IP in ircd.conf */
877 aconf->status = CONF_IPKILL;
879 /* Operator. Line should contain at least */
880 /* password and host where connection is */
881 case 'L': /* guaranteed leaf server */
883 aconf->status = CONF_LEAF;
885 /* Me. Host field is name used for this host */
886 /* and port number is the number of the port */
889 aconf->status = CONF_ME;
891 case 'N': /* Server where I should NOT try to */
892 case 'n': /* connect in case of lp failures */
893 /* but which tries to connect ME */
895 aconf->status = CONF_NOCONNECT_SERVER;
898 aconf->status = CONF_OPERATOR;
900 /* Local Operator, (limited privs --SRB) */
902 aconf->status = CONF_LOCOP;
904 case 'P': /* listen port line */
906 aconf->status = CONF_LISTEN_PORT;
909 case 'R': /* extended K line */
910 case 'r': /* Offers more options of how to restrict */
911 aconf->status = CONF_RESTRICT;
914 case 'T': /* print out different motd's */
915 case 't': /* based on hostmask */
916 aconf->status = CONF_TLINES;
918 case 'U': /* Underworld server, allowed to hack modes */
919 case 'u': /* *Every* server on the net must define the same !!! */
920 aconf->status = CONF_UWORLD;
924 aconf->status = CONF_CLASS;
927 Debug((DEBUG_ERROR, "Error in config file: %s", line));
930 if (IsIllegal(aconf))
933 for (;;) /* Fake loop, that I can use break here --msa */
935 if ((tmp = getfield(NULL, ':')) == NULL)
937 DupString(aconf->host, tmp);
938 if ((tmp = getfield(NULL, (aconf->status == CONF_KILL
939 || aconf->status == CONF_IPKILL) ? '"' : ':')) == NULL)
941 DupString(aconf->passwd, tmp);
942 if ((tmp = getfield(NULL, ':')) == NULL)
944 DupString(aconf->name, tmp);
945 if ((tmp = getfield(NULL, ':')) == NULL)
947 aconf->port = atoi(tmp);
948 tmp = getfield(NULL, ':');
949 if (aconf->status & CONF_ME)
951 server_port = aconf->port;
954 Debug((DEBUG_FATAL, "Your M: line must have the Numeric, "
955 "assigned to you by routing-com, behind the port number!\n"));
957 syslog(LOG_WARNING, "Your M: line must have the Numeric, "
958 "assigned to you by routing-com, behind the port number!\n");
962 SetYXXServerName(&me, atoi(tmp)); /* Our Numeric Nick */
965 aconf->confClass = find_class(atoi(tmp));
969 * If conf line is a class definition, create a class entry
970 * for it and make the conf_line illegal and delete it.
972 if (aconf->status & CONF_CLASS)
974 add_class(atoi(aconf->host), atoi(aconf->passwd),
975 atoi(aconf->name), aconf->port, tmp ? atoi(tmp) : 0);
979 * Associate each conf line with a class by using a pointer
980 * to the correct class record. -avalon
982 if (aconf->status & (CONF_CLIENT_MASK | CONF_LISTEN_PORT))
984 if (aconf->confClass == 0)
985 aconf->confClass = find_class(0);
987 if (aconf->status & (CONF_LISTEN_PORT | CONF_CLIENT))
991 if ((bconf = find_conf_entry(aconf, aconf->status)))
994 bconf->status &= ~CONF_ILLEGAL;
995 if (aconf->status == CONF_CLIENT)
997 char *passwd = bconf->passwd;
998 bconf->passwd = aconf->passwd;
999 aconf->passwd = passwd;
1000 ConfLinks(bconf) -= bconf->clients;
1001 bconf->confClass = aconf->confClass;
1002 if (bconf->confClass)
1003 ConfLinks(bconf) += bconf->clients;
1008 else if (aconf->host && aconf->status == CONF_LISTEN_PORT)
1009 add_listener(aconf);
1011 if (aconf->status & CONF_SERVER_MASK)
1012 if (ncount > MAXCONFLINKS || ccount > MAXCONFLINKS ||
1013 !aconf->host || strchr(aconf->host, '*') ||
1014 strchr(aconf->host, '?') || !aconf->name)
1017 if (aconf->status & (CONF_SERVER_MASK | CONF_LOCOP | CONF_OPERATOR))
1018 if (!strchr(aconf->host, '@') && *aconf->host != '/')
1021 int len = 3; /* *@\0 = 3 */
1023 len += strlen(aconf->host);
1024 newhost = (char *)RunMalloc(len);
1025 sprintf_irc(newhost, "*@%s", aconf->host);
1026 RunFree(aconf->host);
1027 aconf->host = newhost;
1029 if (aconf->status & CONF_SERVER_MASK)
1031 if (BadPtr(aconf->passwd))
1033 else if (!(opt & BOOT_QUICK))
1034 lookup_confhost(aconf);
1037 /* Create expression tree from connect rule...
1038 * If there's a parsing error, nuke the conf structure */
1039 if (aconf->status & (CONF_CRULEALL | CONF_CRULEAUTO))
1041 RunFree(aconf->passwd);
1042 if ((aconf->passwd = (char *)crule_parse(aconf->name)) == NULL)
1051 * Own port and name cannot be changed after the startup.
1052 * (or could be allowed, but only if all links are closed first).
1053 * Configuration info does not override the name and port
1054 * if previously defined. Note, that "info"-field can be
1055 * changed by "/rehash".
1057 if (aconf->status == CONF_ME)
1059 strncpy(me.info, aconf->name, sizeof(me.info) - 1);
1061 portnum = aconf->port;
1065 * Juped nicks are listed in the 'password' field of U:lines,
1066 * the list is comma separated and might be empty and/or contain
1067 * empty elements... the only limit is that it MUST be shorter
1068 * than 512 chars, or they will be cutted out :)
1070 if ((aconf->status == CONF_UWORLD) && (aconf->passwd) && (*aconf->passwd))
1071 addNickJupes(aconf->passwd);
1073 if (aconf->status & CONF_ADMIN)
1074 if (!aconf->host || !aconf->passwd || !aconf->name)
1076 Debug((DEBUG_FATAL, "Your A: line must have 4 fields!\n"));
1078 syslog(LOG_WARNING, "Your A: line must have 4 fields!\n");
1083 collapse(aconf->host);
1084 collapse(aconf->name);
1085 Debug((DEBUG_NOTICE,
1086 "Read Init: (%d) (%s) (%s) (%s) (%u) (%p)",
1087 aconf->status, aconf->host, aconf->passwd,
1088 aconf->name, aconf->port, aconf->confClass));
1097 nextping = nextconnect = now;
1104 * Do (start) DNS lookups of all hostnames in the conf line and convert
1105 * an IP addresses in a.b.c.d number for to IP#s.
1107 static int lookup_confhost(aConfItem *aconf)
1110 Reg3 struct hostent *hp;
1113 if (BadPtr(aconf->host) || BadPtr(aconf->name))
1115 if ((s = strchr(aconf->host, '@')))
1120 * Do name lookup now on hostnames given and store the
1121 * ip numbers in conf structure.
1123 if (!isAlpha(*s) && !isDigit(*s))
1127 * Prepare structure in case we have to wait for a
1128 * reply which we get later and store away.
1130 ln.value.aconf = aconf;
1131 ln.flags = ASYNC_CONF;
1134 aconf->ipnum.s_addr = inet_addr(s);
1135 else if ((hp = gethost_byname(s, &ln)))
1136 memcpy(&(aconf->ipnum), hp->h_addr, sizeof(struct in_addr));
1138 if (aconf->ipnum.s_addr == INADDR_NONE)
1142 if (aconf->ipnum.s_addr == INADDR_NONE)
1143 memset(&aconf->ipnum, 0, sizeof(struct in_addr));
1144 Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
1145 aconf->host, aconf->name));
1150 * Read info from T:lines into trecords which include the file
1151 * timestamp, the hostmask, and the contents of the motd file
1157 atrecord *temp, *last = NULL; /* Init. to avoid compiler warning */
1160 /* Free the old trecords and the associated motd contents first */
1164 while (tdata->tmotd)
1166 amotd = tdata->tmotd->next;
1167 RunFree(tdata->tmotd);
1168 tdata->tmotd = amotd;
1174 for (tmp = conf; tmp; tmp = tmp->next)
1175 if (tmp->status == CONF_TLINES && tmp->host && tmp->passwd)
1177 temp = (atrecord *) RunMalloc(sizeof(atrecord));
1180 temp->hostmask = tmp->host;
1181 temp->tmotd = read_motd(tmp->passwd);
1182 temp->tmotd_tm = motd_tm;
1192 int find_kill(aClient *cptr)
1194 char reply[256], *host, *name;
1196 aGline *agline = NULL;
1201 host = cptr->sockhost;
1202 name = cptr->user->username;
1204 if (strlen(host) > (size_t)HOSTLEN ||
1205 (name ? strlen(name) : 0) > (size_t)HOSTLEN)
1210 for (tmp = conf; tmp; tmp = tmp->next)
1211 /* Added a check against the user's IP address as well.
1212 * If the line is either CONF_KILL or CONF_IPKILL, check it; if and only
1213 * if it's CONF_IPKILL, check the IP address as well (the && below will
1214 * short circuit and the match won't even get run) -Kev
1216 if ((tmp->status & CONF_KLINE) && tmp->host && tmp->name &&
1217 (match(tmp->host, host) == 0 ||
1218 ((tmp->status == CONF_IPKILL) &&
1219 match(tmp->host, inetntoa(cptr->ip)) == 0)) &&
1220 (!name || match(tmp->name, name) == 0) &&
1221 (!tmp->port || (tmp->port == cptr->acpt->port)))
1224 * Can short-circuit evaluation - not taking chances
1225 * because check_time_interval destroys tmp->passwd
1228 if (BadPtr(tmp->passwd))
1230 else if (is_comment(tmp->passwd))
1232 else if (check_time_interval(tmp->passwd, reply))
1237 sendto_one(cptr, reply, me.name, ERR_YOUREBANNEDCREEP, cptr->name);
1240 if (BadPtr(tmp->passwd))
1242 ":%s %d %s :Connection from your host is refused on this server.",
1243 me.name, ERR_YOUREBANNEDCREEP, cptr->name);
1246 if (*tmp->passwd == '"')
1249 sprintf_irc(sendbuf, ":%s %d %s :%s", me.name, ERR_YOUREBANNEDCREEP,
1250 cptr->name, &tmp->passwd[1]);
1251 sbuf[-1] = '.'; /* Overwrite last quote with a dot */
1252 sendbufto_one(cptr);
1254 else if (*tmp->passwd == '!')
1255 killcomment(cptr, cptr->name, &tmp->passwd[1]);
1257 #ifdef COMMENT_IS_FILE
1258 killcomment(cptr, cptr->name, tmp->passwd);
1260 sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
1261 cptr->name, tmp->passwd);
1266 /* find active glines */
1267 /* added a check against the user's IP address to find_gline() -Kev */
1268 else if ((agline = find_gline(cptr, NULL)) && GlineIsActive(agline))
1269 sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
1270 cptr->name, agline->reason);
1272 agline = NULL; /* if a gline was found, it was inactive */
1274 return (tmp ? -1 : (agline ? -2 : 0));
1281 * Works against host/name and calls an outside program
1282 * to determine whether a client is allowed to connect. This allows
1283 * more freedom to determine who is legal and who isn't, for example
1284 * machine load considerations. The outside program is expected to
1285 * return a reply line where the first word is either 'Y' or 'N' meaning
1286 * "Yes Let them in" or "No don't let them in." If the first word
1287 * begins with neither 'Y' or 'N' the default is to let the person on.
1288 * It returns a value of 0 if the user is to be let through -Hoppie
1290 int find_restrict(aClient *cptr)
1293 char reply[80], temprpl[80];
1294 char *rplhold = reply, *host, *name, *s;
1296 int pi[2], rc = 0, n;
1297 FBFILE *file = NULL;
1301 name = cptr->user->username;
1302 host = cptr->sockhost;
1303 Debug((DEBUG_INFO, "R-line check for %s[%s]", name, host));
1305 for (tmp = conf; tmp; tmp = tmp->next)
1307 if (tmp->status != CONF_RESTRICT ||
1308 (tmp->host && host && match(tmp->host, host)) ||
1309 (tmp->name && name && match(tmp->name, name)))
1312 if (BadPtr(tmp->passwd))
1314 sendto_ops("Program missing on R-line %s/%s, ignoring", name, host);
1320 report_error("Error creating pipe for R-line %s: %s", &me);
1323 switch (rc = fork())
1326 report_error("Error forking for R-line %s: %s", &me);
1333 for (i = 2; i < MAXCONNECTIONS; i++)
1339 if (pi[1] != 2 && pi[1] != 1)
1341 execlp(tmp->passwd, tmp->passwd, name, host, 0);
1349 file = fdbopen(pi[0], "r");
1350 while (fbgets(temprpl, sizeof(temprpl) - 1, file))
1352 if ((s = strchr(temprpl, '\n')))
1354 if (strlen(temprpl) + strlen(reply) < sizeof(reply) - 2)
1355 sprintf_irc(rplhold, "%s %s", rplhold, temprpl);
1358 sendto_ops("R-line %s/%s: reply too long!", name, host);
1363 kill(rc, SIGKILL); /* cleanup time */
1367 while (*rplhold == ' ')
1369 rplchar = *rplhold; /* Pull out the yes or no */
1370 while (*rplhold != ' ')
1372 while (*rplhold == ' ')
1374 strcpy(reply, rplhold);
1377 if ((rc = (rplchar == 'n' || rplchar == 'N')))
1382 sendto_one(cptr, ":%s %d %s :Restriction: %s",
1383 me.name, ERR_YOUREBANNEDCREEP, cptr->name, reply);
1391 * output the reason for being k lined from a file - Mmmm
1393 * parv is the sender prefix
1394 * filename is the file that is to be output to the K lined client
1396 static void killcomment(aClient *sptr, char *parv, char *filename)
1398 FBFILE *file = NULL;
1404 if (NULL == (file = fbopen(filename, "r")))
1406 sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv);
1408 ":%s %d %s :Connection from your host is refused on this server.",
1409 me.name, ERR_YOUREBANNEDCREEP, parv);
1413 tm = localtime((time_t *) & sb.st_mtime); /* NetBSD needs cast */
1414 while (fbgets(line, sizeof(line) - 1, file))
1416 if ((tmp = strchr(line, '\n')))
1418 if ((tmp = strchr(line, '\r')))
1421 * ":%s %d %s : %s.",
1422 * me.name, ERR_YOUREBANNEDCREEP, parv,line); */
1423 sendto_one(sptr, rpl_str(RPL_MOTD), me.name, parv, line);
1426 ":%s %d %s :Connection from your host is refused on this server.",
1427 me.name, ERR_YOUREBANNEDCREEP, parv);
1433 * is the K line field an interval or a comment? - Mmmm
1435 static int is_comment(char *comment)
1438 for (i = 0; i < strlen(comment); i++)
1439 if ((comment[i] != ' ') && (comment[i] != '-')
1440 && (comment[i] != ',') && ((comment[i] < '0') || (comment[i] > '9')))
1447 * check against a set of time intervals
1449 static int check_time_interval(char *interval, char *reply)
1453 int perm_min_hours, perm_min_minutes, perm_max_hours, perm_max_minutes;
1454 int nowm, perm_min, perm_max;
1456 tptr = localtime(&now);
1457 nowm = tptr->tm_hour * 60 + tptr->tm_min;
1461 p = strchr(interval, ',');
1464 if (sscanf(interval, "%2d%2d-%2d%2d", &perm_min_hours, &perm_min_minutes,
1465 &perm_max_hours, &perm_max_minutes) != 4)
1473 perm_min = 60 * perm_min_hours + perm_min_minutes;
1474 perm_max = 60 * perm_max_hours + perm_max_minutes;
1476 * The following check allows intervals over midnight ...
1478 if ((perm_min < perm_max)
1479 ? (perm_min <= nowm && nowm <= perm_max)
1480 : (perm_min <= nowm || nowm <= perm_max))
1483 ":%%s %%d %%s :%s %d:%02d to %d:%02d.",
1484 "You are not allowed to connect from",
1485 perm_min_hours, perm_min_minutes, perm_max_hours, perm_max_minutes);
1486 return (ERR_YOUREBANNEDCREEP);
1488 if ((perm_min < perm_max)
1489 ? (perm_min <= nowm + 5 && nowm + 5 <= perm_max)
1490 : (perm_min <= nowm + 5 || nowm + 5 <= perm_max))
1492 sprintf_irc(reply, ":%%s %%d %%s :%d minute%s%s",
1493 perm_min - nowm, (perm_min - nowm) > 1 ? "s " : " ",
1494 "and you will be denied for further access");
1495 return (ERR_YOUWILLBEBANNED);
1502 aMotdItem *read_motd(char *motdfile)
1504 FBFILE *file = NULL;
1505 register aMotdItem *temp, *newmotd, *last;
1510 if (NULL == (file = fbopen(motdfile, "r")))
1512 Debug((DEBUG_ERROR, "Couldn't open \"%s\": %s", motdfile, strerror(errno)));
1515 if (-1 == fbstat(&sb, file))
1519 newmotd = last = NULL;
1520 motd_tm = *localtime((time_t *) & sb.st_mtime); /* NetBSD needs cast */
1521 while (fbgets(line, sizeof(line) - 1, file))
1523 if ((tmp = (char *)strchr(line, '\n')))
1525 if ((tmp = (char *)strchr(line, '\r')))
1527 temp = (aMotdItem *) RunMalloc(sizeof(aMotdItem));
1530 strcpy(temp->line, line);