From feb31e4200b42e0a5e9fb9637fa5f03c7ec05fcb Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 26 Feb 2008 23:50:35 +0100 Subject: IPv6 support. all references to struct sockaddr/in_addr have been removed from src/ngircd. libngipaddr (in src/ipaddr/) hides all the gory details. See src/ipaddr/ng_ipaddr.h for API description. --- src/ipaddr/.cvsignore | 3 + src/ipaddr/Makefile.am | 14 ++++ src/ipaddr/ng_ipaddr.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++ src/ipaddr/ng_ipaddr.h | 115 +++++++++++++++++++++++++++++++++ 4 files changed, 302 insertions(+) create mode 100644 src/ipaddr/.cvsignore create mode 100644 src/ipaddr/Makefile.am create mode 100644 src/ipaddr/ng_ipaddr.c create mode 100644 src/ipaddr/ng_ipaddr.h (limited to 'src/ipaddr') diff --git a/src/ipaddr/.cvsignore b/src/ipaddr/.cvsignore new file mode 100644 index 00000000..051d1bd5 --- /dev/null +++ b/src/ipaddr/.cvsignore @@ -0,0 +1,3 @@ +Makefile +Makefile.in +.deps diff --git a/src/ipaddr/Makefile.am b/src/ipaddr/Makefile.am new file mode 100644 index 00000000..fcbb7cfd --- /dev/null +++ b/src/ipaddr/Makefile.am @@ -0,0 +1,14 @@ +AUTOMAKE_OPTIONS = ansi2knr + +INCLUDES = -I$(srcdir)/../portab + +noinst_LIBRARIES = libngipaddr.a + +libngipaddr_a_SOURCES = ng_ipaddr.c + +noinst_HEADERS = ng_ipaddr.h + +maintainer-clean-local: + rm -f Makefile Makefile.in + +# -eof- diff --git a/src/ipaddr/ng_ipaddr.c b/src/ipaddr/ng_ipaddr.c new file mode 100644 index 00000000..bbfb5a73 --- /dev/null +++ b/src/ipaddr/ng_ipaddr.c @@ -0,0 +1,170 @@ +/* + * Functions for AF_ agnostic ipv4/ipv6 handling. + * + * (c) 2008 Florian Westphal , public domain. + */ + +#include "portab.h" + +#include +#include +#include + +#ifdef HAVE_GETADDRINFO +#include +#include +#endif +#include + +#include + +#include "ng_ipaddr.h" + +GLOBAL bool +ng_ipaddr_init(ng_ipaddr_t *addr, const char *ip_str, UINT16 port) +{ +#ifdef HAVE_GETADDRINFO + int ret; + char portstr[64]; + struct addrinfo *res0; + struct addrinfo hints = { +#ifndef WANT_IPV6 /* only accept v4 addresses */ + .ai_family = AF_INET, +#endif + .ai_flags = AI_NUMERICHOST + }; + + if (ip_str == NULL) + hints.ai_flags |= AI_PASSIVE; + + /* silly, but ngircd stores UINT16 in server config, not string */ + snprintf(portstr, sizeof(portstr), "%u", (unsigned int) port); + ret = getaddrinfo(ip_str, portstr, &hints, &res0); + assert(ret == 0); + if (ret != 0) + return false; + + assert(sizeof(*addr) >= res0->ai_addrlen); + if (sizeof(*addr) >= res0->ai_addrlen) + memcpy(addr, res0->ai_addr, res0->ai_addrlen); + else + ret = -1; + freeaddrinfo(res0); + return ret == 0; +#else /* HAVE_GETADDRINFO */ + if (ip_str == NULL) + ip_str = "0.0.0.0"; + addr->sin4.sin_family = AF_INET; +# ifdef HAVE_INET_ATON + if (inet_aton(ip_str, &addr->sin4.sin_addr) == 0) + return false; +# else + addr->sin4.sin_addr.s_addr = inet_addr(ip_str); + if (addr->sin4.sin_addr.s_addr == (unsigned) -1) + return false; +# endif + ng_ipaddr_setport(addr, port); + return true; +#endif /* HAVE_GETADDRINFO */ +} + + +GLOBAL void +ng_ipaddr_setport(ng_ipaddr_t *a, UINT16 port) +{ +#ifdef WANT_IPV6 + int af; + + assert(a != NULL); + + af = a->sa.sa_family; + + assert(af == AF_INET || af == AF_INET6); + + switch (af) { + case AF_INET: + a->sin4.sin_port = htons(port); + break; + case AF_INET6: + a->sin6.sin6_port = htons(port); + break; + } +#else /* WANT_IPV6 */ + assert(a != NULL); + assert(a->sin4.sin_family == AF_INET); + a->sin4.sin_port = htons(port); +#endif /* WANT_IPV6 */ +} + + + +GLOBAL bool +ng_ipaddr_ipequal(const ng_ipaddr_t *a, const ng_ipaddr_t *b) +{ + assert(a != NULL); + assert(b != NULL); +#ifdef WANT_IPV6 + if (a->sa.sa_family != b->sa.sa_family) + return false; + assert(ng_ipaddr_salen(a) == ng_ipaddr_salen(b)); + switch (a->sa.sa_family) { + case AF_INET6: + return IN6_ARE_ADDR_EQUAL(&a->sin6.sin6_addr, &b->sin6.sin6_addr); + case AF_INET: + return memcmp(&a->sin4.sin_addr, &b->sin4.sin_addr, sizeof(a->sin4.sin_addr)) == 0; + } + return false; +#else + assert(a->sin4.sin_family == AF_INET); + assert(b->sin4.sin_family == AF_INET); + return memcmp(&a->sin4.sin_addr, &b->sin4.sin_addr, sizeof(a->sin4.sin_addr)) == 0; +#endif +} + + +#ifdef WANT_IPV6 +GLOBAL const char * +ng_ipaddr_tostr(const ng_ipaddr_t *addr) +{ + static char strbuf[NG_INET_ADDRSTRLEN]; + + strbuf[0] = 0; + + ng_ipaddr_tostr_r(addr, strbuf); + return strbuf; +} + + +/* str must be at least NG_INET_ADDRSTRLEN bytes long */ +GLOBAL bool +ng_ipaddr_tostr_r(const ng_ipaddr_t *addr, char *str) +{ +#ifdef HAVE_GETNAMEINFO + const struct sockaddr *sa = (const struct sockaddr *) addr; + int ret; + + *str = 0; + + ret = getnameinfo(sa, ng_ipaddr_salen(addr), + str, NG_INET_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST); + /* + * avoid leading ':'. + * causes mis-interpretation of client host in e.g. /WHOIS + */ + if (*str == ':') { + char tmp[NG_INET_ADDRSTRLEN] = "0"; + ret = getnameinfo(sa, ng_ipaddr_salen(addr), + tmp+1, sizeof(tmp) -1, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + strlcpy(str, tmp, NG_INET_ADDRSTRLEN); + } + assert (ret == 0); + return ret == 0; +#else + abort(); /* WANT_IPV6 depends on HAVE_GETNAMEINFO */ +#endif +} + +#endif /* WANT_IPV6 */ + +/* -eof- */ diff --git a/src/ipaddr/ng_ipaddr.h b/src/ipaddr/ng_ipaddr.h new file mode 100644 index 00000000..1e198b0e --- /dev/null +++ b/src/ipaddr/ng_ipaddr.h @@ -0,0 +1,115 @@ +/* + * Functions for AF_ agnostic ipv4/ipv6 handling. + * + * (c) 2008 Florian Westphal , public domain. + */ + +#ifndef NG_IPADDR_HDR +#define NG_IPADDR_HDR +#include "portab.h" + +#ifdef HAVE_ARPA_INET_H +# include +#else +# define PF_INET AF_INET +#endif + + +#ifdef WANT_IPV6 +#define NG_INET_ADDRSTRLEN INET6_ADDRSTRLEN +#else +#define NG_INET_ADDRSTRLEN 16 +#endif + + +#ifdef WANT_IPV6 +typedef union { + struct sockaddr sa; + struct sockaddr_in sin4; + struct sockaddr_in6 sin6; +} ng_ipaddr_t; +#else +/* assume compiler can't deal with typedef struct {... */ +struct NG_IP_ADDR_DONTUSE { + struct sockaddr_in sin4; +}; +typedef struct NG_IP_ADDR_DONTUSE ng_ipaddr_t; +#endif + + +static inline int +ng_ipaddr_af(const ng_ipaddr_t *a) +{ +#ifdef WANT_IPV6 + return a->sa.sa_family; +#else + assert(a->sin4.sin_family == 0 || a->sin4.sin_family == AF_INET); + return a->sin4.sin_family; +#endif +} + + +static inline socklen_t +ng_ipaddr_salen(const ng_ipaddr_t *a) +{ +#ifdef WANT_IPV6 + assert(a->sa.sa_family == AF_INET || a->sa.sa_family == AF_INET6); + if (a->sa.sa_family == AF_INET6) + return sizeof(a->sin6); +#endif + assert(a->sin4.sin_family == AF_INET); + return sizeof(a->sin4); +} + + +static inline UINT16 +ng_ipaddr_getport(const ng_ipaddr_t *a) +{ +#ifdef WANT_IPV6 + int af = a->sa.sa_family; + + assert(af == AF_INET || af == AF_INET6); + + if (af == AF_INET6) + return ntohs(a->sin6.sin6_port); +#endif /* WANT_IPV6 */ + assert(a->sin4.sin_family == AF_INET); + return ntohs(a->sin4.sin_port); +} + +/* + * init a ng_ipaddr_t object. + * @param addr: pointer to ng_ipaddr_t to initialize. + * @param ip_str: ip address in dotted-decimal (ipv4) or hexadecimal (ipv6) notation + * if ip_str is NULL it is treated as 0.0.0.0/[::] + * @param port: transport layer port number to use. + */ +GLOBAL bool ng_ipaddr_init PARAMS((ng_ipaddr_t *addr, const char *ip_str, UINT16 port)); + +/* set sin4/sin6_port, depending on a->sa_family */ +GLOBAL void ng_ipaddr_setport PARAMS((ng_ipaddr_t *a, UINT16 port)); + +/* return true if a and b have the same IP address. If a and b have different AF, return false. */ +GLOBAL bool ng_ipaddr_ipequal PARAMS((const ng_ipaddr_t *a, const ng_ipaddr_t *b)); + + +#ifdef WANT_IPV6 +/* convert struct sockaddr to string, returns pointer to static buffer */ +GLOBAL const char *ng_ipaddr_tostr PARAMS((const ng_ipaddr_t *addr)); + +/* convert struct sockaddr to string. dest must be NG_INET_ADDRSTRLEN bytes long */ +GLOBAL bool ng_ipaddr_tostr_r PARAMS((const ng_ipaddr_t *addr, char *dest)); +#else +static inline const char * +ng_ipaddr_tostr(const ng_ipaddr_t *addr) { return inet_ntoa(addr->sin4.sin_addr); } + +static inline bool +ng_ipaddr_tostr_r(const ng_ipaddr_t *addr, char *d) +{ + strlcpy(d, inet_ntoa(addr->sin4.sin_addr), NG_INET_ADDRSTRLEN); + return true; +} +#endif +#endif + +/* -eof- */ -- cgit 1.4.1