From: Michael Poole Date: Sat, 8 Oct 2005 14:33:12 +0000 (+0000) Subject: Add IPv6 support. X-Git-Tag: v1.4.0-rc1~164 X-Git-Url: http://git.pk910.de/?p=srvx.git;a=commitdiff_plain;h=fd209bd5cc9ffc7989c461a2f2a2011790668d32 Add IPv6 support. 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 --- diff --git a/ChangeLog b/ChangeLog index 1adaa85..e9a3926 100644 --- 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 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 patch-27 Summary: diff --git a/configure.in b/configure.in index 69e75c6..0769fb9 100644 --- a/configure.in +++ b/configure.in @@ -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 ]) 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 +#include ]) +AC_CHECK_MEMBER([struct addrinfo.ai_flags], + [AC_DEFINE([HAVE_STRUCT_ADDRINFO],,[Define if struct addrinfo declared])], + [],[#include +#include +#include ]) 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.])) diff --git a/src/common.h b/src/common.h index e2ee167..6e0d5c1 100644 --- a/src/common.h +++ b/src/common.h @@ -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 */ diff --git a/src/compat.c b/src/compat.c index 820771e..b77aaae 100644 --- a/src/compat.c +++ b/src/compat.c @@ -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; iai_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 diff --git a/src/compat.h b/src/compat.h index 8571259..232af42 100644 --- a/src/compat.h +++ b/src/compat.h @@ -53,6 +53,14 @@ char *alloca(); #include #endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_NETDB_H +#include +#endif + #ifdef HAVE_UNISTD_H #include #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 */ diff --git a/src/gline.c b/src/gline.c index 8071f37..5595413 100644 --- a/src/gline.c +++ b/src/gline.c @@ -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 * diff --git a/src/hash.h b/src/hash.h index 443f9f4..975ccf4 100644 --- a/src/hash.h +++ b/src/hash.h @@ -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 */ diff --git a/src/ioset.c b/src/ioset.c index e102732..8a3aea0 100644 --- a/src/ioset.c +++ b/src/ioset.c @@ -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; diff --git a/src/main.c b/src/main.c index 7c6e2cd..2517516 100644 --- a/src/main.c +++ b/src/main.c @@ -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; diff --git a/src/mod-snoop.c b/src/mod-snoop.c index d0589d2..11a0e55 100644 --- a/src/mod-snoop.c +++ b/src/mod-snoop.c @@ -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; } diff --git a/src/mod-sockcheck.c b/src/mod-sockcheck.c index eeb7ada..4788e13 100644 --- a/src/mod-sockcheck.c +++ b/src/mod-sockcheck.c @@ -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; nip.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; nip)); + 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); } } } diff --git a/src/nickserv.c b/src/nickserv.c index e092ac4..26370cd 100644 --- a/src/nickserv.c +++ b/src/nickserv.c @@ -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)); } diff --git a/src/opserv.c b/src/opserv.c index d3fbb7d..35ead4a 100644 --- a/src/opserv.c +++ b/src/opserv.c @@ -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); diff --git a/src/proto-bahamut.c b/src/proto-bahamut.c index 8df3b0f..4f950ae 100644 --- a/src/proto-bahamut.c +++ b/src/proto-bahamut.c @@ -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; diff --git a/src/proto-common.c b/src/proto-common.c index c740093..b5e5118 100644 --- a/src/proto-common.c +++ b/src/proto-common.c @@ -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; diff --git a/src/proto-p10.c b/src/proto-p10.c index eb2d6ae..cf9b076 100644 --- a/src/proto-p10.c +++ b/src/proto-p10.c @@ -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; diff --git a/src/proto.h b/src/proto.h index 373470c..71e5564 100644 --- a/src/proto.h +++ b/src/proto.h @@ -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; diff --git a/src/tools.c b/src/tools.c index bea5099..a626386 100644 --- a/src/tools.c +++ b/src/tools.c @@ -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) {