/************************************************************************ * 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 sccsid[] = "@(#)s_conf.c 2.56 02 Apr 1994 (C) 1988 University of Oulu, \ Computing Center and Jarkko Oikarinen"; #endif #include "struct.h" #include "common.h" #include "sys.h" #include "numeric.h" #include #include #include #include #ifdef __hpux #include "inet.h" #endif #if defined(PCS) || defined(AIX) || defined(DYNIXPTX) || defined(SVR3) #include #endif #ifdef R_LINES #include #endif #include "h.h" static int check_time_interval PROTO((char *, char *)); static int lookup_confhost PROTO((aConfItem *)); static int is_comment PROTO((char *)); aConfItem *conf = NULL; aGline *gline = 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; { Reg1 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); } } /* * find the first (best) I line to attach. */ int attach_Iline(cptr, hp, sockhost) aClient *cptr; Reg2 struct hostent *hp; char *sockhost; { Reg1 aConfItem *aconf; Reg3 const char *hname; Reg4 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) 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++]) { (void)strncpy(fullname, hname, sizeof(fullname)-1); 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 (!match(aconf->host, uhost)) goto attach_iline; continue; attach_iline: if (index(uhost, '@')) cptr->flags |= FLAGS_DOID; get_sockhost(cptr, uhost); if (aconf->passwd && !strcmp(aconf->passwd, "ONE")) { for (i = highest_fd; i >= 0; i--) if (local[i] && MyClient(local[i]) && local[i]->ip.s_addr == cptr->ip.s_addr) return -1; /* Already got one with that ip# */ } return attach_conf(cptr, aconf); } return -1; } /* * Find the single N line and return pointer to it (from list). * If more than one then return NULL pointer. */ aConfItem *count_cnlines(lp) Reg1 Link *lp; { Reg1 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 && !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; { Reg1 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); return 0; } else lp = &((*lp)->next); } return -1; } static int is_attached(aconf, cptr) aConfItem *aconf; aClient *cptr; { Reg1 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; { Reg1 Link *lp; if (is_attached(aconf, cptr)) return 1; if (IsIllegal(aconf)) return -1; if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT)) && ConfLinks(aconf) >= ConfMaxLinks(aconf) && ConfMaxLinks(aconf) > 0) return -3; /* Use this for printing error message */ lp = make_link(); 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() { Reg1 aConfItem *aconf; for (aconf = conf; aconf; aconf = aconf->next) if (aconf->status & CONF_ADMIN) break; return (aconf); } aConfItem *find_me() { Reg1 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; const char *name; int statmask; { Reg1 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; { Reg1 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; { Reg1 aConfItem *tmp; char userhost[USERLEN+HOSTLEN+3]; (void)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; { Reg1 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; { Reg1 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) Reg2 Link *lp; char *host; Reg3 int statmask; { Reg1 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; { Reg1 aConfItem *tmp; Reg2 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; { Reg1 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, "ONE") && 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; { Reg1 aConfItem **tmp = &conf, *tmp2; Reg2 aClass *cltmp; Reg1 aClient *acptr; Reg2 int i; int ret = 0, found_g; if (sig == 1) { sendto_ops("Got signal SIGHUP, reloading ircd conf. file"); #ifdef notdef /* fucking crap --dog3 */ 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; } 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 expression trees of connect rules */ if ((tmp2->status & (CONF_CRULEALL|CONF_CRULEAUTO)) && (tmp2->passwd != NULL)) crule_free (&(tmp2->passwd)); 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); } for (i = 0; i <= highest_fd; i++) if ((acptr = local[i]) && !IsMe(acptr)) { if (IsServer(acptr)) { det_confs_butmask(acptr, ~(CONF_HUB|CONF_LEAF|CONF_UWORLD|CONF_ILLEGAL)); (void)attach_confs(acptr, acptr->name, CONF_HUB|CONF_LEAF|CONF_UWORLD); } if (found_g = find_kill(acptr)) { sendto_ops(found_g == -2 ? "G-line active for %s" : "K-line active for %s", get_client_name(acptr,FALSE)); if (exit_client(cptr, acptr, &me, found_g == -2 ? "G-lined" : "K-lined") == CPTR_KILLED) ret = CPTR_KILLED; } #if defined(R_LINES_REHASH) && !defined(R_LINES_OFTEN) if (find_restrict(acptr)) { sendto_ops("Restricting %s, closing lp", get_client_name(acptr,FALSE)); if (exit_client(cptr, acptr,&me,"R-lined") == CPTR_KILLED) ret = CPTR_KILLED; } #endif } 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 th4 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(fork()) { 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", "ircd.m4", 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 } extern char *getfield(); /* ** 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}}; Reg1 char *tmp, *s; int fd, i; char line[512], c[80]; int ccount = 0, ncount = 0; aConfItem *aconf = NULL; static Dlink *d1 = NULL, *d2 = NULL; Debug((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile)); if ((fd = openconf()) == -1) { #ifdef M4_PREPROC (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'; } 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] != ':') { Debug((DEBUG_ERROR, "Bad config line: %s", line)); continue; } if (aconf) free_conf(aconf); aconf = make_conf(); 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 'C': /* Server where I should try to connect */ case 'c': /* in case of lp failures */ ccount++; aconf->status = CONF_CONNECT_SERVER; break; /* Connect rule */ case 'D': aconf->status = CONF_CRULEALL; break; /* Connect rule - autos only */ case 'd': aconf->status = CONF_CRULEAUTO; break; case 'H': /* Hub server line */ case 'h': aconf->status = CONF_HUB; break; case 'I': /* Just plain normal irc client trying */ case 'i': /* to connect me */ aconf->status = CONF_CLIENT; break; case 'K': /* Kill user line on irc.conf */ case 'k': aconf->status = CONF_KILL; 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; case 'T': /* print out different motd's */ case 't': /* based on hostmask */ aconf->status = CONF_TLINES; break; case 'U': /* Underworld server, allowed to hack modes */ case 'u': /* *Every* server on the net must define the same !!! */ aconf->status = CONF_UWORLD; 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 = atoi(tmp); if ((tmp = getfield(NULL)) == NULL) break; Class(aconf) = find_class(atoi(tmp)); break; } /* ** 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) { add_class(atoi(aconf->host), atoi(aconf->passwd), atoi(aconf->name), aconf->port, tmp ? atoi(tmp) : 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; if (bconf->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_SERVER_MASK) 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)) if (!index(aconf->host, '@') && *aconf->host != '/') { char *newhost; int len = 3; /* *@\0 = 3 */ len += strlen(aconf->host); newhost = (char *)MyMalloc(len); (void)sprintf(newhost, "*@%s", aconf->host); MyFree(aconf->host); aconf->host = newhost; } if (aconf->status & CONF_SERVER_MASK) { if (BadPtr(aconf->passwd)) continue; else if (!(opt & BOOT_QUICK)) (void)lookup_confhost(aconf); } /* Create expression tree from connect rule... ** If there's a parsing error, nuke the conf structure */ if (aconf->status & (CONF_CRULEALL | CONF_CRULEAUTO)) { MyFree (aconf->passwd); if ((aconf->passwd = (char *) crule_parse (aconf->name)) == NULL) { free_conf (aconf); aconf = NULL; continue; } } /* ** Own port and 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) { strncpyzt(me.info, aconf->name, sizeof(me.info)); if (me.name[0] == '\0' && aconf->host[0]) strncpyzt(me.name, aconf->host, sizeof(me.name)); if (portnum < 0 && aconf->port >= 0) portnum = aconf->port; } (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, Class(aconf))); 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); #ifdef M4_PREPROC (void)wait(0); #endif check_class(); nextping = nextconnect = now; 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) Reg1 aConfItem *aconf; { Reg2 char *s; Reg3 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)) aconf->ipnum.s_addr = inet_addr(s); else if ((hp = gethost_byname(s, &ln))) bcopy(hp->h_addr, (char *)&(aconf->ipnum), sizeof(struct in_addr)); if (aconf->ipnum.s_addr == -1) goto badlookup; return 0; badlookup: if (aconf->ipnum.s_addr == -1) bzero((char *)&aconf->ipnum, sizeof(struct in_addr)); Debug((DEBUG_ERROR,"Host/server name error: (%s) (%s)", aconf->host, aconf->name)); return -1; } /* find_tline * 30 mar 1995 flux (cmlambertus@ucdavis.edu) * associate a conf struct with a client * * cptr - client in question */ aConfItem *find_tline(cptr) aClient *cptr; { char *host, *name; aConfItem *tmp; if (!cptr->user) return NULL; host = cptr->sockhost; name = cptr->user->username; if (strlen(host) > (size_t)HOSTLEN || (name ? strlen(name) : 0) > (size_t)HOSTLEN) return NULL; /* * if the status is a T line, host and passwd exist * and the client host is a match for the hostmask in * the tline, return the confitem struct pointer -flux */ for (tmp = conf; tmp; tmp = tmp->next) if (tmp->status == CONF_TLINES && tmp->host && tmp->passwd && match(tmp->host, host) == 0) return tmp; return NULL; /* no tline found */ } int find_kill(cptr) aClient *cptr; { char reply[256], *host, *name; aConfItem *tmp; aGline *agline = NULL; if (!cptr->user) return 0; host = cptr->sockhost; name = cptr->user->username; if (strlen(host) > (size_t) HOSTLEN || (name ? strlen(name) : 0) > (size_t) HOSTLEN) return (0); reply[0] = '\0'; for (tmp = conf; tmp; tmp = tmp->next) if ((tmp->status == CONF_KILL) && tmp->host && tmp->name && (match(tmp->host, host) == 0) && (!name || match(tmp->name, name) == 0) && (!tmp->port || (tmp->port == cptr->acpt->port))) /* can short-circuit evaluation - not taking chances cos check_time_interval destroys tmp->passwd - Mmmm */ if (BadPtr(tmp->passwd)) break; else if (is_comment(tmp->passwd)) break; else if (check_time_interval(tmp->passwd, reply)) break; if (reply[0]) sendto_one(cptr, reply, me.name, ERR_YOUREBANNEDCREEP, cptr->name); else if (tmp) if (BadPtr(tmp->passwd)) sendto_one(cptr, ":%s %d %s :*** You are not welcome to this server.", me.name, ERR_YOUREBANNEDCREEP, cptr->name); else #ifdef COMMENT_IS_FILE m_killcomment(cptr,cptr->name, tmp->passwd); #else sendto_one(cptr, ":%s %d %s :*** %s.", me.name, ERR_YOUREBANNEDCREEP, cptr->name, tmp->passwd); #endif /* COMMENT_IS_FILE */ /* find active glines */ else if ((agline = find_gline(host, name, NULL)) && agline->active == GLINE_ACTIVE) sendto_one(cptr, ":%s %d %s :*** %s.", me.name, ERR_YOUREBANNEDCREEP, cptr->name, agline->reason); else agline = NULL; /* if a gline was found, it was inactive */ return (tmp ? -1 : (agline ? -2 : 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)) { sendto_ops("Program missing on R-line %s/%s, ignoring", name, host); continue; } if (pipe(pi) == -1) { report_error("Error creating pipe for R-line %s:%s", &me); return 0; } switch (rc = fork()) { case -1 : report_error("Error forking for R-line %s:%s", &me); return 0; case 0 : { Reg1 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, 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) (void)sprintf(rplhold, "%s %s", rplhold, temprpl); else { sendto_ops("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 */ (void)wait(0); 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.name, ERR_YOUREBANNEDCREEP, cptr->name, reply); return -1; } return 0; } #endif /* ** output the reason for being k lined from a file - Mmmm ** sptr is server ** parv is the sender prefix ** filename is the file that is to be output to the K lined client */ int m_killcomment(sptr, parv, filename) aClient *sptr; char *parv, *filename; { int fd; char line[80]; Reg1 char *tmp; struct stat sb; struct tm *tm; /* * stop NFS hangs...most systems should be able to open a file in * 3 seconds. -avalon (curtesy of wumpus) */ (void)alarm(3); fd = open(filename, O_RDONLY); (void)alarm(0); if (fd == -1) { sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv); sendto_one(sptr, ":%s %d %s :*** You are not welcome to this server.", me.name, ERR_YOUREBANNEDCREEP, parv); return 0; } (void)fstat(fd, &sb); tm = localtime((time_t *)&sb.st_mtime); /* NetBSD needs cast */ (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */ while (dgets(fd, line, sizeof(line)-1) > 0) { if ((tmp = (char *)index(line,'\n'))) *tmp = '\0'; if ((tmp = (char *)index(line,'\r'))) *tmp = '\0'; /* sendto_one(sptr, ":%s %d %s : %s.", me.name, ERR_YOUREBANNEDCREEP, parv,line); */ sendto_one(sptr, rpl_str(RPL_MOTD), me.name, parv, line); } sendto_one(sptr, ":%s %d %s :*** You are not welcome to this server.", me.name, ERR_YOUREBANNEDCREEP, parv); (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */ (void)close(fd); return 0; } /* ** is the K line field an interval or a comment? - Mmmm */ static int is_comment(comment) char *comment; { int i; for (i=0; i '9') ) ) return(1); return(0); } /* ** 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 nowm, perm_min, perm_max; tptr = localtime(&now); nowm = 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 <= nowm && nowm <= perm_max) : (perm_min <= nowm || nowm <= 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 <= nowm + 5 && nowm + 5 <= perm_max) : (perm_min <= nowm + 5 || nowm + 5 <= perm_max)) { (void)sprintf(reply, ":%%s %%d %%s :%d minute%s%s", perm_min-nowm,(perm_min-nowm)>1?"s ":" ", "and you will be denied for further access"); return(ERR_YOUWILLBEBANNED); } interval = p; } return(0); }