diff --git a/ChangeLog b/ChangeLog index b419cf09..b5626382 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,7 @@ ngIRCd HEAD + - Add IPv6 support. - Install a LaunchDaemon script to start/stop ngIRCd on Mac OS X. - Implemented IRC commands INFO, SUMMON (dummy), and USERS (dummy) and enhanced test suite to check these commands. (Dana Dahlstrom) @@ -758,4 +759,4 @@ ngIRCd 0.0.1, 31.12.2001 -- -$Id: ChangeLog,v 1.343 2008/02/26 20:35:43 alex Exp $ +$Id: ChangeLog,v 1.344 2008/02/26 22:05:42 fw Exp $ diff --git a/NEWS b/NEWS index d5db7b86..660da600 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ ngIRCd HEAD + - Added IPv6 support. - Implemented IRC commands INFO, SUMMON (dummy), and USERS (dummy) and enhanced test suite to check these commands. (Dana Dahlstrom) - IRC_WHO now supports search patterns and will test this @@ -263,4 +264,4 @@ ngIRCd 0.0.1, 31.12.2001 -- -$Id: NEWS,v 1.87 2008/02/17 13:26:41 alex Exp $ +$Id: NEWS,v 1.88 2008/02/26 22:05:42 fw Exp $ diff --git a/configure.in b/configure.in index 51841e7a..d17bc15e 100644 --- a/configure.in +++ b/configure.in @@ -8,7 +8,7 @@ # (at your option) any later version. # Please read the file COPYING, README and AUTHORS for more information. # -# $Id: configure.in,v 1.125 2006/12/26 16:00:45 alex Exp $ +# $Id: configure.in,v 1.126 2008/02/26 22:04:15 fw Exp $ # # -- Initialisation -- @@ -30,6 +30,7 @@ AH_TEMPLATE([SYSLOG], [Define if syslog should be used for logging]) AH_TEMPLATE([ZLIB], [Define if zlib compression should be enabled]) AH_TEMPLATE([TCPWRAP], [Define if TCP wrappers should be used]) AH_TEMPLATE([IRCPLUS], [Define if IRC+ protocol should be used]) +AH_TEMPLATE([WANT_IPV6], [Define if IPV6 protocol should be enabled]) AH_TEMPLATE([ZEROCONF], [Define if support for Zeroconf should be included]) AH_TEMPLATE([IDENTAUTH], [Define if the server should do IDENT requests]) @@ -426,6 +427,19 @@ if test "$x_ircplus_on" = "yes"; then AC_DEFINE(IRCPLUS, 1) fi +# enable support for IPv6? +x_ipv6_on=no +AC_ARG_ENABLE(ipv6, + [ --enable-ipv6, enable IPv6 protocol support], + if test "$enableval" = "yes"; then x_ipv6_on=yes; fi +) +if test "$x_ipv6_on" = "yes"; then + AC_CHECK_FUNCS([ \ + getaddrinfo getnameinfo \ + ],,AC_MSG_ERROR([required function missing for IPv6 support!])) + AC_DEFINE(WANT_IPV6, 1) +fi + # compile in IRC "sniffer"? x_sniffer_on=no; x_debug_on=no @@ -477,6 +491,7 @@ AC_OUTPUT([ \ doc/src/Makefile \ src/Makefile \ src/portab/Makefile \ + src/ipaddr/Makefile \ src/tool/Makefile \ src/ngircd/Makefile \ src/testsuite/Makefile \ @@ -572,6 +587,8 @@ test "$x_identauth_on" = "yes" \ echo $ECHO_N " I/O backend: $ECHO_C" echo "\"$x_io_backend\"" +echo $ECHO_N " IPv6 protocol: $ECHO_C" +echo "$x_ipv6_on" echo # -eof- diff --git a/src/Makefile.am b/src/Makefile.am index 24929f32..e04ebe1b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,10 +8,10 @@ # (at your option) any later version. # Please read the file COPYING, README and AUTHORS for more information. # -# $Id: Makefile.am,v 1.7 2005/07/22 21:01:03 alex Exp $ +# $Id: Makefile.am,v 1.8 2008/02/26 22:04:15 fw Exp $ # -SUBDIRS = portab tool ngircd testsuite +SUBDIRS = portab tool ipaddr ngircd testsuite maintainer-clean-local: rm -f Makefile Makefile.in config.h config.h.in stamp-h.in 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- */ diff --git a/src/ngircd/Makefile.am b/src/ngircd/Makefile.am index 46513a8a..cfa45e19 100644 --- a/src/ngircd/Makefile.am +++ b/src/ngircd/Makefile.am @@ -8,12 +8,12 @@ # (at your option) any later version. # Please read the file COPYING, README and AUTHORS for more information. # -# $Id: Makefile.am,v 1.50 2007/11/21 12:16:36 alex Exp $ +# $Id: Makefile.am,v 1.51 2008/02/26 22:04:17 fw Exp $ # AUTOMAKE_OPTIONS = ../portab/ansi2knr -INCLUDES = -I$(srcdir)/../portab -I$(srcdir)/../tool +INCLUDES = -I$(srcdir)/../portab -I$(srcdir)/../tool -I$(srcdir)/../ipaddr LINTARGS = -weak -warnunixlib +unixlib -booltype BOOLEAN \ -varuse -retvalother -emptyret -unrecog @@ -25,9 +25,9 @@ ngircd_SOURCES = ngircd.c array.c channel.c client.c conf.c conn.c conn-func.c \ irc-mode.c irc-op.c irc-oper.c irc-server.c irc-write.c lists.c log.c \ match.c numeric.c parse.c rendezvous.c resolve.c -ngircd_LDFLAGS = -L../portab -L../tool +ngircd_LDFLAGS = -L../portab -L../tool -L../ipaddr -ngircd_LDADD = -lngportab -lngtool +ngircd_LDADD = -lngportab -lngtool -lngipaddr noinst_HEADERS = ngircd.h array.h channel.h client.h conf.h conn.h conn-func.h \ conn-zip.h hash.h io.h irc.h irc-channel.h irc-info.h irc-login.h \ diff --git a/src/ngircd/conf.c b/src/ngircd/conf.c index 7ca5567d..3239dd14 100644 --- a/src/ngircd/conf.c +++ b/src/ngircd/conf.c @@ -14,7 +14,7 @@ #include "portab.h" -static char UNUSED id[] = "$Id: conf.c,v 1.103 2007/11/23 16:26:04 fw Exp $"; +static char UNUSED id[] = "$Id: conf.c,v 1.104 2008/02/26 22:04:17 fw Exp $"; #include "imp.h" #include @@ -938,7 +938,7 @@ Handle_SERVER( int Line, char *Var, char *Arg ) return; } if (strcasecmp(Var, "Bind") == 0) { - if (ngt_IPStrToBin(Arg, &New_Server.bind_addr)) + if (ng_ipaddr_init(&New_Server.bind_addr, Arg, 0)) return; Config_Error(LOG_ERR, "%s, line %d (section \"Server\"): Can't parse IP address \"%s\"", @@ -1213,7 +1213,7 @@ Init_Server_Struct( CONF_SERVER *Server ) Resolve_Init(&Server->res_stat); Server->conn_id = NONE; - Server->bind_addr.s_addr = htonl(INADDR_ANY); + memset(&Server->bind_addr, 0, sizeof(&Server->bind_addr)); } /* Init_Server_Struct */ diff --git a/src/ngircd/conf.h b/src/ngircd/conf.h index 5ae7f5d6..b6a5a2ac 100644 --- a/src/ngircd/conf.h +++ b/src/ngircd/conf.h @@ -8,7 +8,7 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: conf.h,v 1.47 2007/11/23 16:28:37 fw Exp $ + * $Id: conf.h,v 1.48 2008/02/26 22:04:17 fw Exp $ * * Configuration management (header) */ @@ -22,6 +22,8 @@ #include "defines.h" #include "array.h" #include "portab.h" +#include "tool.h" +#include "ng_ipaddr.h" typedef struct _Conf_Oper { @@ -42,7 +44,8 @@ typedef struct _Conf_Server RES_STAT res_stat; /* Status of the resolver */ int flags; /* Flags */ CONN_ID conn_id; /* ID of server connection or NONE */ - struct in_addr bind_addr; /* source address to use for outgoing connections */ + ng_ipaddr_t bind_addr; /* source address to use for outgoing connections */ + ng_ipaddr_t dst_addr[2]; /* list of addresses to connect to */ } CONF_SERVER; typedef struct _Conf_Channel @@ -121,6 +124,12 @@ GLOBAL bool Conf_OperCanMode; /* Disable all DNS functions? */ GLOBAL bool Conf_NoDNS; +/* don't listen for incoming ipv6 connections, even if OS supports it? */ +GLOBAL bool Conf_NoListenIpv6; + +/* don't connect to remote systems unsign ipv6? */ +GLOBAL bool Conf_NoConnectIpv6; + /* If an IRC op gives chanop privileges without being a chanop, * ircd2 will ignore the command. This enables a workaround: * It masks the command as coming from the server */ diff --git a/src/ngircd/conn.c b/src/ngircd/conn.c index 6042db2f..9e31e4ee 100644 --- a/src/ngircd/conn.c +++ b/src/ngircd/conn.c @@ -17,7 +17,7 @@ #include "portab.h" #include "io.h" -static char UNUSED id[] = "$Id: conn.c,v 1.220 2007/12/13 01:30:16 fw Exp $"; +static char UNUSED id[] = "$Id: conn.c,v 1.221 2008/02/26 22:04:17 fw Exp $"; #include "imp.h" #include @@ -86,10 +86,9 @@ static void Check_Connections PARAMS(( void )); static void Check_Servers PARAMS(( void )); static void Init_Conn_Struct PARAMS(( CONN_ID Idx )); static bool Init_Socket PARAMS(( int Sock )); -static void New_Server PARAMS(( int Server, struct in_addr *dest)); +static void New_Server PARAMS(( int Server, ng_ipaddr_t *dest )); static void Simple_Message PARAMS(( int Sock, const char *Msg )); -static int Count_Connections PARAMS(( struct sockaddr_in addr )); -static int NewListener PARAMS(( const UINT16 Port )); +static int NewListener PARAMS(( int af, const UINT16 Port )); static array My_Listeners; static array My_ConnArray; @@ -144,10 +143,28 @@ cb_connserver(int sock, UNUSED short what) Conf_Server[Conf_GetServer(idx)].port, idx, strerror(err)); + res = Conf_GetServer(idx); + assert(res >= 0); + Conn_Close(idx, "Can't connect!", NULL, false); + + if (res < 0) + return; + if (ng_ipaddr_af(&Conf_Server[res].dst_addr[0])) { + /* more addresses to try... */ + New_Server(res, &Conf_Server[res].dst_addr[0]); + /* connection to dst_addr[0] in progress, remove this address... */ + Conf_Server[res].dst_addr[0] = Conf_Server[res].dst_addr[1]; + + memset(&Conf_Server[res].dst_addr[1], 0, sizeof(&Conf_Server[res].dst_addr[1])); + } return; } + res = Conf_GetServer(idx); + assert(res >= 0); + if (res >= 0) /* connect succeeded, remove all additional addresses */ + memset(&Conf_Server[res].dst_addr, 0, sizeof(&Conf_Server[res].dst_addr)); Conn_OPTION_DEL( &My_Connections[idx], CONN_ISCONNECTING ); server_login(idx); } @@ -255,7 +272,7 @@ Conn_Exit( void ) static unsigned int -ports_initlisteners(array *a, void (*func)(int,short)) +ports_initlisteners(array *a, int af, void (*func)(int,short)) { unsigned int created = 0; size_t len; @@ -265,7 +282,7 @@ ports_initlisteners(array *a, void (*func)(int,short)) len = array_length(a, sizeof (UINT16)); port = array_start(a); while(len--) { - fd = NewListener( *port ); + fd = NewListener(af, *port); if (fd < 0) { port++; continue; @@ -290,14 +307,18 @@ Conn_InitListeners( void ) { /* Initialize ports on which the server should accept connections */ - unsigned int created; + unsigned int created = 0; if (!io_library_init(CONNECTION_POOL)) { Log(LOG_EMERG, "Cannot initialize IO routines: %s", strerror(errno)); return -1; } - created = ports_initlisteners(&Conf_ListenPorts, cb_listen); +#ifdef WANT_IPV6 + if (!Conf_NoListenIpv6) + created = ports_initlisteners(&Conf_ListenPorts, AF_INET6, cb_listen); +#endif + created += ports_initlisteners(&Conf_ListenPorts, AF_INET, cb_listen); return created; } /* Conn_InitListeners */ @@ -327,68 +348,75 @@ Conn_ExitListeners( void ) } /* Conn_ExitListeners */ -static void -InitSinaddr(struct sockaddr_in *addr, UINT16 Port) +static bool +InitSinaddrListenAddr(int af, ng_ipaddr_t *addr, UINT16 Port) { - struct in_addr inaddr; + bool ret; + const char *listen_addrstr = NULL; +#ifdef WANT_IPV6 + if (af == AF_INET) + listen_addrstr = "0.0.0.0"; +#else + (void)af; +#endif + if (Conf_ListenAddress[0]) /* overrides V4/V6 atm */ + listen_addrstr = Conf_ListenAddress; - memset(addr, 0, sizeof(*addr)); - memset( &inaddr, 0, sizeof(inaddr)); - - addr->sin_family = AF_INET; - addr->sin_port = htons(Port); - inaddr.s_addr = htonl(INADDR_ANY); - addr->sin_addr = inaddr; + ret = ng_ipaddr_init(addr, listen_addrstr, Port); + if (!ret) { + if (!listen_addrstr) + listen_addrstr = ""; + Log(LOG_CRIT, "Can't bind to %s:%u: can't convert ip address \"%s\"", + listen_addrstr, Port, listen_addrstr); + } + return ret; } -static bool -InitSinaddrListenAddr(struct sockaddr_in *addr, UINT16 Port) +static void +set_v6_only(int af, int sock) { - struct in_addr inaddr; +#if defined(IPV6_V6ONLY) && defined(WANT_IPV6) + int on = 1; - InitSinaddr(addr, Port); + if (af != AF_INET6) + return; - if (!Conf_ListenAddress[0]) - return true; - - if (!ngt_IPStrToBin(Conf_ListenAddress, &inaddr)) { - Log( LOG_CRIT, "Can't bind to %s:%u: can't convert ip address \"%s\"", - Conf_ListenAddress, Port, Conf_ListenAddress); - return false; - } - - addr->sin_addr = inaddr; - return true; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) + Log(LOG_ERR, "Could not set IPV6_V6ONLY: %s", strerror(errno)); +#else + (void)af; + (void)sock; +#endif } /* return new listening port file descriptor or -1 on failure */ static int -NewListener( const UINT16 Port ) +NewListener(int af, const UINT16 Port) { /* Create new listening socket on specified port */ - - struct sockaddr_in addr; + ng_ipaddr_t addr; int sock; #ifdef ZEROCONF char name[CLIENT_ID_LEN], *info; #endif + if (!InitSinaddrListenAddr(af, &addr, Port)) + return -1; - InitSinaddrListenAddr(&addr, Port); - - addr.sin_family = AF_INET; - addr.sin_port = htons( Port ); - - sock = socket( PF_INET, SOCK_STREAM, 0); + sock = socket(ng_ipaddr_af(&addr), SOCK_STREAM, 0); if( sock < 0 ) { Log( LOG_CRIT, "Can't create socket: %s!", strerror( errno )); return -1; } + af = ng_ipaddr_af(&addr); + + set_v6_only(af, sock); + if( ! Init_Socket( sock )) return -1; - if (bind(sock, (struct sockaddr *)&addr, (socklen_t)sizeof(addr)) != 0) { + if (bind(sock, (struct sockaddr *)&addr, ng_ipaddr_salen(&addr)) != 0) { Log( LOG_CRIT, "Can't bind socket (port %d) : %s!", Port, strerror( errno )); close( sock ); return -1; @@ -407,8 +435,12 @@ NewListener( const UINT16 Port ) return -1; } - if( Conf_ListenAddress[0]) Log( LOG_INFO, "Now listening on %s:%d (socket %d).", Conf_ListenAddress, Port, sock ); - else Log( LOG_INFO, "Now listening on 0.0.0.0:%d (socket %d).", Port, sock ); +#ifdef WANT_IPV6 + if (af == AF_INET6) + Log(LOG_INFO, "Now listening on [%s]:%d (socket %d).", ng_ipaddr_tostr(&addr), Port, sock); + else +#endif + Log(LOG_INFO, "Now listening on %s:%d (socket %d).", ng_ipaddr_tostr(&addr), Port, sock); #ifdef ZEROCONF /* Get best server description text */ @@ -709,6 +741,7 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient ) CLIENT *c; char *txt; double in_k, out_k; + UINT16 port; #ifdef ZLIB double in_z_k, out_z_k; int in_p, out_p; @@ -736,9 +769,9 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient ) if (! txt) txt = "Reason unknown"; + port = ng_ipaddr_getport(&My_Connections[Idx].addr); Log(LOG_INFO, "Shutting down connection %d (%s) with %s:%d ...", Idx, - LogMsg ? LogMsg : FwdMsg, My_Connections[Idx].host, - ntohs(My_Connections[Idx].addr.sin_port)); + LogMsg ? LogMsg : FwdMsg, My_Connections[Idx].host, port); /* Search client, if any */ c = Conn_GetClient( Idx ); @@ -778,7 +811,7 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient ) Log(LOG_CRIT, "Error closing connection %d (socket %d) with %s:%d - %s! (ignored)", Idx, My_Connections[Idx].sock, My_Connections[Idx].host, - ntohs(My_Connections[Idx].addr.sin_port), strerror(errno)); + port, strerror(errno)); } /* Mark socket as invalid: */ @@ -807,8 +840,7 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient ) out_p = (int)(( out_k * 100 ) / out_z_k ); Log(LOG_INFO, "Connection %d with %s:%d closed (in: %.1fk/%.1fk/%d%%, out: %.1fk/%.1fk/%d%%).", - Idx, My_Connections[Idx].host, - ntohs(My_Connections[Idx].addr.sin_port), + Idx, My_Connections[Idx].host, port, in_k, in_z_k, in_p, out_k, out_z_k, out_p); } else @@ -816,8 +848,7 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient ) { Log(LOG_INFO, "Connection %d with %s:%d closed (in: %.1fk, out: %.1fk).", - Idx, My_Connections[Idx].host, - ntohs(My_Connections[Idx].addr.sin_port), + Idx, My_Connections[Idx].host, port, in_k, out_k); } @@ -940,6 +971,22 @@ Handle_Write( CONN_ID Idx ) } /* Handle_Write */ +static int +Count_Connections(ng_ipaddr_t *a) +{ + int i, cnt; + + cnt = 0; + for (i = 0; i < Pool_Size; i++) { + if (My_Connections[i].sock <= NONE) + continue; + if (ng_ipaddr_ipequal(&My_Connections[i].addr, a)) + cnt++; + } + return cnt; +} /* Count_Connections */ + + static int New_Connection( int Sock ) { @@ -949,14 +996,16 @@ New_Connection( int Sock ) #ifdef TCPWRAP struct request_info req; #endif - struct sockaddr_in new_addr; + ng_ipaddr_t new_addr; + char ip_str[NG_INET_ADDRSTRLEN]; int new_sock, new_sock_len, new_Pool_Size; CLIENT *c; long cnt; assert( Sock > NONE ); /* Connection auf Listen-Socket annehmen */ - new_sock_len = (int)sizeof new_addr; + new_sock_len = (int)sizeof(new_addr); + new_sock = accept(Sock, (struct sockaddr *)&new_addr, (socklen_t *)&new_sock_len); if (new_sock < 0) { @@ -964,14 +1013,18 @@ New_Connection( int Sock ) return -1; } + if (!ng_ipaddr_tostr_r(&new_addr, ip_str)) { + Log(LOG_CRIT, "fd %d: Can't convert IP address!", new_sock); + Simple_Message(new_sock, "ERROR :Internal Server Error"); + close(new_sock); + } + #ifdef TCPWRAP /* Validate socket using TCP Wrappers */ request_init( &req, RQ_DAEMON, PACKAGE_NAME, RQ_FILE, new_sock, RQ_CLIENT_SIN, &new_addr, NULL ); fromhost(&req); - if( ! hosts_access( &req )) - { - /* Access denied! */ - Log( deny_severity, "Refused connection from %s (by TCP Wrappers)!", inet_ntoa( new_addr.sin_addr )); + if (!hosts_access(&req)) { + Log (deny_severity, "Refused connection from %s (by TCP Wrappers)!", ip_str); Simple_Message( new_sock, "ERROR :Connection refused" ); close( new_sock ); return -1; @@ -981,13 +1034,12 @@ New_Connection( int Sock ) /* Socket initialisieren */ if (!Init_Socket( new_sock )) return -1; - + /* Check IP-based connection limit */ - cnt = Count_Connections( new_addr ); - if(( Conf_MaxConnectionsIP > 0 ) && ( cnt >= Conf_MaxConnectionsIP )) - { + cnt = Count_Connections(&new_addr); + if ((Conf_MaxConnectionsIP > 0) && (cnt >= Conf_MaxConnectionsIP)) { /* Access denied, too many connections from this IP address! */ - Log( LOG_ERR, "Refused connection from %s: too may connections (%ld) from this IP address!", inet_ntoa( new_addr.sin_addr ), cnt); + Log( LOG_ERR, "Refused connection from %s: too may connections (%ld) from this IP address!", ip_str, cnt); Simple_Message( new_sock, "ERROR :Connection refused, too many connections from your IP address!" ); close( new_sock ); return -1; @@ -1029,7 +1081,7 @@ New_Connection( int Sock ) return -1; } - c = Client_NewLocal( new_sock, inet_ntoa( new_addr.sin_addr ), CLIENT_UNKNOWN, false ); + c = Client_NewLocal(new_sock, ip_str, CLIENT_UNKNOWN, false ); if( ! c ) { Log(LOG_ALERT, "Can't accept connection: can't create client structure!"); Simple_Message(new_sock, "ERROR :Internal error"); @@ -1043,13 +1095,12 @@ New_Connection( int Sock ) My_Connections[new_sock].client = c; Log( LOG_INFO, "Accepted connection %d from %s:%d on socket %d.", new_sock, - inet_ntoa( new_addr.sin_addr ), ntohs( new_addr.sin_port), Sock ); + ip_str, ng_ipaddr_getport(&new_addr), Sock); /* Hostnamen ermitteln */ - strlcpy( My_Connections[new_sock].host, inet_ntoa( new_addr.sin_addr ), - sizeof( My_Connections[new_sock].host )); + strlcpy(My_Connections[new_sock].host, ip_str, sizeof(My_Connections[new_sock].host)); - Client_SetHostname( c, My_Connections[new_sock].host ); + Client_SetHostname(c, My_Connections[new_sock].host); if (!Conf_NoDNS) Resolve_Addr(&My_Connections[new_sock].res_stat, &new_addr, @@ -1107,10 +1158,10 @@ Read_Request( CONN_ID Idx ) len = read(My_Connections[Idx].sock, readbuf, sizeof(readbuf)); if (len == 0) { - Log(LOG_INFO, "%s:%d (%s) is closing the connection ...", - My_Connections[Idx].host, - ntohs(My_Connections[Idx].addr.sin_port), - inet_ntoa( My_Connections[Idx].addr.sin_addr)); + Log(LOG_INFO, "%s:%u (%s) is closing the connection ...", + My_Connections[Idx].host, + (unsigned int) ng_ipaddr_getport(&My_Connections[Idx].addr), + ng_ipaddr_tostr(&My_Connections[Idx].addr)); Conn_Close(Idx, "Socket closed!", "Client closed connection", false); @@ -1367,37 +1418,44 @@ Check_Servers( void ) static void -New_Server( int Server , struct in_addr *dest) +New_Server( int Server , ng_ipaddr_t *dest) { /* Establish new server link */ - struct sockaddr_in local_addr; - struct sockaddr_in new_addr; - int res, new_sock; + char ip_str[NG_INET_ADDRSTRLEN]; + int af_dest, res, new_sock; CLIENT *c; assert( Server > NONE ); - memset(&new_addr, 0, sizeof( new_addr )); - new_addr.sin_family = AF_INET; - new_addr.sin_addr = *dest; - new_addr.sin_port = htons( Conf_Server[Server].port ); + if (!ng_ipaddr_tostr_r(dest, ip_str)) { + Log(LOG_WARNING, "New_Server: Could not convert IP to string"); + return; + } - new_sock = socket( PF_INET, SOCK_STREAM, 0 ); - if ( new_sock < 0 ) { + Log( LOG_INFO, "Establishing connection to \"%s\", %s, port %d ... ", + Conf_Server[Server].host, ip_str, Conf_Server[Server].port ); + + af_dest = ng_ipaddr_af(dest); + new_sock = socket(af_dest, SOCK_STREAM, 0); + if (new_sock < 0) { Log( LOG_CRIT, "Can't create socket: %s!", strerror( errno )); return; } - if( ! Init_Socket( new_sock )) return; + if (!Init_Socket(new_sock)) + return; - /* if we fail to bind, just continue and let connect() pick a source address */ - InitSinaddr(&local_addr, 0); - local_addr.sin_addr = Conf_Server[Server].bind_addr; - if (bind(new_sock, (struct sockaddr *)&local_addr, (socklen_t)sizeof(local_addr))) - Log(LOG_WARNING, "Can't bind socket to %s: %s!", inet_ntoa(Conf_Server[Server].bind_addr), strerror( errno )); - - res = connect(new_sock, (struct sockaddr *)&new_addr, - (socklen_t)sizeof(new_addr)); + /* is a bind address configured? */ + res = ng_ipaddr_af(&Conf_Server[Server].bind_addr); + /* if yes, bind now. If it fails, warn and let connect() pick a source address */ + if (res && bind(new_sock, (struct sockaddr *) &Conf_Server[Server].bind_addr, + ng_ipaddr_salen(&Conf_Server[Server].bind_addr))) + { + ng_ipaddr_tostr_r(&Conf_Server[Server].bind_addr, ip_str); + Log(LOG_WARNING, "Can't bind socket to %s: %s!", ip_str, strerror(errno)); + } + ng_ipaddr_setport(dest, Conf_Server[Server].port); + res = connect(new_sock, (struct sockaddr *) dest, ng_ipaddr_salen(dest)); if(( res != 0 ) && ( errno != EINPROGRESS )) { Log( LOG_CRIT, "Can't connect socket: %s!", strerror( errno )); close( new_sock ); @@ -1418,8 +1476,9 @@ New_Server( int Server , struct in_addr *dest) Init_Conn_Struct(new_sock); - c = Client_NewLocal( new_sock, inet_ntoa( new_addr.sin_addr ), CLIENT_UNKNOWNSERVER, false ); - if( ! c ) { + ng_ipaddr_tostr_r(dest, ip_str); + c = Client_NewLocal(new_sock, ip_str, CLIENT_UNKNOWNSERVER, false); + if (!c) { Log( LOG_ALERT, "Can't establish connection: can't create client structure!" ); close( new_sock ); return; @@ -1431,7 +1490,7 @@ New_Server( int Server , struct in_addr *dest) /* Register connection */ Conf_Server[Server].conn_id = new_sock; My_Connections[new_sock].sock = new_sock; - My_Connections[new_sock].addr = new_addr; + My_Connections[new_sock].addr = *dest; My_Connections[new_sock].client = c; strlcpy( My_Connections[new_sock].host, Conf_Server[Server].host, sizeof(My_Connections[new_sock].host )); @@ -1510,8 +1569,9 @@ cb_Connect_to_Server(int fd, UNUSED short events) /* Read result of resolver sub-process from pipe and start connection */ int i; size_t len; - struct in_addr dest_addr; - char readbuf[HOST_LEN + 1]; + ng_ipaddr_t dest_addrs[4]; /* we can handle at most 3; but we read up to + four so we can log the 'more than we can handle' + condition */ LogDebug("Resolver: Got forward lookup callback on fd %d, events %d", fd, events); @@ -1528,23 +1588,28 @@ cb_Connect_to_Server(int fd, UNUSED short events) } /* Read result from pipe */ - len = Resolve_Read(&Conf_Server[i].res_stat, readbuf, sizeof(readbuf)-1); + len = Resolve_Read(&Conf_Server[i].res_stat, dest_addrs, sizeof(dest_addrs)); if (len == 0) return; - readbuf[len] = '\0'; - LogDebug("Got result from resolver: \"%s\" (%u bytes read).", readbuf, len); + assert((len % sizeof(ng_ipaddr_t)) == 0); - if (!ngt_IPStrToBin(readbuf, &dest_addr)) { - Log(LOG_ERR, "Can't connect to \"%s\": can't convert ip address %s!", - Conf_Server[i].host, readbuf); - return; + LogDebug("Got result from resolver: %u structs (%u bytes).", len/sizeof(ng_ipaddr_t), len); + + memset(&Conf_Server[i].dst_addr, 0, sizeof(&Conf_Server[i].dst_addr)); + if (len > sizeof(ng_ipaddr_t)) { + /* more than one address for this hostname, remember them + * in case first address is unreachable/not available */ + len -= sizeof(ng_ipaddr_t); + if (len > sizeof(&Conf_Server[i].dst_addr)) { + len = sizeof(&Conf_Server[i].dst_addr); + Log(LOG_NOTICE, "Notice: Resolver returned more IP Addresses for host than we can handle," + " additional addresses dropped"); + } + memcpy(&Conf_Server[i].dst_addr, &dest_addrs[1], len); } - - Log( LOG_INFO, "Establishing connection to \"%s\", %s, port %d ... ", - Conf_Server[i].host, readbuf, Conf_Server[i].port ); /* connect() */ - New_Server(i, &dest_addr); + New_Server(i, dest_addrs); } /* cb_Read_Forward_Lookup */ @@ -1643,19 +1708,6 @@ Simple_Message( int Sock, const char *Msg ) } /* Simple_Error */ -static int -Count_Connections( struct sockaddr_in addr_in ) -{ - int i, cnt; - - cnt = 0; - for( i = 0; i < Pool_Size; i++ ) { - if(( My_Connections[i].sock > NONE ) && ( My_Connections[i].addr.sin_addr.s_addr == addr_in.sin_addr.s_addr )) cnt++; - } - return cnt; -} /* Count_Connections */ - - GLOBAL CLIENT * Conn_GetClient( CONN_ID Idx ) { diff --git a/src/ngircd/conn.h b/src/ngircd/conn.h index 626a6e9b..3bb76ab3 100644 --- a/src/ngircd/conn.h +++ b/src/ngircd/conn.h @@ -8,7 +8,7 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: conn.h,v 1.45 2007/10/04 15:03:56 alex Exp $ + * $Id: conn.h,v 1.46 2008/02/26 22:04:17 fw Exp $ * * Connection management (header) */ @@ -38,6 +38,8 @@ typedef int CONN_ID; #include "defines.h" #include "resolve.h" #include "array.h" +#include "tool.h" +#include "ng_ipaddr.h" #ifdef ZLIB #include @@ -54,7 +56,7 @@ typedef struct _ZipData typedef struct _Connection { int sock; /* Socket handle */ - struct sockaddr_in addr; /* Client address */ + ng_ipaddr_t addr; /* Client address */ RES_STAT res_stat; /* Status of resolver process */ char host[HOST_LEN]; /* Hostname */ array rbuf; /* Read buffer */ diff --git a/src/ngircd/ngircd.c b/src/ngircd/ngircd.c index d2a6d7ea..73c9a151 100644 --- a/src/ngircd/ngircd.c +++ b/src/ngircd/ngircd.c @@ -12,7 +12,7 @@ #include "portab.h" -static char UNUSED id[] = "$Id: ngircd.c,v 1.117 2007/11/21 12:16:36 alex Exp $"; +static char UNUSED id[] = "$Id: ngircd.c,v 1.118 2008/02/26 22:04:17 fw Exp $"; /** * @file @@ -397,7 +397,12 @@ Fill_Version( void ) strlcat( NGIRCd_VersionAddition, "IRCPLUS", sizeof NGIRCd_VersionAddition ); #endif +#ifdef WANT_IPV6 + if (NGIRCd_VersionAddition[0]) + strlcat(NGIRCd_VersionAddition, "+", sizeof(NGIRCd_VersionAddition)); + strlcat(NGIRCd_VersionAddition, "IPv6", sizeof(NGIRCd_VersionAddition)); +#endif if( NGIRCd_VersionAddition[0] ) strlcat( NGIRCd_VersionAddition, "-", sizeof( NGIRCd_VersionAddition )); diff --git a/src/ngircd/resolve.c b/src/ngircd/resolve.c index 1bd7e293..a128694b 100644 --- a/src/ngircd/resolve.c +++ b/src/ngircd/resolve.c @@ -14,7 +14,7 @@ #include "portab.h" -static char UNUSED id[] = "$Id: resolve.c,v 1.28 2008/01/02 11:03:29 fw Exp $"; +static char UNUSED id[] = "$Id: resolve.c,v 1.29 2008/02/26 22:04:17 fw Exp $"; #include "imp.h" #include @@ -35,20 +35,16 @@ static char UNUSED id[] = "$Id: resolve.c,v 1.28 2008/01/02 11:03:29 fw Exp $"; #include "conn.h" #include "defines.h" #include "log.h" -#include "tool.h" #include "exp.h" #include "resolve.h" #include "io.h" -static void Do_ResolveAddr PARAMS(( struct sockaddr_in *Addr, int Sock, int w_fd )); +static void Do_ResolveAddr PARAMS(( const ng_ipaddr_t *Addr, int Sock, int w_fd )); static void Do_ResolveName PARAMS(( const char *Host, int w_fd )); static bool register_callback PARAMS((RES_STAT *s, void (*cbfunc)(int, short))); -#ifdef h_errno -static char *Get_Error PARAMS(( int H_Error )); -#endif static pid_t Resolver_fork(int *pipefds) @@ -82,7 +78,7 @@ Resolver_fork(int *pipefds) * Resolve IP (asynchronous!). */ GLOBAL bool -Resolve_Addr(RES_STAT * s, struct sockaddr_in *Addr, int identsock, +Resolve_Addr(RES_STAT * s, const ng_ipaddr_t *Addr, int identsock, void (*cbfunc) (int, short)) { int pipefd[2]; @@ -92,9 +88,8 @@ Resolve_Addr(RES_STAT * s, struct sockaddr_in *Addr, int identsock, pid = Resolver_fork(pipefd); if (pid > 0) { -#ifdef DEBUG - Log( LOG_DEBUG, "Resolver for %s created (PID %d).", inet_ntoa( Addr->sin_addr ), pid ); -#endif + Log(LOG_DEBUG, "Resolver for %s created (PID %d).", ng_ipaddr_tostr(Addr), pid); + s->pid = pid; s->resolver_fd = pipefd[0]; return register_callback(s, cbfunc); @@ -147,92 +142,294 @@ Resolve_Init(RES_STAT *s) } +#ifndef WANT_IPV6 +#ifdef h_errno +static char * +Get_Error( int H_Error ) +{ + /* Get error message for H_Error */ + switch( H_Error ) { + case HOST_NOT_FOUND: + return "host not found"; + case NO_DATA: + return "name valid but no IP address defined"; + case NO_RECOVERY: + return "name server error"; + case TRY_AGAIN: + return "name server temporary not available"; + } + return "unknown error"; +} +#endif /* h_errno */ +#endif /* WANT_IPV6 */ + + +/* Do "IDENT" (aka "AUTH") lookup and append result to resolved_addr array */ static void -Do_ResolveAddr( struct sockaddr_in *Addr, int identsock, int w_fd ) +Do_IdentQuery(int identsock, array *resolved_addr) +{ +#ifdef IDENTAUTH + char *res; + + assert(identsock >= 0); + +#ifdef DEBUG + Log_Resolver(LOG_DEBUG, "Doing IDENT lookup on socket %d ...", identsock); +#endif + if (identsock < 0) + return; + res = ident_id( identsock, 10 ); +#ifdef DEBUG + Log_Resolver(LOG_DEBUG, "Ok, IDENT lookup on socket %d done: \"%s\"", + identsock, res ? res : "(NULL)" ); +#endif + if (!res) /* no result */ + return; + if (!array_cats(resolved_addr, res)) + Log_Resolver(LOG_WARNING, "Resolver: Cannot copy IDENT result: %s!", strerror(errno)); + + free(res); +#else + (void) identsock; + (void) resolved_addr; +#endif +} + + +/** + * perform reverse DNS lookup and put result string into resbuf. + * If no hostname could be obtained, this function stores the string representation of + * the IP address in resbuf and returns false. + * @param IpAddr ip address to resolve + * @param resbuf result buffer to store DNS name/string representation of ip address + * @reslen size of result buffer (must be >= NGT_INET_ADDRSTRLEN) + * @return true if reverse lookup successful, false otherwise + */ +static bool +ReverseLookup(const ng_ipaddr_t *IpAddr, char *resbuf, size_t reslen) +{ + char tmp_ip_str[NG_INET_ADDRSTRLEN]; + const char *errmsg; +#ifdef HAVE_GETNAMEINFO + static const char funcname[]="getnameinfo"; + int res; + + *resbuf = 0; + + res = getnameinfo((struct sockaddr *) IpAddr, ng_ipaddr_salen(IpAddr), + resbuf, reslen, NULL, 0, NI_NAMEREQD); + if (res == 0) + return true; + + if (res == EAI_SYSTEM) + errmsg = strerror(errno); + else + errmsg = gai_strerror(res); +#else + const struct sockaddr_in *Addr = (const struct sockaddr_in *) IpAddr; + struct hostent *h; + static const char funcname[]="gethostbyaddr"; + + h = gethostbyaddr((char *)&Addr->sin_addr, sizeof(Addr->sin_addr), AF_INET); + if (h) { + if (strlcpy(resbuf, h->h_name, reslen) < reslen) + return true; + errmsg = "hostname too long"; + } else { +# ifdef h_errno + errmsg = Get_Error(h_errno); +# else + errmsg = "unknown error"; +# endif /* h_errno */ + } +#endif /* HAVE_GETNAMEINFO */ + + assert(errmsg); + assert(reslen >= NG_INET_ADDRSTRLEN); + ng_ipaddr_tostr_r(IpAddr, tmp_ip_str); + + Log_Resolver(LOG_WARNING, "%s: Can't resolve address \"%s\": %s", + funcname, tmp_ip_str, errmsg); + strlcpy(resbuf, tmp_ip_str, reslen); + return false; +} + + +/** + * perform DNS lookup of given host name and fill IpAddr with a list of + * ip addresses associated with that name. + * ip addresses found are stored in the "array *IpAddr" argument (type ng_ipaddr_t) + * @param hostname The domain name to look up. + * @param IpAddr pointer to empty and initialized array to store results + * @return true if lookup successful, false if domain name not found + */ +static bool +ForwardLookup(const char *hostname, array *IpAddr) +{ + ng_ipaddr_t addr; +#ifdef HAVE_GETADDRINFO + int res; + struct addrinfo *a, *ai_results; + static const struct addrinfo hints = { +#ifndef WANT_IPV6 + .ai_family = AF_INET, +#endif +#ifdef AI_ADDRCONFIG /* glibc has this, but not e.g. netbsd 4.0 */ + .ai_flags = AI_ADDRCONFIG, +#endif + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP + }; + res = getaddrinfo(hostname, NULL, &hints, &ai_results); + switch (res) { + case 0: break; + case EAI_SYSTEM: + Log_Resolver(LOG_WARNING, "Can't resolve \"%s\": %s", hostname, strerror(errno)); + return false; + default: + Log_Resolver(LOG_WARNING, "Can't resolve \"%s\": %s", hostname, gai_strerror(res)); + return false; + } + + for (a = ai_results; a != NULL; a = a->ai_next) { + assert(a->ai_addrlen <= sizeof(addr)); + + if (a->ai_addrlen > sizeof(addr)) + continue; + + memcpy(&addr, a->ai_addr, a->ai_addrlen); + + if (!array_catb(IpAddr, (char *)&addr, sizeof(addr))) + break; + } + + freeaddrinfo(ai_results); + return a == NULL; +#else + struct hostent *h = gethostbyname(hostname); + + if (!h) { +#ifdef h_errno + Log_Resolver(LOG_WARNING, "Can't resolve \"%s\": %s", hostname, Get_Error(h_errno)); +#else + Log_Resolver(LOG_WARNING, "Can't resolve \"%s\"", hostname); +#endif + return false; + } + memset(&addr, 0, sizeof(addr)); + + addr.sin4.sin_family = AF_INET; + memcpy(&addr.sin4.sin_addr, h->h_addr, sizeof(struct in_addr)); + + return array_copyb(IpAddr, (char *)&addr, sizeof(addr)); +#endif /* HAVE_GETADDRINFO */ +} + + +static bool +Addr_in_list(const array *resolved_addr, const ng_ipaddr_t *Addr) +{ + char tmp_ip_str[NG_INET_ADDRSTRLEN]; + const ng_ipaddr_t *tmpAddrs = array_start(resolved_addr); + size_t len = array_length(resolved_addr, sizeof(*tmpAddrs)); + + assert(len > 0); + assert(tmpAddrs); + + while (len > 0) { + if (ng_ipaddr_ipequal(Addr, tmpAddrs)) + return true; + tmpAddrs++; + len--; + } + /* failed; print list of addresses */ + ng_ipaddr_tostr_r(Addr, tmp_ip_str); + len = array_length(resolved_addr, sizeof(*tmpAddrs)); + tmpAddrs = array_start(resolved_addr); + + while (len > 0) { + Log_Resolver(LOG_WARNING, "Address mismatch: %s != %s", + tmp_ip_str, ng_ipaddr_tostr(tmpAddrs)); + tmpAddrs++; + len--; + } + + return false; +} + + +static void +Log_Forgery_NoIP(const char *ip, const char *host) +{ + Log_Resolver(LOG_WARNING, "Possible forgery: %s resolved to %s " + "(which has no ip address)", ip, host); +} + +static void +Log_Forgery_WrongIP(const char *ip, const char *host) +{ + Log_Resolver(LOG_WARNING,"Possible forgery: %s resolved to %s " + "(which points to different address)", ip, host); +} + + +static void +ArrayWrite(int fd, const array *a) +{ + size_t len = array_bytes(a); + const char *data = array_start(a); + + assert(data); + + if( (size_t)write(fd, data, len) != len ) + Log_Resolver( LOG_CRIT, "Resolver: Can't write to parent: %s!", + strerror(errno)); +} + + +static void +Do_ResolveAddr(const ng_ipaddr_t *Addr, int identsock, int w_fd) { /* Resolver sub-process: resolve IP address and write result into * pipe to parent. */ - char hostname[CLIENT_HOST_LEN]; - char ipstr[CLIENT_HOST_LEN]; - struct hostent *h; + char tmp_ip_str[NG_INET_ADDRSTRLEN]; size_t len; - struct in_addr *addr; - char *ntoaptr; array resolved_addr; -#ifdef IDENTAUTH - char *res; -#endif + array_init(&resolved_addr); - /* Resolve IP address */ + ng_ipaddr_tostr_r(Addr, tmp_ip_str); #ifdef DEBUG - Log_Resolver( LOG_DEBUG, "Now resolving %s ...", inet_ntoa( Addr->sin_addr )); + Log_Resolver(LOG_DEBUG, "Now resolving %s ...", tmp_ip_str); #endif - h = gethostbyaddr( (char *)&Addr->sin_addr, sizeof( Addr->sin_addr ), AF_INET ); - if (!h || strlen(h->h_name) >= sizeof(hostname)) { -#ifdef h_errno - Log_Resolver( LOG_WARNING, "Can't resolve address \"%s\": %s!", inet_ntoa( Addr->sin_addr ), Get_Error( h_errno )); -#else - Log_Resolver( LOG_WARNING, "Can't resolve address \"%s\"!", inet_ntoa( Addr->sin_addr )); -#endif - strlcpy( hostname, inet_ntoa( Addr->sin_addr ), sizeof( hostname )); - } else { - strlcpy( hostname, h->h_name, sizeof( hostname )); + if (!ReverseLookup(Addr, hostname, sizeof(hostname))) + goto dns_done; - h = gethostbyname( hostname ); - if ( h ) { - if (memcmp(h->h_addr, &Addr->sin_addr, sizeof (struct in_addr))) { - addr = (struct in_addr*) h->h_addr; - strlcpy(ipstr, inet_ntoa(*addr), sizeof ipstr); - ntoaptr = inet_ntoa( Addr->sin_addr ); - Log(LOG_WARNING,"Possible forgery: %s resolved to %s (which is at ip %s!)", - ntoaptr, hostname, ipstr); - strlcpy( hostname, ntoaptr, sizeof hostname); - } - } else { - ntoaptr = inet_ntoa( Addr->sin_addr ); - Log(LOG_WARNING, "Possible forgery: %s resolved to %s (which has no ip address)", - ntoaptr, hostname); - strlcpy( hostname, ntoaptr, sizeof hostname); + if (ForwardLookup(hostname, &resolved_addr)) { + if (!Addr_in_list(&resolved_addr, Addr)) { + Log_Forgery_WrongIP(tmp_ip_str, hostname); + strlcpy(hostname, tmp_ip_str, sizeof(hostname)); } + } else { + Log_Forgery_NoIP(tmp_ip_str, hostname); + strlcpy(hostname, tmp_ip_str, sizeof(hostname)); } - Log_Resolver( LOG_DEBUG, "Ok, translated %s to \"%s\".", inet_ntoa( Addr->sin_addr ), hostname ); - - len = strlen( hostname ); - hostname[len] = '\n'; len++; - if (!array_copyb(&resolved_addr, hostname, len )) { - Log_Resolver( LOG_CRIT, "Resolver: Can't copy resolved name: %s!", strerror( errno )); - close( w_fd ); +#ifdef DEBUG + Log_Resolver(LOG_DEBUG, "Ok, translated %s to \"%s\".", tmp_ip_str, hostname); +#endif + dns_done: + len = strlen(hostname); + hostname[len] = '\n'; + if (!array_copyb(&resolved_addr, hostname, ++len)) { + Log_Resolver(LOG_CRIT, "Resolver: Can't copy resolved name: %s!", strerror(errno)); + array_free(&resolved_addr); return; } -#ifdef IDENTAUTH - assert(identsock >= 0); - if (identsock >= 0) { - /* Do "IDENT" (aka "AUTH") lookup and append result to resolved_addr array */ -#ifdef DEBUG - Log_Resolver( LOG_DEBUG, "Doing IDENT lookup on socket %d ...", identsock ); -#endif - res = ident_id( identsock, 10 ); -#ifdef DEBUG - Log_Resolver(LOG_DEBUG, "Ok, IDENT lookup on socket %d done: \"%s\"", - identsock, res ? res : "(NULL)" ); -#endif - if (res && !array_cats(&resolved_addr, res)) { - Log_Resolver(LOG_WARNING, "Resolver: Cannot copy IDENT result: %s!", strerror(errno)); - /* omit ident and return hostname only */ - } + Do_IdentQuery(identsock, &resolved_addr); - if (res) free(res); - } -#else - (void)identsock; -#endif - len = array_bytes(&resolved_addr); - if( (size_t)write( w_fd, array_start(&resolved_addr), len) != len ) - Log_Resolver( LOG_CRIT, "Resolver: Can't write result to parent: %s!", strerror( errno )); + ArrayWrite(w_fd, &resolved_addr); - close(w_fd); array_free(&resolved_addr); } /* Do_ResolveAddr */ @@ -242,65 +439,36 @@ Do_ResolveName( const char *Host, int w_fd ) { /* Resolver sub-process: resolve name and write result into pipe * to parent. */ - - char ip[16]; - struct hostent *h; - struct in_addr *addr; + array IpAddrs; +#ifdef DEBUG + ng_ipaddr_t *addr; size_t len; - - Log_Resolver( LOG_DEBUG, "Now resolving \"%s\" ...", Host ); - - /* Resolve hostname */ - h = gethostbyname( Host ); - if( h ) { - addr = (struct in_addr *)h->h_addr; - strlcpy( ip, inet_ntoa( *addr ), sizeof( ip )); - } else { -#ifdef h_errno - Log_Resolver( LOG_WARNING, "Can't resolve \"%s\": %s!", Host, Get_Error( h_errno )); -#else - Log_Resolver( LOG_WARNING, "Can't resolve \"%s\"!", Host ); #endif + Log_Resolver(LOG_DEBUG, "Now resolving \"%s\" ...", Host); + + array_init(&IpAddrs); + /* Resolve hostname */ + if (!ForwardLookup(Host, &IpAddrs)) { close(w_fd); return; } #ifdef DEBUG - Log_Resolver( LOG_DEBUG, "Ok, translated \"%s\" to %s.", Host, ip ); + len = array_length(&IpAddrs, sizeof(*addr)); + assert(len > 0); + addr = array_start(&IpAddrs); + assert(addr); + for (; len > 0; --len,addr++) { + Log_Resolver(LOG_DEBUG, "translated \"%s\" to %s.", + Host, ng_ipaddr_tostr(addr)); + } #endif /* Write result into pipe to parent */ - len = strlen( ip ); - if ((size_t)write( w_fd, ip, len ) != len) { - Log_Resolver( LOG_CRIT, "Resolver: Can't write to parent: %s!", strerror( errno )); - close( w_fd ); - } + ArrayWrite(w_fd, &IpAddrs); + + array_free(&IpAddrs); } /* Do_ResolveName */ -#ifdef h_errno - -static char * -Get_Error( int H_Error ) -{ - /* Get error message for H_Error */ - - switch( H_Error ) - { - case HOST_NOT_FOUND: - return "host not found"; - case NO_DATA: - return "name valid but no IP address defined"; - case NO_RECOVERY: - return "name server error"; - case TRY_AGAIN: - return "name server temporary not available"; - default: - return "unknown error"; - } -} /* Get_Error */ - -#endif - - static bool register_callback( RES_STAT *s, void (*cbfunc)(int, short)) { diff --git a/src/ngircd/resolve.h b/src/ngircd/resolve.h index ad8ebad0..9fd16bee 100644 --- a/src/ngircd/resolve.h +++ b/src/ngircd/resolve.h @@ -8,7 +8,7 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: resolve.h,v 1.13 2006/05/10 21:24:02 alex Exp $ + * $Id: resolve.h,v 1.14 2008/02/26 22:04:17 fw Exp $ * * Asynchronous resolver (header) */ @@ -18,6 +18,8 @@ #define __resolve_h__ #include "array.h" +#include "tool.h" +#include "ng_ipaddr.h" #include /* This struct must not be accessed directly */ @@ -30,7 +32,7 @@ typedef struct _Res_Stat { #define Resolve_Getfd(x) ((x)->resolver_fd) #define Resolve_INPROGRESS(x) ((x)->resolver_fd >= 0) -GLOBAL bool Resolve_Addr PARAMS(( RES_STAT *s, struct sockaddr_in *Addr, int identsock, void (*cbfunc)(int, short))); +GLOBAL bool Resolve_Addr PARAMS(( RES_STAT *s, const ng_ipaddr_t *Addr, int identsock, void (*cbfunc)(int, short))); GLOBAL bool Resolve_Name PARAMS(( RES_STAT *s, const char *Host, void (*cbfunc)(int, short) )); GLOBAL size_t Resolve_Read PARAMS(( RES_STAT *s, void *buf, size_t buflen)); GLOBAL void Resolve_Init PARAMS(( RES_STAT *s)); diff --git a/src/tool/tool.c b/src/tool/tool.c index 52d7be88..1e723773 100644 --- a/src/tool/tool.c +++ b/src/tool/tool.c @@ -14,7 +14,7 @@ #include "portab.h" -static char UNUSED id[] = "$Id: tool.c,v 1.8 2007/11/25 18:42:38 fw Exp $"; +static char UNUSED id[] = "$Id: tool.c,v 1.9 2008/02/26 22:04:18 fw Exp $"; #include "imp.h" #include @@ -106,23 +106,4 @@ ngt_TrimLastChr( char *String, const char Chr) if( String[len] == Chr ) String[len] = '\0'; } /* ngt_TrimLastChr */ - -GLOBAL bool -ngt_IPStrToBin(const char *ip_str, struct in_addr *inaddr) -{ - /* AF is always AF_INET for now */ -#ifdef HAVE_INET_ATON - if (inet_aton(ip_str, inaddr) == 0) - return false; -#else - inaddr->s_addr = inet_addr(ip_str); - if (inaddr->s_addr == (unsigned)-1) - return false; -#endif - return true; -} - - - - /* -eof- */ diff --git a/src/tool/tool.h b/src/tool/tool.h index 7ce3e2c8..9bb7983f 100644 --- a/src/tool/tool.h +++ b/src/tool/tool.h @@ -8,7 +8,7 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: tool.h,v 1.5 2007/11/25 18:42:38 fw Exp $ + * $Id: tool.h,v 1.6 2008/02/26 22:04:18 fw Exp $ * * Tool functions (Header) */ @@ -29,8 +29,6 @@ GLOBAL void ngt_TrimLastChr PARAMS((char *String, const char Chr )); GLOBAL void ngt_TrimStr PARAMS((char *String )); GLOBAL char *ngt_LowerStr PARAMS((char *String )); - -GLOBAL bool ngt_IPStrToBin PARAMS((const char *ip_str, struct in_addr *inaddr)); #endif