Add IPv6 support.
authorMichael Poole <mdpoole@troilus.org>
Sat, 8 Oct 2005 14:33:12 +0000 (14:33 +0000)
committerMichael Poole <mdpoole@troilus.org>
Sat, 8 Oct 2005 14:33:12 +0000 (14:33 +0000)
configure.in: Check for struct sockaddr.sa_len and struct addrinfo.
    Also check for freeaddrinfo, getaddrinfo and getnameinfo.

src/common.h: Define irc_in_addr and macros to operate on it.  Declare
    irc_ntop(), irc_ntop_mask(), irc_pton(), irc_check_mask() and
    irc_ntoa().  Remove declaration for parse_ipmask() and undefine
    INADDR_NONE, INADDR_LOOPBACK and MATCH_IPMASK().

src/compat.h (struct addrinfo): Define if it and some flags if not
    defined by the system.
  (getaddrinfo): Declare if necessary.
  (getnameinfo): Likewise.
  (freeaddrinfo): Likewise.

src/compat.c (memcpy): Do not try to fall back onto bcopy.
  (getaddrinfo): Define if it is not defined by the system.
  (freeaddrinfo): Likewise.

src/gline.c (gline_alternate_target): Gut in preparation of a
    necessary rewrite.  Blah.

src/hash.h (struct userNode): Change type of ip field.

src/ioset.c (ioset_connect): Update to support non-IPv4 protocols.

src/main.c (uplink_insert): Update to support non-IPv4 protocols.

src/mod-snoop.c (snoop_new_user): Use irc_ntoa() instead of inet_ntoa().

src/mod-sockcheck.c (sockcheck_cache_info): Update address field type
    and length of hostname field.
  (sockcheck_conf): Update type of local_addr.
  (sockcheck_issue_gline): Update to use irc_ntop().
  (expand_var): Get client IPv4 address from its new place.
  (sockcheck_decide): Do not print the IP twice.
  (sockcheck_begin_test): Remove unnecessary typecast.
  (sockcheck_start_client): Do not print the IP twice.
  (sockcheck_queue_address): Use irc_in_addr_t and irc_ntoa() to form
    the target hostname.
  (cmd_hostscan): Update manipulation functions to use new macros and
    functions for irc_in_addr_t.
  (cmd_clearhost): Likewise.
  (sockcheck_new_user): Likewise.
  (sockcheck_read_conf): Use getaddrinfo() instead of IPv4-only
    functions.

src/nickserv.c (cmd_register): Use newer validity checks for IP.

src/opserv.c (IDENT_DATA): Use irc_ntoa() instead of inet_ntoa().
  (struct opservDiscrim): Update ip_mask and ip_mask_bits type.
  (cmd_whois): Use irc_ntoa() instead of inet_ntoa().
  (opserv_new_user_check): Use irc_ntop() instead of inet_ntoa() and
    update to newer validity check macros.
  (opserv_user_cleanup): Use irc_ntop() instead of inet_ntoa().
  (cmd_addtrust): Update to irc_in_addr_t and its functions.
  (foreach_matching_user): Use irc_pton() instead of the mess that
    existed before.
  (opserv_discrim_create): Use irc_pton() instead of parse_ipmask().
  (discrim_match): Use irc_check_mask() instead of MATCH_IPMASK(), and
    remove ip_mask_str check.
  (opserv_discrim_search): Decide whether an IP match is fully
    qualified based on ip_mask_bits instead of ip_mask_str.
  (trace_domains_func): Add IPv6 support.
  (opserv_staff_alert): Update to new IP validity check macro.

src/proto.h (struct uplinkNode): Fix type of bind_addr field.

src/proto-bahamut.c (AddUser): Use irc_in_addr_t.
  (AddService): Default to an all-zero IP instead of loopback.
  (AddClone): Likewise.
  (irc_user): Use new layout for IPv4 in irc_in_addr_t.
  (cmd_nick): Likewise.

src/proto-common.c (create_socket_client): Remove unnecessary
    typecast.
  (generate_hostmask): Update to use irc_in_addr_t and support
    functions instead of IPv4-specific things.

src/proto-p10.c (irc_p10_pton): New function.
  (irc_p10_ntop): New function.
  (irc_user): Use irc_p10_ntop().
  (AddClone): Use irc_p10_pton().

src/tools.c (ctype): New array holding values of characters as hex.
  (irc_ntop): New function.
  (irc_ntop_mask): New function.
  (irc_pton_ip4): New helper function.
  (irc_pton): New function.
  (irc_ntoa): New function.
  (irc_check_mask): New function.
  (user_matches_glob): Use irc_ntoa() instead of inet_ntoa().
  (parse_ipmask): Delete this function.
git-archimport-id: srvx@srvx.net--2005-srvx/srvx--devo--1.3--patch-28

18 files changed:
ChangeLog
configure.in
src/common.h
src/compat.c
src/compat.h
src/gline.c
src/hash.h
src/ioset.c
src/main.c
src/mod-snoop.c
src/mod-sockcheck.c
src/nickserv.c
src/opserv.c
src/proto-bahamut.c
src/proto-common.c
src/proto-p10.c
src/proto.h
src/tools.c

index 1adaa85bd22379f0e4fac2237020ca15bb6e4fd8..e9a3926f69206b524e2ee5c8586155a2c94375b7 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,114 @@
 # arch-tag: automatic-ChangeLog--srvx@srvx.net--2005-srvx/srvx--devo--1.3
 #
 
+2005-10-08 14:33:12 GMT        Michael Poole <mdpoole@troilus.org>     patch-28
+
+    Summary:
+      Add IPv6 support.
+    Revision:
+      srvx--devo--1.3--patch-28
+
+    configure.in: Check for struct sockaddr.sa_len and struct addrinfo.
+        Also check for freeaddrinfo, getaddrinfo and getnameinfo.
+    
+    src/common.h: Define irc_in_addr and macros to operate on it.  Declare
+        irc_ntop(), irc_ntop_mask(), irc_pton(), irc_check_mask() and
+        irc_ntoa().  Remove declaration for parse_ipmask() and undefine
+        INADDR_NONE, INADDR_LOOPBACK and MATCH_IPMASK().
+    
+    src/compat.h (struct addrinfo): Define if it and some flags if not
+        defined by the system.
+      (getaddrinfo): Declare if necessary.
+      (getnameinfo): Likewise.
+      (freeaddrinfo): Likewise.
+    
+    src/compat.c (memcpy): Do not try to fall back onto bcopy.
+      (getaddrinfo): Define if it is not defined by the system.
+      (freeaddrinfo): Likewise.
+    
+    src/gline.c (gline_alternate_target): Gut in preparation of a
+        necessary rewrite.  Blah.
+    
+    src/hash.h (struct userNode): Change type of ip field.
+    
+    src/ioset.c (ioset_connect): Update to support non-IPv4 protocols.
+    
+    src/main.c (uplink_insert): Update to support non-IPv4 protocols.
+    
+    src/mod-snoop.c (snoop_new_user): Use irc_ntoa() instead of inet_ntoa().
+    
+    src/mod-sockcheck.c (sockcheck_cache_info): Update address field type
+        and length of hostname field.
+      (sockcheck_conf): Update type of local_addr.
+      (sockcheck_issue_gline): Update to use irc_ntop().
+      (expand_var): Get client IPv4 address from its new place.
+      (sockcheck_decide): Do not print the IP twice.
+      (sockcheck_begin_test): Remove unnecessary typecast.
+      (sockcheck_start_client): Do not print the IP twice.
+      (sockcheck_queue_address): Use irc_in_addr_t and irc_ntoa() to form
+        the target hostname.
+      (cmd_hostscan): Update manipulation functions to use new macros and
+        functions for irc_in_addr_t.
+      (cmd_clearhost): Likewise.
+      (sockcheck_new_user): Likewise.
+      (sockcheck_read_conf): Use getaddrinfo() instead of IPv4-only
+        functions.
+    
+    src/nickserv.c (cmd_register): Use newer validity checks for IP.
+    
+    src/opserv.c (IDENT_DATA): Use irc_ntoa() instead of inet_ntoa().
+      (struct opservDiscrim): Update ip_mask and ip_mask_bits type.
+      (cmd_whois): Use irc_ntoa() instead of inet_ntoa().
+      (opserv_new_user_check): Use irc_ntop() instead of inet_ntoa() and
+        update to newer validity check macros.
+      (opserv_user_cleanup): Use irc_ntop() instead of inet_ntoa().
+      (cmd_addtrust): Update to irc_in_addr_t and its functions.
+      (foreach_matching_user): Use irc_pton() instead of the mess that
+        existed before.
+      (opserv_discrim_create): Use irc_pton() instead of parse_ipmask().
+      (discrim_match): Use irc_check_mask() instead of MATCH_IPMASK(), and
+        remove ip_mask_str check.
+      (opserv_discrim_search): Decide whether an IP match is fully
+        qualified based on ip_mask_bits instead of ip_mask_str.
+      (trace_domains_func): Add IPv6 support.
+      (opserv_staff_alert): Update to new IP validity check macro.
+    
+    src/proto.h (struct uplinkNode): Fix type of bind_addr field.
+    
+    src/proto-bahamut.c (AddUser): Use irc_in_addr_t.
+      (AddService): Default to an all-zero IP instead of loopback.
+      (AddClone): Likewise.
+      (irc_user): Use new layout for IPv4 in irc_in_addr_t.
+      (cmd_nick): Likewise.
+    
+    src/proto-common.c (create_socket_client): Remove unnecessary
+        typecast.
+      (generate_hostmask): Update to use irc_in_addr_t and support
+        functions instead of IPv4-specific things.
+    
+    src/proto-p10.c (irc_p10_pton): New function.
+      (irc_p10_ntop): New function.
+      (irc_user): Use irc_p10_ntop().
+      (AddClone): Use irc_p10_pton().
+    
+    src/tools.c (ctype): New array holding values of characters as hex.
+      (irc_ntop): New function.
+      (irc_ntop_mask): New function.
+      (irc_pton_ip4): New helper function.
+      (irc_pton): New function.
+      (irc_ntoa): New function.
+      (irc_check_mask): New function.
+      (user_matches_glob): Use irc_ntoa() instead of inet_ntoa().
+      (parse_ipmask): Delete this function.
+
+    modified files:
+     ChangeLog configure.in src/common.h src/compat.c src/compat.h
+     src/gline.c src/hash.h src/ioset.c src/main.c src/mod-snoop.c
+     src/mod-sockcheck.c src/nickserv.c src/opserv.c
+     src/proto-bahamut.c src/proto-common.c src/proto-p10.c
+     src/proto.h src/tools.c
+
+
 2005-10-07 02:51:12 GMT        Michael Poole <mdpoole@troilus.org>     patch-27
 
     Summary:
