
This patch adds IPv4 and IPv6 CIDR support to a few areas using the new bitncmp and inet_parse_cidr functions. --- include/struct.h | 6 ++++- src/channel.c | 52 +++++++++++++++++++++++++++++++++++++----- src/s_bsd.c | 55 +++++++++++++++++++-------------------------- src/s_conf.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 137 insertions(+), 41 deletions(-) diff --git a/include/struct.h b/include/struct.h index 73d4a41..64a78e0 100644 --- a/include/struct.h +++ b/include/struct.h @@ -796,7 +796,11 @@ struct Listener { u_short port; char allow_string[HOSTLEN + 1]; /* allow from */ char vhost_string[HOSTLEN + 1]; /* bind to */ - struct in_addr allow_ip; /* allow from */ + int allow_cidr_bits; + struct + { + char ip[16]; + } allow_ip; /* allow from */ time_t lasttime; /* last time I accepted */ long sendK; /* counters, see below */ u_short sendB; diff --git a/src/channel.c b/src/channel.c index 4c2f61d..4f3bb07 100644 --- a/src/channel.c +++ b/src/channel.c @@ -207,6 +207,37 @@ static char *make_nick_user_host(char *nick, char *name, char *host) return (namebuf); } +/* Determine whether a client matches a CIDR banstr. */ +static int client_matches_cidrstr(aClient *cptr, char *banstr) +{ + char cidrbuf[NICKLEN + USERLEN + HOSTLEN + 6]; + char ipbuf[16]; + char *s; + int bits; + + if (!strchr(banstr, '/')) + return 0; + + s = strchr(banstr, '@'); + if (s) + s++; + else + return 0; + + bits = inet_parse_cidr(cptr->ip_family, s, ipbuf, sizeof(ipbuf)); + if (bits > 0 && bitncmp(&cptr->ip, ipbuf, bits) == 0) + { + /* Check the wildcards in the rest of the string. */ + snprintf(cidrbuf, sizeof(cidrbuf), "%s!%s@%s", + check_string(cptr->name), + check_string(cptr->user->username), + s); + if (match(banstr, cidrbuf) == 0) + return 1; + } + return 0; +} + #ifdef EXEMPT_LISTS /* Exempt list functions (+e) */ @@ -380,7 +411,8 @@ anInvite* is_invited(aClient* cptr, aChannel* chptr) for (invite = chptr->invite_list; invite; invite = invite->next) { - if (!match(invite->invstr, s) || !match(invite->invstr, s2)) + if (!match(invite->invstr, s) || !match(invite->invstr, s2) || + client_matches_cidrstr(cptr, invite->invstr)) break; } return invite; @@ -518,13 +550,15 @@ static int is_banned(aClient *cptr, aChannel *chptr, chanMember *cm) #ifdef EXEMPT_LISTS for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next) - if (!match(exempt->banstr, s) || !match(exempt->banstr, s2)) + if (!match(exempt->banstr, s) || !match(exempt->banstr, s2) || + client_matches_cidrstr(cptr, exempt->banstr)) return 0; #endif for (ban = chptr->banlist; ban; ban = ban->next) if ((match(ban->banstr, s) == 0) || - (match(ban->banstr, s2) == 0)) + (match(ban->banstr, s2) == 0) || + client_matches_cidrstr(cptr, ban->banstr)) break; if (ban) @@ -575,14 +609,16 @@ aBan *nick_is_banned(aChannel *chptr, char *nick, aClient *cptr) for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next) if (exempt->type == MTYP_FULL && ((match(exempt->banstr, s2) == 0) || - (match(exempt->banstr, s) == 0))) + (match(exempt->banstr, s) == 0) || + client_matches_cidrstr(cptr, exempt->banstr))) return NULL; #endif for (ban = chptr->banlist; ban; ban = ban->next) if (ban->type == MTYP_FULL && /* only check applicable bans */ ((match(ban->banstr, s2) == 0) || /* check host before IP */ - (match(ban->banstr, s) == 0))) + (match(ban->banstr, s) == 0) || + client_matches_cidrstr(cptr, ban->banstr))) break; return (ban); } @@ -614,7 +650,8 @@ void remove_matching_bans(aChannel *chptr, aClient *cptr, aClient *from) { bnext = ban->next; if((match(ban->banstr, targhost) == 0) || - (match(ban->banstr, targip) == 0)) + (match(ban->banstr, targip) == 0) || + client_matches_cidrstr(cptr, ban->banstr)) { if (strlen(parabuf) + strlen(ban->banstr) + 10 < (size_t) MODEBUFLEN) { @@ -699,7 +736,8 @@ void remove_matching_exempts(aChannel *chptr, aClient *cptr, aClient *from) { enext = ex->next; if((match(ex->banstr, targhost) == 0) || - (match(ex->banstr, targip) == 0)) + (match(ex->banstr, targip) == 0) || + client_matches_cidrstr(cptr, ex->banstr)) { if (strlen(parabuf) + strlen(ex->banstr) + 10 < (size_t) MODEBUFLEN) { diff --git a/src/s_bsd.c b/src/s_bsd.c index 27b307e..c568b6c 100644 --- a/src/s_bsd.c +++ b/src/s_bsd.c @@ -313,18 +313,19 @@ int add_listener(aPort *aport) len = sizeof(server.addr4); } - if(!BadPtr(aport->allow) && server.sa.sa_family == AF_INET) + if(!BadPtr(aport->allow)) { - int ad[4]; - char ipname[20]; - - ad[0] = ad[1] = ad[2] = ad[3] = 0; + int bits; strncpyzt(lstn.allow_string, aport->allow, sizeof(lstn.allow_string)); - sscanf(lstn.allow_string, "%d.%d.%d.%d", &ad[0], &ad[1], - &ad[2], &ad[3]); - ircsprintf(ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]); - lstn.allow_ip.s_addr = inet_addr(ipname); + + bits = inet_parse_cidr(server.sa.sa_family, + lstn.allow_string, &lstn.allow_ip, + sizeof(lstn.allow_ip)); + if (bits > 0) + lstn.allow_cidr_bits = bits; + else + lstn.allow_cidr_bits = -1; } if(lstn.port <= 0) /* stop stupidity cold */ @@ -1300,33 +1301,10 @@ aClient *add_connection(aListener *lptr, int fd) */ if (acptr->ip_family == AF_INET) { - char *s, *t; - get_sockhost(acptr, (char *) inetntoa((char *) &addr.addr4.sin_addr)); memcpy((char *) &acptr->ip.ip4, (char *) &addr.addr4.sin_addr, sizeof(struct in_addr)); acptr->port = ntohs(addr.addr4.sin_port); - - /* - * Check that this socket (client) is allowed to accept - * connections from this IP#. - */ - for (s = (char *) &lptr->allow_ip, t = (char *) &acptr->ip.ip4, len = 4; - len > 0; len--, s++, t++) - { - if (!*s) - continue; - if (*s != *t) - break; - } - if (len) - { - ircstp->is_ref++; - acptr->fd = -2; - free_client(acptr); - close(fd); - return NULL; - } } else if (acptr->ip_family == AF_INET6) { @@ -1336,6 +1314,19 @@ aClient *add_connection(aListener *lptr, int fd) acptr->port = ntohs(addr.addr6.sin6_port); } + /* + * Check that this socket (client) is allowed to accept + * connections from this IP#. + */ + if (lptr->allow_cidr_bits > 0 && + bitncmp(&acptr->ip, &lptr->allow_ip, lptr->allow_cidr_bits) != 0) + { + ircstp->is_ref++; + acptr->fd = -2; + free_client(acptr); + close(fd); + return NULL; + } lptr->ccount++; lptr->clients++; diff --git a/src/s_conf.c b/src/s_conf.c index 509109c..6d2c8be 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -395,9 +395,39 @@ find_oper(char *name, char *username, char *sockhost, char *hostip) for(i = 0; aoper->hosts[i]; i++) { - if(!mycmp(name, aoper->nick) && (!match(aoper->hosts[i], userhost) + if(!mycmp(name, aoper->nick) && (!match(aoper->hosts[i], userhost) || !match(aoper->hosts[i], userip))) return aoper; + if(strchr(aoper->hosts[i], '/')) + { + char cidrbuf[USERLEN + HOSTLEN + 3]; + char ipbuf1[16], ipbuf2[16]; + char *s; + int bits; + int family; + + s = strchr(aoper->hosts[i], '@'); + if (s) + s++; + else + s = aoper->hosts[i]; + + if (inet_pton(AF_INET, hostip, ipbuf1) == 1) + family = AF_INET; + else if (inet_pton(AF_INET6, hostip, ipbuf1) == 1) + family = AF_INET6; + else + family = 0; + + bits = inet_parse_cidr(family, s, ipbuf2, sizeof(ipbuf2)); + if (bits > 0 && bitncmp(ipbuf1, ipbuf2, bits) == 0) + { + /* Check the wildcards in the rest of the string. */ + ircsprintf(cidrbuf, "%s@%s", username, s); + if (match(aoper->hosts[i], cidrbuf) == 0) + return aoper; + } + } } } return NULL; @@ -526,11 +556,44 @@ attach_Iline(aClient *cptr, struct hostent *hp, char *sockhost) { if (!match(allow->ipmask, useriphost)) return (attach_iline(cptr, allow, iphost, 1)); + else if (strchr(allow->ipmask, '/')) + { + char cidrbuf[USERLEN + 1 + HOSTLEN + 1]; + char ipbuf[16]; + char *s; + int bits; + + s = strchr(allow->ipmask, '@'); + if (s) + s++; + else + continue; + + bits = inet_parse_cidr(cptr->ip_family, s, + ipbuf, sizeof(ipbuf)); + if (bits > 0 && bitncmp(&cptr->ip, ipbuf, bits) == 0) + { + /* Check the wildcards in the rest of the string. */ + ircsprintf(cidrbuf, "%s@%s", cptr->username, s); + if (match(allow->ipmask, cidrbuf) == 0) + return (attach_iline(cptr, allow, iphost, 1)); + } + } } else { if (!match(allow->ipmask, iphost)) return (attach_iline(cptr, allow, iphost, 0)); + else if (strchr(allow->ipmask, '/')) + { + char ipbuf[16]; + int bits; + + bits = inet_parse_cidr(cptr->ip_family, allow->ipmask, + ipbuf, sizeof(ipbuf)); + if (bits > 0 && bitncmp(&cptr->ip, ipbuf, bits) == 0) + return (attach_iline(cptr, allow, iphost, 1)); + } } } } -- 1.7.4.1