diff options
Diffstat (limited to 'ircd/s_conf.c')
-rw-r--r-- | ircd/s_conf.c | 1684 |
1 files changed, 1684 insertions, 0 deletions
diff --git a/ircd/s_conf.c b/ircd/s_conf.c new file mode 100644 index 0000000..2abbbd3 --- /dev/null +++ b/ircd/s_conf.c @@ -0,0 +1,1684 @@ +/************************************************************************ + * IRC - Internet Relay Chat, ircd/s_conf.c + * Copyright (C) 1990 Jarkko Oikarinen and + * University of Oulu, Computing Center + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* -- avalon -- 20 Feb 1992 + * Reversed the order of the params for attach_conf(). + * detach_conf() and attach_conf() are now the same: + * function_conf(aClient *, aConfItem *) + */ + +/* -- Jto -- 20 Jun 1990 + * Added gruner's overnight fix.. + */ + +/* -- Jto -- 16 Jun 1990 + * Moved matches to ../common/match.c + */ + +/* -- Jto -- 03 Jun 1990 + * Added Kill fixes from gruner@lan.informatik.tu-muenchen.de + * Added jarlek's msgbase fix (I still don't understand it... -- Jto) + */ + +/* -- Jto -- 13 May 1990 + * Added fixes from msa: + * Comments and return value to init_conf() + */ + +/* + * -- Jto -- 12 May 1990 + * Added close() into configuration file (was forgotten...) + */ + +#ifndef lint +static char rcsid[] = "@(#)$Id: s_conf.c,v 1.42 1999/05/01 21:29:13 kalt Exp $"; +#endif + +#include "os.h" +#include "s_defines.h" +#define S_CONF_C +#include "s_externs.h" +#undef S_CONF_C + +static int check_time_interval __P((char *, char *)); +static int lookup_confhost __P((aConfItem *)); + +aConfItem *conf = NULL; +aConfItem *kconf = NULL; + +/* + * remove all conf entries from the client except those which match + * the status field mask. + */ +void det_confs_butmask(cptr, mask) +aClient *cptr; +int mask; +{ + Reg Link *tmp, *tmp2; + + for (tmp = cptr->confs; tmp; tmp = tmp2) + { + tmp2 = tmp->next; + if ((tmp->value.aconf->status & mask) == 0) + (void)detach_conf(cptr, tmp->value.aconf); + } +} + +/* + * Match address by #IP bitmask (10.11.12.128/27) + */ +int match_ipmask(mask, cptr) +char *mask; +aClient *cptr; +{ + int i1, i2, i3, i4, m; + u_long lmask, baseip; + char *at; + + if (at = index(mask, '@')) + mask = at + 1; + if (sscanf(mask, "%d.%d.%d.%d/%d", &i1, &i2, &i3, &i4, &m) != 5 || + m < 1 || m > 31) { + sendto_flag(SCH_LOCAL, "Ignoring bad mask: %s", mask); + return -1; + } + lmask = htonl((u_long)0xffffffffL << (32 - m)); /* /24->0xffffff00ul */ + baseip = htonl(i1 * 0x1000000 + i2 * 0x10000 + i3 * 0x100 + i4); +#ifdef INET6 + return 1; +/* return ((cptr->ip.s6_addr & lmask) == baseip) ? 0 : 1;*/ +#else + return ((cptr->ip.s_addr & lmask) == baseip) ? 0 : 1; +#endif +} + +/* + * find the first (best) I line to attach. + */ +int attach_Iline(cptr, hp, sockhost) +aClient *cptr; +Reg struct hostent *hp; +char *sockhost; +{ + Reg aConfItem *aconf; + Reg char *hname; + Reg int i; + static char uhost[HOSTLEN+USERLEN+3]; + static char fullname[HOSTLEN+1]; + + for (aconf = conf; aconf; aconf = aconf->next) + { + if ((aconf->status != CONF_CLIENT) && + (aconf->status != CONF_RCLIENT)) + continue; + if (aconf->port && aconf->port != cptr->acpt->port) + continue; + if (!aconf->host || !aconf->name) + goto attach_iline; + if (hp) + for (i = 0, hname = hp->h_name; hname; + hname = hp->h_aliases[i++]) + { + strncpyzt(fullname, hname, + sizeof(fullname)); + add_local_domain(fullname, + HOSTLEN - strlen(fullname)); + Debug((DEBUG_DNS, "a_il: %s->%s", + sockhost, fullname)); + if (index(aconf->name, '@')) + { + (void)strcpy(uhost, cptr->username); + (void)strcat(uhost, "@"); + } + else + *uhost = '\0'; + (void)strncat(uhost, fullname, + sizeof(uhost) - strlen(uhost)); + if (!match(aconf->name, uhost)) + goto attach_iline; + } + + if (index(aconf->host, '@')) + { + strncpyzt(uhost, cptr->username, sizeof(uhost)); + (void)strcat(uhost, "@"); + } + else + *uhost = '\0'; + (void)strncat(uhost, sockhost, sizeof(uhost) - strlen(uhost)); + if (strchr(aconf->host, '/')) /* 1.2.3.0/24 */ + { + if (match_ipmask(aconf->host, cptr)) + continue; + } else if (match(aconf->host, uhost)) /* 1.2.3.* */ + continue; + if (*aconf->name == '\0' && hp) + { + strncpyzt(uhost, hp->h_name, sizeof(uhost)); + add_local_domain(uhost, sizeof(uhost) - strlen(uhost)); + } +attach_iline: + if (aconf->status & CONF_RCLIENT) + SetRestricted(cptr); + get_sockhost(cptr, uhost); + if ((i = attach_conf(cptr, aconf)) < -1) + find_bounce(cptr, ConfClass(aconf), -1); + return i; + } + find_bounce(cptr, 0, -2); + return -2; /* used in register_user() */ +} + +/* + * Find the single N line and return pointer to it (from list). + * If more than one then return NULL pointer. + */ +aConfItem *count_cnlines(lp) +Reg Link *lp; +{ + Reg aConfItem *aconf, *cline = NULL, *nline = NULL; + + for (; lp; lp = lp->next) + { + aconf = lp->value.aconf; + if (!(aconf->status & CONF_SERVER_MASK)) + continue; + if ((aconf->status == CONF_CONNECT_SERVER || + aconf->status == CONF_ZCONNECT_SERVER) && !cline) + cline = aconf; + else if (aconf->status == CONF_NOCONNECT_SERVER && !nline) + nline = aconf; + } + return nline; +} + +/* +** detach_conf +** Disassociate configuration from the client. +** Also removes a class from the list if marked for deleting. +*/ +int detach_conf(cptr, aconf) +aClient *cptr; +aConfItem *aconf; +{ + Reg Link **lp, *tmp; + + lp = &(cptr->confs); + + while (*lp) + { + if ((*lp)->value.aconf == aconf) + { + if ((aconf) && (Class(aconf))) + { + if (aconf->status & CONF_CLIENT_MASK) + if (ConfLinks(aconf) > 0) + --ConfLinks(aconf); + if (ConfMaxLinks(aconf) == -1 && + ConfLinks(aconf) == 0) + { + free_class(Class(aconf)); + Class(aconf) = NULL; + } + } + if (aconf && !--aconf->clients && IsIllegal(aconf)) + free_conf(aconf); + tmp = *lp; + *lp = tmp->next; + free_link(tmp); + istat.is_conflink--; + return 0; + } + else + lp = &((*lp)->next); + } + return -1; +} + +static int is_attached(aconf, cptr) +aConfItem *aconf; +aClient *cptr; +{ + Reg Link *lp; + + for (lp = cptr->confs; lp; lp = lp->next) + if (lp->value.aconf == aconf) + break; + + return (lp) ? 1 : 0; +} + +/* +** attach_conf +** Associate a specific configuration entry to a *local* +** client (this is the one which used in accepting the +** connection). Note, that this automaticly changes the +** attachment if there was an old one... +*/ +int attach_conf(cptr, aconf) +aConfItem *aconf; +aClient *cptr; +{ + Reg Link *lp; + + if (is_attached(aconf, cptr)) + return 1; + if (IsIllegal(aconf)) + return -1; + if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT | + CONF_RCLIENT))) + { + if (aconf->clients >= ConfMaxLinks(aconf) && + ConfMaxLinks(aconf) > 0) + return -3; /* Use this for printing error message */ + } + if ((aconf->status & (CONF_CLIENT | CONF_RCLIENT))) + { + int hcnt = 0, ucnt = 0; + + /* check on local/global limits per host and per user@host */ + + /* + ** local limits first to save CPU if any is hit. + ** host check is done on the IP address. + ** user check is done on the IDENT reply. + */ + if (ConfMaxHLocal(aconf) > 0 || ConfMaxUHLocal(aconf) > 0) { + Reg aClient *acptr; + Reg int i; + + for (i = highest_fd; i >= 0; i--) + if ((acptr = local[i]) && (cptr != acptr) && + !IsListening(acptr) && + !bcmp((char *)&cptr->ip,(char *)&acptr->ip, + sizeof(cptr->ip))) + { + hcnt++; + if (!strncasecmp(acptr->auth, + cptr->auth, USERLEN)) + ucnt++; + } + if (ConfMaxHLocal(aconf) > 0 && + hcnt >= ConfMaxHLocal(aconf)) + return -4; /* for error message */ + if (ConfMaxUHLocal(aconf) > 0 && + ucnt >= ConfMaxUHLocal(aconf)) + return -5; /* for error message */ + } + /* + ** Global limits + ** host check is done on the hostname (IP if unresolved) + ** user check is done on username + */ + if (ConfMaxHGlobal(aconf) > 0 || ConfMaxUHGlobal(aconf) > 0) + { + Reg aClient *acptr; + Reg int ghcnt = hcnt, gucnt = ucnt; + + for (acptr = client; acptr; acptr = acptr->next) + { + if (!IsPerson(acptr)) + continue; + if (MyConnect(acptr) && + (ConfMaxHLocal(aconf) > 0 || + ConfMaxUHLocal(aconf) > 0)) + continue; + if (!strcmp(cptr->sockhost, acptr->user->host)) + { + if (ConfMaxHGlobal(aconf) > 0 && + ++ghcnt >= ConfMaxHGlobal(aconf)) + return -6; + if (ConfMaxUHGlobal(aconf) > 0 && + !strcmp(cptr->user->username, + acptr->user->username) && + (++gucnt >=ConfMaxUHGlobal(aconf))) + return -7; + } + } + } + } + + lp = make_link(); + istat.is_conflink++; + lp->next = cptr->confs; + lp->value.aconf = aconf; + cptr->confs = lp; + aconf->clients++; + if (aconf->status & CONF_CLIENT_MASK) + ConfLinks(aconf)++; + return 0; +} + + +aConfItem *find_admin() + { + Reg aConfItem *aconf; + + for (aconf = conf; aconf; aconf = aconf->next) + if (aconf->status & CONF_ADMIN) + break; + + return (aconf); + } + +aConfItem *find_me() + { + Reg aConfItem *aconf; + for (aconf = conf; aconf; aconf = aconf->next) + if (aconf->status & CONF_ME) + break; + + return (aconf); + } + +/* + * attach_confs + * Attach a CONF line to a client if the name passed matches that for + * the conf file (for non-C/N lines) or is an exact match (C/N lines + * only). The difference in behaviour is to stop C:*::* and N:*::*. + */ +aConfItem *attach_confs(cptr, name, statmask) +aClient *cptr; +char *name; +int statmask; +{ + Reg aConfItem *tmp; + aConfItem *first = NULL; + int len = strlen(name); + + if (!name || len > HOSTLEN) + return NULL; + for (tmp = conf; tmp; tmp = tmp->next) + { + if ((tmp->status & statmask) && !IsIllegal(tmp) && + ((tmp->status & (CONF_SERVER_MASK|CONF_HUB)) == 0) && + tmp->name && !match(tmp->name, name)) + { + if (!attach_conf(cptr, tmp) && !first) + first = tmp; + } + else if ((tmp->status & statmask) && !IsIllegal(tmp) && + (tmp->status & (CONF_SERVER_MASK|CONF_HUB)) && + tmp->name && !mycmp(tmp->name, name)) + { + if (!attach_conf(cptr, tmp) && !first) + first = tmp; + } + } + return (first); +} + +/* + * Added for new access check meLazy + */ +aConfItem *attach_confs_host(cptr, host, statmask) +aClient *cptr; +char *host; +int statmask; +{ + Reg aConfItem *tmp; + aConfItem *first = NULL; + int len = strlen(host); + + if (!host || len > HOSTLEN) + return NULL; + + for (tmp = conf; tmp; tmp = tmp->next) + { + if ((tmp->status & statmask) && !IsIllegal(tmp) && + (tmp->status & CONF_SERVER_MASK) == 0 && + (!tmp->host || match(tmp->host, host) == 0)) + { + if (!attach_conf(cptr, tmp) && !first) + first = tmp; + } + else if ((tmp->status & statmask) && !IsIllegal(tmp) && + (tmp->status & CONF_SERVER_MASK) && + (tmp->host && mycmp(tmp->host, host) == 0)) + { + if (!attach_conf(cptr, tmp) && !first) + first = tmp; + } + } + return (first); +} + +/* + * find a conf entry which matches the hostname and has the same name. + */ +aConfItem *find_conf_exact(name, user, host, statmask) +char *name, *host, *user; +int statmask; +{ + Reg aConfItem *tmp; + char userhost[USERLEN+HOSTLEN+3]; + + SPRINTF(userhost, "%s@%s", user, host); + + for (tmp = conf; tmp; tmp = tmp->next) + { + if (!(tmp->status & statmask) || !tmp->name || !tmp->host || + mycmp(tmp->name, name)) + continue; + /* + ** Accept if the *real* hostname (usually sockecthost) + ** socket host) matches *either* host or name field + ** of the configuration. + */ + if (match(tmp->host, userhost)) + continue; + if (tmp->status & (CONF_OPERATOR|CONF_LOCOP)) + { + if (tmp->clients < MaxLinks(Class(tmp))) + return tmp; + else + continue; + } + else + return tmp; + } + return NULL; +} + +aConfItem *find_conf_name(name, statmask) +char *name; +int statmask; +{ + Reg aConfItem *tmp; + + for (tmp = conf; tmp; tmp = tmp->next) + { + /* + ** Accept if the *real* hostname (usually sockecthost) + ** matches *either* host or name field of the configuration. + */ + if ((tmp->status & statmask) && + (!tmp->name || match(tmp->name, name) == 0)) + return tmp; + } + return NULL; +} + +aConfItem *find_conf(lp, name, statmask) +char *name; +Link *lp; +int statmask; +{ + Reg aConfItem *tmp; + int namelen = name ? strlen(name) : 0; + + if (namelen > HOSTLEN) + return (aConfItem *) 0; + + for (; lp; lp = lp->next) + { + tmp = lp->value.aconf; + if ((tmp->status & statmask) && + (((tmp->status & (CONF_SERVER_MASK|CONF_HUB)) && + tmp->name && !mycmp(tmp->name, name)) || + ((tmp->status & (CONF_SERVER_MASK|CONF_HUB)) == 0 && + tmp->name && !match(tmp->name, name)))) + return tmp; + } + return NULL; +} + +/* + * Added for new access check meLazy + */ +aConfItem *find_conf_host(lp, host, statmask) +Reg Link *lp; +char *host; +Reg int statmask; +{ + Reg aConfItem *tmp; + int hostlen = host ? strlen(host) : 0; + + if (hostlen > HOSTLEN || BadPtr(host)) + return (aConfItem *)NULL; + for (; lp; lp = lp->next) + { + tmp = lp->value.aconf; + if (tmp->status & statmask && + (!(tmp->status & CONF_SERVER_MASK || tmp->host) || + (tmp->host && !match(tmp->host, host)))) + return tmp; + } + return NULL; +} + +/* + * find_conf_ip + * + * Find a conf line using the IP# stored in it to search upon. + * Added 1/8/92 by Avalon. + */ +aConfItem *find_conf_ip(lp, ip, user, statmask) +char *ip, *user; +Link *lp; +int statmask; +{ + Reg aConfItem *tmp; + Reg char *s; + + for (; lp; lp = lp->next) + { + tmp = lp->value.aconf; + if (!(tmp->status & statmask)) + continue; + s = index(tmp->host, '@'); + *s = '\0'; + if (match(tmp->host, user)) + { + *s = '@'; + continue; + } + *s = '@'; + if (!bcmp((char *)&tmp->ipnum, ip, sizeof(struct IN_ADDR))) + return tmp; + } + return NULL; +} + +/* + * find_conf_entry + * + * - looks for a match on all given fields. + */ +aConfItem *find_conf_entry(aconf, mask) +aConfItem *aconf; +u_int mask; +{ + Reg aConfItem *bconf; + + for (bconf = conf, mask &= ~CONF_ILLEGAL; bconf; bconf = bconf->next) + { + if (!(bconf->status & mask) || (bconf->port != aconf->port)) + continue; + + if ((BadPtr(bconf->host) && !BadPtr(aconf->host)) || + (BadPtr(aconf->host) && !BadPtr(bconf->host))) + continue; + if (!BadPtr(bconf->host) && mycmp(bconf->host, aconf->host)) + continue; + + if ((BadPtr(bconf->passwd) && !BadPtr(aconf->passwd)) || + (BadPtr(aconf->passwd) && !BadPtr(bconf->passwd))) + continue; + if (!BadPtr(bconf->passwd) && + mycmp(bconf->passwd, aconf->passwd)) + continue; + + if ((BadPtr(bconf->name) && !BadPtr(aconf->name)) || + (BadPtr(aconf->name) && !BadPtr(bconf->name))) + continue; + if (!BadPtr(bconf->name) && mycmp(bconf->name, aconf->name)) + continue; + break; + } + return bconf; +} + +/* + * rehash + * + * Actual REHASH service routine. Called with sig == 0 if it has been called + * as a result of an operator issuing this command, else assume it has been + * called as a result of the server receiving a HUP signal. + */ +int rehash(cptr, sptr, sig) +aClient *cptr, *sptr; +int sig; +{ + Reg aConfItem **tmp = &conf, *tmp2 = NULL; + Reg aClass *cltmp; + Reg aClient *acptr; + Reg int i; + int ret = 0; + + if (sig == 1) + { + sendto_flag(SCH_NOTICE, + "Got signal SIGHUP, reloading ircd.conf file"); +#ifdef ULTRIX + if (fork() > 0) + exit(0); + write_pidfile(); +#endif + } + + for (i = 0; i <= highest_fd; i++) + if ((acptr = local[i]) && !IsMe(acptr)) + { + /* + * Nullify any references from client structures to + * this host structure which is about to be freed. + * Could always keep reference counts instead of + * this....-avalon + */ + acptr->hostp = NULL; +#if defined(R_LINES_REHASH) && !defined(R_LINES_OFTEN) + if (find_restrict(acptr)) + { + sendto_flag(SCH_NOTICE, + "Restricting %s, closing lp", + get_client_name(acptr,FALSE)); + acptr->exitc = EXITC_RLINE; + if (exit_client(cptr,acptr,&me,"R-lined") == + FLUSH_BUFFER) + ret = FLUSH_BUFFER; + } +#endif + } + + while ((tmp2 = *tmp)) + if (tmp2->clients || tmp2->status & CONF_LISTEN_PORT) + { + /* + ** Configuration entry is still in use by some + ** local clients, cannot delete it--mark it so + ** that it will be deleted when the last client + ** exits... + */ + if (!(tmp2->status & (CONF_LISTEN_PORT|CONF_CLIENT))) + { + *tmp = tmp2->next; + tmp2->next = NULL; + } + else + tmp = &tmp2->next; + tmp2->status |= CONF_ILLEGAL; + } + else + { + *tmp = tmp2->next; + free_conf(tmp2); + } + + tmp = &kconf; + while ((tmp2 = *tmp)) + { + *tmp = tmp2->next; + free_conf(tmp2); + } + + /* + * We don't delete the class table, rather mark all entries + * for deletion. The table is cleaned up by check_class. - avalon + */ + for (cltmp = NextClass(FirstClass()); cltmp; cltmp = NextClass(cltmp)) + MaxLinks(cltmp) = -1; + + if (sig != 2) + flush_cache(); + (void) initconf(0); + close_listeners(); + + /* + * flush out deleted I and P lines although still in use. + */ + for (tmp = &conf; (tmp2 = *tmp); ) + if (!(tmp2->status & CONF_ILLEGAL)) + tmp = &tmp2->next; + else + { + *tmp = tmp2->next; + tmp2->next = NULL; + if (!tmp2->clients) + free_conf(tmp2); + } +#ifdef CACHED_MOTD + read_motd(IRCDMOTD_PATH); +#endif + rehashed = 1; + return ret; +} + +/* + * openconf + * + * returns -1 on any error or else the fd opened from which to read the + * configuration file from. This may either be the file direct or one end + * of a pipe from m4. + */ +int openconf() +{ +#ifdef M4_PREPROC + int pi[2], i; + + if (pipe(pi) == -1) + return -1; + switch(vfork()) + { + case -1 : + return -1; + case 0 : + (void)close(pi[0]); + if (pi[1] != 1) + { + (void)dup2(pi[1], 1); + (void)close(pi[1]); + } + (void)dup2(1,2); + for (i = 3; i < MAXCONNECTIONS; i++) + if (local[i]) + (void) close(i); + /* + * m4 maybe anywhere, use execvp to find it. Any error + * goes out with report_error. Could be dangerous, + * two servers running with the same fd's >:-) -avalon + */ + (void)execlp("m4", "m4", IRCDM4_PATH, configfile, 0); + report_error("Error executing m4 %s:%s", &me); + _exit(-1); + default : + (void)close(pi[1]); + return pi[0]; + } +#else + return open(configfile, O_RDONLY); +#endif +} + +/* +** initconf() +** Read configuration file. +** +** returns -1, if file cannot be opened +** 0, if file opened +*/ + +#define MAXCONFLINKS 150 + +int initconf(opt) +int opt; +{ + static char quotes[9][2] = {{'b', '\b'}, {'f', '\f'}, {'n', '\n'}, + {'r', '\r'}, {'t', '\t'}, {'v', '\v'}, + {'\\', '\\'}, { 0, 0}}; + Reg char *tmp, *s; + int fd, i; + char line[512], c[80], *tmp2 = NULL, *tmp3 = NULL, *tmp4 = NULL; + int ccount = 0, ncount = 0; + aConfItem *aconf = NULL; + + Debug((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile)); + if ((fd = openconf()) == -1) + { +#if defined(M4_PREPROC) && !defined(USE_IAUTH) + (void)wait(0); +#endif + return -1; + } + (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */ + while ((i = dgets(fd, line, sizeof(line) - 1)) > 0) + { + line[i] = '\0'; + if ((tmp = (char *)index(line, '\n'))) + *tmp = 0; + else while(dgets(fd, c, sizeof(c) - 1) > 0) + if ((tmp = (char *)index(c, '\n'))) + { + *tmp = 0; + break; + } + /* + * Do quoting of characters and # detection. + */ + for (tmp = line; *tmp; tmp++) + { + if (*tmp == '\\') + { + for (i = 0; quotes[i][0]; i++) + if (quotes[i][0] == *(tmp+1)) + { + *tmp = quotes[i][1]; + break; + } + if (!quotes[i][0]) + *tmp = *(tmp+1); + if (!*(tmp+1)) + break; + else + for (s = tmp; (*s = *(s+1)); s++) + ; + } + else if (*tmp == '#') + { + *tmp = '\0'; + break; /* Ignore the rest of the line */ + } + } + if (!*line || line[0] == '#' || line[0] == '\n' || + line[0] == ' ' || line[0] == '\t') + continue; + /* Could we test if it's conf line at all? -Vesa */ + if (line[1] != IRCDCONF_DELIMITER) + { + Debug((DEBUG_ERROR, "Bad config line: %s", line)); + continue; + } + if (aconf) + free_conf(aconf); + aconf = make_conf(); + + if (tmp2) + MyFree(tmp2); + tmp3 = tmp4 = NULL; + tmp = getfield(line); + if (!tmp) + continue; + switch (*tmp) + { + case 'A': /* Name, e-mail address of administrator */ + case 'a': /* of this server. */ + aconf->status = CONF_ADMIN; + break; + case 'B': /* Name of alternate servers */ + case 'b': + aconf->status = CONF_BOUNCE; + break; + case 'C': /* Server where I should try to connect */ + /* in case of lp failures */ + ccount++; + aconf->status = CONF_CONNECT_SERVER; + break; + case 'c': + ccount++; + aconf->status = CONF_ZCONNECT_SERVER; + break; + case 'D': /* auto connect restrictions */ + case 'd': + aconf->status = CONF_DENY; + break; + case 'H': /* Hub server line */ + case 'h': + aconf->status = CONF_HUB; + break; + case 'I': /* Just plain normal irc client trying */ + /* to connect me */ + aconf->status = CONF_CLIENT; + break; + case 'i' : /* Restricted client */ + aconf->status = CONF_RCLIENT; + break; + case 'K': /* Kill user line on irc.conf */ + aconf->status = CONF_KILL; + break; + case 'k': + aconf->status = CONF_OTHERKILL; + break; + /* Operator. Line should contain at least */ + /* password and host where connection is */ + case 'L': /* guaranteed leaf server */ + case 'l': + aconf->status = CONF_LEAF; + break; + /* Me. Host field is name used for this host */ + /* and port number is the number of the port */ + case 'M': + case 'm': + aconf->status = CONF_ME; + break; + case 'N': /* Server where I should NOT try to */ + case 'n': /* connect in case of lp failures */ + /* but which tries to connect ME */ + ++ncount; + aconf->status = CONF_NOCONNECT_SERVER; + break; + case 'O': + aconf->status = CONF_OPERATOR; + break; + /* Local Operator, (limited privs --SRB) */ + case 'o': + aconf->status = CONF_LOCOP; + break; + case 'P': /* listen port line */ + case 'p': + aconf->status = CONF_LISTEN_PORT; + break; + case 'Q': /* a server that you don't want in your */ + case 'q': /* network. USE WITH CAUTION! */ + aconf->status = CONF_QUARANTINED_SERVER; + break; +#ifdef R_LINES + case 'R': /* extended K line */ + case 'r': /* Offers more options of how to restrict */ + aconf->status = CONF_RESTRICT; + break; +#endif + case 'S': /* Service. Same semantics as */ + case 's': /* CONF_OPERATOR */ + aconf->status = CONF_SERVICE; + break; +#if 0 + case 'U': /* Uphost, ie. host where client reading */ + case 'u': /* this should connect. */ + /* This is for client only, I must ignore this */ + /* ...U-line should be removed... --msa */ + break; +#endif + case 'V': /* Server link version requirements */ + aconf->status = CONF_VER; + break; + case 'Y': + case 'y': + aconf->status = CONF_CLASS; + break; + default: + Debug((DEBUG_ERROR, "Error in config file: %s", line)); + break; + } + if (IsIllegal(aconf)) + continue; + + for (;;) /* Fake loop, that I can use break here --msa */ + { + if ((tmp = getfield(NULL)) == NULL) + break; + DupString(aconf->host, tmp); + if ((tmp = getfield(NULL)) == NULL) + break; + DupString(aconf->passwd, tmp); + if ((tmp = getfield(NULL)) == NULL) + break; + DupString(aconf->name, tmp); + if ((tmp = getfield(NULL)) == NULL) + break; + aconf->port = 0; + if (sscanf(tmp, "0x%x", &aconf->port) != 1 || + aconf->port == 0) + aconf->port = atoi(tmp); + if (aconf->status == CONF_CONNECT_SERVER) + DupString(tmp2, tmp); + if (aconf->status == CONF_ZCONNECT_SERVER) + DupString(tmp2, tmp); + if ((tmp = getfield(NULL)) == NULL) + break; + Class(aconf) = find_class(atoi(tmp)); + /* the following are only used for Y: */ + if ((tmp3 = getfield(NULL)) == NULL) + break; + tmp4 = getfield(NULL); + break; + } + istat.is_confmem += aconf->host ? strlen(aconf->host)+1 : 0; + istat.is_confmem += aconf->passwd ? strlen(aconf->passwd)+1 :0; + istat.is_confmem += aconf->name ? strlen(aconf->name)+1 : 0; + + /* + ** Bounce line fields are mandatory + */ + if (aconf->status == CONF_BOUNCE && aconf->port == 0) + continue; + /* + ** If conf line is a class definition, create a class entry + ** for it and make the conf_line illegal and delete it. + */ + if (aconf->status & CONF_CLASS) + { + if (atoi(aconf->host) >= 0) + add_class(atoi(aconf->host), + atoi(aconf->passwd), + atoi(aconf->name), aconf->port, + tmp ? atoi(tmp) : 0, +/* tmp3 ? atoi(tmp3) : 0, +** tmp3 && index(tmp3, '.') ? +** atoi(index(tmp3, '.') + 1) : 0, +** the next 3 lines should be replaced by the previous sometime in the +** future. It is only kept for "backward" compatibility and not needed, +** but I'm in good mood today -krys +*/ + tmp3 ? atoi(tmp3) : (atoi(aconf->name) > 0) ? atoi(aconf->name) : 0, + tmp3 && index(tmp3, '.') ? + atoi(index(tmp3, '.') + 1) : (atoi(aconf->name) < 0) ? -1 * atoi(aconf->name) : 0, +/* end of backward compatibility insanity */ + tmp4 ? atoi(tmp4) : 0, + tmp4 && index(tmp4, '.') ? + atoi(index(tmp4, '.') + 1) : 0); + continue; + } + /* + ** associate each conf line with a class by using a pointer + ** to the correct class record. -avalon + */ + if (aconf->status & (CONF_CLIENT_MASK|CONF_LISTEN_PORT)) + { + if (Class(aconf) == 0) + Class(aconf) = find_class(0); + if (MaxLinks(Class(aconf)) < 0) + Class(aconf) = find_class(0); + } + if (aconf->status & (CONF_LISTEN_PORT|CONF_CLIENT)) + { + aConfItem *bconf; + + if ((bconf = find_conf_entry(aconf, aconf->status))) + { + delist_conf(bconf); + bconf->status &= ~CONF_ILLEGAL; + if (aconf->status == CONF_CLIENT) + { + bconf->class->links -= bconf->clients; + bconf->class = aconf->class; + bconf->class->links += bconf->clients; + } + free_conf(aconf); + aconf = bconf; + } + else if (aconf->host && + aconf->status == CONF_LISTEN_PORT) + (void)add_listener(aconf); + } + if (aconf->status & CONF_SERVICE) + aconf->port &= SERVICE_MASK_ALL; + if (aconf->status & (CONF_SERVER_MASK|CONF_SERVICE)) + if (ncount > MAXCONFLINKS || ccount > MAXCONFLINKS || + !aconf->host || index(aconf->host, '*') || + index(aconf->host,'?') || !aconf->name) + continue; + + if (aconf->status & + (CONF_SERVER_MASK|CONF_LOCOP|CONF_OPERATOR|CONF_SERVICE)) + if (!index(aconf->host, '@') && *aconf->host != '/') + { + char *newhost; + int len = 3; /* *@\0 = 3 */ + + len += strlen(aconf->host); + newhost = (char *)MyMalloc(len); + SPRINTF(newhost, "*@%s", aconf->host); + MyFree(aconf->host); + aconf->host = newhost; + istat.is_confmem += 2; + } + if (aconf->status & CONF_SERVER_MASK) + { + if (BadPtr(aconf->passwd)) + continue; + else if (!(opt & BOOT_QUICK)) + (void)lookup_confhost(aconf); + } + if (aconf->status & (CONF_CONNECT_SERVER | CONF_ZCONNECT_SERVER)) + { + aconf->ping = (aCPing *)MyMalloc(sizeof(aCPing)); + bzero((char *)aconf->ping, sizeof(*aconf->ping)); + istat.is_confmem += sizeof(*aconf->ping); + if (tmp2 && index(tmp2, '.')) + aconf->ping->port = atoi(index(tmp2, '.') + 1); + else + aconf->ping->port = aconf->port; + if (tmp2) + { + MyFree(tmp2); + tmp2 = NULL; + } + + } + /* + ** Name cannot be changed after the startup. + ** (or could be allowed, but only if all links are closed + ** first). + ** Configuration info does not override the name and port + ** if previously defined. Note, that "info"-field can be + ** changed by "/rehash". + */ + if (aconf->status == CONF_ME) + { + if (me.info != DefInfo) + MyFree(me.info); + me.info = MyMalloc(REALLEN+1); + strncpyzt(me.info, aconf->name, REALLEN+1); + if (ME[0] == '\0' && aconf->host[0]) + strncpyzt(ME, aconf->host, + sizeof(ME)); + if (aconf->port) + setup_ping(aconf); + } + (void)collapse(aconf->host); + (void)collapse(aconf->name); + Debug((DEBUG_NOTICE, + "Read Init: (%d) (%s) (%s) (%s) (%d) (%d)", + aconf->status, aconf->host, aconf->passwd, + aconf->name, aconf->port, + aconf->class ? ConfClass(aconf) : 0)); + + if (aconf->status & (CONF_KILL|CONF_OTHERKILL)) + { + aconf->next = kconf; + kconf = aconf; + } + else + { + aconf->next = conf; + conf = aconf; + } + aconf = NULL; + } + if (aconf) + free_conf(aconf); + (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */ + (void)close(fd); +#if defined(M4_PREPROC) && !defined(USE_IAUTH) + (void)wait(0); +#endif + check_class(); + nextping = nextconnect = timeofday; + return 0; +} + +/* + * lookup_confhost + * Do (start) DNS lookups of all hostnames in the conf line and convert + * an IP addresses in a.b.c.d number for to IP#s. + */ +static int lookup_confhost(aconf) +Reg aConfItem *aconf; +{ + Reg char *s; + Reg struct hostent *hp; + Link ln; + + if (BadPtr(aconf->host) || BadPtr(aconf->name)) + goto badlookup; + if ((s = index(aconf->host, '@'))) + s++; + else + s = aconf->host; + /* + ** Do name lookup now on hostnames given and store the + ** ip numbers in conf structure. + */ + if (!isalpha(*s) && !isdigit(*s)) + goto badlookup; + + /* + ** Prepare structure in case we have to wait for a + ** reply which we get later and store away. + */ + ln.value.aconf = aconf; + ln.flags = ASYNC_CONF; + + if (isdigit(*s)) +#ifdef INET6 + if(!inet_pton(AF_INET6, s, aconf->ipnum.s6_addr)) + bcopy(minus_one, aconf->ipnum.s6_addr, IN6ADDRSZ); +#else + aconf->ipnum.s_addr = inetaddr(s); +#endif + else if ((hp = gethost_byname(s, &ln))) + bcopy(hp->h_addr, (char *)&(aconf->ipnum), + sizeof(struct IN_ADDR)); + +#ifdef INET6 + if (AND16(aconf->ipnum.s6_addr) == 255) +#else + if (aconf->ipnum.s_addr == -1) +#endif + goto badlookup; + return 0; +badlookup: +#ifdef INET6 + if (AND16(aconf->ipnum.s6_addr) == 255) +#else + if (aconf->ipnum.s_addr == -1) +#endif + bzero((char *)&aconf->ipnum, sizeof(struct IN_ADDR)); + Debug((DEBUG_ERROR,"Host/server name error: (%s) (%s)", + aconf->host, aconf->name)); + return -1; +} + +int find_kill(cptr, doall, comment) +aClient *cptr; +int doall; +char **comment; +{ + static char reply[256]; + char *host, *ip, *name, *ident, *check; + aConfItem *tmp; + int now; + + if (!cptr->user) + return 0; + + host = cptr->sockhost; +#ifdef INET6 + ip = (char *) inetntop(AF_INET6, (char *)&cptr->ip, mydummy, + MYDUMMY_SIZE); +#else + ip = (char *) inetntoa((char *)&cptr->ip); +#endif + if (!strcmp(host, ip)) + ip = NULL; /* we don't have a name for the ip# */ + name = cptr->user->username; + ident = cptr->auth; + + if (strlen(host) > (size_t) HOSTLEN || + (name ? strlen(name) : 0) > (size_t) HOSTLEN) + return (0); + + *reply = '\0'; + + for (tmp = kconf; tmp; tmp = tmp->next) + { + if (!doall && (BadPtr(tmp->passwd) || !isdigit(*tmp->passwd))) + continue; + if (!(tmp->status & (CONF_KILL | CONF_OTHERKILL))) + continue; /* should never happen with kconf */ + if (!tmp->host || !tmp->name) + continue; + if (tmp->status == CONF_KILL) + check = name; + else + check = ident; + /* host & IP matching.. */ + if (!ip) /* unresolved */ + { + if (strchr(tmp->host, '/')) + { + if (match_ipmask((*tmp->host == '=') ? + tmp->host+1: tmp->host, cptr)) + continue; + } + else + if (match((*tmp->host == '=') ? tmp->host+1 : + tmp->host, host)) + continue; + } + else if (*tmp->host == '=') /* numeric only */ + continue; + else /* resolved */ + if (strchr(tmp->host, '/')) + { + if (match_ipmask(tmp->host, cptr)) + continue; + } + else + if (match(tmp->host, ip) && + match(tmp->host, host)) + continue; + + /* user & port matching */ + if ((!check || match(tmp->name, check) == 0) && + (!tmp->port || (tmp->port == cptr->acpt->port))) + { + now = 0; + if (!BadPtr(tmp->passwd) && isdigit(*tmp->passwd) && + !(now = check_time_interval(tmp->passwd, reply))) + continue; + if (now == ERR_YOUWILLBEBANNED) + tmp = NULL; + break; + } + } + + if (*reply) + sendto_one(cptr, reply, ME, now, cptr->name); + else if (tmp) + sendto_one(cptr, ":%s %d %s :%s%s", ME, + ERR_YOUREBANNEDCREEP, cptr->name, + BadPtr(tmp->passwd) ? + "You are not welcome to this server" : + "You are not welcome to this server: ", + BadPtr(tmp->passwd) ? "" : tmp->passwd); + + if (tmp && !BadPtr(tmp->passwd)) + *comment = tmp->passwd; + else + *comment = NULL; + + return (tmp ? -1 : 0); +} + +/* + * For type stat, check if both name and host masks match. + * Return -1 for match, 0 for no-match. + */ +int find_two_masks(name, host, stat) +char *name, *host; +int stat; +{ + aConfItem *tmp; + + for (tmp = conf; tmp; tmp = tmp->next) + if ((tmp->status == stat) && tmp->host && tmp->name && + (match(tmp->host, host) == 0) && + (match(tmp->name, name) == 0)) + break; + return (tmp ? -1 : 0); +} + +/* + * For type stat, check if name matches and any char from key matches + * to chars in passwd field. + * Return -1 for match, 0 for no-match. + */ +int find_conf_flags(name, key, stat) +char *name, *key; +int stat; +{ + aConfItem *tmp; + int l; + + if (index(key, '/') == NULL) + return 0; + l = ((char *)index(key, '/') - key) + 1; + for (tmp = conf; tmp; tmp = tmp->next) + if ((tmp->status == stat) && tmp->passwd && tmp->name && + (strncasecmp(key, tmp->passwd, l) == 0) && + (match(tmp->name, name) == 0) && + (strpbrk(key + l, tmp->passwd + l))) + break; + return (tmp ? -1 : 0); +} + +#ifdef R_LINES +/* find_restrict works against host/name and calls an outside program + * to determine whether a client is allowed to connect. This allows + * more freedom to determine who is legal and who isn't, for example + * machine load considerations. The outside program is expected to + * return a reply line where the first word is either 'Y' or 'N' meaning + * "Yes Let them in" or "No don't let them in." If the first word + * begins with neither 'Y' or 'N' the default is to let the person on. + * It returns a value of 0 if the user is to be let through -Hoppie + */ +int find_restrict(cptr) +aClient *cptr; +{ + aConfItem *tmp; + char reply[80], temprpl[80]; + char *rplhold = reply, *host, *name, *s; + char rplchar = 'Y'; + int pi[2], rc = 0, n; + + if (!cptr->user) + return 0; + name = cptr->user->username; + host = cptr->sockhost; + Debug((DEBUG_INFO, "R-line check for %s[%s]", name, host)); + + for (tmp = conf; tmp; tmp = tmp->next) + { + if (tmp->status != CONF_RESTRICT || + (tmp->host && host && match(tmp->host, host)) || + (tmp->name && name && match(tmp->name, name))) + continue; + + if (BadPtr(tmp->passwd)) + continue; + + if (pipe(pi) == -1) + { + report_error("Error creating pipe for R-line %s:%s", + &me); + return 0; + } + switch (rc = vfork()) + { + case -1 : + report_error("Error forking for R-line %s:%s", &me); + return 0; + case 0 : + { + Reg int i; + + (void)close(pi[0]); + for (i = 2; i < MAXCONNECTIONS; i++) + if (i != pi[1]) + (void)close(i); + if (pi[1] != 2) + (void)dup2(pi[1], 2); + (void)dup2(2, 1); + if (pi[1] != 2 && pi[1] != 1) + (void)close(pi[1]); + (void)execlp(tmp->passwd, tmp->passwd, name, host, + cptr->username, 0); + _exit(-1); + } + default : + (void)close(pi[1]); + break; + } + *reply = '\0'; + (void)dgets(-1, NULL, 0); /* make sure buffer marked empty */ + while ((n = dgets(pi[0], temprpl, sizeof(temprpl)-1)) > 0) + { + temprpl[n] = '\0'; + if ((s = (char *)index(temprpl, '\n'))) + *s = '\0'; + if (strlen(temprpl) + strlen(reply) < sizeof(reply)-2) + SPRINTF(rplhold,"%s %s", rplhold, temprpl); + else + { + sendto_flag(SCH_ERROR, + "R-line %s/%s: reply too long!", + name, host); + break; + } + } + (void)dgets(-1, NULL, 0); /* make sure buffer marked empty */ + (void)close(pi[0]); + (void)kill(rc, SIGKILL); /* cleanup time */ +#if !defined(USE_IAUTH) + (void)wait(0); +#endif + + rc = 0; + while (*rplhold == ' ') + rplhold++; + rplchar = *rplhold; /* Pull out the yes or no */ + while (*rplhold != ' ') + rplhold++; + while (*rplhold == ' ') + rplhold++; + (void)strcpy(reply,rplhold); + rplhold = reply; + + if ((rc = (rplchar == 'n' || rplchar == 'N'))) + break; + } + if (rc) + { + sendto_one(cptr, ":%s %d %s :Restriction: %s", + ME, ERR_YOUREBANNEDCREEP, cptr->name, reply); + return -1; + } + return 0; +} +#endif + + +/* +** check against a set of time intervals +*/ + +static int check_time_interval(interval, reply) +char *interval, *reply; +{ + struct tm *tptr; + char *p; + int perm_min_hours, perm_min_minutes, + perm_max_hours, perm_max_minutes; + int now, perm_min, perm_max; + + tptr = localtime(&timeofday); + now = tptr->tm_hour * 60 + tptr->tm_min; + + while (interval) + { + p = (char *)index(interval, ','); + if (p) + *p = '\0'; + if (sscanf(interval, "%2d%2d-%2d%2d", + &perm_min_hours, &perm_min_minutes, + &perm_max_hours, &perm_max_minutes) != 4) + { + if (p) + *p = ','; + return(0); + } + if (p) + *(p++) = ','; + perm_min = 60 * perm_min_hours + perm_min_minutes; + perm_max = 60 * perm_max_hours + perm_max_minutes; + /* + ** The following check allows intervals over midnight ... + */ + if ((perm_min < perm_max) + ? (perm_min <= now && now <= perm_max) + : (perm_min <= now || now <= perm_max)) + { + (void)sprintf(reply, + ":%%s %%d %%s :%s %d:%02d to %d:%02d.", + "You are not allowed to connect from", + perm_min_hours, perm_min_minutes, + perm_max_hours, perm_max_minutes); + return(ERR_YOUREBANNEDCREEP); + } + if ((perm_min < perm_max) + ? (perm_min <= now + 5 && now + 5 <= perm_max) + : (perm_min <= now + 5 || now + 5 <= perm_max)) + { + (void)sprintf(reply, ":%%s %%d %%s :%d minute%s%s", + perm_min-now,(perm_min-now)>1?"s ":" ", + "and you will be denied for further access"); + return(ERR_YOUWILLBEBANNED); + } + interval = p; + } + return(0); +} + +/* +** find_bounce +** send a bounce numeric to a client. +** fd is optional, and only makes sense if positive and when cptr is NULL +** fd == -1 : not fd, class is a class number. +** fd == -2 : not fd, class isn't a class number. +*/ +void find_bounce(cptr, class, fd) +aClient *cptr; +int class, fd; + { + Reg aConfItem *aconf; + + for (aconf = conf; aconf; aconf = aconf->next) + { + if (aconf->status != CONF_BOUNCE) + continue; + + if (fd >= 0) + /* + ** early rejection, + ** connection class and hostname are unknown + */ + if (*aconf->host == '\0') + { + char rpl[BUFSIZE]; + + SPRINTF(rpl, rpl_str(RPL_BOUNCE,"unknown"), + aconf->name, aconf->port); + strcat(rpl, "\r\n"); +#ifdef INET6 + sendto(class, rpl, strlen(rpl), 0, 0, 0); +#else + send(class, rpl, strlen(rpl), 0); +#endif + return; + } + else + continue; + + /* fd < 0 */ + /* + ** "too many" type rejection, class is known. + ** check if B line is for a class #, + ** and if it is for a hostname. + */ + if (fd != -2 && + !strchr(aconf->host, '.') && isdigit(*aconf->host)) + { + if (class != atoi(aconf->host)) + continue; + } + else + if (strchr(aconf->host, '/')) + { + if (match_ipmask(aconf->host, cptr)) + continue; + } + else if (match(aconf->host, cptr->sockhost)) + continue; + + sendto_one(cptr, rpl_str(RPL_BOUNCE, cptr->name), aconf->name, + aconf->port); + return; + } + + } + +/* +** find_denied +** for a given server name, make sure no D line matches any of the +** servers currently present on the net. +*/ +aConfItem * +find_denied(name, class) + char *name; + int class; +{ + aConfItem *aconf; + + for (aconf = conf; aconf; aconf = aconf->next) + { + if (aconf->status != CONF_DENY) + continue; + if (!aconf->name) + continue; + if (match(aconf->name, name) && aconf->port != class) + continue; + if (isdigit(*aconf->passwd)) + { + aConfItem *aconf2; + int ck = atoi(aconf->passwd); + + for (aconf2 = conf; aconf2; aconf2 = aconf2->next) + { + if (aconf2->status != CONF_NOCONNECT_SERVER) + continue; + if (!aconf2->class || ConfClass(aconf2) != ck) + continue; + if (find_client(aconf2->host, NULL)) + return aconf2; + } + } + if (aconf->host) + { + aServer *asptr; + + for (asptr = svrtop; asptr; asptr = asptr->nexts) + if (aconf->host && + !match(aconf->host, asptr->bcptr->name)) + return aconf; + } + } + return NULL; +} |