X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fs_conf.c;h=7db9ff2e4722e68136e3532d6cc7cc5446b475df;hb=refs%2Fheads%2Fupstream;hp=b9498824c78eb8a5adb3412b3ecbc67abfd09901;hpb=f978f05748e565ff9230742dcfd478dcfad67340;p=ircu2.10.12-pk.git diff --git a/ircd/s_conf.c b/ircd/s_conf.c index b949882..7db9ff2 100644 --- a/ircd/s_conf.c +++ b/ircd/s_conf.c @@ -17,1541 +17,1170 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/** @file + * @brief ircd configuration file driver + * @version $Id$ + */ +#include "config.h" -#include "sys.h" -#include -#if HAVE_FCNTL_H -#include -#endif - -#if HAVE_SYS_WAIT_H -# include -#endif -#ifndef WEXITSTATUS -# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) -#endif -#ifndef WIFEXITED -# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) -#endif - -#include -#ifdef R_LINES -#include -#endif -#if HAVE_UNISTD_H -#include -#endif -#include -#include -#include -#include -#ifdef USE_SYSLOG -#include -#endif -#include "h.h" -#include "struct.h" -#include "s_serv.h" -#include "opercmds.h" -#include "numeric.h" -#include "send.h" #include "s_conf.h" +#include "IPcheck.h" #include "class.h" -#include "s_misc.h" -#include "match.h" -#include "common.h" -#include "s_err.h" -#include "s_bsd.h" -#include "ircd.h" +#include "client.h" #include "crule.h" -#include "res.h" -#include "support.h" -#include "parse.h" -#include "numnicks.h" -#include "sprintf_irc.h" -#include "IPcheck.h" -#include "hash.h" +#include "ircd_features.h" #include "fileio.h" +#include "gline.h" +#include "hash.h" +#include "ircd.h" +#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 "struct.h" +#include "sys.h" -RCSTAG_CC("$Id$"); +/* #include -- Now using assert in ircd_log.h */ +#include +#include +#include +#include +#include +#include +#include +#include -static int check_time_interval(char *, char *); -static int lookup_confhost(aConfItem *); -static int is_comment(char *); -static void killcomment(aClient *sptr, char *parv, char *filename); +/** 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 void killcomment(struct Client* sptr, const char* filename) +{ + FBFILE* file = 0; + char line[80]; + struct stat sb; -aConfItem *conf = NULL; -aGline *gline = NULL; -aGline *badchan = NULL; -aMotdItem *motd = NULL; -aMotdItem *rmotd = NULL; -atrecord *tdata = NULL; -struct tm motd_tm; + if (NULL == (file = fbopen(filename, "r"))) { + 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); + while (fbgets(line, sizeof(line) - 1, file)) { + char* end = line + strlen(line); + while (end > line) { + --end; + if ('\n' == *end || '\r' == *end) + *end = '\0'; + else + break; + } + send_reply(sptr, RPL_MOTD, line); + } + send_reply(sptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, + ":Connection from your host is refused on this server."); + fbclose(file); +} -/* - * field breakup for ircd.conf file. +/** Allocate a new struct ConfItem and link it to #GlobalConfList. + * @return Newly allocated structure. */ -static char *gfline = NULL; -char *getfield(char *newline, char fs) +struct ConfItem* make_conf(int type) { - char *end, *field; + struct ConfItem* aconf; + + aconf = (struct ConfItem*) MyMalloc(sizeof(struct ConfItem)); + assert(0 != aconf); + ++GlobalConfCount; + memset(aconf, 0, sizeof(struct ConfItem)); + aconf->status = type; + aconf->next = GlobalConfList; + GlobalConfList = aconf; + return aconf; +} - if (newline) - gfline = newline; +/** 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->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); + --GlobalConfCount; +} - if (gfline == NULL) - return NULL; +/** 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; - end = field = gfline; + assert(0 != aconf); + assert(0 != cptr); + assert(0 < aconf->clients); - if (fs != ':') - { - if (*end == fs) - ++end; - else - fs = ':'; - } - do - { - while (*end != fs) - { - if (!*end) - { - end = NULL; - break; - } - ++end; + 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); } - while (end && fs != ':' && *++end != ':' && *end != '\n'); +} - if (end == NULL) - { - gfline = NULL; - if ((end = strchr(field, '\n')) == NULL) - end = field + strlen(field); +/** 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 - gfline = end + 1; - - *end = '\0'; + aconf->addrbits = -1; +} - return field; +/** 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, const struct irc_in_addr *addr, const char *h_name) +{ + struct ConfItem* aconf = (struct ConfItem*) vptr; + assert(aconf); + aconf->dns_pending = 0; + if (addr) + memcpy(&aconf->address.addr, addr, sizeof(aconf->address.addr)); } -/* - * Remove all conf entries from the client except those which match - * the status field mask. +/** 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. */ -void det_confs_butmask(aClient *cptr, int mask) +static void conf_dns_lookup(struct ConfItem* aconf) { - Reg1 Link *tmp, *tmp2; + if (!aconf->dns_pending) { + char buf[HOSTLEN + 1]; - for (tmp = cptr->confs; tmp; tmp = tmp2) - { - tmp2 = tmp->next; - if ((tmp->value.aconf->status & mask) == 0) - detach_conf(cptr, tmp->value.aconf); + host_from_uh(buf, aconf->host, HOSTLEN); + gethost_byname(buf, conf_dns_callback, aconf); + aconf->dns_pending = 1; } } -/* - * Find the first (best) I line to attach. + +/** 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. */ -enum AuthorizationCheckResult attach_Iline(aClient *cptr, struct hostent *hp, - char *sockhost) +void +lookup_confhost(struct ConfItem *aconf) { - Reg1 aConfItem *aconf; - Reg3 const char *hname; - Reg4 int i; - static char uhost[HOSTLEN + USERLEN + 3]; - static char fullname[HOSTLEN + 1]; + 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 (IsIP6Char(*aconf->host)) { + if (!ircd_aton(&aconf->address.addr, aconf->host)) { + Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)", + aconf->host, aconf->name)); + } + } + else + conf_dns_lookup(aconf); +} - 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) - continue; - if (hp) - for (i = 0, hname = hp->h_name; hname; hname = hp->h_aliases[i++]) - { - size_t fullnamelen = 0; - size_t label_count = 0; - int error; - - 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. - */ - error = (*hname == '.') ? 1 : 0; /* May not start with a '.' */ - if (!error) - { - char *p; - for (p = fullname; *p; ++p, ++fullnamelen) - { - if (*p == '.') - { - if (p[-1] == '-' /* Label may not end on '-' */ - || p[1] == 0) /* May not end on a '.' */ - { - 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') - { - error = 1; /* In case this is top domain */ - continue; - } - if (!(*p >= 'a' && *p <= 'z') - && !(*p >= 'A' && *p <= 'Z') && *p != '-') - { - error = 1; - break; - } - } - } - if (error) - { - hp = NULL; - break; - } - - add_local_domain(fullname, HOSTLEN - fullnamelen); - Debug((DEBUG_DNS, "a_il: %s->%s", 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 (!match(aconf->name, uhost)) - { - if (strchr(uhost, '@')) - cptr->flags |= FLAGS_DOID; - goto attach_iline; - } - } +/** 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) +{ + struct ConfItem* conf; + assert(0 != name); - if (strchr(aconf->host, '@')) - { - strncpy(uhost, cptr->username, sizeof(uhost) - 2); - uhost[sizeof(uhost) - 2] = 0; - strcat(uhost, "@"); - } - else - *uhost = '\0'; - strncat(uhost, sockhost, sizeof(uhost) - 1 - strlen(uhost)); - uhost[sizeof(uhost) - 1] = 0; - if (match(aconf->host, uhost)) - continue; - if (strchr(uhost, '@')) - cptr->flags |= FLAGS_DOID; - if (hp && hp->h_name) - { - strncpy(uhost, hp->h_name, HOSTLEN); - uhost[HOSTLEN] = 0; - add_local_domain(uhost, HOSTLEN - strlen(uhost)); + for (conf = GlobalConfList; conf; conf = conf->next) { + if (CONF_SERVER == conf->status) { + /* + * Check first servernames, then try hostnames. + * XXX - match returns 0 if there _is_ a match... guess they + * haven't decided what true is yet + */ + if (0 == match(name, conf->name)) + return conf; } - attach_iline: - get_sockhost(cptr, uhost); + } + return 0; +} - if (aconf->passwd) - { - if (isDigit(*aconf->passwd) && !aconf->passwd[1]) /* Special case: exactly one digit */ - { - /* 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# */ - } -#ifdef USEONE - else if (!strcmp(aconf->passwd, "ONE")) - { - for (i = highest_fd; i >= 0; i--) - if (loc_clients[i] && MyUser(loc_clients[i]) && - loc_clients[i]->ip.s_addr == cptr->ip.s_addr) - return ACR_TOO_MANY_FROM_IP; /* Already got one with that ip# */ - } -#endif +/** 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(const char* name, int mask) +{ + struct CRuleConf* p = cruleConfList; + assert(0 != 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 attach_conf(cptr, aconf); } - return ACR_NO_AUTHORIZATION; + return 0; } -/* - * Find the single N line and return pointer to it (from list). - * If more than one then return NULL pointer. +/** 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. */ -aConfItem *count_cnlines(Link *lp) +void det_confs_butmask(struct Client* cptr, int mask) { - Reg1 aConfItem *aconf, *cline = NULL, *nline = NULL; + struct SLink* link; + struct SLink* next; + assert(0 != cptr); + + for (link = cli_confs(cptr); link; link = next) { + next = link->next; + if ((link->value.aconf->status & mask) == 0) + detach_conf(cptr, link->value.aconf); + } +} - for (; lp; lp = lp->next) - { - aconf = lp->value.aconf; - if (!(aconf->status & CONF_SERVER_MASK)) +/** Find the first (best) Client block to attach. + * @param cptr Client for whom to check rules. + * @return Authorization check result. + */ +enum AuthorizationCheckResult attach_iline(struct Client* cptr) +{ + struct ConfItem* aconf; + + assert(0 != cptr); + + 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. + */ + 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->status == CONF_CONNECT_SERVER && !cline) - cline = aconf; - else if (aconf->status == CONF_NOCONNECT_SERVER && !nline) - nline = aconf; + 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 nline; + return ACR_NO_AUTHORIZATION; } -/* - * detach_conf +/** Interpret \a client as a client specifier and show which Client + * block(s) match that client. * - * Disassociate configuration from the 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. */ -int detach_conf(aClient *cptr, aConfItem *aconf) +struct ConfItem *conf_debug_iline(const char *client) { - Reg1 Link **lp, *tmp; + 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; + } - lp = &(cptr->confs); + /* 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; + } - while (*lp) - { - if ((*lp)->value.aconf == aconf) - { - if (aconf && (aconf->confClass) - && (aconf->status & CONF_CLIENT_MASK) && ConfLinks(aconf) > 0) - --ConfLinks(aconf); - if (aconf && !--aconf->clients && IsIllegal(aconf)) - free_conf(aconf); - tmp = *lp; - *lp = tmp->next; - free_link(tmp); - return 0; + /* 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; } - else - lp = &((*lp)->next); + + /* 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->address.port && aconf->address.port != listener) { + fprintf(stdout, "Listener port mismatch: %u != %u\n", aconf->address.port, listener); + continue; + } + if (aconf->username && match(aconf->username, username)) { + fprintf(stdout, "Username mismatch: %s != %s\n", aconf->username, username); + continue; + } + if (aconf->host && match(aconf->host, hostname)) { + fprintf(stdout, "Hostname mismatch: %s != %s\n", aconf->host, hostname); + continue; + } + 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; + } + 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; + } + + /* If no authorization, say so and exit. */ + if (!aconf) + { + fprintf(stdout, "No authorization found.\n"); + return NULL; + } + + /* 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; + + /* 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; } -static int is_attached(aConfItem *aconf, aClient *cptr) +/** 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) { - Reg1 Link *lp; + 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. */ -enum AuthorizationCheckResult attach_conf(aClient *cptr, aConfItem *aconf) +enum AuthorizationCheckResult attach_conf(struct Client *cptr, struct ConfItem *aconf) { - Reg1 Link *lp; + struct SLink *lp; if (is_attached(aconf, cptr)) 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 */ + 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; - aconf->clients++; + cli_confs(cptr) = lp; + ++aconf->clients; if (aconf->status & CONF_CLIENT_MASK) ConfLinks(aconf)++; return ACR_OK; } -aConfItem *find_admin(void) +/** Return our LocalConf configuration structure. + * @return A pointer to #localConf. + */ +const struct LocalConf* conf_get_local(void) { - Reg1 aConfItem *aconf; - - for (aconf = conf; aconf; aconf = aconf->next) - if (aconf->status & CONF_ADMIN) - break; - - return (aconf); + return &localConf; } -aConfItem *find_me(void) +/** 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) { - Reg1 aConfItem *aconf; - for (aconf = conf; aconf; aconf = aconf->next) - if (aconf->status & CONF_ME) - break; + struct ConfItem* tmp; + struct ConfItem* first = NULL; - return (aconf); -} + assert(0 != name); -/* - * 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(aClient *cptr, const char *name, int statmask) -{ - Reg1 aConfItem *tmp; - aConfItem *first = NULL; - int len = strlen(name); + if (HOSTLEN < strlen(name)) + return 0; - 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) == ACR_OK && !first) - first = tmp; - } - else if ((tmp->status & statmask) && !IsIllegal(tmp) && - (tmp->status & (CONF_SERVER_MASK | CONF_HUB)) && - tmp->name && !strCasediff(tmp->name, name)) - { - if (attach_conf(cptr, tmp) == ACR_OK && !first) - first = tmp; + for (tmp = GlobalConfList; tmp; tmp = tmp->next) { + if (0 != (tmp->status & statmask) && !IsIllegal(tmp)) { + assert(0 != tmp->name); + if (0 == match(tmp->name, name) || 0 == ircd_strcmp(tmp->name, name)) { + if (ACR_OK == attach_conf(cptr, tmp) && !first) + first = tmp; + } } } - return (first); + 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. */ -aConfItem *attach_confs_host(aClient *cptr, char *host, int statmask) +struct ConfItem* attach_confs_byhost(struct Client* cptr, const char* host, + int statmask) { - Reg1 aConfItem *tmp; - aConfItem *first = NULL; - int len = strlen(host); + struct ConfItem* tmp; + struct ConfItem* first = 0; - if (!host || len > HOSTLEN) - return NULL; + assert(0 != host); + if (HOSTLEN < strlen(host)) + return 0; - 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) == ACR_OK && !first) - first = tmp; - } - else if ((tmp->status & statmask) && !IsIllegal(tmp) && - (tmp->status & CONF_SERVER_MASK) && - (tmp->host && strCasediff(tmp->host, host) == 0)) - { - if (attach_conf(cptr, tmp) == ACR_OK && !first) - first = tmp; + for (tmp = GlobalConfList; tmp; tmp = tmp->next) { + if (0 != (tmp->status & statmask) && !IsIllegal(tmp)) { + assert(0 != tmp->host); + if (0 == match(tmp->host, host) || 0 == ircd_strcmp(tmp->host, host)) { + if (ACR_OK == attach_conf(cptr, tmp) && !first) + first = tmp; + } } } - return (first); + 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. */ -aConfItem *find_conf_exact(char *name, char *user, char *host, int statmask) +struct ConfItem* find_conf_exact(const char* name, struct Client *cptr, int statmask) { - Reg1 aConfItem *tmp; - char userhost[USERLEN + HOSTLEN + 3]; - - sprintf_irc(userhost, "%s@%s", user, host); + struct ConfItem *tmp; - for (tmp = conf; tmp; tmp = tmp->next) - { + for (tmp = GlobalConfList; tmp; tmp = tmp->next) { if (!(tmp->status & statmask) || !tmp->name || !tmp->host || - strCasediff(tmp->name, name)) + 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->addrbits < 0) { - if (tmp->clients < MaxLinks(tmp->confClass)) - return tmp; - else - continue; + 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 NULL; + return 0; } -aConfItem *find_conf(Link *lp, const char *name, int statmask) +/** 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) { - Reg1 aConfItem *tmp; - int namelen = name ? strlen(name) : 0; + struct ConfItem* tmp; + assert(0 != name); - if (namelen > HOSTLEN) - return (aConfItem *)0; + if (HOSTLEN < strlen(name)) + return 0; - for (; lp; lp = lp->next) - { + for (; lp; lp = lp->next) { tmp = lp->value.aconf; - if ((tmp->status & statmask) && - (((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) && - tmp->name && !strCasediff(tmp->name, name)) || - ((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) == 0 && - tmp->name && !match(tmp->name, name)))) - return tmp; + if (0 != (tmp->status & statmask)) { + assert(0 != tmp->name); + if (0 == ircd_strcmp(tmp->name, name) || 0 == match(tmp->name, name)) + return tmp; + } } - return NULL; + 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. */ -aConfItem *find_conf_host(Link *lp, char *host, int statmask) +struct ConfItem* find_conf_byhost(struct SLink* lp, const char* host, + int statmask) { - Reg1 aConfItem *tmp; - int hostlen = host ? strlen(host) : 0; + struct ConfItem* tmp = NULL; + assert(0 != host); - if (hostlen > HOSTLEN || BadPtr(host)) - return (aConfItem *)NULL; - for (; lp; lp = lp->next) - { + if (HOSTLEN < strlen(host)) + return 0; + + 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; + if (0 != (tmp->status & statmask)) { + assert(0 != tmp->host); + if (0 == match(tmp->host, host)) + return tmp; + } } - return NULL; + 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. */ -aConfItem *find_conf_ip(Link *lp, char *ip, char *user, int statmask) +struct ConfItem* find_conf_byip(struct SLink* lp, const struct irc_in_addr* ip, + int statmask) { - Reg1 aConfItem *tmp; - Reg2 char *s; + struct ConfItem* tmp; - for (; lp; lp = lp->next) - { + for (; lp; lp = lp->next) { tmp = lp->value.aconf; - if (!(tmp->status & statmask)) - continue; - s = strchr(tmp->host, '@'); - *s = '\0'; - if (match(tmp->host, user)) - { - *s = '@'; - continue; - } - *s = '@'; - if (!memcmp(&tmp->ipnum, ip, sizeof(struct in_addr))) + if (0 != (tmp->status & statmask) + && !irc_in_addr_cmp(&tmp->address.addr, ip)) return tmp; } - return NULL; + 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 aConfItem *find_conf_entry(aConfItem *aconf, unsigned int mask) +const struct CRuleConf* conf_get_crule_list(void) { - Reg1 aConfItem *bconf; + return cruleConfList; +} - for (bconf = conf, mask &= ~CONF_ILLEGAL; bconf; bconf = bconf->next) - { - if (!(bconf->status & mask) || (bconf->port != aconf->port)) - continue; +/** 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; +} - if ((BadPtr(bconf->host) && !BadPtr(aconf->host)) || - (BadPtr(aconf->host) && !BadPtr(bconf->host))) - continue; - if (!BadPtr(bconf->host) && strCasediff(bconf->host, aconf->host)) - continue; +/** Return #denyConfList. + * @return #denyConfList + */ +const struct DenyConf* conf_get_deny_list(void) +{ + return denyConfList; +} - if ((BadPtr(bconf->passwd) && !BadPtr(aconf->passwd)) || - (BadPtr(aconf->passwd) && !BadPtr(bconf->passwd))) - continue; - if (!BadPtr(bconf->passwd) && (!isDigit(*bconf->passwd) || bconf->passwd[1]) -#ifdef USEONE - && strCasediff(bconf->passwd, "ONE") -#endif - && strCasediff(bconf->passwd, aconf->passwd)) - 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 ((BadPtr(bconf->name) && !BadPtr(aconf->name)) || - (BadPtr(aconf->name) && !BadPtr(bconf->name))) - continue; - if (!BadPtr(bconf->name) && strCasediff(bconf->name, aconf->name)) - continue; - break; + for (qline = GlobalQuarantineList; qline; qline = qline->next) + if (!ircd_strcmp(qline->chname, chname)) + return qline->reason; + return NULL; +} + +/** 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. */ -int rehash(aClient *cptr, int sig) +void +yyerror(const char *msg) { - Reg1 aConfItem **tmp = &conf, *tmp2; - Reg2 aConfClass *cltmp; - Reg1 aClient *acptr; - Reg2 aMotdItem *temp; - Reg2 int i; - int ret = 0, found_g; - - if (sig == 1) - sendto_ops("Got signal SIGHUP, reloading ircd conf. file"); - - for (i = 0; i <= highest_fd; i++) - if ((acptr = loc_clients[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; - } + 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; +} - while ((tmp2 = *tmp)) - if (tmp2->clients || tmp2->status & CONF_LISTEN_PORT) - { +/** 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 Client* acptr; + int i; + int ret = 0; + int found_g = 0; + + if (1 == sig) + sendto_opmask_butone(0, SNO_OLDSNO, + "Got signal SIGHUP, reloading ircd conf. file"); + + while ((tmp2 = *tmp)) { + if (tmp2->clients) { /* * 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; + 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 - { + 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(); + clear_quarantines(); + + class_mark_delete(); + mark_listeners_closing(); + auth_mark_closing(); + close_mappings(); + + read_configuration_file(); + if (sig != 2) - flush_cache(); - if (initconf(0) == -1) /* This calls check_class(), */ - check_class(); /* unless it fails */ + 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 = &conf; (tmp2 = *tmp);) - if (!(tmp2->status & CONF_ILLEGAL)) - tmp = &tmp2->next; - else - { + for (tmp = &GlobalConfList; (tmp2 = *tmp);) { + if (CONF_ILLEGAL == (tmp2->status & CONF_ILLEGAL)) { *tmp = tmp2->next; tmp2->next = NULL; if (!tmp2->clients) - free_conf(tmp2); + free_conf(tmp2); } + else + tmp = &tmp2->next; + } - for (i = 0; i <= highest_fd; i++) { - if ((acptr = loc_clients[i]) && !IsMe(acptr)) { - if (IsServer(acptr)) { - det_confs_butmask(acptr, - ~(CONF_HUB | CONF_LEAF | CONF_UWORLD | CONF_ILLEGAL)); - attach_confs(acptr, acptr->name, CONF_HUB | CONF_LEAF | CONF_UWORLD); - } + for (i = 0; i <= HighestFd; i++) { + if ((acptr = LocalClientArray[i])) { + assert(!IsMe(acptr)); + 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, FALSE)); - if (exit_client(cptr, acptr, &me, found_g == -2 ? "G-lined" : - "K-lined") == CPTR_KILLED) - ret = CPTR_KILLED; - } -#if defined(R_LINES) && 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; + 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; } -#endif } } - /* free old motd structs */ - while (motd) { - temp = motd->next; - RunFree(motd); - motd = temp; - } - while (rmotd) { - temp = rmotd->next; - RunFree(rmotd); - rmotd = temp; - } - /* reload motd files */ - read_tlines(); - rmotd = read_motd(RPATH); - motd = read_motd(MPATH); + attach_conf_uworld(&me); return ret; } -/* - * initconf - * - * Read configuration file. - * - * returns -1, if file cannot be opened - * 0, if file opened +/** Read configuration file for the very first time. + * @return Non-zero on success, zero on failure. */ -#define MAXCONFLINKS 150 - -int initconf(int opt) +int init_conf(void) { - static char quotes[9][2] = { - {'b', '\b'}, - {'f', '\f'}, - {'n', '\n'}, - {'r', '\r'}, - {'t', '\t'}, - {'v', '\v'}, - {'\\', '\\'}, - {0, 0} - }; - Reg1 char *tmp, *s; - FBFILE *file; - int i; - char line[512]; - int ccount = 0, ncount = 0; - aConfItem *aconf = NULL; - - Debug((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile)); - if (NULL == (file = fbopen(configfile, "r"))) - { - return -1; - } - 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_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 */ - 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 '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; -#ifdef R_LINES - case 'R': /* extended K line */ - case 'r': /* Offers more options of how to restrict */ - aconf->status = CONF_RESTRICT; - break; -#endif - 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) - { - portnum = aconf->port; - if (portnum == 0) - portnum = PORTNUM; - me.port = portnum; - vserv.sin_port = htons(portnum); - if (!tmp) - { - Debug((DEBUG_FATAL, "Your M: line must have the Numeric, " - "assigned to you by routing-com, behind the port number!\n")); -#ifdef USE_SYSLOG - syslog(LOG_WARNING, "Your M: line must have the Numeric, " - "assigned to you by routing-com, behind the port number!\n"); -#endif - 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 | CONF_LISTEN_PORT)) - { - if (aconf->confClass == 0) - aconf->confClass = 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) - { - char *passwd = bconf->passwd; - bconf->passwd = aconf->passwd; - aconf->passwd = passwd; - ConfLinks(bconf) -= bconf->clients; - bconf->confClass = aconf->confClass; - if (bconf->confClass) - ConfLinks(bconf) += bconf->clients; - } - free_conf(aconf); - aconf = bconf; - } - else if (aconf->host && aconf->status == CONF_LISTEN_PORT) - add_listener(aconf); - } - if (aconf->status & CONF_SERVER_MASK) - if (ncount > MAXCONFLINKS || ccount > MAXCONFLINKS || - !aconf->host || strchr(aconf->host, '*') || - strchr(aconf->host, '?') || !aconf->name) - continue; - - if (aconf->status & (CONF_SERVER_MASK | CONF_LOCOP | CONF_OPERATOR)) - if (!strchr(aconf->host, '@') && *aconf->host != '/') - { - char *newhost; - int len = 3; /* *@\0 = 3 */ - - len += strlen(aconf->host); - newhost = (char *)RunMalloc(len); - sprintf_irc(newhost, "*@%s", aconf->host); - RunFree(aconf->host); - aconf->host = newhost; - } - if (aconf->status & CONF_SERVER_MASK) - { - if (BadPtr(aconf->passwd)) - continue; - else if (!(opt & BOOT_QUICK)) - 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)) - { - RunFree(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) - { - strncpy(me.info, aconf->name, sizeof(me.info) - 1); - if (me.name[0] == '\0' && aconf->host[0]) - { - strncpy(me.name, aconf->host, sizeof(me.name) - 1); - if (vserv.sin_addr.s_addr == htonl(INADDR_ANY)) /* Not already initialised on commandline with -w ? */ - { - struct hostent *hep; - hep = gethostbyname(me.name); - if (hep && hep->h_addrtype == AF_INET && hep->h_addr_list[0] && !hep->h_addr_list[1]) - { - memcpy(&vserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr)); - memcpy(&cserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr)); - } - } - } - if (portnum == 0) - portnum = aconf->port; - have_server_port = 0; - } + if (0 == localConf.name || 0 == localConf.numeric) + return 0; + if (conf_error) + return 0; - /* - * 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")); -#ifdef USE_SYSLOG - syslog(LOG_WARNING, "Your A: line must have 4 fields!\n"); -#endif - exit(-1); - } + if (0 == localConf.location1) + DupString(localConf.location1, ""); + if (0 == localConf.location2) + DupString(localConf.location2, ""); + if (0 == localConf.contact) + DupString(localConf.contact, ""); - 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 = conf; - conf = aconf; - aconf = NULL; + return 1; } - if (aconf) - free_conf(aconf); - fbclose(file); - 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(aConfItem *aconf) -{ - Reg2 char *s; - Reg3 struct hostent *hp; - Link ln; - - if (BadPtr(aconf->host) || BadPtr(aconf->name)) - goto badlookup; - if ((s = strchr(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))) - memcpy(&(aconf->ipnum), hp->h_addr, sizeof(struct in_addr)); - - if (aconf->ipnum.s_addr == INADDR_NONE) - goto badlookup; - return 0; -badlookup: - if (aconf->ipnum.s_addr == INADDR_NONE) - memset(&aconf->ipnum, 0, sizeof(struct in_addr)); - Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)", - aconf->host, aconf->name)); - return -1; -} - -/* 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() +int find_kill(struct Client *cptr) { - aConfItem *tmp; - atrecord *temp, *last = NULL; /* Init. to avoid compiler warning */ - aMotdItem *amotd; - - /* Free the old trecords and the associated motd contents first */ - while (tdata) - { - last = tdata->next; - while (tdata->tmotd) - { - amotd = tdata->tmotd->next; - RunFree(tdata->tmotd); - tdata->tmotd = amotd; - } - RunFree(tdata); - tdata = last; - } - - for (tmp = conf; tmp; tmp = tmp->next) - if (tmp->status == CONF_TLINES && tmp->host && tmp->passwd) - { - temp = (atrecord *) RunMalloc(sizeof(atrecord)); - if (!temp) - outofmemory(); - 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; - } -} + const char* host; + const char* name; + const char* realname; + struct DenyConf* deny; + struct Gline* agline = NULL; -int find_kill(aClient *cptr) -{ - char reply[256], *host, *name; - aConfItem *tmp; - aGline *agline = NULL; + assert(0 != cptr); - if (!cptr->user) + if (!cli_user(cptr)) 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) - /* 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, inetntoa(cptr->ip)) == 0)) && - (!name || match(tmp->name, name) == 0) && - (!tmp->port || (tmp->port == cptr->acpt->port))) - { - /* - * Can short-circuit evaluation - not taking chances - * because 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 :Connection from your host is refused on this server.", - me.name, ERR_YOUREBANNEDCREEP, cptr->name); - 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]); - 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 - } - } - - /* find active glines */ - /* added a check against the user's IP address to find_gline() -Kev */ - else if ((agline = find_gline(cptr, NULL)) && GlineIsActive(agline)) - 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)); -} + host = cli_sockhost(cptr); + name = cli_user(cptr)->username; + realname = cli_info(cptr); -#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(aClient *cptr) -{ - aConfItem *tmp; - char reply[80], temprpl[80]; - char *rplhold = reply, *host, *name, *s; - char rplchar = 'Y'; - int pi[2], rc = 0, n; - FBFILE *file = NULL; - - if (!cptr->user) - return 0; - name = cptr->user->username; - host = cptr->sockhost; - Debug((DEBUG_INFO, "R-line check for %s[%s]", name, host)); + assert(strlen(host) <= HOSTLEN); + assert((name ? strlen(name) : 0) <= HOSTLEN); + assert((realname ? strlen(realname) : 0) <= REALLEN); - 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))) + /* 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 (BadPtr(tmp->passwd)) - { - sendto_ops("Program missing on R-line %s/%s, ignoring", name, host); + 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; - } - 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; - - close(pi[0]); - for (i = 2; i < MAXCONNECTIONS; i++) - if (i != pi[1]) - close(i); - if (pi[1] != 2) - dup2(pi[1], 2); - dup2(2, 1); - if (pi[1] != 2 && pi[1] != 1) - close(pi[1]); - execlp(tmp->passwd, tmp->passwd, name, host, 0); - exit(-1); - } - default: - close(pi[1]); - break; - } - *reply = '\0'; - file = fdbopen(pi[0], "r"); - while (fbgets(temprpl, sizeof(temprpl) - 1, file)) - { - if ((s = strchr(temprpl, '\n'))) - *s = '\0'; - if (strlen(temprpl) + strlen(reply) < sizeof(reply) - 2) - sprintf_irc(rplhold, "%s %s", rplhold, temprpl); + if (EmptyString(deny->message)) + send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, + ":Connection from your host is refused on this server."); + else { + if (deny->flags & DENY_FLAGS_FILE) + killcomment(cptr, deny->message); else - { - sendto_ops("R-line %s/%s: reply too long!", name, host); - break; - } + send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", deny->message); } - fbclose(file); - kill(rc, SIGKILL); /* cleanup time */ - wait(0); - - rc = 0; - while (*rplhold == ' ') - rplhold++; - rplchar = *rplhold; /* Pull out the yes or no */ - while (*rplhold != ' ') - rplhold++; - while (*rplhold == ' ') - rplhold++; - 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 - */ -static void killcomment(aClient *sptr, char *parv, char *filename) -{ - FBFILE *file = NULL; - char line[80]; - Reg1 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); - 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); + 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; } - sendto_one(sptr, - ":%s %d %s :Connection from your host is refused on this server.", - me.name, ERR_YOUREBANNEDCREEP, parv); - fbclose(file); - return; + + return 0; } -/* - * is the K line field an interval or a comment? - Mmmm +/** 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. */ -static int is_comment(char *comment) +enum AuthorizationCheckResult conf_check_client(struct Client *cptr) { - size_t i; - for (i = 0; i < strlen(comment); i++) - if ((comment[i] != ' ') && (comment[i] != '-') - && (comment[i] != ',') && ((comment[i] < '0') || (comment[i] > '9'))) - return (1); + enum AuthorizationCheckResult acr = ACR_OK; - return (0); + if ((acr = attach_iline(cptr))) { + Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]", + cli_name(cptr), cli_sockhost(cptr))); + return acr; + } + return ACR_OK; } -/* - * check against a set of time intervals +/** 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. + * @param cptr Peer server to check. + * @return 0 if accepted, -1 if access denied. */ -static int check_time_interval(char *interval, char *reply) +int conf_check_server(struct Client *cptr) { - struct tm *tptr; - char *p; - int perm_min_hours, perm_min_minutes, perm_max_hours, perm_max_minutes; - int nowm, perm_min, perm_max; + struct ConfItem* c_conf = NULL; + struct SLink* lp; - tptr = localtime(&now); - nowm = tptr->tm_hour * 60 + tptr->tm_min; + Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", + cli_name(cptr), cli_sockhost(cptr))); - 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)) - { - printf(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); + 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 = 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, cli_name(cptr), CONF_SERVER); + if (!c_conf) { + sendto_opmask_butone(0, SNO_OLDSNO, + "Connect Error: lost Connect block for %s", + cli_name(cptr)); + det_confs_butmask(cptr, 0); + return -1; } - interval = p; } - return (0); -} -aMotdItem *read_motd(char *motdfile) -{ - FBFILE *file = NULL; - register aMotdItem *temp, *newmotd, *last; - struct stat sb; - char line[80]; - register char *tmp; + /* 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 (NULL == (file = fbopen(motdfile, "r"))) - { - Debug((DEBUG_ERROR, "Couldn't open \"%s\": %s", motdfile, strerror(errno))); - return NULL; - } - if (-1 == fbstat(&sb, 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 = (aMotdItem *) RunMalloc(sizeof(aMotdItem)); - if (!temp) - outofmemory(); - strcpy(temp->line, line); - temp->next = NULL; - if (!newmotd) - newmotd = temp; - else - last->next = temp; - last = temp; + /* + * 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, &cli_ip(cptr), CONF_SERVER); + /* + * detach all conf lines that got attached by attach_confs() + */ + det_confs_butmask(cptr, 0); + /* + * if no Connect block, then deny access + */ + if (!c_conf) { + Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s]", + cli_name(cptr), cli_username(cptr), cli_sockhost(cptr))); + return -1; } - fbclose(file); - return newmotd; + /* + * attach the Connect block to the client structure for later use. + */ + attach_conf(cptr, c_conf); + + 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]", + cli_name(cptr), cli_sockhost(cptr))); + return 0; } +