index 69e75c6144aea3d3b34063bdfaa78cdbc4f6367e..0769fb9da56ff0d6f2c5f2a4e2fdaf2a912a45d8 100644 (file)
@@ -73,13 +73,22 @@ AC_CHECK_MEMBER([struct dirent.d_type],
         [AC_DEFINE(HAVE_DIRENT_D_TYPE, 1, [Define if struct dirent exists and includes the d_type element.])],,[#include <dirent.h>])
 
 dnl portability stuff, hurray! -Jedi
+AC_CHECK_MEMBER([struct sockaddr.sa_len],
+                [AC_DEFINE([HAVE_SOCKADDR_SA_LEN],,[Define if struct sockaddr has sa_len field])],
+                [],[#include <sys/types.h>
+#include <sys/socket.h>])
+AC_CHECK_MEMBER([struct addrinfo.ai_flags],
+                [AC_DEFINE([HAVE_STRUCT_ADDRINFO],,[Define if struct addrinfo declared])],
+                [],[#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>])
 AC_CHECK_FUNCS(gettimeofday)
 if test $ac_cv_func_gettimeofday = no; then
   AC_CHECK_FUNCS(ftime,,AC_MSG_ERROR([ftime or gettimeofday required.  srvx build will fail.]))
 fi
 
 dnl We have fallbacks in case these are missing, so just check for them.
-AC_CHECK_FUNCS(bcopy getpagesize memcpy memset strdup strerror strsignal localtime_r setrlimit inet_ntoa getopt getopt_long regcomp regexec regfree sysconf,,)
+AC_CHECK_FUNCS(freeaddrinfo getaddrinfo getnameinfo getpagesize memcpy memset strdup strerror strsignal localtime_r setrlimit getopt getopt_long regcomp regexec regfree sysconf,,)
 
 dnl Check for absolutely required library functions.
 AC_CHECK_FUNCS(select socket strcspn strspn strtod strtoul,,AC_MSG_ERROR([a required function was not found.  srvx build will fail.]))
index e2ee1675e422257a2ef60c6fb6ac20d76949cd4b..6e0d5c120b35a487bee707672f28b69ffdc5c2eb 100644 (file)
@@ -38,13 +38,6 @@ extern struct tm *localtime_r(const time_t *clock, struct tm *res);
 #define false 0
 #endif
 
-#ifndef INADDR_NONE
-#define INADDR_NONE 0xffffffffL
-#endif
-#ifndef INADDR_LOOPBACK
-#define INADDR_LOOPBACK 0x7f000001L
-#endif
-
 #define ArrayLength(x)         (sizeof(x)/sizeof(x[0]))
 #define safestrncpy(dest, src, len) do { char *d = (dest); const char *s = (src); size_t l = strlen(s)+1;  if ((len) < l) l = (len); memmove(d, s, l); d[l-1] = 0; } while (0)
 
@@ -130,6 +123,38 @@ extern time_t now;
 extern int quit_services;
 extern struct log_type *MAIN_LOG;
 
+typedef union irc_in_addr {
+    uint32_t in6_32[4];
+    uint16_t in6[8];
+    uint8_t in6_8[16];
+} irc_in_addr_t;
+
+#define irc_in_addr_is_valid(ADDR) (((ADDR).in6[0] && (ADDR).in6[0] != 65535) \
+                                    || (ADDR).in6[1] != (ADDR).in6[0] \
+                                    || (ADDR).in6[2] != (ADDR).in6[0] \
+                                    || (ADDR).in6[3] != (ADDR).in6[0] \
+                                    || (ADDR).in6[4] != (ADDR).in6[0] \
+                                    || (ADDR).in6[5] != (ADDR).in6[0] \
+                                    || (ADDR).in6[6] != (ADDR).in6[0] \
+                                    || (ADDR).in6[7] != (ADDR).in6[0])
+#define irc_in_addr_is_ipv4(ADDR) (!(ADDR).in6[0] && !(ADDR).in6[1] \
+                                   && !(ADDR).in6[2] && !(ADDR).in6[3] \
+                                   && !(ADDR).in6[4] && (ADDR).in6[6] \
+                                   && (!(ADDR).in6[5] || (ADDR).in6[5] == 65535))
+#define irc_in_addr_is_ipv6(ADDR) !irc_in_addr_is_ipv4(ADDR)
+#define irc_in_addr_is_loopback(ADDR) (irc_in_addr_is_ipv4(ADDR) ? (ADDR).in6_8[12] == 127 \
+                                       : (ADDR).in6[0] == 0 && (ADDR).in6[1] == 0 \
+                                       && (ADDR).in6[2] == 0 && (ADDR).in6[3] == 0 \
+                                       && (ADDR).in6[4] == 0 && (ADDR).in6[5] == 0 \
+                                       && (ADDR).in6[6] == 0 && (ADDR).in6[7] == 1)
+#define IRC_NTOP_MAX_SIZE 40
+unsigned int irc_ntop(char *output, unsigned int out_size, const irc_in_addr_t *addr);
+#define IRC_NTOP_MASK_MAX_SIZE (IRC_NTOP_MAX_SIZE + 4)
+unsigned int irc_ntop_mask(char *output, unsigned int out_size, const irc_in_addr_t *addr, unsigned char bits);
+unsigned int irc_pton(irc_in_addr_t *addr, unsigned char *bits, const char *input);
+unsigned int irc_check_mask(const irc_in_addr_t *check, const irc_in_addr_t *mask, unsigned char bits);
+const char *irc_ntoa(const irc_in_addr_t *addr);
+
 int create_socket_client(struct uplinkNode *target);
 void close_socket(void);
 
@@ -154,8 +179,6 @@ char *sanitize_ircmask(char *text);
 
 unsigned long ParseInterval(const char *interval);
 unsigned long ParseVolume(const char *volume);
-int parse_ipmask(const char *str, struct in_addr *addr, unsigned long *mask);
-#define MATCH_IPMASK(test, addr, mask) (((ntohl(test.s_addr) & mask) ^ (ntohl(addr.s_addr) & mask)) == 0)
 
 #define MD5_CRYPT_LENGTH 42
 /* buffer[] must be at least MD5_CRYPT_LENGTH bytes long */
index 820771ee7dba6908f8572b18f8c2c673c5ebaf36..b77aaae38424159d12e34f75743f21034ffb6b7f 100644 (file)
@@ -40,10 +40,6 @@ extern gettimeofday(struct timeval * tv, struct timezone * tz);
 #ifndef HAVE_MEMCPY
 extern void * memcpy(void * dest, void const * src, unsigned long n)
 {
-#ifdef HAVE_BCOPY
-    bcopy(src,dest,n);
-    return dest;
-#else
 /* very slow, your fault for not having memcpy()*/
     unsigned char * td=dest;
     unsigned char * ts=src;
@@ -55,7 +51,6 @@ extern void * memcpy(void * dest, void const * src, unsigned long n)
     for (i=0; i<n; i++)
         td[i] = ts[i];
     return dest;
-#endif
 }
 #endif
 
@@ -353,3 +348,62 @@ extern char const * strerror(int errornum)
     return "Unknown error";
 }
 #endif
+
+#ifndef HAVE_GETADDRINFO
+
+int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res)
+{
+    /* Only support IPv4 if OS doesn't provide this function. */
+    struct sockaddr_in sin;
+
+    if (hints && hints->ai_family != AF_INET)
+        return 1;
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+
+    if (node) {
+        if (hints && hints->ai_flags & AI_NUMERICHOST) {
+            if (!inet_aton(node, &sin.sin_addr))
+                return 2;
+        } else {
+            struct hostent *he;
+            he = gethostbyname(node);
+            if (!he)
+                return 3;
+            memcpy(&sin.sin_addr, he->h_addr, he->h_length);
+        }
+    } else if (hints && hints->ai_flags & AI_PASSIVE) {
+        /* leave it unspecifed */
+    } else {
+        inet_aton("127.0.0.1", &sin.sin_addr);
+    }
+
+    if (!service)
+        sin.sin_port = ntohs(0);
+    else if (!(sin.sin_port = ntohs(atoi(service))))
+        return 4;
+
+    *res = calloc(1, sizeof(**res) + sizeof(sin));
+    (*res)->ai_family = sin.sin_family;
+    (*res)->ai_socktype = hints && hints->ai_socktype ? hints->ai_socktype : SOCK_STREAM;
+    (*res)->ai_protocol = hints && hints->ai_socktype ? hints->ai_socktype : 0;
+    (*res)->ai_addrlen = sizeof(sin);
+    (*res)->ai_addr = (struct sockaddr*)(*res + 1);
+    memcpy((*res)->ai_addr, &sin, (*res)->ai_addrlen);
+    (*res)->ai_canonname = 0;
+    (*res)->ai_next = 0;
+    return 0;
+}
+
+/* TODO: implement fallback getnameinfo() */
+
+void freeaddrinfo(struct addrinfo *res)
+{
+    struct addrinfo *next;
+    for (; res; res = next) {
+        next = res->ai_next;
+        free(res);
+    }
+}
+
+#endif
index 85712597e36b1a4bcf20360f1ddc5facbc16ff2e..232af42ed6373465fb33a96062c751061a6a9051 100644 (file)
@@ -53,6 +53,14 @@ char *alloca();
 #include <sys/types.h>
 #endif
 
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -87,4 +95,30 @@ extern char * strdup(char const * str);
 extern char const * strerror(int errornum);
 #endif
 
+#ifndef HAVE_STRUCT_ADDRINFO
+
+struct addrinfo {
+    int ai_flags;
+    int ai_family;
+    int ai_socktype;
+    int ai_protocol;
+    size_t ai_addrlen;
+    struct sockaddr *ai_addr;
+    char *ai_canonname;
+    struct addrinfo *ai_next;
+};
+
+#define AI_PASSIVE 1
+#define AI_CANONNAME 2
+
+#endif /* !defined(HAVE_STRUCT_ADDRINFO) */
+
+#ifndef HAVE_GETADDRINFO
+
+int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
+int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags);
+void freeaddrinfo(struct addrinfo *res);
+
+#endif
+
 #endif /* COMPAT_H */
