
These functions handle the matching and parsing of CIDR IPv4 and IPv6 addresses. Also save the binary IPv6 address for remote clients, now that we will be using it. --- include/h.h | 3 + src/Makefile.in | 1 + src/bitncmp.c | 60 ++++++++ src/inet_parse_cidr.c | 368 +++++++++++++++++++++++++++++++++++++++++++++++++ src/s_user.c | 2 + 5 files changed, 434 insertions(+), 0 deletions(-) create mode 100644 src/bitncmp.c create mode 100644 src/inet_parse_cidr.c diff --git a/include/h.h b/include/h.h index 36c8bd9..c434cef 100644 --- a/include/h.h +++ b/include/h.h @@ -388,4 +388,7 @@ int ssl_smart_shutdown(SSL *); #include "find.h" +int bitncmp(const void *l, const void *r, size_t n); +int inet_parse_cidr(int af, const char *src, void *dst, size_t size); + #endif /* H_H */ diff --git a/src/Makefile.in b/src/Makefile.in index e06a7f4..5a63328 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -23,6 +23,7 @@ SOURCES = blalloc.c bsd.c channel.c clientlist.c clones.c confparse.c \ probability.c res.c s_auth.c s_bsd.c s_conf.c s_debug.c s_err.c \ s_misc.c s_numeric.c s_serv.c s_user.c sbuf.c scache.c send.c \ struct.c support.c throttle.c userban.c whowas.c zlink.c ssl.c \ + bitncmp.c inet_parse_cidr.c \ $(ENGINE) $(CRYPTO) OBJECTS = $(SOURCES:.c=.o) version.o diff --git a/src/bitncmp.c b/src/bitncmp.c new file mode 100644 index 0000000..af674fb --- /dev/null +++ b/src/bitncmp.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1996, 1999, 2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: bitncmp.c,v 1.5 2008/11/14 02:36:51 marka Exp $"; +#endif + +#include <sys/types.h> +#include <string.h> + +/* + * int + * bitncmp(l, r, n) + * compare bit masks l and r, for n bits. + * return: + * -1, 1, or 0 in the libc tradition. + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0x11110000 in its fourth octet. + * author: + * Paul Vixie (ISC), June 1996 + */ +int +bitncmp(const void *l, const void *r, size_t n) { + unsigned int lb, rb; + size_t b; + int x; + + b = n / 8; + x = memcmp(l, r, b); + if (x || (n % 8) == 0) + return (x); + + lb = ((const unsigned char *)l)[b]; + rb = ((const unsigned char *)r)[b]; + for (b = n % 8; b > 0; b--) { + if ((lb & 0x80) != (rb & 0x80)) { + if (lb & 0x80) + return (1); + return (-1); + } + lb <<= 1; + rb <<= 1; + } + return (0); +} diff --git a/src/inet_parse_cidr.c b/src/inet_parse_cidr.c new file mode 100644 index 0000000..e985d74 --- /dev/null +++ b/src/inet_parse_cidr.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <string.h> + +#define NS_IN6ADDRSZ 16 +#define NS_INT16SZ 2 +#define NS_INADDRSZ 4 + +#define IsDigit(c) ((c) >= '0' && (c) <= '9') + +static int inet_parse_cidr_ipv4(const char *src, unsigned char *dst, size_t size); +static int inet_parse_cidr_ipv6(const char *src, unsigned char *dst, size_t size); +int inet_parse_cidr(int af, const char *src, void *dst, size_t size); + +/* + * int inet_parse_cidr(af, src, dst, size) + * convert network address from presentation to network format. + * accepts inet_pton()'s input plus trailing "/CIDR". + * return: + * number of bits specified in the /CIDR prefix length, which can + * have defaults (like /32 for IPv4) or -1 if an error occurred. + * note: + * 192.5.5.1/28 has a nonzero host part, which means it isn't a network + * as called for by inet_cidr_pton() but it can be a host address with + * an included netmask. + * author: + * Paul Vixie (ISC), October 1998 + * + * Ned Crigler, March 2011 - functions imported from PostgreSQL with + * trailing .* wildcard support added. + */ +int +inet_parse_cidr(int af, const char *src, void *dst, size_t size) +{ + unsigned char *ucdst = (unsigned char *)dst; + + if (af == AF_INET) + return inet_parse_cidr_ipv4(src, ucdst, size); + else if (af == AF_INET6) + return inet_parse_cidr_ipv6(src, ucdst, size); + else + { + errno = EAFNOSUPPORT; + return -1; + } +} + +static int +inet_parse_cidr_ipv4(const char *src, unsigned char *dst, size_t size) +{ + const unsigned char *odst = dst; + int n, + ch, + tmp, + bits; + + if (size < NS_INADDRSZ) + goto emsgsize; + else + size = NS_INADDRSZ; + + /* Get the mantissa. */ + while (ch = *src++, IsDigit((unsigned char) ch)) + { + tmp = 0; + do + { + n = (int)ch - '0'; + tmp *= 10; + tmp += n; + if (tmp > 255) + goto enoent; + } while ((ch = *src++) != '\0' && IsDigit((unsigned char) ch)); + if (size-- == 0) + goto emsgsize; + *dst++ = (unsigned char) tmp; + if (ch == '\0' || ch == '/') + break; + if (ch != '.') + goto enoent; + + /* Handle a trailing .* wildcard. */ + if (src[0] == '*' && src[1] == '\0' && dst - odst < 4) + { + bits = (dst - odst) * 8; + + /* Extend address to four octets. */ + while (size-- > 0) + *dst++ = 0; + return bits; + } + } + + /* Get the prefix length if any. */ + bits = -1; + if (ch == '/' && IsDigit((unsigned char) src[0]) && dst > odst) + { + /* CIDR width specifier. Nothing can follow it. */ + ch = *src++; /* Skip over the /. */ + bits = 0; + do + { + n = (int)ch - '0'; + bits *= 10; + bits += n; + } while ((ch = *src++) != '\0' && IsDigit((unsigned char) ch)); + if (ch != '\0') + goto enoent; + if (bits > 32) + goto emsgsize; + } + + /* Firey death and destruction unless we prefetched EOS. */ + if (ch != '\0') + goto enoent; + + /* Prefix length can default to /32 only if all four octets spec'd. */ + if (bits == -1) + { + if (dst - odst == 4) + bits = 32; + else + goto enoent; + } + + /* If nothing was written to the destination, we found no address. */ + if (dst == odst) + goto enoent; + + /* If prefix length overspecifies mantissa, life is bad. */ + if ((bits / 8) > (dst - odst)) + goto enoent; + + /* Extend address to four octets. */ + while (size-- > 0) + *dst++ = 0; + + return bits; + +enoent: + errno = ENOENT; + return (-1); + +emsgsize: + errno = EMSGSIZE; + return (-1); +} + +static int +getbits(const char *src, int *bitsp) +{ + int n; + int val; + char ch; + + val = 0; + n = 0; + while ((ch = *src++) != '\0') + { + if (ch >= '0' && ch <= '9') + { + if (n++ != 0 && val == 0) /* no leading zeros */ + return (0); + val *= 10; + val += (int)ch - '0'; + if (val > 128) /* range */ + return (0); + continue; + } + return (0); + } + if (n == 0) + return (0); + *bitsp = val; + return (1); +} + +static int +getv4(const char *src, unsigned char *dst, int *bitsp) +{ + unsigned char *odst = dst; + int n; + unsigned int val; + char ch; + + val = 0; + n = 0; + while ((ch = *src++) != '\0') + { + if (IsDigit(ch)) + { + if (n++ != 0 && val == 0) /* no leading zeros */ + return (0); + val *= 10; + val += (unsigned int)ch - '0'; + if (val > 255) /* range */ + return (0); + continue; + } + if (ch == '.' || ch == '/') + { + if (dst - odst > 3) /* too many octets? */ + return (0); + *dst++ = (unsigned char)val; + if (ch == '/') + return (getbits(src, bitsp)); + val = 0; + n = 0; + continue; + } + return (0); + } + if (n == 0) + return (0); + if (dst - odst > 3) /* too many octets? */ + return (0); + *dst++ = (unsigned char)val; + return (1); +} + +static int +inet_parse_cidr_ipv6(const char *src, unsigned char *dst, size_t size) +{ + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + unsigned char tmp[NS_IN6ADDRSZ], + *tp, + *endp, + *colonp; + const char *xdigits, + *curtok; + int ch, + saw_xdigit; + unsigned int val; + int digits; + int bits; + + if (size < NS_IN6ADDRSZ) + goto emsgsize; + + memset((tp = tmp), '\0', NS_IN6ADDRSZ); + endp = tp + NS_IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + goto enoent; + curtok = src; + saw_xdigit = 0; + val = 0; + digits = 0; + bits = -1; + while ((ch = *src++) != '\0') + { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) + { + val <<= 4; + val |= (unsigned int)(pch - xdigits); + if (++digits > 4) + goto enoent; + saw_xdigit = 1; + continue; + } + if (ch == ':') + { + curtok = src; + if (!saw_xdigit) + { + if (colonp) + goto enoent; + colonp = tp; + continue; + } + else if (*src == '\0') + goto enoent; + if (endp - tp < NS_INT16SZ) + goto emsgsize; + *tp++ = (unsigned char) (val >> 8) & 0xff; + *tp++ = (unsigned char) val & 0xff; + saw_xdigit = 0; + digits = 0; + val = 0; + continue; + } + if (ch == '.' && (endp - tp <= NS_INADDRSZ) && + getv4(curtok, tp, &bits) > 0) + { + tp += NS_INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + if (ch == '/' && getbits(src, &bits) > 0) + break; + goto enoent; + } + if (saw_xdigit) + { + if (endp - tp < NS_INT16SZ) + goto enoent; + *tp++ = (unsigned char) (val >> 8) & 0xff; + *tp++ = (unsigned char) val & 0xff; + } + if (bits == -1) + bits = 128; + + endp = tmp + 16; + + if (colonp != NULL) + { + /* + * Since some memmove()'s erroneously fail to handle overlapping + * regions, we'll do the shift by hand. + */ + const int n = (int)(tp - colonp); + int i; + + if (tp == endp) + goto enoent; + for (i = 1; i <= n; i++) + { + endp[-i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + goto enoent; + + /* + * Copy out the result. + */ + memcpy(dst, tmp, NS_IN6ADDRSZ); + + return (bits); + +enoent: + errno = ENOENT; + return (-1); + +emsgsize: + errno = EMSGSIZE; + return (-1); +} + diff --git a/src/s_user.c b/src/s_user.c index dd55f9b..35736fb 100644 --- a/src/s_user.c +++ b/src/s_user.c @@ -2161,6 +2161,8 @@ do_user(char *nick, aClient *cptr, aClient *sptr, char *username, char *host, else sptr->ip_family = 0; } + else if (inet_pton(AF_INET6, ip, &sptr->ip.ip6) == 1) + sptr->ip_family = AF_INET6; else { char *end; -- 1.7.4.1