/* * IRC - Internet Relay Chat, ircd/s_conf.c * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Computing Center * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: s_conf.c 1571 2006-05-22 01:19:04Z sirvulcan $ */ #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" #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_bsd.h" #include "s_debug.h" #include "s_misc.h" #include "send.h" #ifdef USE_SSL #include "ssl.h" #endif /* USE_SSL */ #include "ircd_struct.h" #include "support.h" #include "sys.h" #include #include #include #include #include #include #include #include #include #include #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff #endif struct ConfItem* GlobalConfList = 0; int GlobalConfCount = 0; struct qline* GlobalQuarantineList = 0; struct sline* GlobalSList = 0; struct csline* GlobalConnStopList = 0; struct svcline* GlobalServicesList = 0; struct blline* GlobalBLList = 0; unsigned int GlobalBLCount = 0; char* GlobalForwards[256]; static struct LocalConf localConf; static struct CRuleConf* cruleConfList; static struct ServerConf* serverConfList; static struct DenyConf* denyConfList; static int dnsbl_flags[] = { DFLAG_BITMASK, 'b', DFLAG_REPLY, 'r', DFLAG_ALLOW, 'a', DFLAG_MARK, 'm', DFLAG_DENY, 'd' }; static int oper_access[] = { OFLAG_GLOBAL, 'O', OFLAG_ADMIN, 'A', 0, 0 }; char oflagbuf[128]; char *oflagstr(long oflag) { int *i; int flag; char m; char *p = oflagbuf; for (i = &oper_access[0], m = *(i + 1); (flag = *i); i += 2, m = *(i + 1)) if (oflag & flag) { *p = m; p++; } *p = '\0'; return oflagbuf; } char dflagstr(const char* dflags) { unsigned int *flag_p; unsigned int x_flag = 0; const char *flagstr; flagstr = dflags; /* This should never happen... */ assert(flagstr != 0); for (; *flagstr; flagstr++) { for (flag_p = (unsigned int*)dnsbl_flags; flag_p[0]; flag_p += 2) { if (flag_p[1] == *flagstr) break; } if (!flag_p[0]) continue; x_flag |= flag_p[0]; } return x_flag; } /* * output the reason for being k lined from a file - Mmmm * sptr is client being dumped * filename is the file that is to be output to the K lined client */ static void killcomment(struct Client* sptr, const char* filename) { FBFILE* file = 0; char line[80]; struct stat sb; struct tm* 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); tm = localtime((time_t*) &sb.st_mtime); /* NetBSD needs cast */ 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); } struct ConfItem* make_conf(void) { struct ConfItem* aconf; aconf = (struct ConfItem*) MyMalloc(sizeof(struct ConfItem)); assert(0 != aconf); #ifdef DEBUGMODE ++GlobalConfCount; #endif memset(aconf, 0, sizeof(struct ConfItem)); aconf->status = CONF_ILLEGAL; aconf->ipnum.s_addr = INADDR_NONE; 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 = 0; } void free_conf(struct ConfItem *aconf) { Debug((DEBUG_DEBUG, "free_conf: %s %s %d", aconf->host ? aconf->host : "*", aconf->name ? aconf->name : "*", aconf->port)); if (aconf->dns_pending) delete_resolver_queries(aconf); MyFree(aconf->host); if (aconf->passwd) memset(aconf->passwd, 0, strlen(aconf->passwd)); MyFree(aconf->passwd); MyFree(aconf->name); MyFree(aconf); #ifdef DEBUGMODE --GlobalConfCount; #endif } /* * detach_conf - Disassociate configuration from the client. */ 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); } } /* * 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 */ static void conf_dns_callback(void* vptr, struct DNSReply* reply) { struct ConfItem* aconf = (struct ConfItem*) vptr; aconf->dns_pending = 0; if (reply) memcpy(&aconf->ipnum, reply->hp->h_addr, sizeof(struct in_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 */ static struct DNSReply* 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; } 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. */ static void lookup_confhost(struct ConfItem *aconf) { char *tmp, *tmp2; struct DNSReply* reply; if (EmptyString(aconf->host) || EmptyString(aconf->name)) { Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)", aconf->host, aconf->name)); return; } /* * Do name lookup now on hostnames given and store the * ip numbers in conf structure. */ if ((tmp = strchr(aconf->host, '/'))) { *(tmp++) = '\0'; aconf->origin.s_addr = inet_addr(aconf->host); tmp2 = aconf->host; DupString(aconf->host, tmp); free(tmp2); } else aconf->origin.s_addr = INADDR_NONE; 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) { Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)", aconf->host, aconf->name)); } } else if ((reply = conf_dns_lookup(aconf))) memcpy(&aconf->ipnum, reply->hp->h_addr, sizeof(struct in_addr)); } /* * 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) */ struct ConfItem* conf_find_server(const char* name) { struct ConfItem* conf; assert(0 != name); 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; } } 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 (CRULE_ALL) not d lines (CRULE_AUTO). */ 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 0; } /* * Remove all conf entries from the client except those which match * the status field mask. */ void det_confs_butmask(struct Client* cptr, int mask) { 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); } } /* * check_limit_and_attach - check client limits and attach I:line * * Made it accept 1 charactor, and 2 charactor limits (0->99 now), * and dislallow more than 255 people here as well as in ipcheck. * removed the old "ONE" scheme too. * -- Isomer 2000-06-22 */ static enum AuthorizationCheckResult check_limit_and_attach(struct Client* cptr, struct ConfItem* aconf) { int number = 255; if (aconf->passwd) { if (IsDigit(*aconf->passwd) && !aconf->passwd[1]) number = *aconf->passwd-'0'; else if (IsDigit(*aconf->passwd) && IsDigit(aconf->passwd[1]) && !aconf->passwd[2]) number = (*aconf->passwd-'0')*10+(aconf->passwd[1]-'0'); } if (IPcheck_nr(cptr) > number) return ACR_TOO_MANY_FROM_IP; return attach_conf(cptr, aconf); } /* * Find the first (best) I line to attach. */ enum AuthorizationCheckResult 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; assert(0 != cptr); if (cli_dns_reply(cptr)) hp = cli_dns_reply(cptr)->hp; for (aconf = GlobalConfList; aconf; aconf = aconf->next) { if (aconf->status != CONF_CLIENT) continue; if (aconf->port && aconf->port != cli_listener(cptr)->port) continue; if (!aconf->host || !aconf->name) 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", cli_sockhost(cptr), fullname)); if (strchr(aconf->name, '@')) { strcpy(uhost, cli_username(cptr)); 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, '@')) SetFlag(cptr, FLAG_DOID); return check_limit_and_attach(cptr, aconf); } } } if (strchr(aconf->host, '@')) { ircd_strncpy(uhost, cli_username(cptr), sizeof(uhost) - 2); uhost[sizeof(uhost) - 2] = 0; strcat(uhost, "@"); } else *uhost = '\0'; strncat(uhost, cli_sock_ip(cptr), sizeof(uhost) - 1 - strlen(uhost)); uhost[sizeof(uhost) - 1] = 0; if (match(aconf->host, uhost)) { char* ip_start; char* cidr_start; struct in_addr conf_addr; int bits; ip_start = strrchr(aconf->host, '@'); if (ip_start == NULL) ip_start = aconf->host; else { *ip_start = 0; if (match(aconf->host, cli_username(cptr))) { *ip_start = '@'; continue; } *ip_start++ = '@'; } cidr_start = strchr(ip_start, '/'); if (!cidr_start) continue; *cidr_start = 0; if (inet_aton(ip_start, &conf_addr) == 0) { *cidr_start = '/'; continue; } bits = atoi(cidr_start + 1); *cidr_start = '/'; if ((bits < 1) || (bits > 32)) continue; if ((cli_ip(cptr).s_addr & NETMASK(bits)) != conf_addr.s_addr) continue; } if (strchr(uhost, '@')) SetFlag(cptr, FLAG_DOID); return check_limit_and_attach(cptr, aconf); } return ACR_NO_AUTHORIZATION; } static int is_attached(struct ConfItem *aconf, struct Client *cptr) { struct SLink *lp; for (lp = cli_confs(cptr); lp; lp = lp->next) { if (lp->value.aconf == aconf) 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... */ enum AuthorizationCheckResult attach_conf(struct Client *cptr, struct ConfItem *aconf) { 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)) && ConfLinks(aconf) >= ConfMaxLinks(aconf) && ConfMaxLinks(aconf) > 0) return ACR_TOO_MANY_IN_CLASS; /* Use this for printing error message */ lp = make_link(); lp->next = cli_confs(cptr); lp->value.aconf = aconf; cli_confs(cptr) = lp; ++aconf->clients; if (aconf->status & CONF_CLIENT_MASK) ConfLinks(aconf)++; return ACR_OK; } const struct LocalConf* conf_get_local(void) { return &localConf; } /* * attach_confs_byname * * Attach a CONF line to a client if the name passed matches that for * the conf file (for non-C lines) or is an exact match (C lines * only). The difference in behaviour is to stop C:*::*. */ struct ConfItem* attach_confs_byname(struct Client* cptr, const char* name, int statmask) { struct ConfItem* tmp; struct ConfItem* first = NULL; assert(0 != name); if (HOSTLEN < strlen(name)) return 0; 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; } /* * Added for new access check meLazy */ struct ConfItem* attach_confs_byhost(struct Client* cptr, const char* host, int statmask) { struct ConfItem* tmp; struct ConfItem* first = 0; assert(0 != host); if (HOSTLEN < strlen(host)) return 0; 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; } /* * find a conf entry which matches the hostname and has the same name. */ struct ConfItem* find_conf_exact(const char* name, const char* user, const char* host, int statmask) { struct ConfItem *tmp; char userhost[USERLEN + HOSTLEN + 3]; if (user) ircd_snprintf(0, userhost, sizeof(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)) continue; if (tmp->status & (CONF_OPERATOR | CONF_LOCOP)) { if (tmp->clients < MaxLinks(tmp->conn_class)) return tmp; else continue; } else return tmp; } return 0; } /* * find a conf entry by CIDR host entry which has the same name. */ struct ConfItem* find_conf_cidr(const char* name, const char* user, struct in_addr cli_addr, int statmask) { struct ConfItem *tmp; char *ip_start; char *cidr_start; struct in_addr conf_addr; int bits; for (tmp = GlobalConfList; tmp; tmp = tmp->next) { if (!(tmp->status & statmask) || !tmp->name || !tmp->host || 0 != ircd_strcmp(tmp->name, name)) continue; ip_start = strrchr(tmp->host, '@'); if (ip_start == NULL) ip_start = tmp->host; else { *ip_start = 0; if (match(tmp->host, user)) { *ip_start = '@'; continue; } *ip_start = '@'; ip_start++; } cidr_start = strchr(ip_start, '/'); if (!cidr_start) continue; *cidr_start = 0; if (inet_aton(ip_start, &conf_addr) == 0) { *cidr_start = '/'; continue; } bits = atoi(cidr_start + 1); *cidr_start = '/'; if ((bits < 1) || (bits > 32)) continue; if ((cli_addr.s_addr & NETMASK(bits)) != conf_addr.s_addr) continue; if (tmp->status & (CONF_OPERATOR | CONF_LOCOP)) { if (tmp->clients < MaxLinks(tmp->conn_class)) return tmp; else continue; } else return tmp; } return 0; } struct ConfItem* find_conf_byname(struct SLink* lp, const char* name, int statmask) { struct ConfItem* tmp; assert(0 != name); if (HOSTLEN < strlen(name)) return 0; for (; lp; lp = lp->next) { tmp = lp->value.aconf; if (0 != (tmp->status & statmask)) { assert(0 != tmp->name); if (0 == ircd_strcmp(tmp->name, name) || 0 == match(tmp->name, name)) return tmp; } } return 0; } /* * Added for new access check meLazy */ struct ConfItem* find_conf_byhost(struct SLink* lp, const char* host, int statmask) { struct ConfItem* tmp = NULL; assert(0 != host); if (HOSTLEN < strlen(host)) return 0; for (; lp; lp = lp->next) { tmp = lp->value.aconf; if (0 != (tmp->status & statmask)) { assert(0 != tmp->host); if (0 == match(tmp->host, host)) return tmp; } } return 0; } /* * find_conf_ip * * Find a conf line using the IP# stored in it to search upon. * Added 1/8/92 by Avalon. */ struct ConfItem* find_conf_byip(struct SLink* lp, const char* 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; } } return 0; } /* * find_conf_entry * * - looks for a match on all given fields. */ static struct ConfItem *find_conf_entry(struct ConfItem *aconf, unsigned int mask) { struct ConfItem *bconf; assert(0 != aconf); mask &= ~CONF_ILLEGAL; for (bconf = GlobalConfList; bconf; bconf = bconf->next) { if (!(bconf->status & mask) || (bconf->port != aconf->port)) continue; 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; if ((EmptyString(bconf->passwd) && !EmptyString(aconf->passwd)) || (EmptyString(aconf->passwd) && !EmptyString(bconf->passwd))) continue; if (!EmptyString(bconf->passwd) && (!IsDigit(*bconf->passwd) || bconf->passwd[1]) && 0 != ircd_strcmp(bconf->passwd, aconf->passwd)) continue; 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; } return bconf; } /* * If conf line is a class definition, create a class entry * for it and make the conf_line illegal and delete it. */ void conf_add_class(const char* const* fields, int count) { if (count < 6) return; add_class(atoi(fields[1]), atoi(fields[2]), atoi(fields[3]), atoi(fields[4]), atoi(fields[5])); } void conf_add_listener(const char* const* fields, int count) { int is_server = 0; int is_hidden = 0; #ifdef USE_SSL int is_ssl = 0; #endif /* USE_SSL */ /* * need a port */ if (count < 5 || EmptyString(fields[4])) return; if (!EmptyString(fields[3])) { const char* x = fields[3]; if ('S' == ToUpper(*x)) is_server = 1, ++x; if ('H' == ToUpper(*x)) #ifdef USE_SSL is_hidden = 1, ++x; if ('E' == ToUpper(*x)) is_ssl = 1, ++x; #else is_hidden = 1; #endif /* USE_SSL */ } /* port vhost mask */ #ifdef USE_SSL add_listener(atoi(fields[4]), fields[2], fields[1], is_server, is_hidden, is_ssl); #else add_listener(atoi(fields[4]), fields[2], fields[1], is_server, is_hidden); #endif /* USE_SSL */ } void conf_add_lbline(const char* const* fields, int count) { unsigned char ch = fields[1][0]; MyFree(GlobalForwards[ch]); DupString(GlobalForwards[ch], fields[2]); } void clear_lblines(void) { unsigned int ii; for (ii = 0; ii < 256; ++ii) MyFree(GlobalForwards[ii]); } void conf_add_quarantine(const char* const* fields, int count) { struct qline *qline; if (count < 3 || EmptyString(fields[1]) || EmptyString(fields[2]) || (fields[1][0] != '#' && fields[1][0] != '&')) return; qline = (struct qline *) MyMalloc(sizeof(struct qline)); DupString(qline->chname, fields[1]); DupString(qline->reason, fields[2]); qline->next = GlobalQuarantineList; GlobalQuarantineList = qline; } char* find_quarantine(const char* chname) { struct qline *qline; for (qline = GlobalQuarantineList; qline; qline = qline->next) if (!ircd_strcmp(qline->chname, chname)) return qline->reason; return NULL; } void clear_quarantines(void) { struct qline *qline; while ((qline = GlobalQuarantineList)) { GlobalQuarantineList = qline->next; MyFree(qline->reason); MyFree(qline->chname); MyFree(qline); } GlobalQuarantineList = 0; } int find_csline(struct Client* sptr, const char* mask) { struct csline *csline; for (csline = GlobalConnStopList; csline; csline = csline->next) { if (!match(csline->mask, mask)) { send_reply(sptr, RPL_BOUNCE, csline->server, csline->port); return 1; } } return 0; } void conf_add_csline(const char* const* fields, int count) { struct csline *csline; if (count < 3 || EmptyString(fields[1]) || EmptyString(fields[2]) || EmptyString(fields[2])) return; csline = (struct csline *) MyMalloc(sizeof(struct csline)); DupString(csline->mask, fields[1]); DupString(csline->server, fields[2]); DupString(csline->port, fields[3]); csline->next = GlobalConnStopList; GlobalConnStopList = csline; } void clear_cslines(void) { struct csline *csline; while ((csline = GlobalConnStopList)) { GlobalConnStopList = csline->next; MyFree(csline->mask); MyFree(csline->server); MyFree(csline->port); MyFree(csline); } GlobalConnStopList = 0; } void conf_add_dnsbl_line(const char* const* fields, int count) { struct blline *blline; if (count < 2 || EmptyString(fields[1]) || EmptyString(fields[2]) || EmptyString(fields[3]) || EmptyString(fields[4]) || EmptyString(fields[5]) || EmptyString(fields[6])) { log_write(LS_CONFIG, L_CRIT, 0, "Your X: line must have 6 fields!"); return; } ++GlobalBLCount; blline = (struct blline *) MyMalloc(sizeof(struct blline)); memset(blline, 0, sizeof(struct blline)); DupString(blline->server, fields[1]); DupString(blline->name, fields[2]); DupString(blline->flags, fields[3]); DupString(blline->replies, fields[4]); DupString(blline->reply, fields[5]); DupString(blline->rank, fields[6]); blline->next = GlobalBLList; GlobalBLList = blline; } void clear_dnsbl_list(void) { struct blline *blline; while ((blline = GlobalBLList)) { GlobalBLList = blline->next; MyFree(blline->server); MyFree(blline->name); MyFree(blline->flags); MyFree(blline->replies); MyFree(blline->reply); MyFree(blline->rank); MyFree(blline); } GlobalBLList = 0; } extern int find_dnsbl(struct Client* sptr, const char* dnsbl) { struct SLink *lp; for (lp = cli_sdnsbls(sptr); lp; lp = lp->next) { if (!ircd_strcmp(lp->value.cp, dnsbl)) return 1; } return 0; } extern int add_dnsbl(struct Client* sptr, const char* dnsbl) { struct SLink *lp; if (!find_dnsbl(sptr, dnsbl)) { lp = make_link(); memset(lp, 0, sizeof(struct SLink)); lp->next = cli_sdnsbls(sptr); lp->value.cp = (char*) MyMalloc(strlen(dnsbl) + 1); assert(0 != lp->value.cp); strcpy(lp->value.cp, dnsbl); cli_sdnsbls(sptr) = lp; } return 0; } extern int del_dnsbl(struct Client *sptr, char *dnsbl) { struct SLink **lp; struct SLink *tmp; int ret = -1; for (lp = &(cli_sdnsbls(sptr)); *lp;) { if (!ircd_strcmp(dnsbl, (*lp)->value.cp)) { tmp = *lp; *lp = tmp->next; MyFree(tmp->value.cp); free_link(tmp); ret = 0; } else lp = &(*lp)->next; } return ret; } /* Check if ip (returned by the dnsbl) is a match for check (in X line) * * @check = 127.0.0.54 or 0.0.54 or 0.54 or 1 * @ip = 127.0.0.54 * example, check=2, ip=127.0.0.2; match * check=2, ip=124.9.124.2; no match * check=127.0.0.1, ip=127.0.0.1; match * returns 1 for match, 0 for not a match. */ int dnsbl_result_match(char* check, const char* ip) { char full_check[4][4] = {"127","0","0","0"}; char check_str[16]; int i, s, j; int octet=3; for(i=strlen(check);i>=0;i--) { if(check[i] == '.' || i==0) { j = 0; if(i>0) s=i+1; else s=0; for(;check[s] != '\0' && check[s] != '.';s++) { if(j > 3) return 0; /* overrun protection */ full_check[octet][j++] = check[s]; } full_check[octet--][j] = '\0'; } } sprintf(check_str, "%s.%s.%s.%s", full_check[0], full_check[1], full_check[2], full_check[3]); return strcmp(check_str, ip) == 0; } /* Find an X:line matching the rbl reply and mark the client appropreately * * @sptr * @replyip = the 127.0.0.x ip returned by the dnsrbl * @checkhost = the hostname we looked up, eg d.c.b.a.dnsbl.sorbs.net * Example: replyip="127.0.0.10", checkhost="28.148.169.68.dnsbl.sorbs.net" */ int find_blline(struct Client* sptr, const char* replyip, char *checkhost) { struct blline *blline; char *dhname; char *csep; char oct[4]; /* last bit of replyip, and a null */ char cstr_buf[HOSTLEN +1]; char *cstr = cstr_buf; int da = 0; int ret = 0; int c = 0; int j = 0; unsigned int x_flag = 0; /* Weird sanity checks :) */ if (!sptr || !replyip || !checkhost) { log_write(LS_DNSBL, L_INFO, 0, "find_blline missing parameter(s) aborting check."); Debug((DEBUG_DEBUG, "find_blline missing parameter(s) aborting check.")); return 0; } /* Pull the last octet out of the reply ip into oct */ for(j=strlen(replyip);j>0;j--) { if(replyip[j] == '.') { ircd_strncpy(oct, replyip+j+1, 3); oct[3] = 0; break; } } if(!*oct) { log_write(LS_DNSBL, L_INFO, 0, "find_blline passed invalid replyip %s.", replyip); Debug((DEBUG_DEBUG, "find_blline passed invalid replyip %s.", replyip)); return 0; /* malformed replyip */ } /* Find the users IP address from the dnsbl reply msg */ for(dhname = checkhost,c=0;*dhname;dhname++) if(*dhname == '.') { if(++c >= 4) break; } dhname++; if(c!=4 || !*dhname) { log_write(LS_DNSBL, L_INFO, 0, "find_blline passed invalid checkhost %s.", checkhost); Debug((DEBUG_DEBUG, "find_blline passed invalid checkhost %s.", checkhost)); return 0; } /* Walk the whole list of X lines */ for (blline = GlobalBLList; blline; blline = blline->next) { if (!ircd_strcmp(dhname, blline->server)) { x_flag = dflagstr(blline->flags); memset(cstr, 0, HOSTLEN +1); ircd_strncpy(cstr, blline->replies, HOSTLEN); /* what bits we are looking for */ if (x_flag & DFLAG_BITMASK) { int total = 0; /* bits are added together */ /* For each int in the replies, add its bits to total */ for(csep = cstr; *(csep+1); csep++); /* set csep to the end */ for (;csep >= cstr; csep--) if(*csep == ',' || csep==cstr) { total += atoi(csep+1); *csep = 0; } if (total & atoi(oct)) { /* bitwise AND */ log_write(LS_DNSBL, L_INFO, 0, "DNSBL Matched %p %s (B)", sptr, blline->name); SetDNSBL(sptr); if (x_flag & DFLAG_MARK) SetDNSBLMarked(sptr); if ((x_flag & DFLAG_ALLOW) && (!IsDNSBLDenied(sptr))) SetDNSBLAllowed(sptr); if (x_flag & DFLAG_DENY) { ClearDNSBLAllowed(sptr); SetDNSBLDenied(sptr); } if (atoi(blline->rank) > cli_dnsbllastrank(sptr)) { ircd_strncpy(cli_dnsbl(sptr), blline->name, BUFSIZE); ircd_strncpy(cli_dnsblformat(sptr), blline->reply, BUFSIZE); } cli_dnsbllastrank(sptr) = atoi(blline->rank); add_dnsbl(sptr, blline->name); ret = 1; } } else if (x_flag & DFLAG_REPLY) { for(csep = cstr; *(csep+1); csep++); /* set csep to the end */ for (;csep >= cstr; csep--) { if (*csep == ',' || csep==cstr) { char *checkval; if (*csep == ',') checkval = csep+1; else checkval = cstr; if (dnsbl_result_match(checkval, replyip)) { log_write(LS_DNSBL, L_INFO, 0, "DNSBL Matched %p %s (R)", sptr, blline->name); SetDNSBL(sptr); if (x_flag & DFLAG_MARK) SetDNSBLMarked(sptr); if ((x_flag & DFLAG_ALLOW) && (!IsDNSBLDenied(sptr))) SetDNSBLAllowed(sptr); if (x_flag & DFLAG_DENY) { ClearDNSBLAllowed(sptr); SetDNSBLDenied(sptr); } if (atoi(blline->rank) > cli_dnsbllastrank(sptr)) { ircd_strncpy(cli_dnsbl(sptr), blline->name, BUFSIZE); ircd_strncpy(cli_dnsblformat(sptr), blline->reply, BUFSIZE); } cli_dnsbllastrank(sptr) = atoi(blline->rank); add_dnsbl(sptr, blline->name); da = 0; ret = 1; } *csep = 0; } } } } } return ret; } void conf_add_local(const char* const* fields, int count) { if (count < 6 || EmptyString(fields[1]) || EmptyString(fields[5])) { log_write(LS_CONFIG, L_CRIT, 0, "Your M: line must have 6 fields!"); return; } /* * these two can only be set the first time */ if (0 == localConf.name) { if (string_is_hostname(fields[1])) DupString(localConf.name, fields[1]); } if (0 == localConf.numeric) { localConf.numeric = atoi(fields[5]); if (0 == localConf.numeric) log_write(LS_CONFIG, L_WARNING, 0, "Your M: line must have a Numeric value greater than 0"); } /* * these two can be changed while the server is running */ if (string_is_address(fields[2])) { if (INADDR_NONE == (localConf.vhost_address.s_addr = inet_addr(fields[2]))) localConf.vhost_address.s_addr = INADDR_ANY; } MyFree(localConf.description); DupString(localConf.description, fields[3]); /* * XXX - shouldn't be setting these directly here */ ircd_strncpy(cli_info(&me), fields[3], REALLEN); set_virtual_host(localConf.vhost_address); } void conf_add_admin(const char* const* fields, int count) { /* * if you have one, it MUST have 3 lines */ if (count < 4) { log_write(LS_CONFIG, L_CRIT, 0, "Your A: line must have 4 fields!"); return; } MyFree(localConf.location1); DupString(localConf.location1, fields[1]); MyFree(localConf.location2); DupString(localConf.location2, fields[2]); MyFree(localConf.contact); DupString(localConf.contact, fields[3]); } /* * conf_add_crule - Create expression tree from connect rule and add it * to the crule list */ void conf_add_crule(const char* const* fields, int count, int type) { struct CRuleNode* node; assert(0 != fields); if (count < 4 || EmptyString(fields[1]) || EmptyString(fields[3])) return; if ((node = crule_parse(fields[3]))) { struct CRuleConf* p = (struct CRuleConf*) MyMalloc(sizeof(struct CRuleConf)); assert(0 != p); DupString(p->hostmask, fields[1]); collapse(p->hostmask); DupString(p->rule, fields[3]); p->type = type; p->node = node; p->next = cruleConfList; cruleConfList = p; } } 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; } const struct CRuleConf* conf_get_crule_list(void) { return cruleConfList; } void conf_add_server(const char* const* fields, int count) { struct ServerConf* server; struct in_addr addr; assert(0 != fields); /* * missing host, password, or alias? */ if (count < 6 || EmptyString(fields[1]) || EmptyString(fields[2]) || EmptyString(fields[3])) return; /* * check the host */ if (string_is_hostname(fields[1])) addr.s_addr = INADDR_NONE; else if (INADDR_NONE == (addr.s_addr = inet_addr(fields[1]))) return; server = (struct ServerConf*) MyMalloc(sizeof(struct ServerConf)); assert(0 != server); DupString(server->hostname, fields[1]); DupString(server->passwd, fields[2]); DupString(server->alias, fields[3]); server->address.s_addr = addr.s_addr; server->port = atoi(fields[4]); server->dns_pending = 0; server->connected = 0; server->hold = 0; server->conn_class = find_class(atoi(fields[5])); server->next = serverConfList; serverConfList = server; /* if (INADDR_NONE == server->address.s_addr) */ /* lookup_confhost(server); */ } void conf_add_deny(const char* const* fields, int count, int ip_kill) { struct DenyConf* conf; if (count < 4 || EmptyString(fields[1]) || EmptyString(fields[3])) return; conf = (struct DenyConf*) MyMalloc(sizeof(struct DenyConf)); assert(0 != conf); memset(conf, 0, sizeof(struct DenyConf)); if (fields[1][0] == '$' && fields[1][1] == 'R') conf->flags |= DENY_FLAGS_REALNAME; DupString(conf->hostmask, fields[1]); collapse(conf->hostmask); if (!EmptyString(fields[2])) { const char* p = fields[2]; if ('!' == *p) { conf->flags |= DENY_FLAGS_FILE; ++p; } DupString(conf->message, p); } DupString(conf->usermask, fields[3]); collapse(conf->usermask); if (ip_kill) { /* * Here we use the same kludge as in listener.c to parse * a.b.c.d, or a.b.c.*, or a.b.c.d/e. */ int c_class; char ipname[16]; int ad[4] = { 0 }; int bits2 = 0; /* very simple check to make sure this could even be a valid IP kline, * this does preclude us ever have IP klines of the form *.xxx.yyy.zzz * though - I can't see it being a problem. -- hikari */ if (!IsDigit(conf->hostmask[0])) { sendto_opmask_butone(0, SNO_OLDSNO, "Mangled IP in IP K-Line: k:%s:%s:%s", conf->hostmask, conf->message, conf->usermask); return; } c_class = sscanf(conf->hostmask, "%d.%d.%d.%d/%d", &ad[0], &ad[1], &ad[2], &ad[3], &bits2); if (c_class != 5) { conf->bits = c_class * 8; } else { conf->bits = bits2; } ircd_snprintf(0, ipname, sizeof(ipname), "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]); /* * This ensures endian correctness */ conf->address = inet_addr(ipname); Debug((DEBUG_DEBUG, "IPkill: %s = %08x/%i (%08x)", ipname, conf->address, conf->bits, NETMASK(conf->bits))); conf->flags |= DENY_FLAGS_IP; } conf->next = denyConfList; denyConfList = conf; } 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); } denyConfList = 0; } const struct DenyConf* conf_get_deny_list(void) { return denyConfList; } /* * read_actual_config * * cfile - name of configuration file * * returns 1 if read, 0 if unable to open */ #define MAXCONFLINKS 150 int read_actual_config(const char *cfile) { enum { MAX_FIELDS = 15 }; const char* field_vector[MAX_FIELDS + 1]; int quoted, ccount = 0, field_count = 0; char *src, *asrc, *dest, line[512]; struct ConfItem *aconf = 0; FBFILE *file; GlobalBLCount = 0; Debug((DEBUG_DEBUG, "read_actual_config: ircd.conf = %s", cfile)); sendto_opmask_butone(0, SNO_OLDSNO, "Reading configuration file: %s", cfile); if (0 == (file = fbopen(cfile, "r"))) { sendto_opmask_butone(0, SNO_OLDSNO, "Unable to open configuration file: %s", cfile); return 0; } feature_unmark(); /* unmark all features for resetting later */ while (fbgets(line, sizeof(line) - 1, file)) { int is_include = 0; /* Skip comments and whitespaces */ if ('#' == *line || IsSpace(*line)) continue; if ((src = strchr(line, '\n'))) *src = '\0'; if ((asrc = strstr(line, "include"))) is_include = 1; if (':' != line[1] && !is_include) { Debug((DEBUG_ERROR, "Bad config line: %s", line)); sendto_opmask_butone(0, SNO_OLDSNO, "Bad Config line: %s", line); continue; } for (field_count = 0; field_count <= MAX_FIELDS; field_count++) field_vector[field_count] = NULL; for (field_count = 0; field_count <= MAX_FIELDS; field_count++) field_vector[field_count] = NULL; /* * do escapes, quoting, comments, and field breakup in place * in one pass with a poor mans state machine */ field_vector[0] = line; field_count = 1; quoted = 0; for (src = line, dest = line; *src; ) { switch (*src) { case '\\': ++src; switch (*src) { case 'b': *dest++ = '\b'; ++src; break; case 'f': *dest++ = '\f'; ++src; break; case 'n': *dest++ = '\n'; ++src; break; case 'r': *dest++ = '\r'; ++src; break; case 't': *dest++ = '\t'; ++src; break; case 'v': *dest++ = '\v'; ++src; break; case '\\': *dest++ = '\\'; ++src; break; case '\0': break; default: *dest++ = *src++; break; } break; case '"': if (quoted) quoted = 0; else quoted = 1; /* * strip quotes */ ++src; break; case ':': if (quoted) *dest++ = *src++; else { *dest++ = '\0'; field_vector[field_count++] = dest; if (field_count > MAX_FIELDS) *src = '\0'; else ++src; } break; case '#': *src = '\0'; break; default: *dest++ = *src++; break; } } *dest = '\0'; if (field_count < 2 || EmptyString(field_vector[0])) continue; if (aconf) free_conf(aconf); if (0 == ircd_strcmp(field_vector[0], "include")) { read_actual_config(field_vector[1]); continue; } aconf = make_conf(); switch (*field_vector[0]) { case 'A': /* Name, e-mail address of administrator */ case 'a': /* of this server. CONF_ADMIN */ conf_add_admin(field_vector, field_count); aconf->status = CONF_ILLEGAL; break; case 'B': /* 'services' lines (aka 'bot lines'). */ conf_add_svcline(field_vector, field_count); aconf->status = CONF_ILLEGAL; break; case 'b': /* service forward lines */ conf_add_lbline(field_vector, field_count); aconf->status = CONF_ILLEGAL; 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': /* CONF_CRULEALL */ conf_add_crule(field_vector, field_count, CRULE_ALL); aconf->status = CONF_ILLEGAL; break; /* Connect rule - autos only */ case 'd': /* CONF_CRULEAUTO */ conf_add_crule(field_vector, field_count, CRULE_AUTO); aconf->status = CONF_ILLEGAL; break; case 'F': /* Feature line */ case 'f': feature_set(0, &field_vector[1], field_count - 1); aconf->status = CONF_ILLEGAL; 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 */ conf_add_deny(field_vector, field_count, 0); aconf->status = CONF_ILLEGAL; break; case 'k': /* Kill user line based on IP in ircd.conf */ conf_add_deny(field_vector, field_count, 1); aconf->status = CONF_ILLEGAL; 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': /* CONF_ME */ conf_add_local(field_vector, field_count); aconf->status = CONF_ILLEGAL; 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': /* CONF_LISTEN_PORT */ conf_add_listener(field_vector, field_count); aconf->status = CONF_ILLEGAL; break; case 'Q': /* CONF_QUARANTINE */ case 'q': conf_add_quarantine(field_vector, field_count); aconf->status = CONF_ILLEGAL; break; case 'R': /* CONF_CONNSTOP */ case 'r': conf_add_csline(field_vector, field_count); aconf->status = CONF_ILLEGAL; break; case 'T': /* print out different motd's */ case 't': /* based on hostmask - CONF_TLINES */ motd_add(field_vector[1], field_vector[2]); aconf->status = CONF_ILLEGAL; 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 'X': case 'x': conf_add_dnsbl_line(field_vector, field_count); aconf->status = CONF_ILLEGAL; break; case 'Y': case 'y': /* CONF_CLASS */ conf_add_class(field_vector, field_count); aconf->status = CONF_ILLEGAL; break; case 'S': /* Super Spoof line */ case 's': conf_add_sline(field_vector, field_count); aconf->status = CONF_ILLEGAL; break; default: Debug((DEBUG_ERROR, "Error in config file: %s", line)); sendto_opmask_butone(0, SNO_OLDSNO, "Unknown prefix in config file: %c", *field_vector[0]); aconf->status = CONF_ILLEGAL; break; } if (IsIllegal(aconf)) continue; if (!EmptyString(field_vector[1])) DupString(aconf->host, field_vector[1]); if (!EmptyString(field_vector[2])) DupString(aconf->passwd, field_vector[2]); if (field_count > 3 && !EmptyString(field_vector[3])) DupString(aconf->name, field_vector[3]); if (field_count > 4 && !EmptyString(field_vector[4])) { if (aconf->status & CONF_OPERATOR) { int* i; int flag; char *m = "O"; if (*field_vector[4]) DupString(m, field_vector[4]); for (; *m; m++) { for (i = oper_access; (flag = *i); i += 2) if (*m == (char)(*(i + 1))) { aconf->port |= flag; break; } } } else aconf->port = atoi(field_vector[4]); } if (field_count > 5 && !EmptyString(field_vector[5])) aconf->conn_class = find_class(atoi(field_vector[5])); /* * 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->conn_class == 0) aconf->conn_class = find_class(0); } 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->conn_class = aconf->conn_class; if (bconf->conn_class) 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); ircd_snprintf(0, newhost, len, "*@%s", aconf->host); MyFree(aconf->host); aconf->host = newhost; } } if (aconf->status & CONF_SERVER) { if (EmptyString(aconf->passwd)) continue; lookup_confhost(aconf); } /* * 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); 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->conn_class)); aconf->next = GlobalConfList; GlobalConfList = aconf; aconf = NULL; } if (aconf) free_conf(aconf); fbclose(file); return 1; } /* * read_configuration_file * * Read configuration file. * * returns 0, if file cannot be opened * 1, if file read */ int read_configuration_file(void) { /* unmark all features for resetting later */ feature_unmark(); /* try reading the actual ircd.conf */ if (!read_actual_config(configfile)) return 0; /* reset unmarked features */ feature_mark(); /* * Set our local FLAG_HUB if necessary. */ if (feature_bool(FEAT_HUB)) SetFlag(&me, FLAG_HUB); else ClrFlag(&me, FLAG_HUB); return 1; } /* * rehash * * Actual REHASH service routine. Called with sig == 0 if it has been called * as a result of an operator issuing this command, else assume it has been * called as a result of the server receiving a HUP signal. */ int rehash(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 (CONF_CLIENT == (tmp2->status & CONF_CLIENT)) tmp = &tmp2->next; else { *tmp = tmp2->next; tmp2->next = 0; } tmp2->status |= CONF_ILLEGAL; } else { *tmp = tmp2->next; free_conf(tmp2); } } conf_erase_crule_list(); conf_erase_deny_list(); motd_clear(); /* * delete the juped nicks list */ clearNickJupes(); clear_quarantines(); clear_slines(); clear_cslines(); clear_dnsbl_list(); clear_svclines(); clear_lblines(); if (sig != 2) flush_resolver_cache(); restart_resolver(); class_mark_delete(); mark_listeners_closing(); read_configuration_file(); log_reopen(); /* reopen log files */ #ifdef USE_SSL ssl_init(); #endif /* USE_SSL */ 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 (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, cli_name(acptr), CONF_HUB | CONF_LEAF | CONF_UWORLD); } /* 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_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; } } } return ret; } /* * init_conf * * Read configuration file. * * returns 0, if file cannot be opened * 1, if file read */ int init_conf(void) { if (read_configuration_file()) { /* * 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 */ if (0 == localConf.name || 0 == localConf.numeric) return 0; if (0 == localConf.location1) DupString(localConf.location1, ""); if (0 == localConf.location2) DupString(localConf.location2, ""); if (0 == localConf.contact) DupString(localConf.contact, ""); return 1; } return 0; } /* * find_kill * input: * client pointer * returns: * 0: Client may continue to try and connect * -1: Client was K/k:'d - sideeffect: reason was sent. * -2: Client was G/g:'d - sideeffect: reason was sent. * sideeffects: * Client may have been sent a reason why they are denied, as above. */ int find_kill(struct Client *cptr) { const char* host; const char* name; const char* realname; struct DenyConf* deny; struct Gline* agline = NULL; assert(0 != cptr); if (!cli_user(cptr)) return 0; host = cli_sockhost(cptr); name = cli_user(cptr)->username; realname = cli_info(cptr); assert(strlen(host) <= HOSTLEN); assert((name ? strlen(name) : 0) <= HOSTLEN); assert((realname ? strlen(realname) : 0) <= REALLEN); /* 2000-07-14: Rewrote this loop for massive speed increases. * -- Isomer */ for (deny = denyConfList; deny; deny = deny->next) { if (0 != match(deny->usermask, name)) continue; if (EmptyString(deny->hostmask)) break; if (deny->flags & DENY_FLAGS_REALNAME) { /* K: by real name */ if (0 == match(deny->hostmask + 2, realname)) break; } else if (deny->flags & DENY_FLAGS_IP) { /* k: by IP */ Debug((DEBUG_DEBUG, "ip: %08x network: %08x/%i mask: %08x", cli_ip(cptr).s_addr, deny->address, deny->bits, NETMASK(deny->bits))); if ((cli_ip(cptr).s_addr & NETMASK(deny->bits)) == deny->address) break; } else if (0 == match(deny->hostmask, host)) break; } if (deny) { 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 send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", deny->message); } } else if ((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)); } if (deny) return -1; if (agline) return -2; return 0; } /* * Ordinary client access check. Look for conf lines which have the same * status as the flags passed. */ enum AuthorizationCheckResult conf_check_client(struct Client *cptr) { enum AuthorizationCheckResult acr = ACR_OK; ClearAccess(cptr); 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_server() * * Check access for a server given its name (passed in cptr struct). * Must check for all C 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 line. * * Returns * 0 = Success * -1 = Access denied * -2 = Bad socket. */ int conf_check_server(struct Client *cptr) { struct ConfItem* c_conf = NULL; struct SLink* lp; Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", cli_name(cptr), cli_sockhost(cptr))); if (IsUnknown(cptr) && !attach_confs_byname(cptr, cli_name(cptr), CONF_SERVER)) { Debug((DEBUG_DNS, "No C:line for %s", cli_sockhost(cptr))); return -1; } lp = cli_confs(cptr); /* * We initiated this connection so the client should have a C 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 C:line for %s", cli_name(cptr)); det_confs_butmask(cptr, 0); return -1; } } ClearAccess(cptr); if (!c_conf) { if (cli_dns_reply(cptr)) { int i; struct hostent* hp = cli_dns_reply(cptr)->hp; const char* name = hp->h_name; /* * If we are missing a C 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(cli_sockhost(cptr), 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, cli_sockhost(cptr), 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*) &(cli_ip(cptr)), CONF_SERVER); /* * detach all conf lines that got attached by attach_confs() */ det_confs_butmask(cptr, 0); /* * if no C line, 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; } /* * attach the C line to the client structure for later use. */ attach_conf(cptr, c_conf); attach_confs_byname(cptr, cli_name(cptr), CONF_HUB | CONF_LEAF | CONF_UWORLD); if (INADDR_NONE == c_conf->ipnum.s_addr) c_conf->ipnum.s_addr = cli_ip(cptr).s_addr; Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", cli_name(cptr), cli_sockhost(cptr))); return 0; } void conf_add_svcline(const char* const* fields, int count) { struct svcline *new_svc; /* b:X2:X2@X2.AfterNET.Services:* */ /* b:AUTH:AuthServ@OperServ.AfterNET.Services:AUTH */ if (count < 2 || EmptyString(fields[1]) || EmptyString(fields[2])) return; new_svc = (struct svcline *)MyMalloc(sizeof(struct svcline)); DupString(new_svc->cmd, fields[1]); DupString(new_svc->target, fields[2]); if (!EmptyString(fields[3])) DupString(new_svc->prepend, fields[3]); new_svc->next = GlobalServicesList; GlobalServicesList = new_svc; } void clear_svclines(void) { struct svcline *svc; while ((svc = GlobalServicesList)) { GlobalServicesList = svc->next; MyFree(svc->cmd); MyFree(svc->target); if (!EmptyString(svc->prepend)) MyFree(svc->prepend); MyFree(svc); } GlobalServicesList = 0; } struct svcline *find_svc(const char *cmd) { struct svcline *confbot = NULL; for (confbot = GlobalServicesList; confbot; confbot = confbot->next) { if (confbot->cmd && !match(confbot->cmd, cmd)) return confbot; } return NULL; } void conf_add_sline(const char* const* fields, int count) { struct prefix *p; struct sline *sline; if (count < 2 || EmptyString(fields[1])) return; if (!EmptyString(fields[3])) if (EmptyString(fields[4])) { log_write(LS_CONFIG, L_CRIT, 0, "S-line: (%s) If third field is present, 4:th field must not be empty.", fields[1]); return; } if (!EmptyString(fields[4])) if (EmptyString(fields[3])) { log_write(LS_CONFIG, L_CRIT, 0, "S-line: (%s) If fourth field is present, 3:rd field must not be empty.", fields[1]); return; } p = (struct prefix *) MyMalloc(sizeof(struct prefix)); sline = (struct sline *) MyMalloc(sizeof(struct sline)); DupString(sline->spoofhost, fields[1]); if (!EmptyString(fields[2])) DupString(sline->passwd, fields[2]); else sline->passwd = NULL; if (!EmptyString(fields[3])) { DupString(sline->realhost, fields[3]); if (check_if_ipmask(sline->realhost)) { if (str2prefix(sline->realhost, p) != 0) { sline->address = p->address; sline->bits = p->bits; Debug((DEBUG_DEBUG, "S-Line: %s = %08x/%i (%08x)", sline->realhost, sline->address, sline->bits, NETMASK(sline->bits))); sline->flags = SLINE_FLAGS_IP; } else { Debug((DEBUG_FATAL, "S-Line: \"%s\" appears not to be a valid IP address, might be wildcarded.", sline->realhost)); sline->flags = SLINE_FLAGS_HOSTNAME; } } else sline->flags = SLINE_FLAGS_HOSTNAME; } else { sline->realhost = NULL; sline->flags = 0; } if (!EmptyString(fields[4])) DupString(sline->username, fields[4]); else sline->username = NULL; sline->next = GlobalSList; GlobalSList = sline; MyFree(p); } void clear_slines(void) { struct sline *sline; while ((sline = GlobalSList)) { GlobalSList = sline->next; MyFree(sline->spoofhost); if (!EmptyString(sline->passwd)) MyFree(sline->passwd); if (!EmptyString(sline->realhost)) MyFree(sline->realhost); if (!EmptyString(sline->username)) MyFree(sline->username); MyFree(sline); } GlobalSList = 0; } /* * conf_check_slines() * * Check S lines for the specified client, passed in cptr struct. * If the client's IP is S-lined, process the substitution here. * * Precondition * cptr != NULL * * Returns * 0 = No S-line found * 1 = S-line found and substitution done. * * -mbuna 9/2001 * -froo 1/2003 * */ int conf_check_slines(struct Client *cptr) { struct sline *sconf; char *hostonly; for (sconf = GlobalSList; sconf; sconf = sconf->next) { if (sconf->flags == SLINE_FLAGS_IP) { if (((cli_ip(cptr)).s_addr & NETMASK(sconf->bits)) != sconf->address.s_addr) continue; } else if (sconf->flags == SLINE_FLAGS_HOSTNAME) { if ((match(sconf->realhost, cli_sockhost(cptr)) != 0) && (match(sconf->realhost, cli_sock_ip(cptr)) != 0)) /* wildcarded IP address */ continue; } else { continue; } if (match(sconf->username, cli_user(cptr)->username) == 0) { /* Ignore user part if u@h. */ if ((hostonly = strchr(sconf->spoofhost, '@'))) hostonly++; else hostonly = sconf->spoofhost; if(!*hostonly) continue; ircd_strncpy(cli_user(cptr)->host, hostonly, HOSTLEN); log_write(LS_USER, L_INFO, LOG_NOSNOTICE, "S-Line (%s@%s) by (%#R)", cli_user(cptr)->username, hostonly, cptr); return 1; } } return 0; } /* * str2prefix() - converts a string to in_addr and bits. * based on str2prefix_ipv4() from Kunihiro Ishiguro's Zebra * * -froo 1/2003 */ int str2prefix(char *str, struct prefix *p) { int ret; char *ptr, *cp; /* Find slash inside string. */ if ((ptr = (char *)strchr(str, '/')) == NULL) { /* String doesn't contail slash. */ /* Convert string to prefix. */ if ((ret = inet_aton(str, &p->address)) == 0) return 0; /* If address doesn't contain slash we assume it host address. */ p->bits = IPV4_MAX_BITLEN; return ret; } else { cp = (char *)MyMalloc((ptr - str) + 1); ircd_strncpy(cp, str, ptr - str); *(cp + (ptr - str)) = '\0'; ret = inet_aton(cp, &p->address); MyFree(cp); /* Get prefix length. */ ret = (unsigned char) atoi(++ptr); if (ret > 32) return 0; p->bits = ret; } return ret; }