index 8071f370adcedaa93dcec9ca7a1980bdd17d6131..559541340ea58aa1da4bec3540023910494ba1b1 100644 (file)
@@ -170,8 +170,6 @@ static char *
 gline_alternate_target(const char *target)
 {
     const char *hostname;
-    unsigned long ip;
-    char *res;
 
     /* If no host part, bail. */
     if (!(hostname = strchr(target, '@')))
@@ -179,23 +177,17 @@ gline_alternate_target(const char *target)
     /* If host part contains wildcards, bail. */
     if (hostname[strcspn(hostname, "*?/")])
         return NULL;
-    /* If host part looks like an IP, parse it that way. */
-    if (!hostname[strspn(hostname+1, "0123456789.")+1]) {
-        struct in_addr in;
-        struct hostent *he;
-        if (inet_aton(hostname+1, &in)
-            && (he = gethostbyaddr((char*)&in, sizeof(in), AF_INET))) {
-            res = malloc((hostname - target) + 2 + strlen(he->h_name));
-            sprintf(res, "%.*s@%s", (int)(hostname - target), target, he->h_name);
-            return res;
-        } else
-            return NULL;
-    } else if (getipbyname(hostname+1, &ip)) {
-        res = malloc((hostname - target) + 18);
-        sprintf(res, "%.*s@%lu.%lu.%lu.%lu", (int)(hostname - target), target, ip & 255, (ip >> 8) & 255, (ip >> 16) & 255, (ip >> 24) & 255);
-        return res;
-    } else
-        return NULL;
+    /* Get parsed address and canonical name for host. */
+#if 0
+    irc_in_addr_t in; /* move this to the right place */
+    if (irc_pton(&in, NULL, hostname+1)) {
+        if (getnameinfo(/*TODO*/))
+              return NULL;
+    } else if (!getaddrinfo(/*TODO*/)) {
+    } else return NULL;
+#else
+    return NULL;
+#endif
 }
 
 struct gline *
