X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fs_conf.c;h=7db9ff2e4722e68136e3532d6cc7cc5446b475df;hb=refs%2Fheads%2Fupstream-ssl;hp=772463d07c859692956c8cb2833f146055b68b0e;hpb=093e1bca4fe08bda4246f752b76183ef2559497c;p=ircu2.10.12-pk.git diff --git a/ircd/s_conf.c b/ircd/s_conf.c index 772463d..7db9ff2 100644 --- a/ircd/s_conf.c +++ b/ircd/s_conf.c @@ -16,15 +16,19 @@ * 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. - * - * $Id$ */ -#include "s_conf.h" +/** @file + * @brief ircd configuration file driver + * @version $Id$ + */ +#include "config.h" +#include "s_conf.h" #include "IPcheck.h" #include "class.h" #include "client.h" #include "crule.h" +#include "ircd_features.h" #include "fileio.h" #include "gline.h" #include "hash.h" @@ -32,26 +36,27 @@ #include "ircd_alloc.h" #include "ircd_chattr.h" #include "ircd_log.h" +#include "ircd_reply.h" +#include "ircd_snprintf.h" #include "ircd_string.h" #include "list.h" #include "listener.h" #include "match.h" +#include "motd.h" #include "numeric.h" #include "numnicks.h" #include "opercmds.h" #include "parse.h" #include "res.h" +#include "s_auth.h" #include "s_bsd.h" #include "s_debug.h" #include "s_misc.h" #include "send.h" -#include "sprintf_irc.h" #include "struct.h" -#include "support.h" #include "sys.h" -#include -#include +/* #include -- Now using assert in ircd_log.h */ #include #include #include @@ -61,265 +66,225 @@ #include #include -#ifndef INADDR_NONE -#define INADDR_NONE 0xffffffff -#endif - -struct ConfItem* GlobalConfList = 0; -int GlobalConfCount = 0; -struct MotdItem* motd = NULL; -struct MotdItem* rmotd = NULL; -struct TRecord* tdata = NULL; -struct tm motd_tm; - - -/* - * is the K line field an interval or a comment? - Mmmm - */ -static int is_comment(const char *comment) -{ - unsigned int i; - unsigned int len = strlen(comment); - for (i = 0; i < len; ++i) { - if (!IsKTimeChar(comment[i])) - return 1; - } - return 0; -} - -/* - * check against a set of time intervals +/** Global list of all ConfItem structures. */ +struct ConfItem *GlobalConfList; +/** Count of items in #GlobalConfList. */ +int GlobalConfCount; +/** Global list of service mappings. */ +struct s_map *GlobalServiceMapList; +/** Global list of channel quarantines. */ +struct qline *GlobalQuarantineList; + +/** Current line number in scanner input. */ +int lineno; + +/** Configuration information for #me. */ +struct LocalConf localConf; +/** Global list of connection rules. */ +struct CRuleConf* cruleConfList; +/** Global list of K-lines. */ +struct DenyConf* denyConfList; + +/** Tell a user that they are banned, dumping the message from a file. + * @param sptr Client being rejected + * @param filename Send this file's contents to \a sptr */ -static int check_time_interval(char *interval, char *reply) +static void killcomment(struct Client* sptr, const char* filename) { - struct tm* tptr; - char* p; - int perm_min_hours; - int perm_min_minutes; - int perm_max_hours; - int perm_max_minutes; - int nowm; - int perm_min; - int perm_max; - - tptr = localtime(&CurrentTime); - nowm = tptr->tm_hour * 60 + tptr->tm_min; - - while (interval) { - p = strchr(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)) - { - sprintf_irc(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)) - { - sprintf_irc(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; -} - -/* - * 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 - */ -static void killcomment(struct Client *sptr, char *parv, char *filename) -{ - FBFILE* file = NULL; + FBFILE* file = 0; char line[80]; - char* tmp; struct stat sb; - struct tm* tm; if (NULL == (file = fbopen(filename, "r"))) { - sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv); - sendto_one(sptr, - ":%s %d %s :Connection from your host is refused on this server.", - me.name, ERR_YOUREBANNEDCREEP, parv); + send_reply(sptr, ERR_NOMOTD); + send_reply(sptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, + ":Connection from your host is refused on this server."); return; } fbstat(&sb, file); - tm = localtime((time_t*) &sb.st_mtime); /* NetBSD needs cast */ while (fbgets(line, sizeof(line) - 1, file)) { - if ((tmp = strchr(line, '\n'))) - *tmp = '\0'; - if ((tmp = strchr(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); + char* end = line + strlen(line); + while (end > line) { + --end; + if ('\n' == *end || '\r' == *end) + *end = '\0'; + else + break; + } + send_reply(sptr, RPL_MOTD, line); } - sendto_one(sptr, - ":%s %d %s :Connection from your host is refused on this server.", - me.name, ERR_YOUREBANNEDCREEP, parv); + send_reply(sptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, + ":Connection from your host is refused on this server."); fbclose(file); } -struct ConfItem* make_conf(void) +/** Allocate a new struct ConfItem and link it to #GlobalConfList. + * @return Newly allocated structure. + */ +struct ConfItem* make_conf(int type) { struct ConfItem* aconf; aconf = (struct ConfItem*) MyMalloc(sizeof(struct ConfItem)); assert(0 != aconf); -#ifdef DEBUGMODE ++GlobalConfCount; -#endif - aconf->status = CONF_ILLEGAL; - aconf->clients = 0; - aconf->ipnum.s_addr = INADDR_NONE; - aconf->host = 0; - aconf->passwd = 0; - aconf->name = 0; - aconf->port = 0; - aconf->hold = 0; - aconf->dns_pending = 0; - aconf->confClass = 0; - aconf->next = 0; + memset(aconf, 0, sizeof(struct ConfItem)); + aconf->status = type; + aconf->next = GlobalConfList; + GlobalConfList = aconf; return aconf; } -void delist_conf(struct ConfItem *aconf) -{ - if (aconf == GlobalConfList) - GlobalConfList = GlobalConfList->next; - else { - struct ConfItem *bconf; - - for (bconf = GlobalConfList; aconf != bconf->next; bconf = bconf->next) - ; - bconf->next = aconf->next; - } - aconf->next = NULL; -} - +/** Free a struct ConfItem and any resources it owns. + * @param aconf Item to free. + */ void free_conf(struct ConfItem *aconf) { Debug((DEBUG_DEBUG, "free_conf: %s %s %d", aconf->host ? aconf->host : "*", aconf->name ? aconf->name : "*", - aconf->port)); + aconf->address.port)); if (aconf->dns_pending) delete_resolver_queries(aconf); + MyFree(aconf->username); MyFree(aconf->host); + MyFree(aconf->origin_name); if (aconf->passwd) memset(aconf->passwd, 0, strlen(aconf->passwd)); MyFree(aconf->passwd); MyFree(aconf->name); + MyFree(aconf->hub_limit); MyFree(aconf); -#ifdef DEBUGMODE --GlobalConfCount; -#endif } -/* - * conf_dns_callback - called when resolver query finishes - * if the query resulted in a successful search, hp will contain - * a non-null pointer, otherwise hp will be null. - * if successful save hp in the conf item it was called with +/** Disassociate configuration from the client. + * @param cptr Client to operate on. + * @param aconf ConfItem to detach. + */ +static void detach_conf(struct Client* cptr, struct ConfItem* aconf) +{ + struct SLink** lp; + struct SLink* tmp; + + assert(0 != aconf); + assert(0 != cptr); + assert(0 < aconf->clients); + + lp = &(cli_confs(cptr)); + + while (*lp) { + if ((*lp)->value.aconf == aconf) { + if (aconf->conn_class && (aconf->status & CONF_CLIENT_MASK) && ConfLinks(aconf) > 0) + --ConfLinks(aconf); + + assert(0 < aconf->clients); + if (0 == --aconf->clients && IsIllegal(aconf)) + free_conf(aconf); + tmp = *lp; + *lp = tmp->next; + free_link(tmp); + return; + } + lp = &((*lp)->next); + } +} + +/** Parse a user\@host mask into username and host or IP parts. + * If \a host contains no username part, set \a aconf->username to + * NULL. If the host part of \a host looks like an IP mask, set \a + * aconf->addrbits and \a aconf->address to match. Otherwise, set + * \a aconf->host, and set \a aconf->addrbits to -1. + * @param[in,out] aconf Configuration item to set. + * @param[in] host user\@host mask to parse. + */ +void conf_parse_userhost(struct ConfItem *aconf, char *host) +{ + char *host_part; + unsigned char addrbits; + + MyFree(aconf->username); + MyFree(aconf->host); + host_part = strchr(host, '@'); + if (host_part) { + *host_part = '\0'; + DupString(aconf->username, host); + host_part++; + } else { + aconf->username = NULL; + host_part = host; + } + DupString(aconf->host, host_part); + if (ipmask_parse(aconf->host, &aconf->address.addr, &addrbits)) + aconf->addrbits = addrbits; + else + aconf->addrbits = -1; +} + +/** Copies a completed DNS query into its ConfItem. + * @param vptr Pointer to struct ConfItem for the block. + * @param hp DNS reply, or NULL if the lookup failed. */ -static void conf_dns_callback(void* vptr, struct DNSReply* reply) +static void conf_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name) { struct ConfItem* aconf = (struct ConfItem*) vptr; + assert(aconf); aconf->dns_pending = 0; - if (reply) - memcpy(&aconf->ipnum, reply->hp->h_addr, sizeof(struct in_addr)); + if (addr) + memcpy(&aconf->address.addr, addr, sizeof(aconf->address.addr)); } -/* - * conf_dns_lookup - do a nameserver lookup of the conf host - * if the conf entry is currently doing a ns lookup do nothing, otherwise - * if the lookup returns a null pointer, set the conf dns_pending flag +/** Start a nameserver lookup of the conf host. If the conf entry is + * currently doing a lookup, do nothing. + * @param aconf ConfItem for which to start a request. */ -static struct DNSReply* conf_dns_lookup(struct ConfItem* aconf) +static void conf_dns_lookup(struct ConfItem* aconf) { - struct DNSReply* dns_reply = 0; if (!aconf->dns_pending) { char buf[HOSTLEN + 1]; - struct DNSQuery query; - query.vptr = aconf; - query.callback = conf_dns_callback; - host_from_uh(buf, aconf->host, HOSTLEN); - buf[HOSTLEN] = '\0'; - if (0 == (dns_reply = gethost_byname(buf, &query))) - aconf->dns_pending = 1; + host_from_uh(buf, aconf->host, HOSTLEN); + gethost_byname(buf, conf_dns_callback, aconf); + aconf->dns_pending = 1; } - return dns_reply; } -/* - * 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. +/** Start lookups of all addresses in the conf line. The origin must + * be a numeric IP address. If the remote host field is not an IP + * address, start a DNS lookup for it. + * @param aconf Connection to do lookups for. */ -static void lookup_confhost(struct ConfItem *aconf) +void +lookup_confhost(struct ConfItem *aconf) { - struct DNSReply* reply; - if (EmptyString(aconf->host) || EmptyString(aconf->name)) { Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)", aconf->host, aconf->name)); return; } + if (aconf->origin_name + && !ircd_aton(&aconf->origin.addr, aconf->origin_name)) { + Debug((DEBUG_ERROR, "Origin name error: (%s) (%s)", + aconf->origin_name, aconf->name)); + } /* * Do name lookup now on hostnames given and store the * ip numbers in conf structure. */ - if (IsDigit(*aconf->host)) { - /* - * rfc 1035 sez host names may not start with a digit - * XXX - this has changed code needs to be updated - */ - aconf->ipnum.s_addr = inet_addr(aconf->host); - if (INADDR_NONE == aconf->ipnum.s_addr) { + if (IsIP6Char(*aconf->host)) { + if (!ircd_aton(&aconf->address.addr, aconf->host)) { Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)", - aconf->host, aconf->name)); + aconf->host, aconf->name)); } } - else if ((reply = conf_dns_lookup(aconf))) - memcpy(&aconf->ipnum, reply->hp->h_addr, sizeof(struct in_addr)); + else + conf_dns_lookup(aconf); } -/* - * conf_find_server - find a server by name or hostname - * returns a server conf item pointer if found, 0 otherwise - * - * NOTE: at some point when we don't have to scan the entire - * list it may be cheaper to look for server names and host - * names in separate loops (original code did it that way) +/** Find a server by name or hostname. + * @param name Server name to find. + * @return Pointer to the corresponding ConfItem, or NULL if none exists. */ struct ConfItem* conf_find_server(const char* name) { @@ -340,301 +305,240 @@ struct ConfItem* conf_find_server(const char* name) return 0; } -/* - * conf_eval_crule - evaluate connection rules - * returns the name of the rule triggered if found, 0 otherwise - * - * Evaluate connection rules... If no rules found, allow the - * connect. Otherwise stop with the first true rule (ie: rules - * are ored together. Oper connects are effected only by D - * lines (CRULEALL) not d lines (CRULEAUTO). +/** Evaluate connection rules. + * @param name Name of server to check + * @param mask Filter for CRule types (only consider if type & \a mask != 0). + * @return Name of rule that forbids the connection; NULL if no prohibitions. */ -const char* conf_eval_crule(struct ConfItem* conf) +const char* conf_eval_crule(const char* name, int mask) { - struct ConfItem* rule; - assert(0 != conf); + struct CRuleConf* p = cruleConfList; + assert(0 != name); - for (rule = GlobalConfList; rule; rule = rule->next) { - if ((CONF_CRULEALL == rule->status) && (0 == match(rule->host, conf->name))) { - if (crule_eval(rule->passwd)) - return rule->name; + for ( ; p; p = p->next) { + if (0 != (p->type & mask) && 0 == match(p->hostmask, name)) { + if (crule_eval(p->node)) + return p->rule; } } return 0; } - -/* - * field breakup for ircd.conf file. - */ -static char* getfield(char* newline, char fs) -{ - static char* gfline = NULL; - char* end; - char* field; - - if (newline) - gfline = newline; - - if (gfline == NULL) - return NULL; - - end = field = gfline; - - if (fs != ':') { - if (*end == fs) - ++end; - else - fs = ':'; - } - do { - while (*end != fs) { - if (!*end) { - end = NULL; - break; - } - ++end; - } - } while (end && fs != ':' && *++end != ':' && *end != '\n') ; - - if (end == NULL) { - gfline = NULL; - if ((end = strchr(field, '\n')) == NULL) - end = field + strlen(field); - } - else - gfline = end + 1; - - *end = '\0'; - - return field; -} - -/* - * Remove all conf entries from the client except those which match +/** Remove all conf entries from the client except those which match * the status field mask. + * @param cptr Client to operate on. + * @param mask ConfItem types to keep. */ -void det_confs_butmask(struct Client *cptr, int mask) +void det_confs_butmask(struct Client* cptr, int mask) { - struct SLink *tmp, *tmp2; + struct SLink* link; + struct SLink* next; + assert(0 != cptr); - for (tmp = cptr->confs; tmp; tmp = tmp2) { - tmp2 = tmp->next; - if ((tmp->value.aconf->status & mask) == 0) - detach_conf(cptr, tmp->value.aconf); + for (link = cli_confs(cptr); link; link = next) { + next = link->next; + if ((link->value.aconf->status & mask) == 0) + detach_conf(cptr, link->value.aconf); } } -/* - * validate_hostent - make sure hostnames are valid in a hostent struct - * XXX - this is terrible, what's worse is it used to be in the inner - * loop of scanning all the I:lines --Bleep +/** Find the first (best) Client block to attach. + * @param cptr Client for whom to check rules. + * @return Authorization check result. */ -static int validate_hostent(struct hostent* hp) +enum AuthorizationCheckResult attach_iline(struct Client* cptr) { - char fullname[HOSTLEN + 1]; - int i = 0; - int error = 0; - const char* hname; + struct ConfItem* aconf; - for (hname = hp->h_name; hname; hname = hp->h_aliases[i++]) { - unsigned int fullnamelen = 0; - unsigned int label_count = 0; + assert(0 != cptr); - ircd_strncpy(fullname, hname, HOSTLEN); - fullname[HOSTLEN] = '\0'; - /* - * Disallow a hostname label to contain anything but a [-a-zA-Z0-9]. - * It may not start or end on a '.'. - * A label may not end on a '-', the maximum length of a label is - * 63 characters. - * On top of that (which seems to be the RFC) we demand that the - * top domain does not contain any digits. + for (aconf = GlobalConfList; aconf; aconf = aconf->next) { + if (aconf->status != CONF_CLIENT) + continue; + /* If you change any of this logic, please make corresponding + * changes in conf_debug_iline() below. */ - error = (*hname == '.') ? 1 : 0; /* May not start with a '.' */ - if (!error) { - char *p; - for (p = fullname; *p; ++p, ++fullnamelen) { - if (*p == '.') { - /* Label may not end on '-' and May not end on a '.' */ - if (p[-1] == '-' || p[1] == 0) { - error = 1; - break; - } - label_count = 0; - error = 0; /* Was not top domain */ - continue; - } - if (++label_count > 63) { - /* Label not longer then 63 */ - error = 1; - break; - } - if (*p >= '0' && *p <= '9') { - /* In case this is top domain */ - error = 1; - continue; - } - if (!(*p >= 'a' && *p <= 'z') - && !(*p >= 'A' && *p <= 'Z') && *p != '-') { - error = 1; - break; - } - } - } - if (error) - break; + if (aconf->address.port && aconf->address.port != cli_listener(cptr)->addr.port) + continue; + if (aconf->username && match(aconf->username, cli_username(cptr))) + continue; + if (aconf->host && match(aconf->host, cli_sockhost(cptr))) + continue; + if ((aconf->addrbits >= 0) + && !ipmask_check(&cli_ip(cptr), &aconf->address.addr, aconf->addrbits)) + continue; + if (IPcheck_nr(cptr) > aconf->maximum) + return ACR_TOO_MANY_FROM_IP; + if (aconf->username) + SetFlag(cptr, FLAG_DOID); + return attach_conf(cptr, aconf); } - return (0 == error); + return ACR_NO_AUTHORIZATION; } -/* - * check_limit_and_attach - check client limits and attach I:line +/** Interpret \a client as a client specifier and show which Client + * block(s) match that client. + * + * The client specifier may contain an IP address, hostname, listener + * port, or a combination of those separated by commas. IP addresses + * and hostnamese may be preceded by "username@"; the last given + * username will be used for the match. + * + * @param[in] client Client specifier. + * @return Matching Client block structure. */ -static int check_limit_and_attach(struct Client* cptr, struct ConfItem* aconf) +struct ConfItem *conf_debug_iline(const char *client) { - if (aconf->passwd) { - /* Special case: exactly one digit */ - if (IsDigit(*aconf->passwd) && !aconf->passwd[1]) { - /* - * Refuse connections when there are already - * clients connected with the same IP number - */ - unsigned short nr = *aconf->passwd - '0'; - if (IPcheck_nr(cptr) > nr) - return ACR_TOO_MANY_FROM_IP; /* Already got nr with that ip# */ + struct irc_in_addr address; + struct ConfItem *aconf; + struct DenyConf *deny; + char *sep; + unsigned short listener; + char username[USERLEN+1], hostname[HOSTLEN+1], realname[REALLEN+1]; + + /* Initialize variables. */ + listener = 0; + memset(&address, 0, sizeof(address)); + memset(&username, 0, sizeof(username)); + memset(&hostname, 0, sizeof(hostname)); + memset(&realname, 0, sizeof(realname)); + + /* Parse client specifier. */ + while (*client) { + struct irc_in_addr tmpaddr; + long tmp; + + /* Try to parse as listener port number first. */ + tmp = strtol(client, &sep, 10); + if (tmp && (*sep == '\0' || *sep == ',')) { + listener = tmp; + client = sep + (*sep != '\0'); + continue; + } + + /* Maybe username@ before an IP address or hostname? */ + tmp = strcspn(client, ",@"); + if (client[tmp] == '@') { + if (tmp > USERLEN) + tmp = USERLEN; + ircd_strncpy(username, client, tmp); + /* and fall through */ + client += tmp + 1; } -#ifdef USEONE - else if (0 == strcmp(aconf->passwd, "ONE")) { - int i; - for (i = HighestFd; i > -1; --i) { - if (LocalClientArray[i] && MyUser(LocalClientArray[i]) && - LocalClientArray[i]->ip.s_addr == cptr->ip.s_addr) - return ACR_TOO_MANY_FROM_IP; /* Already got one with that ip# */ + + /* Looks like an IP address? */ + tmp = ircd_aton(&tmpaddr, client); + if (tmp && (client[tmp] == '\0' || client[tmp] == ',')) { + memcpy(&address, &tmpaddr, sizeof(address)); + client += tmp + (client[tmp] != '\0'); + continue; + } + + /* Realname? */ + if (client[0] == '$' && client[1] == 'R') { + client += 2; + for (tmp = 0; *client != '\0' && *client != ',' && tmp < REALLEN; ++client, ++tmp) { + if (*client == '\\') + realname[tmp] = *++client; + else + realname[tmp] = *client; } + continue; } -#endif - } - return attach_conf(cptr, aconf); -} -/* - * Find the first (best) I line to attach. - */ -int attach_iline(struct Client* cptr) -{ - struct ConfItem* aconf; - const char* hname; - int i; - static char uhost[HOSTLEN + USERLEN + 3]; - static char fullname[HOSTLEN + 1]; - struct hostent* hp = 0; - - if (cptr->dns_reply) { - hp = cptr->dns_reply->hp; - if (!validate_hostent(hp)) - hp = 0; + /* Else must be a hostname. */ + tmp = strcspn(client, ","); + if (tmp > HOSTLEN) + tmp = HOSTLEN; + ircd_strncpy(hostname, client, tmp); + client += tmp + (client[tmp] != '\0'); } + + /* Walk configuration to find matching Client block. */ for (aconf = GlobalConfList; aconf; aconf = aconf->next) { if (aconf->status != CONF_CLIENT) continue; - if (aconf->port && aconf->port != cptr->listener->port) + if (aconf->address.port && aconf->address.port != listener) { + fprintf(stdout, "Listener port mismatch: %u != %u\n", aconf->address.port, listener); continue; - if (!aconf->host || !aconf->name) + } + if (aconf->username && match(aconf->username, username)) { + fprintf(stdout, "Username mismatch: %s != %s\n", aconf->username, username); continue; - if (hp) { - for (i = 0, hname = hp->h_name; hname; hname = hp->h_aliases[i++]) { - ircd_strncpy(fullname, hname, HOSTLEN); - fullname[HOSTLEN] = '\0'; - - Debug((DEBUG_DNS, "a_il: %s->%s", cptr->sockhost, fullname)); - - if (strchr(aconf->name, '@')) { - strcpy(uhost, cptr->username); - strcat(uhost, "@"); - } - else - *uhost = '\0'; - strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost)); - uhost[sizeof(uhost) - 1] = 0; - if (0 == match(aconf->name, uhost)) { - if (strchr(uhost, '@')) - cptr->flags |= FLAGS_DOID; - return check_limit_and_attach(cptr, aconf); - } - } } - if (strchr(aconf->host, '@')) { - ircd_strncpy(uhost, cptr->username, sizeof(uhost) - 2); - uhost[sizeof(uhost) - 2] = 0; - strcat(uhost, "@"); + if (aconf->host && match(aconf->host, hostname)) { + fprintf(stdout, "Hostname mismatch: %s != %s\n", aconf->host, hostname); + continue; } - else - *uhost = '\0'; - strncat(uhost, cptr->sock_ip, sizeof(uhost) - 1 - strlen(uhost)); - uhost[sizeof(uhost) - 1] = 0; - if (match(aconf->host, uhost)) + if ((aconf->addrbits >= 0) + && !ipmask_check(&address, &aconf->address.addr, aconf->addrbits)) { + fprintf(stdout, "IP address mismatch: %s != %s\n", aconf->name, ircd_ntoa(&address)); continue; - if (strchr(uhost, '@')) - cptr->flags |= FLAGS_DOID; - - return check_limit_and_attach(cptr, aconf); + } + fprintf(stdout, "Match! username=%s host=%s ip=%s class=%s maxlinks=%u password=%s\n", + (aconf->username ? aconf->username : "(null)"), + (aconf->host ? aconf->host : "(null)"), + (aconf->name ? aconf->name : "(null)"), + ConfClass(aconf), aconf->maximum, + (aconf->passwd ? aconf->passwd : "(null)")); + break; } - return ACR_NO_AUTHORIZATION; -} - -/* - * detach_conf - Disassociate configuration from the client. - */ -int detach_conf(struct Client *cptr, struct ConfItem *aconf) -{ - struct SLink** lp; - struct SLink* tmp; - assert(0 != aconf); - assert(0 != cptr); - assert(0 < aconf->clients); + /* If no authorization, say so and exit. */ + if (!aconf) + { + fprintf(stdout, "No authorization found.\n"); + return NULL; + } - lp = &(cptr->confs); + /* Look for a Kill block with the user's name on it. */ + for (deny = denyConfList; deny; deny = deny->next) { + if (deny->usermask && match(deny->usermask, username)) + continue; + if (deny->realmask && match(deny->realmask, realname)) + continue; + if (deny->bits > 0) { + if (!ipmask_check(&address, &deny->address, deny->bits)) + continue; + } else if (deny->hostmask && match(deny->hostmask, hostname)) + continue; - while (*lp) { - if ((*lp)->value.aconf == aconf) { - if (aconf->confClass && (aconf->status & CONF_CLIENT_MASK) && - ConfLinks(aconf) > 0) - --ConfLinks(aconf); - if (0 == --aconf->clients && IsIllegal(aconf)) - free_conf(aconf); - tmp = *lp; - *lp = tmp->next; - free_link(tmp); - return 0; - } - else - lp = &((*lp)->next); + /* Looks like a match; report it. */ + fprintf(stdout, "Denied! usermask=%s realmask=\"%s\" hostmask=%s (bits=%u)\n", + deny->usermask ? deny->usermask : "(null)", + deny->realmask ? deny->realmask : "(null)", + deny->hostmask ? deny->hostmask : "(null)", + deny->bits); } - return -1; + + return aconf; } +/** Check whether a particular ConfItem is already attached to a + * Client. + * @param aconf ConfItem to search for + * @param cptr Client to check + * @return Non-zero if \a aconf is attached to \a cptr, zero if not. + */ static int is_attached(struct ConfItem *aconf, struct Client *cptr) { struct SLink *lp; - for (lp = cptr->confs; lp; lp = lp->next) + for (lp = cli_confs(cptr); lp; lp = lp->next) { if (lp->value.aconf == aconf) - break; - - return (lp) ? 1 : 0; + return 1; + } + return 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... +/** Associate a specific configuration entry to a *local* client (this + * is the one which used in accepting the connection). Note, that this + * automatically changes the attachment if there was an old one... + * @param cptr Client to attach \a aconf to + * @param aconf ConfItem to attach + * @return Authorization check result. */ -int attach_conf(struct Client *cptr, struct ConfItem *aconf) +enum AuthorizationCheckResult attach_conf(struct Client *cptr, struct ConfItem *aconf) { struct SLink *lp; @@ -642,46 +546,33 @@ int attach_conf(struct Client *cptr, struct ConfItem *aconf) return ACR_ALREADY_AUTHORIZED; if (IsIllegal(aconf)) return ACR_NO_AUTHORIZATION; - if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT)) && + if ((aconf->status & (CONF_OPERATOR | CONF_CLIENT)) && ConfLinks(aconf) >= ConfMaxLinks(aconf) && ConfMaxLinks(aconf) > 0) return ACR_TOO_MANY_IN_CLASS; /* Use this for printing error message */ lp = make_link(); - lp->next = cptr->confs; + lp->next = cli_confs(cptr); lp->value.aconf = aconf; - cptr->confs = lp; + cli_confs(cptr) = lp; ++aconf->clients; if (aconf->status & CONF_CLIENT_MASK) ConfLinks(aconf)++; return ACR_OK; } -struct ConfItem *find_admin(void) +/** Return our LocalConf configuration structure. + * @return A pointer to #localConf. + */ +const struct LocalConf* conf_get_local(void) { - struct ConfItem *aconf; - - for (aconf = GlobalConfList; aconf; aconf = aconf->next) { - if (aconf->status & CONF_ADMIN) - break; - } - return aconf; + return &localConf; } -struct ConfItem* find_me(void) -{ - struct ConfItem* aconf; - for (aconf = GlobalConfList; aconf; aconf = aconf->next) { - if (aconf->status & CONF_ME) - break; - } - return aconf; -} - -/* - * attach_confs_byname - * - * 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:*::*. +/** Attach ConfItems to a client if the name passed matches that for + * the ConfItems or is an exact match for them. + * @param cptr Client getting the ConfItem attachments. + * @param name Filter to match ConfItem::name. + * @param statmask Filter to limit ConfItem::status. + * @return First ConfItem attached to \a cptr. */ struct ConfItem* attach_confs_byname(struct Client* cptr, const char* name, int statmask) @@ -706,8 +597,12 @@ struct ConfItem* attach_confs_byname(struct Client* cptr, const char* name, return first; } -/* - * Added for new access check meLazy +/** Attach ConfItems to a client if the host passed matches that for + * the ConfItems or is an exact match for them. + * @param cptr Client getting the ConfItem attachments. + * @param host Filter to match ConfItem::host. + * @param statmask Filter to limit ConfItem::status. + * @return First ConfItem attached to \a cptr. */ struct ConfItem* attach_confs_byhost(struct Client* cptr, const char* host, int statmask) @@ -731,46 +626,48 @@ struct ConfItem* attach_confs_byhost(struct Client* cptr, const char* host, return first; } -/* - * find a conf entry which matches the hostname and has the same name. +/** Find a ConfItem that has the same name and user+host fields as + * specified. Requires an exact match for \a name. + * @param name Name to match + * @param cptr Client to match against + * @param statmask Filter for ConfItem::status + * @return First found matching ConfItem. */ -struct ConfItem* find_conf_exact(const char* name, const char* user, - const char* host, int statmask) +struct ConfItem* find_conf_exact(const char* name, struct Client *cptr, int statmask) { struct ConfItem *tmp; - char userhost[USERLEN + HOSTLEN + 3]; - - /* - * XXX - buffer overflow possible, unchecked variables - */ - if (user) - sprintf_irc(userhost, "%s@%s", user, host); - else - ircd_strncpy(userhost, host, sizeof(userhost) - 1); for (tmp = GlobalConfList; tmp; tmp = tmp->next) { if (!(tmp->status & statmask) || !tmp->name || !tmp->host || 0 != ircd_strcmp(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)) + if (tmp->username + && (EmptyString(cli_username(cptr)) + || match(tmp->username, cli_username(cptr)))) continue; - if (tmp->status & (CONF_OPERATOR | CONF_LOCOP)) { - if (tmp->clients < MaxLinks(tmp->confClass)) - return tmp; - else + if (tmp->addrbits < 0) + { + if (match(tmp->host, cli_sockhost(cptr))) continue; } - else - return tmp; + else if (!ipmask_check(&cli_ip(cptr), &tmp->address.addr, tmp->addrbits)) + continue; + if ((tmp->status & CONF_OPERATOR) + && (MaxLinks(tmp->conn_class) > 0) + && (tmp->clients >= MaxLinks(tmp->conn_class))) + continue; + return tmp; } return 0; } +/** Find a ConfItem from a list that has a name that matches \a name. + * @param lp List to search in. + * @param name Filter for ConfItem::name field; matches either exactly + * or as a glob. + * @param statmask Filter for ConfItem::status. + * @return First matching ConfItem from \a lp. + */ struct ConfItem* find_conf_byname(struct SLink* lp, const char* name, int statmask) { @@ -791,13 +688,16 @@ struct ConfItem* find_conf_byname(struct SLink* lp, const char* name, return 0; } -/* - * Added for new access check meLazy +/** Find a ConfItem from a list that has a host that matches \a host. + * @param lp List to search in. + * @param host Filter for ConfItem::host field; matches as a glob. + * @param statmask Filter for ConfItem::status. + * @return First matching ConfItem from \a lp. */ struct ConfItem* find_conf_byhost(struct SLink* lp, const char* host, int statmask) { - struct ConfItem* tmp; + struct ConfItem* tmp = NULL; assert(0 != host); if (HOSTLEN < strlen(host)) @@ -814,90 +714,201 @@ struct ConfItem* find_conf_byhost(struct SLink* lp, const char* host, return 0; } -/* - * find_conf_ip - * - * Find a conf line using the IP# stored in it to search upon. - * Added 1/8/92 by Avalon. +/** Find a ConfItem from a list that has an address equal to \a ip. + * @param lp List to search in. + * @param ip Filter for ConfItem::address field; matches exactly. + * @param statmask Filter for ConfItem::status. + * @return First matching ConfItem from \a lp. */ -struct ConfItem* find_conf_byip(struct SLink* lp, const char* ip, +struct ConfItem* find_conf_byip(struct SLink* lp, const struct irc_in_addr* ip, int statmask) { struct ConfItem* tmp; for (; lp; lp = lp->next) { tmp = lp->value.aconf; - if (0 != (tmp->status & statmask)) { - if (0 == memcmp(&tmp->ipnum, ip, sizeof(struct in_addr))) - return tmp; - } + if (0 != (tmp->status & statmask) + && !irc_in_addr_cmp(&tmp->address.addr, ip)) + return tmp; } return 0; } -/* - * find_conf_entry - * - * - looks for a match on all given fields. +/** Free all CRules from #cruleConfList. */ +void conf_erase_crule_list(void) +{ + struct CRuleConf* next; + struct CRuleConf* p = cruleConfList; + + for ( ; p; p = next) { + next = p->next; + crule_free(&p->node); + MyFree(p->hostmask); + MyFree(p->rule); + MyFree(p); + } + cruleConfList = 0; +} + +/** Return #cruleConfList. + * @return #cruleConfList */ -static struct ConfItem *find_conf_entry(struct ConfItem *aconf, - unsigned int mask) +const struct CRuleConf* conf_get_crule_list(void) { - struct ConfItem *bconf; - assert(0 != aconf); + return cruleConfList; +} - mask &= ~CONF_ILLEGAL; +/** Free all deny rules from #denyConfList. */ +void conf_erase_deny_list(void) +{ + struct DenyConf* next; + struct DenyConf* p = denyConfList; + for ( ; p; p = next) { + next = p->next; + MyFree(p->hostmask); + MyFree(p->usermask); + MyFree(p->message); + MyFree(p->realmask); + MyFree(p); + } + denyConfList = 0; +} - for (bconf = GlobalConfList; bconf; bconf = bconf->next) { - if (!(bconf->status & mask) || (bconf->port != aconf->port)) - continue; +/** Return #denyConfList. + * @return #denyConfList + */ +const struct DenyConf* conf_get_deny_list(void) +{ + return denyConfList; +} - if ((EmptyString(bconf->host) && !EmptyString(aconf->host)) || - (EmptyString(aconf->host) && !EmptyString(bconf->host))) - continue; - if (!EmptyString(bconf->host) && 0 != ircd_strcmp(bconf->host, aconf->host)) - continue; +/** Find any existing quarantine for the named channel. + * @param chname Channel name to search for. + * @return Reason for channel's quarantine, or NULL if none exists. + */ +const char* +find_quarantine(const char *chname) +{ + struct qline *qline; - if ((EmptyString(bconf->passwd) && !EmptyString(aconf->passwd)) || - (EmptyString(aconf->passwd) && !EmptyString(bconf->passwd))) - continue; - if (!EmptyString(bconf->passwd) && (!IsDigit(*bconf->passwd) || bconf->passwd[1]) -#ifdef USEONE - && 0 != ircd_strcmp(bconf->passwd, "ONE") -#endif - && 0 != ircd_strcmp(bconf->passwd, aconf->passwd)) - continue; + for (qline = GlobalQuarantineList; qline; qline = qline->next) + if (!ircd_strcmp(qline->chname, chname)) + return qline->reason; + return NULL; +} - if ((EmptyString(bconf->name) && !EmptyString(aconf->name)) || - (EmptyString(aconf->name) && !EmptyString(bconf->name))) - continue; - if (!EmptyString(bconf->name) && 0 != ircd_strcmp(bconf->name, aconf->name)) - continue; - break; +/** Free all qline structs from #GlobalQuarantineList. */ +void clear_quarantines(void) +{ + struct qline *qline; + while ((qline = GlobalQuarantineList)) + { + GlobalQuarantineList = qline->next; + MyFree(qline->reason); + MyFree(qline->chname); + MyFree(qline); } - 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. +/** When non-zero, indicates that a configuration error has been seen in this pass. */ +static int conf_error; +/** When non-zero, indicates that the configuration file was loaded at least once. */ +static int conf_already_read; +extern void yyparse(void); +extern int init_lexer(void); +extern void deinit_lexer(void); + +/** Read configuration file. + * @return Zero on failure, non-zero on success. */ +int read_configuration_file(void) +{ + conf_error = 0; + feature_unmark(); /* unmark all features for resetting later */ + clear_nameservers(); /* clear previous list of DNS servers */ + if (!init_lexer()) + return 0; + yyparse(); + deinit_lexer(); + feature_mark(); /* reset unmarked features */ + conf_already_read = 1; + return 1; +} + +/** Report an error message about the configuration file. + * @param msg The error to report. + */ +void +yyerror(const char *msg) +{ + sendto_opmask_butone(0, SNO_ALL, "Config file parse error line %d: %s", + lineno, msg); + log_write(LS_CONFIG, L_ERROR, 0, "Config file parse error line %d: %s", + lineno, msg); + if (!conf_already_read) + fprintf(stderr, "Config file parse error line %d: %s\n", lineno, msg); + conf_error = 1; +} + +/** Attach CONF_UWORLD items to a server and everything attached to it. */ +static void +attach_conf_uworld(struct Client *cptr) +{ + struct DLink *lp; + + attach_confs_byhost(cptr, cli_name(cptr), CONF_UWORLD); + for (lp = cli_serv(cptr)->down; lp; lp = lp->next) + attach_conf_uworld(lp->value.cptr); +} + +/** Free all memory associated with service mapping \a smap. + * @param smap[in] The mapping to free. + */ +void free_mapping(struct s_map *smap) +{ + struct nick_host *nh, *next; + for (nh = smap->services; nh; nh = next) + { + next = nh->next; + MyFree(nh); + } + MyFree(smap->name); + MyFree(smap->command); + MyFree(smap->prepend); + MyFree(smap); +} + +/** Unregister and free all current service mappings. */ +static void close_mappings(void) +{ + struct s_map *map, *next; + + for (map = GlobalServiceMapList; map; map = next) { + next = map->next; + unregister_mapping(map); + free_mapping(map); + } + GlobalServiceMapList = NULL; +} + +/** Reload the configuration file. + * @param cptr Client that requested rehash (if a signal, &me). + * @param sig Type of rehash (0 = oper-requested, 1 = signal, 2 = + * oper-requested but do not restart resolver) + * @return CPTR_KILLED if any client was K/G-lined because of the + * rehash; otherwise 0. */ int rehash(struct Client *cptr, int sig) { struct ConfItem** tmp = &GlobalConfList; struct ConfItem* tmp2; - struct ConfClass* cltmp; struct Client* acptr; - struct MotdItem* temp; int i; int ret = 0; int found_g = 0; if (1 == sig) - sendto_ops("Got signal SIGHUP, reloading ircd conf. file"); + sendto_opmask_butone(0, SNO_OLDSNO, + "Got signal SIGHUP, reloading ircd conf. file"); while ((tmp2 = *tmp)) { if (tmp2->clients) { @@ -907,625 +918,206 @@ int rehash(struct Client *cptr, int sig) * that it will be deleted when the last client * exits... */ - if (!(tmp2->status & CONF_CLIENT)) { + if (CONF_CLIENT == (tmp2->status & CONF_CLIENT)) + tmp = &tmp2->next; + else { *tmp = tmp2->next; tmp2->next = 0; } - 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)) - MarkDelete(cltmp); + conf_erase_crule_list(); + conf_erase_deny_list(); + motd_clear(); /* * delete the juped nicks list */ clearNickJupes(); - if (sig != 2) - flush_resolver_cache(); + clear_quarantines(); + class_mark_delete(); mark_listeners_closing(); + auth_mark_closing(); + close_mappings(); - if (!conf_init()) /* This calls check_class(), */ - check_class(); /* unless it fails */ + read_configuration_file(); - /* - * make sure that the server listener is re-added so it doesn't get - * closed - */ + if (sig != 2) + restart_resolver(); + + log_reopen(); /* reopen log files */ + + auth_close_unused(); close_listeners(); + class_delete_marked(); /* unless it fails */ /* * Flush out deleted I and P lines although still in use. */ for (tmp = &GlobalConfList; (tmp2 = *tmp);) { - if (!(tmp2->status & CONF_ILLEGAL)) - tmp = &tmp2->next; - else - { + if (CONF_ILLEGAL == (tmp2->status & CONF_ILLEGAL)) { *tmp = tmp2->next; tmp2->next = NULL; if (!tmp2->clients) free_conf(tmp2); } + else + tmp = &tmp2->next; } + for (i = 0; i <= HighestFd; i++) { if ((acptr = LocalClientArray[i])) { assert(!IsMe(acptr)); - if (IsServer(acptr)) { - det_confs_butmask(acptr, - ~(CONF_HUB | CONF_LEAF | CONF_UWORLD | CONF_ILLEGAL)); - attach_confs_byname(acptr, acptr->name, - CONF_HUB | CONF_LEAF | CONF_UWORLD); - } + if (IsServer(acptr)) + det_confs_butmask(acptr, ~(CONF_UWORLD | CONF_ILLEGAL)); + /* Because admin's are getting so uppity about people managing to + * get past K/G's etc, we'll "fix" the bug by actually explaining + * whats going on. + */ if ((found_g = find_kill(acptr))) { - sendto_op_mask(found_g == -2 ? SNO_GLINE : SNO_OPERKILL, - found_g == -2 ? "G-line active for %s" : "K-line active for %s", - get_client_name(acptr, HIDE_IP)); + sendto_opmask_butone(0, found_g == -2 ? SNO_GLINE : SNO_OPERKILL, + found_g == -2 ? "G-line active for %s%s" : + "K-line active for %s%s", + IsUnknown(acptr) ? "Unregistered Client ":"", + get_client_name(acptr, SHOW_IP)); if (exit_client(cptr, acptr, &me, found_g == -2 ? "G-lined" : "K-lined") == CPTR_KILLED) ret = CPTR_KILLED; } } } - /* - * free old motd structs - */ - while (motd) { - temp = motd->next; - MyFree(motd); - motd = temp; - } - while (rmotd) { - temp = rmotd->next; - MyFree(rmotd); - rmotd = temp; - } - /* reload motd files */ - read_tlines(); - rmotd = read_motd(RPATH); - motd = read_motd(MPATH); + + attach_conf_uworld(&me); + return ret; } -/* - * conf_init - * - * Read configuration file. - * - * returns 0, if file cannot be opened - * 1, if file read +/** Read configuration file for the very first time. + * @return Non-zero on success, zero on failure. */ -#define MAXCONFLINKS 150 - - -int conf_init(void) +int init_conf(void) { - static char quotes[9][2] = { - {'b', '\b'}, - {'f', '\f'}, - {'n', '\n'}, - {'r', '\r'}, - {'t', '\t'}, - {'v', '\v'}, - {'\\', '\\'}, - {0, 0} - }; - char *tmp, *s; - FBFILE *file; - int i; - char line[512]; - int ccount = 0; - struct ConfItem *aconf = 0; - - Debug((DEBUG_DEBUG, "conf_init: ircd.conf = %s", configfile)); - if (0 == (file = fbopen(configfile, "r"))) { - return 0; - } - while (fbgets(line, sizeof(line) - 1, file)) { - if ((tmp = strchr(line, '\n'))) - *tmp = '\0'; + if (read_configuration_file()) { /* - * Do quoting of characters and # detection. + * make sure we're sane to start if the config + * file read didn't get everything we need. + * XXX - should any of these abort the server? + * TODO: add warning messages */ - 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_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 */ - aconf->status = CONF_KILL; - break; - case 'k': /* Kill user line based on IP in ircd.conf */ - aconf->status = CONF_IPKILL; - 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 '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 '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, (aconf->status == CONF_KILL - || aconf->status == CONF_IPKILL) ? '"' : ':')) == 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); - tmp = getfield(NULL, ':'); - if (aconf->status & CONF_ME) { - if (!tmp) { - Debug((DEBUG_FATAL, "Your M: line must have the Numeric, " - "assigned to you by routing-com, behind the port number!\n")); - ircd_log(L_WARNING, "Your M: line must have the Numeric, " - "assigned to you by routing-com, behind the port number!\n"); - exit(-1); - } - SetYXXServerName(&me, atoi(tmp)); /* Our Numeric Nick */ - } - else if (tmp) - aconf->confClass = 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) { - if (aconf->confClass == 0) - aconf->confClass = find_class(0); - } - if (aconf->status & CONF_LISTEN_PORT) { - int is_server = 0; - int is_hidden = 0; - if (!EmptyString(aconf->name)) { - const char* x = aconf->name; - if ('S' == ToUpper(*x)) - is_server = 1; - ++x; - if ('H' == ToUpper(*x)) - is_hidden = 1; - } - add_listener(aconf->port, aconf->passwd, aconf->host, - is_server, is_hidden); - continue; - } - if (aconf->status & CONF_CLIENT) { - struct ConfItem *bconf; - - if ((bconf = find_conf_entry(aconf, aconf->status))) { - delist_conf(bconf); - bconf->status &= ~CONF_ILLEGAL; - if (aconf->status == CONF_CLIENT) { - /* - * copy the password field in case it changed - */ - MyFree(bconf->passwd); - bconf->passwd = aconf->passwd; - aconf->passwd = 0; - - ConfLinks(bconf) -= bconf->clients; - bconf->confClass = aconf->confClass; - if (bconf->confClass) - ConfLinks(bconf) += bconf->clients; - } - free_conf(aconf); - aconf = bconf; - } - } - if (aconf->status & CONF_SERVER) { - if (ccount > MAXCONFLINKS || !aconf->host || strchr(aconf->host, '*') || - strchr(aconf->host, '?') || !aconf->name) - continue; - } - if (aconf->status & (CONF_LOCOP | CONF_OPERATOR)) { - if (!strchr(aconf->host, '@')) { - char* newhost; - int len = 3; /* *@\0 = 3 */ - - len += strlen(aconf->host); - newhost = (char*) MyMalloc(len); - assert(0 != newhost); - sprintf_irc(newhost, "*@%s", aconf->host); - MyFree(aconf->host); - aconf->host = newhost; - } - } - if (aconf->status & CONF_SERVER) { - if (EmptyString(aconf->passwd)) - continue; - 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; - } - } + if (0 == localConf.name || 0 == localConf.numeric) + return 0; + if (conf_error) + return 0; - /* - * 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) { - ircd_strncpy(me.info, aconf->name, REALLEN); - } + if (0 == localConf.location1) + DupString(localConf.location1, ""); + if (0 == localConf.location2) + DupString(localConf.location2, ""); + if (0 == localConf.contact) + DupString(localConf.contact, ""); - /* - * Juped nicks are listed in the 'password' field of U:lines, - * the list is comma separated and might be empty and/or contain - * empty elements... the only limit is that it MUST be shorter - * than 512 chars, or they will be cutted out :) - */ - if ((aconf->status == CONF_UWORLD) && (aconf->passwd) && (*aconf->passwd)) - addNickJupes(aconf->passwd); - - if (aconf->status & CONF_ADMIN) { - if (!aconf->host || !aconf->passwd || !aconf->name) { - Debug((DEBUG_FATAL, "Your A: line must have 4 fields!\n")); - ircd_log(L_WARNING, "Your A: line must have 4 fields!\n"); - exit(-1); - } - } - collapse(aconf->host); - collapse(aconf->name); - Debug((DEBUG_NOTICE, - "Read Init: (%d) (%s) (%s) (%s) (%u) (%p)", - aconf->status, aconf->host, aconf->passwd, - aconf->name, aconf->port, aconf->confClass)); - aconf->next = GlobalConfList; - GlobalConfList = aconf; - aconf = NULL; + return 1; } - if (aconf) - free_conf(aconf); - fbclose(file); - check_class(); - nextping = nextconnect = CurrentTime; - return 1; + return 0; } -/* read_tlines - * Read info from T:lines into TRecords which include the file - * timestamp, the hostmask, and the contents of the motd file - * -Ghostwolf 7sep97 +/** Searches for a K/G-line for a client. If one is found, notify the + * user and disconnect them. + * @param cptr Client to search for. + * @return 0 if client is accepted; -1 if client was locally denied + * (K-line); -2 if client was globally denied (G-line). */ -void read_tlines() -{ - struct ConfItem *tmp; - struct TRecord *temp; - struct TRecord *last = NULL; /* Init. to avoid compiler warning */ - struct MotdItem *amotd; - - /* Free the old trecords and the associated motd contents first */ - while (tdata) - { - last = tdata->next; - while (tdata->tmotd) - { - amotd = tdata->tmotd->next; - MyFree(tdata->tmotd); - tdata->tmotd = amotd; - } - MyFree(tdata); - tdata = last; - } - - for (tmp = GlobalConfList; tmp; tmp = tmp->next) { - if (tmp->status == CONF_TLINES && tmp->host && tmp->passwd) { - temp = (struct TRecord*) MyMalloc(sizeof(struct TRecord)); - assert(0 != temp); - - temp->hostmask = tmp->host; - temp->tmotd = read_motd(tmp->passwd); - temp->tmotd_tm = motd_tm; - temp->next = NULL; - if (!tdata) - tdata = temp; - else - last->next = temp; - last = temp; - } - } -} - int find_kill(struct Client *cptr) { - char reply[256]; const char* host; const char* name; - struct ConfItem* tmp; + const char* realname; + struct DenyConf* deny; struct Gline* agline = NULL; assert(0 != cptr); - if (!cptr->user) + if (!cli_user(cptr)) return 0; - host = cptr->sockhost; - name = cptr->user->username; + host = cli_sockhost(cptr); + name = cli_user(cptr)->username; + realname = cli_info(cptr); -#if 0 - /* - * whee :) - * XXX - if this ever happens, we're already screwed - */ - if (strlen(host) > HOSTLEN || - (name ? strlen(name) : 0) > HOSTLEN) - return (0); -#endif + assert(strlen(host) <= HOSTLEN); + assert((name ? strlen(name) : 0) <= HOSTLEN); + assert((realname ? strlen(realname) : 0) <= REALLEN); - reply[0] = '\0'; + /* 2000-07-14: Rewrote this loop for massive speed increases. + * -- Isomer + */ + for (deny = denyConfList; deny; deny = deny->next) { + if (deny->usermask && match(deny->usermask, name)) + continue; + if (deny->realmask && match(deny->realmask, realname)) + continue; + if (deny->bits > 0) { + if (!ipmask_check(&cli_ip(cptr), &deny->address, deny->bits)) + continue; + } else if (deny->hostmask && match(deny->hostmask, host)) + continue; - for (tmp = GlobalConfList; tmp; tmp = tmp->next) { - /* Added a check against the user's IP address as well. - * If the line is either CONF_KILL or CONF_IPKILL, check it; if and only - * if it's CONF_IPKILL, check the IP address as well (the && below will - * short circuit and the match won't even get run) -Kev - */ - if ((tmp->status & CONF_KLINE) && tmp->host && tmp->name && - (match(tmp->host, host) == 0 || - ((tmp->status == CONF_IPKILL) && - match(tmp->host, ircd_ntoa((const char*) &cptr->ip)) == 0)) && - (!name || match(tmp->name, name) == 0) && - (!tmp->port || (tmp->port == cptr->listener->port))) - { - /* - * Can short-circuit evaluation - not taking chances - * because check_time_interval destroys tmp->passwd - * - Mmmm - */ - if (EmptyString(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 (EmptyString(tmp->passwd)) - sendto_one(cptr, - ":%s %d %s :Connection from your host is refused on this server.", - me.name, ERR_YOUREBANNEDCREEP, cptr->name); + if (EmptyString(deny->message)) + send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, + ":Connection from your host is refused on this server."); else { - if (*tmp->passwd == '"') { - char *sbuf = - sprintf_irc(sendbuf, ":%s %d %s :%s", me.name, ERR_YOUREBANNEDCREEP, - cptr->name, &tmp->passwd[1]); - sbuf[-1] = '.'; /* Overwrite last quote with a dot */ - sendbufto_one(cptr); - } - else if (*tmp->passwd == '!') - killcomment(cptr, cptr->name, &tmp->passwd[1]); + if (deny->flags & DENY_FLAGS_FILE) + killcomment(cptr, deny->message); else -#ifdef COMMENT_IS_FILE - killcomment(cptr, cptr->name, tmp->passwd); -#else - sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP, - cptr->name, tmp->passwd); -#endif + send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", deny->message); } + return -1; } - /* find active glines */ - /* added a check against the user's IP address to find_gline() -Kev */ - /* FIX ME KEV! gline_find take char *userhost not struct Client! -GW */ - else if ((agline = gline_find(cptr, 0)) && GlineIsActive(agline)) - sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP, - cptr->name, GlineReason(agline)); - else - agline = NULL; /* if a gline was found, it was inactive */ - - return (tmp ? -1 : (agline ? -2 : 0)); -} - -struct MotdItem* read_motd(const char* motdfile) -{ - FBFILE* file = NULL; - struct MotdItem* temp; - struct MotdItem* newmotd; - struct MotdItem* last; - struct stat sb; - char line[80]; - char* tmp; - - if (NULL == (file = fbopen(motdfile, "r"))) { - Debug((DEBUG_ERROR, "Couldn't open \"%s\": %s", motdfile, strerror(errno))); - return NULL; - } - if (-1 == fbstat(&sb, file)) { - fbclose(file); - return NULL; - } - newmotd = last = NULL; - motd_tm = *localtime((time_t *) & sb.st_mtime); /* NetBSD needs cast */ - while (fbgets(line, sizeof(line) - 1, file)) { - if ((tmp = (char *)strchr(line, '\n'))) - *tmp = '\0'; - if ((tmp = (char *)strchr(line, '\r'))) - *tmp = '\0'; - temp = (struct MotdItem*) MyMalloc(sizeof(struct MotdItem)); - assert(0 != temp); - strcpy(temp->line, line); - temp->next = NULL; - if (!newmotd) - newmotd = temp; - else - last->next = temp; - last = temp; + if (!feature_bool(FEAT_DISABLE_GLINES) && (agline = gline_lookup(cptr, 0))) { + /* + * find active glines + * added a check against the user's IP address to find_gline() -Kev + */ + send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", GlineReason(agline)); + return -2; } - fbclose(file); - return newmotd; -} + return 0; +} -/* - * Ordinary client access check. Look for conf lines which have the same - * status as the flags passed. +/** Attempt to attach Client blocks to \a cptr. If attach_iline() + * fails for the client, emit a debugging message. + * @param cptr Client to check for access. + * @return Access check result. */ enum AuthorizationCheckResult conf_check_client(struct Client *cptr) { - int acr; - - ClearAccess(cptr); + enum AuthorizationCheckResult acr = ACR_OK; if ((acr = attach_iline(cptr))) { Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]", - cptr->name, cptr->sockhost)); + cli_name(cptr), cli_sockhost(cptr))); return acr; } return ACR_OK; } -/* - * check_server() - * - * Check access for a server given its name (passed in cptr struct). +/** Check access for a server given its name (passed in cptr struct). * Must check for all C/N lines which have a name which matches the * name given and a host which matches. A host alias which is the * same as the server name is also acceptable in the host field of a * C/N line. - * - * Returns - * 0 = Success - * -1 = Access denied - * -2 = Bad socket. + * @param cptr Peer server to check. + * @return 0 if accepted, -1 if access denied. */ int conf_check_server(struct Client *cptr) { @@ -1533,90 +1125,62 @@ int conf_check_server(struct Client *cptr) struct SLink* lp; Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", - cptr->name, cptr->sockhost)); + cli_name(cptr), cli_sockhost(cptr))); - if (IsUnknown(cptr) && !attach_confs_byname(cptr, cptr->name, CONF_SERVER)) { - Debug((DEBUG_DNS, "No C/N lines for %s", cptr->sockhost)); + if (IsUnknown(cptr) && !attach_confs_byname(cptr, cli_name(cptr), CONF_SERVER)) { + Debug((DEBUG_DNS, "No C/N lines for %s", cli_sockhost(cptr))); return -1; } - lp = cptr->confs; + lp = cli_confs(cptr); /* * We initiated this connection so the client should have a C and N * line already attached after passing through the connect_server() * function earlier. */ if (IsConnecting(cptr) || IsHandshake(cptr)) { - c_conf = find_conf_byname(lp, cptr->name, CONF_SERVER); + c_conf = find_conf_byname(lp, cli_name(cptr), CONF_SERVER); if (!c_conf) { - sendto_ops("Connect Error: lost C:line for %s", cptr->name); + sendto_opmask_butone(0, SNO_OLDSNO, + "Connect Error: lost Connect block for %s", + cli_name(cptr)); det_confs_butmask(cptr, 0); return -1; } } - ClearAccess(cptr); + /* Try finding the Connect block by DNS name and IP next. */ + if (!c_conf && !(c_conf = find_conf_byhost(lp, cli_sockhost(cptr), CONF_SERVER))) + c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER); - if (!c_conf) { - if (cptr->dns_reply) { - int i; - struct hostent* hp = cptr->dns_reply->hp; - const char* name = hp->h_name; - /* - * If we are missing a C or N line from above, search for - * it under all known hostnames we have for this ip#. - */ - for (i = 0; name; name = hp->h_aliases[i++]) { - if ((c_conf = find_conf_byhost(lp, name, CONF_SERVER))) { - ircd_strncpy(cptr->sockhost, name, HOSTLEN); - break; - } - } - if (!c_conf) { - for (i = 0; hp->h_addr_list[i]; i++) { - if ((c_conf = find_conf_byip(lp, hp->h_addr_list[i], CONF_SERVER))) - break; - } - } - } - else { - /* - * Check for C lines with the hostname portion the ip number - * of the host the server runs on. This also checks the case where - * there is a server connecting from 'localhost'. - */ - c_conf = find_conf_byhost(lp, cptr->sockhost, CONF_SERVER); - } - } /* * Attach by IP# only if all other checks have failed. * It is quite possible to get here with the strange things that can * happen when using DNS in the way the irc server does. -avalon */ if (!c_conf) - c_conf = find_conf_byip(lp, (const char*) &cptr->ip, CONF_SERVER); + c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER); /* * detach all conf lines that got attached by attach_confs() */ det_confs_butmask(cptr, 0); /* - * if no C or no N lines, then deny access + * if no Connect block, then deny access */ if (!c_conf) { Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s]", - cptr->name, cptr->username, cptr->sockhost)); + cli_name(cptr), cli_username(cptr), cli_sockhost(cptr))); return -1; } - ircd_strncpy(cptr->name, c_conf->name, HOSTLEN); /* - * attach the C and N lines to the client structure for later use. + * attach the Connect block to the client structure for later use. */ attach_conf(cptr, c_conf); - attach_confs_byname(cptr, cptr->name, CONF_HUB | CONF_LEAF | CONF_UWORLD); - if (INADDR_NONE == c_conf->ipnum.s_addr) - c_conf->ipnum.s_addr = cptr->ip.s_addr; + if (!irc_in_addr_valid(&c_conf->address.addr)) + memcpy(&c_conf->address.addr, &cli_ip(cptr), sizeof(c_conf->address.addr)); - Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", cptr->name, cptr->sockhost)); + Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", + cli_name(cptr), cli_sockhost(cptr))); return 0; }