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 int initconf(int opt)
778 static char quotes[9][2] = {
792 int ccount = 0, ncount = 0;
793 aConfItem *aconf = NULL;
795 Debug((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile));
796 if (NULL == (file = fbopen(configfile, "r")))
800 while (fbgets(line, sizeof(line) - 1, file))
802 if ((tmp = strchr(line, '\n')))
805 * Do quoting of characters and # detection.
807 for (tmp = line; *tmp; tmp++)
811 for (i = 0; quotes[i][0]; i++)
812 if (quotes[i][0] == *(tmp + 1))
822 for (s = tmp; (*s = *(s + 1)); s++)
825 else if (*tmp == '#')
828 if (!*line || line[0] == '#' || line[0] == '\n' ||
829 line[0] == ' ' || line[0] == '\t')
831 /* Could we test if it's conf line at all? -Vesa */
834 Debug((DEBUG_ERROR, "Bad config line: %s", line));
841 tmp = getfield(line, ':');
846 case 'A': /* Name, e-mail address of administrator */
847 case 'a': /* of this server. */
848 aconf->status = CONF_ADMIN;
850 case 'C': /* Server where I should try to connect */
851 case 'c': /* in case of lp failures */
853 aconf->status = CONF_CONNECT_SERVER;
857 aconf->status = CONF_CRULEALL;
859 /* Connect rule - autos only */
861 aconf->status = CONF_CRULEAUTO;
863 case 'H': /* Hub server line */
865 aconf->status = CONF_HUB;
867 case 'I': /* Just plain normal irc client trying */
868 case 'i': /* to connect me */
869 aconf->status = CONF_CLIENT;
871 case 'K': /* Kill user line on irc.conf */
872 aconf->status = CONF_KILL;
874 case 'k': /* Kill user line based on IP in ircd.conf */
875 aconf->status = CONF_IPKILL;
877 /* Operator. Line should contain at least */
878 /* password and host where connection is */
879 case 'L': /* guaranteed leaf server */
881 aconf->status = CONF_LEAF;
883 /* Me. Host field is name used for this host */
884 /* and port number is the number of the port */
887 aconf->status = CONF_ME;
889 case 'N': /* Server where I should NOT try to */
890 case 'n': /* connect in case of lp failures */
891 /* but which tries to connect ME */
893 aconf->status = CONF_NOCONNECT_SERVER;
896 aconf->status = CONF_OPERATOR;
898 /* Local Operator, (limited privs --SRB) */
900 aconf->status = CONF_LOCOP;
902 case 'P': /* listen port line */
904 aconf->status = CONF_LISTEN_PORT;
907 case 'R': /* extended K line */
908 case 'r': /* Offers more options of how to restrict */
909 aconf->status = CONF_RESTRICT;
912 case 'T': /* print out different motd's */
913 case 't': /* based on hostmask */
914 aconf->status = CONF_TLINES;
916 case 'U': /* Underworld server, allowed to hack modes */
917 case 'u': /* *Every* server on the net must define the same !!! */
918 aconf->status = CONF_UWORLD;
922 aconf->status = CONF_CLASS;
925 Debug((DEBUG_ERROR, "Error in config file: %s", line));
928 if (IsIllegal(aconf))
931 for (;;) /* Fake loop, that I can use break here --msa */
933 if ((tmp = getfield(NULL, ':')) == NULL)
935 DupString(aconf->host, tmp);
936 if ((tmp = getfield(NULL, (aconf->status == CONF_KILL
937 || aconf->status == CONF_IPKILL) ? '"' : ':')) == NULL)
939 DupString(aconf->passwd, tmp);
940 if ((tmp = getfield(NULL, ':')) == NULL)
942 DupString(aconf->name, tmp);
943 if ((tmp = getfield(NULL, ':')) == NULL)
945 aconf->port = atoi(tmp);
946 tmp = getfield(NULL, ':');
947 if (aconf->status & CONF_ME)
949 portnum = aconf->port;
953 vserv.sin_port = htons(portnum);
956 Debug((DEBUG_FATAL, "Your M: line must have the Numeric, "
957 "assigned to you by routing-com, behind the port number!\n"));
959 syslog(LOG_WARNING, "Your M: line must have the Numeric, "
960 "assigned to you by routing-com, behind the port number!\n");
964 SetYXXServerName(&me, atoi(tmp)); /* Our Numeric Nick */
967 aconf->confClass = find_class(atoi(tmp));
971 * If conf line is a class definition, create a class entry
972 * for it and make the conf_line illegal and delete it.
974 if (aconf->status & CONF_CLASS)
976 add_class(atoi(aconf->host), atoi(aconf->passwd),
977 atoi(aconf->name), aconf->port, tmp ? atoi(tmp) : 0);
981 * Associate each conf line with a class by using a pointer
982 * to the correct class record. -avalon
984 if (aconf->status & (CONF_CLIENT_MASK | CONF_LISTEN_PORT))
986 if (aconf->confClass == 0)
987 aconf->confClass = find_class(0);
989 if (aconf->status & (CONF_LISTEN_PORT | CONF_CLIENT))
993 if ((bconf = find_conf_entry(aconf, aconf->status)))
996 bconf->status &= ~CONF_ILLEGAL;
997 if (aconf->status == CONF_CLIENT)
999 char *passwd = bconf->passwd;
1000 bconf->passwd = aconf->passwd;
1001 aconf->passwd = passwd;
1002 ConfLinks(bconf) -= bconf->clients;
1003 bconf->confClass = aconf->confClass;
1004 if (bconf->confClass)
1005 ConfLinks(bconf) += bconf->clients;
1010 else if (aconf->host && aconf->status == CONF_LISTEN_PORT)
1011 add_listener(aconf);
1013 if (aconf->status & CONF_SERVER_MASK)
1014 if (ncount > MAXCONFLINKS || ccount > MAXCONFLINKS ||
1015 !aconf->host || strchr(aconf->host, '*') ||
1016 strchr(aconf->host, '?') || !aconf->name)
1019 if (aconf->status & (CONF_SERVER_MASK | CONF_LOCOP | CONF_OPERATOR))
1020 if (!strchr(aconf->host, '@') && *aconf->host != '/')
1023 int len = 3; /* *@\0 = 3 */
1025 len += strlen(aconf->host);
1026 newhost = (char *)RunMalloc(len);
1027 sprintf_irc(newhost, "*@%s", aconf->host);
1028 RunFree(aconf->host);
1029 aconf->host = newhost;
1031 if (aconf->status & CONF_SERVER_MASK)
1033 if (BadPtr(aconf->passwd))
1035 else if (!(opt & BOOT_QUICK))
1036 lookup_confhost(aconf);
1039 /* Create expression tree from connect rule...
1040 * If there's a parsing error, nuke the conf structure */
1041 if (aconf->status & (CONF_CRULEALL | CONF_CRULEAUTO))
1043 RunFree(aconf->passwd);
1044 if ((aconf->passwd = (char *)crule_parse(aconf->name)) == NULL)
1053 * Own port and name cannot be changed after the startup.
1054 * (or could be allowed, but only if all links are closed first).
1055 * Configuration info does not override the name and port
1056 * if previously defined. Note, that "info"-field can be
1057 * changed by "/rehash".
1059 if (aconf->status == CONF_ME)
1061 strncpy(me.info, aconf->name, sizeof(me.info) - 1);
1062 if (me.name[0] == '\0' && aconf->host[0])
1064 strncpy(me.name, aconf->host, sizeof(me.name) - 1);
1065 if (vserv.sin_addr.s_addr == htonl(INADDR_ANY)) /* Not already initialised on commandline with -w ? */
1067 struct hostent *hep;
1068 hep = gethostbyname(me.name);
1069 if (hep && hep->h_addrtype == AF_INET && hep->h_addr_list[0] && !hep->h_addr_list[1])
1071 memcpy(&vserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr));
1072 memcpy(&cserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr));
1077 portnum = aconf->port;
1078 have_server_port = 0;
1082 * Juped nicks are listed in the 'password' field of U:lines,
1083 * the list is comma separated and might be empty and/or contain
1084 * empty elements... the only limit is that it MUST be shorter
1085 * than 512 chars, or they will be cutted out :)
1087 if ((aconf->status == CONF_UWORLD) && (aconf->passwd) && (*aconf->passwd))
1088 addNickJupes(aconf->passwd);
1090 if (aconf->status & CONF_ADMIN)
1091 if (!aconf->host || !aconf->passwd || !aconf->name)
1093 Debug((DEBUG_FATAL, "Your A: line must have 4 fields!\n"));
1095 syslog(LOG_WARNING, "Your A: line must have 4 fields!\n");
1100 collapse(aconf->host);
1101 collapse(aconf->name);
1102 Debug((DEBUG_NOTICE,
1103 "Read Init: (%d) (%s) (%s) (%s) (%u) (%p)",
1104 aconf->status, aconf->host, aconf->passwd,
1105 aconf->name, aconf->port, aconf->confClass));
1114 nextping = nextconnect = now;
1121 * Do (start) DNS lookups of all hostnames in the conf line and convert
1122 * an IP addresses in a.b.c.d number for to IP#s.
1124 static int lookup_confhost(aConfItem *aconf)
1127 Reg3 struct hostent *hp;
1130 if (BadPtr(aconf->host) || BadPtr(aconf->name))
1132 if ((s = strchr(aconf->host, '@')))
1137 * Do name lookup now on hostnames given and store the
1138 * ip numbers in conf structure.
1140 if (!isAlpha(*s) && !isDigit(*s))
1144 * Prepare structure in case we have to wait for a
1145 * reply which we get later and store away.
1147 ln.value.aconf = aconf;
1148 ln.flags = ASYNC_CONF;
1151 aconf->ipnum.s_addr = inet_addr(s);
1152 else if ((hp = gethost_byname(s, &ln)))
1153 memcpy(&(aconf->ipnum), hp->h_addr, sizeof(struct in_addr));
1155 if (aconf->ipnum.s_addr == INADDR_NONE)
1159 if (aconf->ipnum.s_addr == INADDR_NONE)
1160 memset(&aconf->ipnum, 0, sizeof(struct in_addr));
1161 Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
1162 aconf->host, aconf->name));
1167 * Read info from T:lines into trecords which include the file
1168 * timestamp, the hostmask, and the contents of the motd file
1174 atrecord *temp, *last = NULL; /* Init. to avoid compiler warning */
1177 /* Free the old trecords and the associated motd contents first */
1181 while (tdata->tmotd)
1183 amotd = tdata->tmotd->next;
1184 RunFree(tdata->tmotd);
1185 tdata->tmotd = amotd;
1191 for (tmp = conf; tmp; tmp = tmp->next)
1192 if (tmp->status == CONF_TLINES && tmp->host && tmp->passwd)
1194 temp = (atrecord *) RunMalloc(sizeof(atrecord));
1197 temp->hostmask = tmp->host;
1198 temp->tmotd = read_motd(tmp->passwd);
1199 temp->tmotd_tm = motd_tm;
1209 int find_kill(aClient *cptr)
1211 char reply[256], *host, *name;
1213 aGline *agline = NULL;
1218 host = cptr->sockhost;
1219 name = cptr->user->username;
1221 if (strlen(host) > (size_t)HOSTLEN ||
1222 (name ? strlen(name) : 0) > (size_t)HOSTLEN)
1227 for (tmp = conf; tmp; tmp = tmp->next)
1228 /* Added a check against the user's IP address as well.
1229 * If the line is either CONF_KILL or CONF_IPKILL, check it; if and only
1230 * if it's CONF_IPKILL, check the IP address as well (the && below will
1231 * short circuit and the match won't even get run) -Kev
1233 if ((tmp->status & CONF_KLINE) && tmp->host && tmp->name &&
1234 (match(tmp->host, host) == 0 ||
1235 ((tmp->status == CONF_IPKILL) &&
1236 match(tmp->host, inetntoa(cptr->ip)) == 0)) &&
1237 (!name || match(tmp->name, name) == 0) &&
1238 (!tmp->port || (tmp->port == cptr->acpt->port)))
1241 * Can short-circuit evaluation - not taking chances
1242 * because check_time_interval destroys tmp->passwd
1245 if (BadPtr(tmp->passwd))
1247 else if (is_comment(tmp->passwd))
1249 else if (check_time_interval(tmp->passwd, reply))
1254 sendto_one(cptr, reply, me.name, ERR_YOUREBANNEDCREEP, cptr->name);
1257 if (BadPtr(tmp->passwd))
1259 ":%s %d %s :Connection from your host is refused on this server.",
1260 me.name, ERR_YOUREBANNEDCREEP, cptr->name);
1263 if (*tmp->passwd == '"')
1266 sprintf_irc(sendbuf, ":%s %d %s :%s", me.name, ERR_YOUREBANNEDCREEP,
1267 cptr->name, &tmp->passwd[1]);
1268 sbuf[-1] = '.'; /* Overwrite last quote with a dot */
1269 sendbufto_one(cptr);
1271 else if (*tmp->passwd == '!')
1272 killcomment(cptr, cptr->name, &tmp->passwd[1]);
1274 #ifdef COMMENT_IS_FILE
1275 killcomment(cptr, cptr->name, tmp->passwd);
1277 sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
1278 cptr->name, tmp->passwd);
1283 /* find active glines */
1284 /* added a check against the user's IP address to find_gline() -Kev */
1285 else if ((agline = find_gline(cptr, NULL)) && GlineIsActive(agline))
1286 sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
1287 cptr->name, agline->reason);
1289 agline = NULL; /* if a gline was found, it was inactive */
1291 return (tmp ? -1 : (agline ? -2 : 0));
1298 * Works against host/name and calls an outside program
1299 * to determine whether a client is allowed to connect. This allows
1300 * more freedom to determine who is legal and who isn't, for example
1301 * machine load considerations. The outside program is expected to
1302 * return a reply line where the first word is either 'Y' or 'N' meaning
1303 * "Yes Let them in" or "No don't let them in." If the first word
1304 * begins with neither 'Y' or 'N' the default is to let the person on.
1305 * It returns a value of 0 if the user is to be let through -Hoppie
1307 int find_restrict(aClient *cptr)
1310 char reply[80], temprpl[80];
1311 char *rplhold = reply, *host, *name, *s;
1313 int pi[2], rc = 0, n;
1314 FBFILE *file = NULL;
1318 name = cptr->user->username;
1319 host = cptr->sockhost;
1320 Debug((DEBUG_INFO, "R-line check for %s[%s]", name, host));
1322 for (tmp = conf; tmp; tmp = tmp->next)
1324 if (tmp->status != CONF_RESTRICT ||
1325 (tmp->host && host && match(tmp->host, host)) ||
1326 (tmp->name && name && match(tmp->name, name)))
1329 if (BadPtr(tmp->passwd))
1331 sendto_ops("Program missing on R-line %s/%s, ignoring", name, host);
1337 report_error("Error creating pipe for R-line %s: %s", &me);
1340 switch (rc = fork())
1343 report_error("Error forking for R-line %s: %s", &me);
1350 for (i = 2; i < MAXCONNECTIONS; i++)
1356 if (pi[1] != 2 && pi[1] != 1)
1358 execlp(tmp->passwd, tmp->passwd, name, host, 0);
1366 file = fdbopen(pi[0], "r");
1367 while (fbgets(temprpl, sizeof(temprpl) - 1, file))
1369 if ((s = strchr(temprpl, '\n')))
1371 if (strlen(temprpl) + strlen(reply) < sizeof(reply) - 2)
1372 sprintf_irc(rplhold, "%s %s", rplhold, temprpl);
1375 sendto_ops("R-line %s/%s: reply too long!", name, host);
1380 kill(rc, SIGKILL); /* cleanup time */
1384 while (*rplhold == ' ')
1386 rplchar = *rplhold; /* Pull out the yes or no */
1387 while (*rplhold != ' ')
1389 while (*rplhold == ' ')
1391 strcpy(reply, rplhold);
1394 if ((rc = (rplchar == 'n' || rplchar == 'N')))
1399 sendto_one(cptr, ":%s %d %s :Restriction: %s",
1400 me.name, ERR_YOUREBANNEDCREEP, cptr->name, reply);
1408 * output the reason for being k lined from a file - Mmmm
1410 * parv is the sender prefix
1411 * filename is the file that is to be output to the K lined client
1413 static void killcomment(aClient *sptr, char *parv, char *filename)
1415 FBFILE *file = NULL;
1421 if (NULL == (file = fbopen(filename, "r")))
1423 sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv);
1425 ":%s %d %s :Connection from your host is refused on this server.",
1426 me.name, ERR_YOUREBANNEDCREEP, parv);
1430 tm = localtime((time_t *) & sb.st_mtime); /* NetBSD needs cast */
1431 while (fbgets(line, sizeof(line) - 1, file))
1433 if ((tmp = strchr(line, '\n')))
1435 if ((tmp = strchr(line, '\r')))
1438 * ":%s %d %s : %s.",
1439 * me.name, ERR_YOUREBANNEDCREEP, parv,line); */
1440 sendto_one(sptr, rpl_str(RPL_MOTD), me.name, parv, line);
1443 ":%s %d %s :Connection from your host is refused on this server.",
1444 me.name, ERR_YOUREBANNEDCREEP, parv);
1450 * is the K line field an interval or a comment? - Mmmm
1452 static int is_comment(char *comment)
1455 for (i = 0; i < strlen(comment); i++)
1456 if ((comment[i] != ' ') && (comment[i] != '-')
1457 && (comment[i] != ',') && ((comment[i] < '0') || (comment[i] > '9')))
1464 * check against a set of time intervals
1466 static int check_time_interval(char *interval, char *reply)
1470 int perm_min_hours, perm_min_minutes, perm_max_hours, perm_max_minutes;
1471 int nowm, perm_min, perm_max;
1473 tptr = localtime(&now);
1474 nowm = tptr->tm_hour * 60 + tptr->tm_min;
1478 p = strchr(interval, ',');
1481 if (sscanf(interval, "%2d%2d-%2d%2d", &perm_min_hours, &perm_min_minutes,
1482 &perm_max_hours, &perm_max_minutes) != 4)
1490 perm_min = 60 * perm_min_hours + perm_min_minutes;
1491 perm_max = 60 * perm_max_hours + perm_max_minutes;
1493 * The following check allows intervals over midnight ...
1495 if ((perm_min < perm_max)
1496 ? (perm_min <= nowm && nowm <= perm_max)
1497 : (perm_min <= nowm || nowm <= perm_max))
1500 ":%%s %%d %%s :%s %d:%02d to %d:%02d.",
1501 "You are not allowed to connect from",
1502 perm_min_hours, perm_min_minutes, perm_max_hours, perm_max_minutes);
1503 return (ERR_YOUREBANNEDCREEP);
1505 if ((perm_min < perm_max)
1506 ? (perm_min <= nowm + 5 && nowm + 5 <= perm_max)
1507 : (perm_min <= nowm + 5 || nowm + 5 <= perm_max))
1509 sprintf_irc(reply, ":%%s %%d %%s :%d minute%s%s",
1510 perm_min - nowm, (perm_min - nowm) > 1 ? "s " : " ",
1511 "and you will be denied for further access");
1512 return (ERR_YOUWILLBEBANNED);
1519 aMotdItem *read_motd(char *motdfile)
1521 FBFILE *file = NULL;
1522 register aMotdItem *temp, *newmotd, *last;
1527 if (NULL == (file = fbopen(motdfile, "r")))
1529 Debug((DEBUG_ERROR, "Couldn't open \"%s\": %s", motdfile, strerror(errno)));
1532 if (-1 == fbstat(&sb, file))
1536 newmotd = last = NULL;
1537 motd_tm = *localtime((time_t *) & sb.st_mtime); /* NetBSD needs cast */
1538 while (fbgets(line, sizeof(line) - 1, file))
1540 if ((tmp = (char *)strchr(line, '\n')))
1542 if ((tmp = (char *)strchr(line, '\r')))
1544 temp = (aMotdItem *) RunMalloc(sizeof(aMotdItem));
1547 strcpy(temp->line, line);