index 443f9f4ddd5a41af8d1ddbd1d878f5abb6ce0f6f..975ccf44ad355de9d487a9fecf33ffc14b64393e 100644 (file)
@@ -108,7 +108,7 @@ struct userNode {
     unsigned int num_local : 18;
 #endif
     unsigned int dead : 1;        /* Is user waiting to be recycled? */
-    struct in_addr ip;            /* User's IP address */
+    irc_in_addr_t ip;             /* User's IP address */
     long modes;                   /* user flags +isw etc... */
 
     time_t timestamp;             /* Time of last nick change */
index e102732bc317dcd5b609babb3713bab813c681bb..8a3aea063c42a55e1da21b567d773101293021f0 100644 (file)
@@ -130,17 +130,22 @@ struct io_fd *
 ioset_connect(struct sockaddr *local, unsigned int sa_size, const char *peer, unsigned int port, int blocking, void *data, void (*connect_cb)(struct io_fd *fd, int error)) {
     int fd, res;
     struct io_fd *io_fd;
-    struct sockaddr_in sin;
-    unsigned long ip;
-
-    if (!getipbyname(peer, &ip)) {
-        log_module(MAIN_LOG, LOG_ERROR, "getipbyname(%s) failed.", peer);
+    struct addrinfo hints, *ai;
+    char portnum[10];
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = local ? local->sa_family : 0;
+    hints.ai_socktype = SOCK_STREAM;
+    snprintf(portnum, sizeof(portnum), "%u", port);
+    if (getaddrinfo(peer, portnum, &hints, &ai)) {
+        log_module(MAIN_LOG, LOG_ERROR, "getaddrinfo(%s, %s) failed.", peer, portnum);
         return NULL;
     }
-    sin.sin_addr.s_addr = ip;
+
     if (local) {
         if ((fd = socket(local->sa_family, SOCK_STREAM, 0)) < 0) {
             log_module(MAIN_LOG, LOG_ERROR, "socket() for %s returned errno %d (%s)", peer, errno, strerror(errno));
+            freeaddrinfo(ai);
             return NULL;
         }
         if (bind(fd, local, sa_size) < 0) {
@@ -149,18 +154,19 @@ ioset_connect(struct sockaddr *local, unsigned int sa_size, const char *peer, un
     } else {
         if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
             log_module(MAIN_LOG, LOG_ERROR, "socket() for %s returned errno %d (%s).", peer, errno, strerror(errno));
+            freeaddrinfo(ai);
             return NULL;
         }
     }
-    sin.sin_family = AF_INET;
-    sin.sin_port = htons(port);
+
     if (blocking) {
-        res = connect(fd, (struct sockaddr*)&sin, sizeof(sin));
+        res = connect(fd, ai->ai_addr, ai->ai_addrlen);
         io_fd = ioset_add(fd);
     } else {
         io_fd = ioset_add(fd);
-        res = connect(fd, (struct sockaddr*)&sin, sizeof(sin));
+        res = connect(fd, ai->ai_addr, ai->ai_addrlen);
     }
+    freeaddrinfo(ai);
     if (!io_fd) {
         close(fd);
         return NULL;
index 7c6e2cd64077ea56d33697600a22ed34fa449750..2517516f6bb677f0d62caf31ad8c1465651ce6a2 100644 (file)
@@ -141,10 +141,9 @@ uplink_insert(const char *key, void *data, UNUSED_ARG(void *extra))
 {
     struct uplinkNode *uplink = malloc(sizeof(struct uplinkNode));
     struct record_data *rd = data;
+    struct addrinfo hints, *ai;
     int enabled = 1;
     char *str;
-    struct sockaddr_in *sin;
-    unsigned long addr;
 
     if(!uplink)
     {
@@ -174,15 +173,17 @@ uplink_insert(const char *key, void *data, UNUSED_ARG(void *extra))
     uplink->tries = 0;
 
     str = database_get_data(rd->d.object, "bind_address", RECDB_QSTRING);
-    uplink->bind_addr_len = sizeof(*sin);
-    if (str && getipbyname(str, &addr)) 
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_flags = AI_PASSIVE;
+    hints.ai_socktype = SOCK_STREAM;
+    if (!getaddrinfo(str, NULL, &hints, &ai))
     {
-       sin = calloc(1, uplink->bind_addr_len);
-       sin->sin_family = AF_INET;
-       sin->sin_addr.s_addr = addr;
-       uplink->bind_addr = sin;
-    } 
-    else 
+        uplink->bind_addr_len = ai->ai_addrlen;
+       uplink->bind_addr = calloc(1, ai->ai_addrlen);
+        memcpy(uplink->bind_addr, ai->ai_addr, ai->ai_addrlen);
+        freeaddrinfo(ai);
+    }
+    else
     {
        uplink->bind_addr = NULL;
        uplink->bind_addr_len = 0;
index d0589d2f1d177aed4a31512d75d6740697a8e5d6..11a0e55050cc5bb12ae3f5cdf6e6cac97aae30f5 100644 (file)
@@ -100,7 +100,7 @@ snoop_new_user(struct userNode *user) {
     if (!snoop_cfg.enabled) return 0;
     if (user->uplink->burst && !snoop_cfg.show_bursts) return 0;
     UPDATE_TIMESTAMP();
-    SNOOP("$bNICK$b %s %s@%s [%s] on %s", user->nick, user->ident, user->hostname, inet_ntoa(user->ip), user->uplink->name);
+    SNOOP("$bNICK$b %s %s@%s [%s] on %s", user->nick, user->ident, user->hostname, irc_ntoa(&user->ip), user->uplink->name);
     return 0;
 }
 
index eeb7adad8a38d29f0b179dd817e14010a9757a4a..4788e13f2f5ee4cd9fe1e0b64020110393728d96 100644 (file)
@@ -51,11 +51,11 @@ enum sockcheck_decision {
 };
 
 typedef struct {
-    enum sockcheck_decision decision;
-    time_t last_touched;
+    irc_in_addr_t addr;
     const char *reason;
-    struct in_addr addr;
-    char hostname[16];
+    time_t last_touched;
+    enum sockcheck_decision decision;
+    char hostname[IRC_NTOP_MAX_SIZE]; /* acts as key for checked_ip_dict */
 } *sockcheck_cache_info;
 
 DECLARE_LIST(sci_list, sockcheck_cache_info);
@@ -75,7 +75,7 @@ static dict_t checked_ip_dict;
  * state and the input).  Mealy state machines require fewer states to
  * match the same input than Moore machines (where the output is only
  * a function of the current state).
- * 
+ *
  * A state is characterized by sending some data (possibly nothing),
  * waiting a certain amount of time to receive one of zero or more
  * responses, and a decision (accept, reject, continue to another
@@ -129,7 +129,7 @@ static struct {
     unsigned int max_read;
     unsigned int gline_duration;
     unsigned int max_cache_age;
-    struct sockaddr_in *local_addr;
+    struct sockaddr *local_addr;
     int local_addr_len;
 } sockcheck_conf;
 
@@ -203,11 +203,10 @@ sockcheck_list_unref(struct sockcheck_list *list)
 static void
 sockcheck_issue_gline(sockcheck_cache_info sci)
 {
-    char *target = alloca(3+strlen(sci->hostname));
-    strcpy(target, "*@");
-    strcpy(target+2, sci->hostname);
-    log_module(PC_LOG, LOG_INFO, "Issuing gline for client at IP %s hostname %s: %s", inet_ntoa(sci->addr), sci->hostname, sci->reason);
-    gline_add("ProxyCheck", target, sockcheck_conf.gline_duration, sci->reason, now, 1);
+    char addr[IRC_NTOP_MAX_SIZE + 2] = {'*', '@', '\0'};
+    irc_ntop(addr + 2, sizeof(addr) - 2, &sci->addr);
+    log_module(PC_LOG, LOG_INFO, "Issuing gline for client at %s: %s", addr + 2, sci->reason);
+    gline_add("ProxyCheck", addr, sockcheck_conf.gline_duration, sci->reason, now, 1);
 }
 
 static struct sockcheck_client *
@@ -261,7 +260,8 @@ sockcheck_print_client(const struct sockcheck_client *client)
     log_module(PC_LOG, LOG_INFO, "client %p: { addr = %p { decision = %s; last_touched = "FMT_TIME_T"; reason = %s; hostname = \"%s\" }; "
         "test_index = %d; state = %p { port = %d; type = %s; template = \"%s\"; ... }; "
         "fd = %p(%d); read = %p; read_size = %d; read_used = %d; read_pos = %d; }",
-        client, client->addr, decs[client->addr->decision], client->addr->last_touched, client->addr->reason, client->addr->hostname,
+        client, client->addr, decs[client->addr->decision], client->addr->last_touched,
+        client->addr->reason, client->addr->hostname,
         client->test_index, client->state,
         (client->state ? client->state->port : 0),
         (client->state ? decs[client->state->type] : "N/A"),
@@ -301,7 +301,7 @@ expand_var(const struct sockcheck_client *client, char var, char **p_expansion,
         exp_length = strlen(expansion);
         break;
     case 'i':
-       exp4 = client->addr->addr.s_addr;
+       exp4 = client->addr->addr.in6_32[3];
        exp_length = sizeof(exp4);
        expansion = (char*)&exp4;
        break;
@@ -435,7 +435,7 @@ sockcheck_decide(struct sockcheck_client *client, enum sockcheck_decision decisi
     case ACCEPT:
        /* do nothing */
         if (SOCKCHECK_DEBUG) {
-            log_module(PC_LOG, LOG_INFO, "Proxy check passed for client at IP %s hostname %s.", inet_ntoa(client->addr->addr), client->addr->hostname);
+            log_module(PC_LOG, LOG_INFO, "Proxy check passed for client at %s.", client->addr->hostname);
         }
         break;
     case REJECT:
@@ -443,7 +443,7 @@ sockcheck_decide(struct sockcheck_client *client, enum sockcheck_decision decisi
        proxies_detected++;
        sockcheck_issue_gline(client->addr);
         if (SOCKCHECK_DEBUG) {
-            log_module(PC_LOG, LOG_INFO, "Proxy check rejects client at IP %s hostname %s (%s)", inet_ntoa(client->addr->addr), client->addr->hostname, client->addr->reason);
+            log_module(PC_LOG, LOG_INFO, "Proxy check rejects client at %s (%s)", client->addr->hostname, client->addr->reason);
         }
        /* Don't compare test_index != 0 directly, because somebody
         * else may have reordered the tests already. */
@@ -669,7 +669,7 @@ sockcheck_begin_test(struct sockcheck_client *client)
         client->state = client->tests->list[client->test_index];
         client->read_pos = 0;
         client->read_used = 0;
-        client->fd = io_fd = ioset_connect((struct sockaddr*)sockcheck_conf.local_addr, sizeof(struct sockaddr), client->addr->hostname, client->state->port, 0, client, sockcheck_connected);
+        client->fd = io_fd = ioset_connect(sockcheck_conf.local_addr, sockcheck_conf.local_addr_len, client->addr->hostname, client->state->port, 0, client, sockcheck_connected);
         if (!io_fd) {
             client->test_index++;
             continue;
@@ -701,17 +701,17 @@ sockcheck_start_client(unsigned int idx)
     sockcheck_num_clients++;
     if (!tests) return;
     client = client_list[idx] = sockcheck_alloc_client(sci);
-    log_module(PC_LOG, LOG_INFO, "Proxy-checking client at %s (%s) as client %d (%p) of %d.", inet_ntoa(sci->addr), sci->hostname, idx, client, sockcheck_num_clients);
+    log_module(PC_LOG, LOG_INFO, "Proxy-checking client at %s as client %d (%p) of %d.", sci->hostname, idx, client, sockcheck_num_clients);
     client->test_rep = 0;
     client->client_index = idx;
     sockcheck_begin_test(client);
 }
 
 void
-sockcheck_queue_address(struct in_addr addr)
+sockcheck_queue_address(irc_in_addr_t addr)
 {
     sockcheck_cache_info sci;
-    char *ipstr=inet_ntoa(addr);
+    const char *ipstr = irc_ntoa(&addr);
 
     sci = dict_find(checked_ip_dict, ipstr, NULL);
     if (sci) {
@@ -1017,25 +1017,24 @@ static MODCMD_FUNC(cmd_defproxy)
 static MODCMD_FUNC(cmd_hostscan)
 {
     unsigned int n;
-    unsigned long addr;
-    struct in_addr ipaddr;
-    char hnamebuf[64];
+    irc_in_addr_t ipaddr;
+    char hnamebuf[IRC_NTOP_MAX_SIZE];
 
     for (n=1; n<argc; n++) {
        struct userNode *un = GetUserH(argv[n]);
 
         if (un) {
-            if ((un->ip.s_addr == 0) || (ntohl(un->ip.s_addr) == INADDR_LOOPBACK)) {
+            if (!irc_in_addr_is_valid(un->ip)
+                || irc_in_addr_is_loopback(un->ip)) {
                 reply("PCMSG_UNSCANNABLE_IP", un->nick);
             } else {
-                strcpy(hnamebuf, inet_ntoa(un->ip));
+                irc_ntop(hnamebuf, sizeof(hnamebuf), &un->ip);
                 sockcheck_queue_address(un->ip);
                 reply("PCMSG_ADDRESS_QUEUED", hnamebuf);
             }
         } else {
             char *scanhost = argv[n];
-            if (getipbyname(scanhost, &addr)) {
-                ipaddr.s_addr = htonl(addr);
+            if (!irc_pton(&ipaddr, NULL, scanhost)) {
                 sockcheck_queue_address(ipaddr);
                 reply("PCMSG_ADDRESS_QUEUED", scanhost);
             } else {
@@ -1049,14 +1048,14 @@ static MODCMD_FUNC(cmd_hostscan)
 static MODCMD_FUNC(cmd_clearhost)
 {
     unsigned int n;
-    char hnamebuf[64];
+    char hnamebuf[IRC_NTOP_MAX_SIZE];
 
     for (n=1; n<argc; n++) {
         struct userNode *un = GetUserH(argv[n]);
         const char *scanhost;
 
         if (un) {
-            strcpy(hnamebuf, inet_ntoa(un->ip));
+            irc_ntop(hnamebuf, sizeof(hnamebuf), &un->ip);
             scanhost = hnamebuf;
         } else {
             scanhost = argv[n];
@@ -1106,8 +1105,8 @@ static MODCMD_FUNC(cmd_stats_proxycheck)
 static int
 sockcheck_new_user(struct userNode *user) {
     /* If they have a bum IP, or are bursting in, don't proxy-check or G-line them. */
-    if (user->ip.s_addr
-        && (ntohl(user->ip.s_addr) != INADDR_LOOPBACK)
+    if (irc_in_addr_is_valid(user->ip)
+        && !irc_in_addr_is_loopback(user->ip)
         && !user->uplink->burst)
         sockcheck_queue_address(user->ip);
     return 0;
@@ -1130,6 +1129,7 @@ static void
 sockcheck_read_conf(void)
 {
     dict_t my_node;
+    struct addrinfo *ai;
     const char *str;
 
     /* set the defaults here in case the entire record is missing */
@@ -1154,25 +1154,14 @@ sockcheck_read_conf(void)
         str = database_get_data(my_node, "gline_duration", RECDB_QSTRING);
         if (str) sockcheck_conf.gline_duration = ParseInterval(str);
        str = database_get_data(my_node, "address", RECDB_QSTRING);
-       if (str) {
-           struct sockaddr_in *sin;
-           unsigned long addr;
-
-           sockcheck_conf.local_addr_len = sizeof(*sin);
-           if (getipbyname(str, &addr)) {
-               sin = malloc(sockcheck_conf.local_addr_len);
-               sin->sin_family = AF_INET;
-               sin->sin_port = 0;
-               sin->sin_addr.s_addr = addr;
-#ifdef HAVE_SIN_LEN
-               sin->sin_len = 0;
-#endif
-               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-               sockcheck_conf.local_addr = sin;
-           } else {
-               log_module(PC_LOG, LOG_ERROR, "Error: Unable to get host named `%s', not checking a specific address.", str);
-               sockcheck_conf.local_addr = NULL;
-           }
+        if (!getaddrinfo(str, NULL, NULL, &ai)) {
+           sockcheck_conf.local_addr_len = ai->ai_addrlen;
+            sockcheck_conf.local_addr = calloc(1, ai->ai_addrlen);
+            memcpy(sockcheck_conf.local_addr, ai->ai_addr, ai->ai_addrlen);
+        } else {
+            sockcheck_conf.local_addr_len = 0;
+            sockcheck_conf.local_addr = NULL;
+            log_module(PC_LOG, LOG_ERROR, "Error: Unable to get host named `%s', not checking a specific address.", str);
        }
     }
 }
index e092ac4174fa819ad22afd0702814a172fc86e86..26370cde0bda7755210f1fbe409303951b3b236d 100644 (file)
@@ -1111,6 +1111,7 @@ nickserv_set_email_addr(struct handle_info *hi, const char *new_email_addr)
 
 static NICKSERV_FUNC(cmd_register)
 {
+    irc_in_addr_t ip;
     struct handle_info *hi;
     const char *email_addr, *password;
     int no_auth;
@@ -1195,7 +1196,7 @@ static NICKSERV_FUNC(cmd_register)
         string_list_append(hi->masks, strdup("*@*"));
     } else {
         string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
-        if (user->ip.s_addr && user->hostname[strspn(user->hostname, "0123456789.")])
+        if (irc_in_addr_is_valid(user->ip) && !irc_pton(&ip, NULL, user->hostname))
             string_list_append(hi->masks, generate_hostmask(user, GENMASK_OMITNICK|GENMASK_BYIP|GENMASK_NO_HIDING|GENMASK_ANY_IDENT));
     }
 
index d3fbb7dff4c5530ccc1d7d2d3bac5c4dd7e793b4..35ead4a223f4fee44a57dc93fdd449845776ed7c 100644 (file)
@@ -78,7 +78,7 @@
 #define KEY_ISSUED "issued"
 
 #define IDENT_FORMAT           "%s [%s@%s/%s]"
-#define IDENT_DATA(user)       user->nick, user->ident, user->hostname, inet_ntoa(user->ip)
+#define IDENT_DATA(user)       user->nick, user->ident, user->hostname, irc_ntoa(&user->ip)
 #define MAX_CHANNELS_WHOIS     50
 #define OSMSG_PART_REASON       "%s has no reason."
 #define OSMSG_KICK_REQUESTED    "Kick requested by %s."
@@ -318,14 +318,15 @@ opserv_free_hostinfo(void *data)
 
 typedef struct opservDiscrim {
     struct chanNode *channel;
-    char *mask_nick, *mask_ident, *mask_host, *mask_info, *server, *ip_mask_str, *reason, *accountmask;
-    unsigned long limit, ip_mask;
-    struct in_addr ip_addr;
+    char *mask_nick, *mask_ident, *mask_host, *mask_info, *server, *reason, *accountmask;
+    irc_in_addr_t ip_mask;
+    unsigned long limit;
+    time_t min_ts, max_ts;
     unsigned int min_level, max_level, domain_depth, duration, min_clones, min_channels, max_channels;
+    unsigned char ip_mask_bits;
     unsigned int match_opers : 1, option_log : 1;
     unsigned int chan_req_modes : 2, chan_no_modes : 2;
     int authed : 2, info_space : 2;
-    time_t min_ts, max_ts;
 } *discrim_t;
 
 struct discrim_and_source {
@@ -1172,7 +1173,7 @@ static MODCMD_FUNC(cmd_whois)
     reply("OSMSG_WHOIS_HOST", target->ident, target->hostname);
     if (IsFakeHost(target))
         reply("OSMSG_WHOIS_FAKEHOST", target->fakehost);
-    reply("OSMSG_WHOIS_IP", inet_ntoa(target->ip));
+    reply("OSMSG_WHOIS_IP", irc_ntoa(&target->ip));
     if (target->modes) {
        bpos = 0;
 #define buffer_cat(str) (herelen = strlen(str), memcpy(buffer+bpos, str, herelen), bpos += herelen)
@@ -1722,6 +1723,7 @@ opserv_new_user_check(struct userNode *user)
 {
     struct opserv_hostinfo *ohi;
     struct gag_entry *gag;
+    char addr[IRC_NTOP_MAX_SIZE];
 
     /* Check to see if we should ignore them entirely. */
     if (IsLocal(user) || IsService(user))
@@ -1740,9 +1742,10 @@ opserv_new_user_check(struct userNode *user)
     }
 
     /* Add to host info struct */
-    if (!(ohi = dict_find(opserv_hostinfo_dict, inet_ntoa(user->ip), NULL))) {
+    irc_ntop(addr, sizeof(addr), &user->ip);
+    if (!(ohi = dict_find(opserv_hostinfo_dict, addr, NULL))) {
         ohi = calloc(1, sizeof(*ohi));
-        dict_insert(opserv_hostinfo_dict, strdup(inet_ntoa(user->ip)), ohi);
+        dict_insert(opserv_hostinfo_dict, strdup(addr), ohi);
         userList_init(&ohi->clients);
     }
     userList_append(&ohi->clients, user);
@@ -1760,8 +1763,10 @@ opserv_new_user_check(struct userNode *user)
     }
 
     /* Only warn or G-line if there's an untrusted max and their IP is sane. */
-    if (opserv_conf.untrusted_max && user->ip.s_addr && (ntohl(user->ip.s_addr) != INADDR_LOOPBACK)) {
-        struct trusted_host *th = dict_find(opserv_trusted_hosts, inet_ntoa(user->ip), NULL);
+    if (opserv_conf.untrusted_max
+        && irc_in_addr_is_valid(user->ip)
+        && !irc_in_addr_is_loopback(user->ip)) {
+        struct trusted_host *th = dict_find(opserv_trusted_hosts, addr, NULL);
         unsigned int limit = th ? th->limit : opserv_conf.untrusted_max;
         if (!limit) {
             /* 0 means unlimited hosts */
@@ -1771,7 +1776,7 @@ opserv_new_user_check(struct userNode *user)
                 send_message(ohi->clients.list[nn], opserv, "OSMSG_CLONE_WARNING");
         } else if (ohi->clients.used > limit) {
             char target[18];
-            sprintf(target, "*@%s", inet_ntoa(user->ip));
+            sprintf(target, "*@%s", addr);
             gline_add(opserv->nick, target, opserv_conf.clone_gline_duration, "AUTO Excessive connections from a single host.", now, 1);
         }
     }
@@ -1783,6 +1788,7 @@ static void
 opserv_user_cleanup(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
 {
     struct opserv_hostinfo *ohi;
+    char addr[IRC_NTOP_MAX_SIZE];
 
     if (IsLocal(user)) {
         /* Try to remove it from the reserved nick dict without
@@ -1791,9 +1797,11 @@ opserv_user_cleanup(struct userNode *user, UNUSED_ARG(struct userNode *killer),
         dict_remove(opserv_reserved_nick_dict, user->nick);
         return;
     }
-    if ((ohi = dict_find(opserv_hostinfo_dict, inet_ntoa(user->ip), NULL))) {
+    irc_ntop(addr, sizeof(addr), &user->ip);
+    if ((ohi = dict_find(opserv_hostinfo_dict, addr, NULL))) {
         userList_remove(&ohi->clients, user);
-        if (ohi->clients.used == 0) dict_remove(opserv_hostinfo_dict, inet_ntoa(user->ip));
+        if (ohi->clients.used == 0)
+            dict_remove(opserv_hostinfo_dict, addr);
     }
 }
 
@@ -2118,7 +2126,7 @@ static MODCMD_FUNC(cmd_addtrust)
 {
     unsigned long interval;
     char *reason, *tmp;
-    struct in_addr tmpaddr;
+    irc_in_addr_t tmpaddr;
     unsigned int count;
 
     if (dict_find(opserv_trusted_hosts, argv[1], NULL)) {
@@ -2126,7 +2134,7 @@ static MODCMD_FUNC(cmd_addtrust)
         return 0;
     }
 
-    if (!inet_aton(argv[1], &tmpaddr)) {
+    if (!irc_pton(&tmpaddr, NULL, argv[1])) {
         reply("OSMSG_BAD_IP", argv[1]);
         return 0;
     }
@@ -2446,15 +2454,8 @@ foreach_matching_user(const char *hostmask, discrim_search_func func, void *extr
     discrim->info_space = -1;
     dupmask = strdup(hostmask);
     if (split_ircmask(dupmask, &discrim->mask_nick, &discrim->mask_ident, &discrim->mask_host)) {
-        if (discrim->mask_host && !discrim->mask_host[strspn(discrim->mask_host, "0123456789.?*")]) {
-            if (!parse_ipmask(discrim->mask_host, &discrim->ip_addr, &discrim->ip_mask)) {
-                log_module(OS_LOG, LOG_ERROR, "Couldn't parse %s as an IP mask!", discrim->mask_host);
-                free(discrim);
-                free(dupmask);
-                return 0;
-            }
-            discrim->mask_host = 0;
-        }
+        if (!irc_pton(&discrim->ip_mask, &discrim->ip_mask_bits, discrim->mask_host))
+            discrim->ip_mask_bits = 0;
         matched = opserv_discrim_search(discrim, func, extra);
     } else {
        log_module(OS_LOG, LOG_ERROR, "Couldn't split IRC mask for gag %s!", hostmask);
@@ -2939,8 +2940,11 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in
        } else if (irccasecmp(argv[i], "server") == 0) {
            discrim->server = argv[++i];
        } else if (irccasecmp(argv[i], "ip") == 0) {
-           j = parse_ipmask(argv[++i], &discrim->ip_addr, &discrim->ip_mask);
-           if (!j) discrim->ip_mask_str = argv[i];
+            j = irc_pton(&discrim->ip_mask, &discrim->ip_mask_bits, argv[++i]);
+            if (!j) {
+                send_message(user, opserv, "OSMSG_BAD_IP", argv[i]);
+                goto fail;
+            }
     } else if (irccasecmp(argv[i], "account") == 0) {
         if (discrim->authed == 0) {
             send_message(user, opserv, "OSMSG_ACCOUNTMASK_AUTHED");
@@ -3106,21 +3110,20 @@ discrim_match(discrim_t discrim, struct userNode *user)
         || (discrim->mask_info && !match_ircglob(user->info, discrim->mask_info))
         || (discrim->server && !match_ircglob(user->uplink->name, discrim->server))
         || (discrim->accountmask && (!user->handle_info || !match_ircglob(user->handle_info->handle, discrim->accountmask)))
-        || (discrim->ip_mask && !MATCH_IPMASK(user->ip, discrim->ip_addr, discrim->ip_mask))) {
+        || (discrim->ip_mask_bits && !irc_check_mask(&user->ip, &discrim->ip_mask, discrim->ip_mask_bits))
+        )
+        return 0;
+    if (discrim->channel && !GetUserMode(discrim->channel, user))
         return 0;
-    }
-    if (discrim->channel && !GetUserMode(discrim->channel, user)) return 0;
     access = user->handle_info ? user->handle_info->opserv_level : 0;
     if ((access < discrim->min_level)
         || (access > discrim->max_level)) {
         return 0;
     }
-    if (discrim->ip_mask_str) {
-        if (!match_ircglob(inet_ntoa(user->ip), discrim->ip_mask_str)) return 0;
-    }
     if (discrim->min_clones > 1) {
-        struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, inet_ntoa(user->ip), NULL);
-        if (!ohi || (ohi->clients.used < discrim->min_clones)) return 0;
+        struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, irc_ntoa(&user->ip), NULL);
+        if (!ohi || (ohi->clients.used < discrim->min_clones))
+            return 0;
     }
     return 1;
 }
@@ -3147,8 +3150,8 @@ opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data)
                 userList_append(&matched, mn->user);
             }
         }
-    } else if (discrim->ip_mask_str && !discrim->ip_mask_str[strcspn(discrim->ip_mask_str, "?*")]) {
-        struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, discrim->ip_mask_str, NULL);
+    } else if (discrim->ip_mask_bits == 128) {
+        struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, irc_ntoa(&discrim->ip_mask), NULL);
         if (!ohi) {
             userList_clean(&matched);
             return 0;
@@ -3293,29 +3296,42 @@ static int
 trace_domains_func(struct userNode *match, void *extra)
 {
     struct discrim_and_source *das = extra;
+    irc_in_addr_t ip;
     unsigned long *count;
     unsigned int depth;
     char *hostname;
+    char ipmask[IRC_NTOP_MASK_MAX_SIZE];
 
-    if (!match->hostname[strspn(match->hostname, "0123456789.")]) {
-        char ipmask[16];
-        unsigned long matchip = ntohl(match->ip.s_addr);
-        /* raw IP address.. use up to first three octets of IP */
-        switch (das->discrim->domain_depth) {
-        default:
-            snprintf(ipmask, sizeof(ipmask), "%lu.%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255, (matchip>>8)&255);
-            break;
-        case 2:
-            snprintf(ipmask, sizeof(ipmask), "%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255);
-            break;
-        case 1:
-            snprintf(ipmask, sizeof(ipmask), "%lu.*", (matchip>>24)&255);
-            break;
-        }
+    if (irc_pton(&ip, NULL, match->hostname)) {
+        if (irc_in_addr_is_ipv4(ip)) {
+            unsigned long matchip = ntohl(ip.in6_32[3]);
+            /* raw IP address.. use up to first three octets of IP */
+            switch (das->discrim->domain_depth) {
+            default:
+                snprintf(ipmask, sizeof(ipmask), "%lu.%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255, (matchip>>8)&255);
+                break;
+            case 2:
+                snprintf(ipmask, sizeof(ipmask), "%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255);
+                break;
+            case 1:
+                snprintf(ipmask, sizeof(ipmask), "%lu.*", (matchip>>24)&255);
+                break;
+            }
+        } else if (irc_in_addr_is_ipv6(ip)) {
+            switch (das->discrim->domain_depth) {
+            case 1:  depth = 16; goto ipv6_pfx;
+            case 2:  depth = 24; goto ipv6_pfx;
+            case 3:  depth = 32; goto ipv6_pfx;
+            default: depth = das->discrim->domain_depth;
+            ipv6_pfx:
+                irc_ntop_mask(ipmask, sizeof(ipmask), &ip, depth);
+            }
+        } else safestrncpy(ipmask, match->hostname, sizeof(ipmask));
+        ipmask[sizeof(ipmask) - 1] = '\0';
         hostname = ipmask;
     } else {
         hostname = match->hostname + strlen(match->hostname);
-        for (depth=das->discrim->domain_depth; 
+        for (depth=das->discrim->domain_depth;
              depth && (hostname > match->hostname);
              depth--) {
             hostname--;
@@ -3764,7 +3780,7 @@ opserv_staff_alert(struct userNode *user, UNUSED_ARG(struct handle_info *old_han
     else
         return;
 
-    if (user->ip.s_addr)
+    if (irc_in_addr_is_valid(user->ip))
         send_channel_notice(opserv_conf.staff_auth_channel, opserv, IDENT_FORMAT" authed to %s account %s", IDENT_DATA(user), type, user->handle_info->handle);
     else
         send_channel_notice(opserv_conf.staff_auth_channel, opserv, "%s [%s@%s] authed to %s account %s", user->nick, user->ident, user->hostname, type, user->handle_info->handle);
index 8df3b0f519041217ef8fd86a98aa7dc6192b4d7e..4f950aee745559caf5d6fca3980248d1566ce04a 100644 (file)
@@ -112,7 +112,7 @@ is_valid_nick(const char *nick) {
 }
 
 struct userNode *
-AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *userinfo, time_t timestamp, struct in_addr realip, const char *stamp) {
+AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *userinfo, time_t timestamp, irc_in_addr_t realip, const char *stamp) {
     struct userNode *uNode, *oldUser;
     unsigned int nn;
 
@@ -174,7 +174,7 @@ struct userNode *
 AddService(const char *nick, const char *modes, const char *desc, const char *hostname) {
     time_t timestamp = now;
     struct userNode *old_user = GetUserH(nick);
-    struct in_addr ipaddr = { INADDR_LOOPBACK };
+    static const irc_in_addr_t ipaddr;
     if (old_user) {
         if (IsLocal(old_user))
             return old_user;
@@ -189,7 +189,7 @@ struct userNode *
 AddClone(const char *nick, const char *ident, const char *hostname, const char *desc) {
     time_t timestamp = now;
     struct userNode *old_user = GetUserH(nick);
-    struct in_addr ipaddr = { INADDR_LOOPBACK };
+    static const irc_in_addr_t ipaddr;
     if (old_user) {
         if (IsLocal(old_user))
             return old_user;
@@ -258,7 +258,7 @@ irc_user(struct userNode *user) {
     modes[modelen] = 0;
     putsock("NICK %s %d "FMT_TIME_T" +%s %s %s %s %d %u :%s",
             user->nick, user->uplink->hops+2, user->timestamp, modes,
-            user->ident, user->hostname, user->uplink->name, 0, ntohl(user->ip.s_addr), user->info);
+            user->ident, user->hostname, user->uplink->name, 0, ntohl(user->ip.in6_32[3]), user->info);
 }
 
 void
@@ -780,12 +780,13 @@ static CMD_FUNC(cmd_nick) {
         /* new nick from a server */
         char id[8];
         unsigned long stamp;
-        struct in_addr ip;
+        irc_in_addr_t ip;
 
         if (argc < 10) return 0;
         stamp = strtoul(argv[8], NULL, 0);
         if (stamp) inttobase64(id, stamp, IDLEN);
-        ip.s_addr = (argc > 10) ? atoi(argv[9]) : 0;
+        if (argc > 10)
+            ip.in6_32[3] = htonl(atoi(argv[9]));
         un = AddUser(GetServerH(argv[7]), argv[1], argv[5], argv[6], argv[4], argv[argc-1], atoi(argv[3]), ip, (stamp ? id : 0));
     }
     return 1;
index c740093c38edaf6b254a9c1e2f41e7a01f0bcded..b5e5118a02e246706df003fbfdc5f21fb122dd52 100644 (file)
@@ -134,7 +134,7 @@ create_socket_client(struct uplinkNode *target)
 
     log_module(MAIN_LOG, LOG_INFO, "Connecting to %s:%i...", addr, port);
 
-    socket_io_fd = ioset_connect((struct sockaddr*)cManager.uplink->bind_addr, sizeof(struct sockaddr), addr, port, 1, 0, NULL);
+    socket_io_fd = ioset_connect(cManager.uplink->bind_addr, cManager.uplink->bind_addr_len, addr, port, 1, 0, NULL);
     if (!socket_io_fd) {
         log_module(MAIN_LOG, LOG_ERROR, "Connection to uplink failed: %s (%d)", strerror(errno), errno);
         target->state = DISCONNECTED;
@@ -654,8 +654,8 @@ irc_make_chanmode(struct chanNode *chan, char *out)
 char *
 generate_hostmask(struct userNode *user, int options)
 {
-    char *nickname, *ident, *hostname;
-    char *mask;
+    irc_in_addr_t ip;
+    char *nickname, *ident, *hostname, *mask;
     int len, ii;
 
     /* figure out string parts */
@@ -680,32 +680,21 @@ generate_hostmask(struct userNode *user, int options)
     } else if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
         hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
         sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
-    } else if (options & GENMASK_STRICT_HOST) {
-        if (options & GENMASK_BYIP)
-            hostname = inet_ntoa(user->ip);
-    } else if ((options & GENMASK_BYIP) || !hostname[strspn(hostname, "0123456789.")]) {
-        /* Should generate an IP-based hostmask.  By popular acclaim, a /16
-         * hostmask is used by default. */
-        unsigned masked_ip, mask, masklen;
-        masklen = 16;
-        mask = ~0 << masklen;
-        masked_ip = ntohl(user->ip.s_addr) & mask;
-        hostname = alloca(32);
-        if (options & GENMASK_SRVXMASK) {
-            sprintf(hostname, "%d.%d.%d.%d/%d", (masked_ip>>24)&0xFF, (masked_ip>>16)&0xFF, (masked_ip>>8)&0xFF, masked_ip&0xFF, masklen);
+    } else if (options & GENMASK_STRICT_HOST && options & GENMASK_BYIP) {
+        hostname = (char*)irc_ntoa(&user->ip);
+    } else if ((options & GENMASK_BYIP) || irc_pton(&ip, NULL, hostname)) {
+        /* Should generate an IP-based hostmask. */
+        hostname = alloca(IRC_NTOP_MAX_SIZE);
+        hostname[IRC_NTOP_MAX_SIZE-1] = '\0';
+        if (irc_in_addr_is_ipv4(user->ip)) {
+            /* By popular acclaim, a /16 hostmask is used. */
+            sprintf(hostname, "%d.%d.*", user->ip.in6_8[12], user->ip.in6_8[13]);
+        } else if (irc_in_addr_is_ipv6(user->ip)) {
+            /* Who knows what the default mask should be?  Use a /48 to start with. */
+            sprintf(hostname, "%x:%x:%x:*", user->ip.in6[0], user->ip.in6[1], user->ip.in6[2]);
         } else {
-            int ofs = 0;
-            for (ii=0; ii<4; ii++) {
-                if (masklen) {
-                    ofs += sprintf(hostname+ofs, "%d.", (masked_ip>>24)&0xFF);
-                    masklen -= 8;
-                    masked_ip <<= 8;
-                } else {
-                    ofs += sprintf(hostname+ofs, "*.");
-                }
-            }
-            /* Truncate the last . */
-            hostname[ofs-1] = 0;
+            /* Unknown type; just copy IP directly. */
+            irc_ntop(hostname, IRC_NTOP_MAX_SIZE, &user->ip);
         }
     } else {
         int cnt;
index eb2d6ae8cb934bb1d7ede34f9ae8fa4299bcc1d8..cf9b07637dfb630ae2e3e333a53ec46b8984adfb 100644 (file)
@@ -374,13 +374,84 @@ irc_server(struct server *srv)
     }
 }
 
+static void
+irc_p10_pton(irc_in_addr_t *ip, const char *input)
+{
+    if (strlen(input) == 6) {
+        unsigned int value;
+        memset(ip, 0, 6 * sizeof(ip->in6[0]));
+        value = base64toint(input, 6);
+        ip->in6[6] = htons(value >> 16);
+        ip->in6[7] = htons(value & 65535);
+    } else {
+        unsigned int pos = 0;
+        do {
+            if (*input == '_') {
+                unsigned int left;
+                for (left = (25 - strlen(input)) / 3; left; left--)
+                    ip->in6[pos++] = 0;
+                input++;
+            } else {
+                ip->in6[pos++] = ntohs(base64toint(input, 3));
+                input += 3;
+            }
+        } while (pos < 8);
+    }
+}
+
+static void
+irc_p10_ntop(char *output, const irc_in_addr_t *ip)
+{
+    if (irc_in_addr_is_ipv4(*ip)) {
+        unsigned int in4;
+        in4 = (ntohs(ip->in6[6]) << 16) | ntohs(ip->in6[7]);
+        inttobase64(output, in4, 6);
+        output[6] = '\0';
+    } else if (irc_in_addr_is_ipv6(*ip)) {
+        unsigned int max_start, max_zeros, curr_zeros, zero, ii;
+        /* Can start by printing out the leading non-zero parts. */
+        for (ii = 0; (ip->in6[ii]) && (ii < 8); ++ii) {
+            inttobase64(output, ntohs(ip->in6[ii]), 3);
+            output += 3;
+        }
+        /* Find the longest run of zeros. */
+        for (max_start = zero = ii, max_zeros = curr_zeros = 0; ii < 8; ++ii) {
+            if (!ip->in6[ii])
+                curr_zeros++;
+            else if (curr_zeros > max_zeros) {
+                max_start = ii - curr_zeros;
+                max_zeros = curr_zeros;
+                curr_zeros = 0;
+            }
+        }
+        if (curr_zeros > max_zeros) {
+            max_start = ii - curr_zeros;
+            max_zeros = curr_zeros;
+            curr_zeros = 0;
+        }
+        /* Print the rest of the address */
+        for (ii = zero; ii < 8; ) {
+            if ((ii == max_start) && max_zeros) {
+                *output++ = '_';
+                ii += max_zeros;
+            } else {
+                inttobase64(output, ntohs(ip->in6[ii]), 3);
+                output += 3;
+            }
+        }
+        *output = '\0';
+    } else {
+        strcpy(output, "???");
+    }
+}
+
 void
 irc_user(struct userNode *user)
 {
-    char b64ip[7];
+    char b64ip[25];
     if (!user)
         return;
-    inttobase64(b64ip, ntohl(user->ip.s_addr), 6);
+    irc_p10_ntop(b64ip, &user->ip);
     if (user->modes) {
         int modelen;
         char modes[32];
@@ -1912,7 +1983,7 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char *
     safestrncpy(uNode->info, userinfo, sizeof(uNode->info));
     safestrncpy(uNode->hostname, hostname, sizeof(uNode->hostname));
     safestrncpy(uNode->numeric, numeric, sizeof(uNode->numeric));
-    uNode->ip.s_addr = htonl(base64toint(realip, 6));
+    irc_p10_pton(&uNode->ip, realip);
     uNode->timestamp = timestamp;
     modeList_init(&uNode->channels);
     uNode->uplink = uplink;
index 373470c421754781c9ce9e8edba060f85fddc41c..71e55647aa6f636c6e2265539d9358a28551f6de 100644 (file)
@@ -60,7 +60,7 @@ struct uplinkNode
     char               *host;
     int                        port;
 
-    struct sockaddr_in         *bind_addr;
+    struct sockaddr    *bind_addr;
     int                bind_addr_len;
 
     char               *password;
index bea5099f745fefe02279d66a632c5ee680639a27..a626386e47a3cbbd87f261bbdaf20bed55b71157 100644 (file)
@@ -50,13 +50,23 @@ static const unsigned char convert2n[256] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0, 
+  52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
    0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
   15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
    0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
   41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0
 };
 
+static const unsigned char ctype[256] = {
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
+   0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
 unsigned long int
 base64toint(const char* s, int count)
 {
@@ -78,6 +88,271 @@ const char* inttobase64(char* buf, unsigned int v, unsigned int count)
   return buf;
 }
 
+unsigned int
+irc_ntop(char *output, unsigned int out_size, const irc_in_addr_t *addr)
+{
+    static const char hexdigits[] = "0123456789abcdef";
+    unsigned int pos;
+
+    assert(output);
+    assert(addr);
+
+    if (irc_in_addr_is_ipv4(*addr)) {
+        unsigned int ip4;
+
+        ip4 = (ntohs(addr->in6[6]) << 16) | ntohs(addr->in6[7]);
+        pos = snprintf(output, out_size, "%u.%u.%u.%u", (ip4 >> 24), (ip4 >> 16) & 255, (ip4 >> 8) & 255, ip4 & 255);
+   } else {
+        unsigned int part, max_start, max_zeros, curr_zeros, ii;
+
+        /* Find longest run of zeros. */
+        for (max_start = max_zeros = curr_zeros = ii = 0; ii < 8; ++ii) {
+            if (!addr->in6[ii])
+                curr_zeros++;
+            else if (curr_zeros > max_zeros) {
+                max_start = ii - curr_zeros;
+                max_zeros = curr_zeros;
+                curr_zeros = 0;
+            }
+        }
+        if (curr_zeros > max_zeros) {
+            max_start = ii - curr_zeros;
+            max_zeros = curr_zeros;
+        }
+
+        /* Print out address. */
+#define APPEND(CH) do { if (pos < out_size) output[pos] = (CH); pos++; } while (0)
+        for (pos = 0, ii = 0; ii < 8; ++ii) {
+            if ((max_zeros > 0) && (ii == max_start)) {
+                if (ii == 0)
+                    APPEND(':');
+                APPEND(':');
+                ii += max_zeros - 1;
+                continue;
+            }
+            part = ntohs(addr->in6[ii]);
+            if (part >= 0x1000)
+                APPEND(hexdigits[part >> 12]);
+            if (part >= 0x100)
+                APPEND(hexdigits[(part >> 8) & 15]);
+            if (part >= 0x10)
+                APPEND(hexdigits[(part >> 4) & 15]);
+            APPEND(hexdigits[part & 15]);
+            if (ii < 7)
+                APPEND(':');
+        }
+#undef APPEND
+        output[pos < out_size ? pos : out_size - 1] = '\0';
+    }
+
+    return pos;
+}
+
+unsigned int
+irc_ntop_mask(char *output, unsigned int out_size, const irc_in_addr_t *addr, unsigned char bits)
+{
+    char base_addr[IRC_NTOP_MAX_SIZE];
+    int len;
+
+    if (bits >= 128)
+        return irc_ntop(output, out_size, addr);
+    if (!irc_ntop(base_addr, sizeof(base_addr), addr))
+        return 0;
+    len = snprintf(output, out_size, "%s/%d", base_addr, bits);
+    if ((unsigned int)len >= out_size)
+        return 0;
+    return len;
+}
+
+static unsigned int
+irc_pton_ip4(const char *input, unsigned char *pbits, uint32_t *output)
+{
+    unsigned int dots = 0, pos = 0, part = 0, ip = 0, bits = 32;
+
+    /* Intentionally no support for bizarre IPv4 formats (plain
+     * integers, octal or hex components) -- only vanilla dotted
+     * decimal quads, optionally with trailing /nn.
+     */
+    if (input[0] == '.')
+        return 0;
+    while (1) switch (input[pos]) {
+    default:
+        if (dots < 3)
+            return 0;
+    out:
+        ip |= part << (24 - 8 * dots++);
+        *output = htonl(ip);
+        if (pbits)
+            *pbits = bits;
+        return pos;
+    case '.':
+        if (input[++pos] == '.')
+            return 0;
+        ip |= part << (24 - 8 * dots++);
+        part = 0;
+        if (input[pos] == '*') {
+            while (input[++pos] == '*') ;
+            if (input[pos] != '\0')
+                return 0;
+            if (pbits)
+                *pbits = dots * 8;
+            *output = htonl(ip);
+            return pos;
+        }
+        break;
+    case '/':
+        if (!pbits || !isdigit(input[pos + 1]))
+            return 0;
+        for (bits = 0; isdigit(input[++pos]); )
+            bits = bits * 10 + input[pos] - '0';
+        if (bits > 32)
+            return 0;
+        goto out;
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+        part = part * 10 + input[pos++] - '0';
+        if (part > 255)
+            return 0;
+        break;
+    }
+}
+
+unsigned int
+irc_pton(irc_in_addr_t *addr, unsigned char *bits, const char *input)
+{
+    const char *part_start = NULL;
+    char *colon;
+    char *dot;
+    unsigned int part = 0, pos = 0, ii = 0, cpos = 8;
+
+    assert(input);
+    memset(addr, 0, sizeof(*addr));
+    colon = strchr(input, ':');
+    dot = strchr(input, '.');
+
+    if (colon && (!dot || (dot > colon))) {
+        /* Parse IPv6, possibly like ::127.0.0.1.
+         * This is pretty straightforward; the only trick is borrowed
+         * from Paul Vixie (BIND): when it sees a "::" continue as if
+         * it were a single ":", but note where it happened, and fill
+         * with zeros afterwards.
+         */
+        if (input[pos] == ':') {
+            if ((input[pos+1] != ':') || (input[pos+2] == ':'))
+                return 0;
+            cpos = 0;
+            pos += 2;
+            part_start = input + pos;
+        }
+        while (ii < 8) switch (input[pos]) {
+        case '0': case '1': case '2': case '3': case '4':
+        case '5': case '6': case '7': case '8': case '9':
+        case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+        case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+            part = (part << 4) | (ctype[(unsigned char)input[pos++]] & 15);
+            if (part > 0xffff)
+                return 0;
+            break;
+        case ':':
+            part_start = input + ++pos;
+            if (input[pos] == '.')
+                return 0;
+            addr->in6[ii++] = htons(part);
+            part = 0;
+            if (input[pos] == ':') {
+                if (cpos < 8)
+                    return 0;
+                cpos = ii;
+            }
+            break;
+        case '.': {
+            uint32_t ip4;
+            unsigned int len;
+            len = irc_pton_ip4(part_start, bits, &ip4);
+            if (!len || (ii > 6))
+                return 0;
+            memcpy(addr->in6 + ii, &ip4, sizeof(ip4));
+            if (bits)
+                *bits += 96;
+            ii += 2;
+            pos = part_start + len - input;
+            goto finish;
+        }
+        case '/':
+            if (!bits || !isdigit(input[pos + 1]))
+                return 0;
+            addr->in6[ii++] = htons(part);
+            for (part = 0; isdigit(input[++pos]); )
+                part = part * 10 + input[pos] - '0';
+            if (part > 128)
+                return 0;
+            *bits = part;
+            goto finish;
+        case '*':
+            while (input[++pos] == '*') ;
+            if (input[pos] != '\0' || cpos < 8)
+                return 0;
+            if (bits)
+                *bits = ii * 16;
+            return pos;
+        default:
+            addr->in6[ii++] = htons(part);
+            if (cpos == 8 && ii < 8)
+                return 0;
+            if (bits)
+                *bits = 128;
+            goto finish;
+        }
+    finish:
+        /* Shift stuff after "::" up and fill middle with zeros. */
+        if (cpos < 8) {
+            unsigned int jj;
+            for (jj = 0; jj < ii - cpos; jj++)
+                addr->in6[7 - jj] = addr->in6[ii - jj - 1];
+            for (jj = 0; jj < 8 - ii; jj++)
+                addr->in6[cpos + jj] = 0;
+        }
+    } else if (dot) {
+        unsigned int ip4;
+        pos = irc_pton_ip4(input, bits, &ip4);
+        if (pos) {
+            addr->in6[5] = htons(65535);
+            addr->in6[6] = htons(ntohl(ip4) >> 16);
+            addr->in6[7] = htons(ntohl(ip4) & 65535);
+            if (bits)
+                *bits += 96;
+        }
+    } else if (input[0] == '*') {
+        while (input[++pos] == '*') ;
+        if (input[pos] != '\0')
+            return 0;
+        if (bits)
+            *bits = 0;
+    }
+    return pos;
+}
+
+const char *irc_ntoa(const irc_in_addr_t *addr)
+{
+    static char ntoa[IRC_NTOP_MAX_SIZE];
+    irc_ntop(ntoa, sizeof(ntoa), addr);
+    return ntoa;
+}
+
+unsigned int
+irc_check_mask(const irc_in_addr_t *check, const irc_in_addr_t *mask, unsigned char bits)
+{
+    unsigned int ii;
+
+    for (ii = 0; (ii < 8) && (bits > 16); bits -= 16, ++ii)
+        if (check->in6[ii] != mask->in6[ii])
+            return 0;
+    if (ii < 8 && bits > 0
+        && (ntohs(check->in6[ii] ^ mask->in6[ii]) >> (16 - bits)))
+        return 0;
+    return 1;
+}
+
 static char irc_tolower[256];
 #undef tolower
 #define tolower(X) irc_tolower[(unsigned char)(X)]
@@ -330,11 +605,11 @@ user_matches_glob(struct userNode *user, const char *orig_glob, int include_nick
     glob = marker + 1;
     /* If it might be an IP glob, test that. */
     if (!glob[strspn(glob, "0123456789./*?")]
-        && match_ircglob(inet_ntoa(user->ip), glob))
+        && match_ircglob(irc_ntoa(&user->ip), glob))
         return 1;
     /* Check for a fakehost match. */
     if (IsFakeHost(user) && match_ircglob(user->fakehost, glob))
-            return 1;
+        return 1;
     /* Check for an account match. */
     if (hidden_host_suffix && user->handle_info) {
         char hidden_host[HOSTLEN+1];
@@ -554,74 +829,6 @@ ParseVolume(const char *volume)
     return accum + partial;
 }
 
-int
-parse_ipmask(const char *str, struct in_addr *addr, unsigned long *mask)
-{
-    int accum, pos;
-    unsigned long t_a, t_m;
-
-    t_a = t_m = pos = 0;
-    if (addr)
-        addr->s_addr = htonl(t_a);
-    if (mask)
-        *mask = t_m;
-    while (*str) {
-        if (!isdigit(*str))
-            return 0;
-        accum = 0;
-        do {
-            accum = (accum * 10) + *str++ - '0';
-        } while (isdigit(*str));
-        if (accum > 255)
-            return 0;
-        t_a = (t_a << 8) | accum;
-        t_m = (t_m << 8) | 255;
-        pos += 8;
-        if (*str == '.') {
-            str++;
-            while (*str == '*') {
-                str++;
-                if (*str == '.') {
-                    t_a <<= 8;
-                    t_m <<= 8;
-                    pos += 8;
-                    str++;
-                } else if (*str == 0) {
-                    t_a <<= 32 - pos;
-                    t_m <<= 32 - pos;
-                    pos = 32;
-                    goto out;
-                } else
-                    return 0;
-            }
-        } else if (*str == '/') {
-            int start = pos;
-            accum = 0;
-            do {
-                accum = (accum * 10) + *str++ - '0';
-            } while (isdigit(*str));
-            while (pos < start+accum && pos < 32) {
-                t_a = (t_a << 1) | 0;
-                t_m = (t_m << 1) | 1;
-                pos++;
-            }
-            if (pos != start+accum)
-                return 0;
-        } else if (*str == 0)
-            break;
-        else
-            return 0;
-    }
-out:
-    if (pos != 32)
-        return 0;
-    if (addr)
-        addr->s_addr = htonl(t_a);
-    if (mask)
-        *mask = t_m;
-    return 1;
-}
-
 char *
 unsplit_string(char *set[], unsigned int max, char *dest)
 {