From: amosjeffries <> Date: Fri, 14 Dec 2007 11:29:22 +0000 (+0000) Subject: Import IPAddress class and unit-tests from squid3-ipv6 branch X-Git-Tag: BASIC_TPROXY4~261 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=41d93087a0a99fa4a4c34eba4cf25606229567ff;p=thirdparty%2Fsquid.git Import IPAddress class and unit-tests from squid3-ipv6 branch This class forms the basis of the IPv6 capable code for squid --- diff --git a/include/IPAddress.h b/include/IPAddress.h new file mode 100644 index 0000000000..baac56a9a5 --- /dev/null +++ b/include/IPAddress.h @@ -0,0 +1,431 @@ +/* + * $Id: IPAddress.h,v 1.1 2007/12/14 04:29:22 amosjeffries Exp $ + * + * DEBUG: section 14 IP Storage and Handling + * AUTHOR: Amos Jeffries + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * the Regents of the University of California. Please see the + * COPYRIGHT file for full details. Squid incorporates software + * developed and/or copyrighted by other sources. Please see the + * CREDITS file for full details. + * + * This IPAddress code is copyright (C) 2007 by Treehouse Networks Ltd + * of New Zealand. It is published and Lisenced as an extension of + * squid under the same conditions as the main squid application. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef _INC_IPADDRESS_H +#define _INC_IPADDRESS_H + +#if HAVE_SYS_SOCKET_H +#include +#endif +#if HAVE_NETINET_IN_H +#include +#endif +#ifdef _SQUID_MSWIN_ +#include +#endif +#if HAVE_NETDB_H && !defined(_SQUID_NETDB_H_) /* protect NEXTSTEP */ +#define _SQUID_NETDB_H_ +#ifdef _SQUID_NEXT_ +#include +#endif +#include +#endif + + +#include + +#include "getaddrinfo.h" +#include "getnameinfo.h" +#include "inet_ntop.h" +#include "inet_pton.h" + +/* FreeBSD hack: + * This OS has at least one version that defines these as private + * kernel macros commented as being 'non-standard'. + * We need to use them, much nicer than the OS-provided __u*_*[] + */ +#if USE_IPV6 && defined(_SQUID_FREEBSD_) +#define s6_addr8 __u6_addr.__u6_addr8 +#define s6_addr16 __u6_addr.__u6_addr16 +#define s6_addr32 __u6_addr.__u6_addr32 +#endif + +/// Length of buffer that needs to be allocated to old a null-terminated IP-string +// Yuck. But there are still structures that need it to be an 'integer constant'. +#define MAX_IPSTRLEN 75 + +/** + * Holds and manipulates IPv4, IPv6, and Socket Addresses. + */ + +class IPAddress +{ + +public: + /** @name Constructors and Destructor */ + /*@{*/ + IPAddress(); + IPAddress(const IPAddress &); + + /** + * This constructor takes its own copy of the object pointed to for memory-safe usage later. + * The caller must itself perform and ptr memory-management needed. + * + \deprecated Use of pointers can be nasty. Consider this a last-resort. + * Prefer the by-reference (&) version instead. + */ + IPAddress(IPAddress *); + + IPAddress(const struct in_addr &); + + IPAddress(const struct sockaddr_in &); +#if USE_IPV6 + + IPAddress(const struct in6_addr &); + + IPAddress(const struct sockaddr_in6 &); +#endif + + IPAddress(const struct hostent &); + IPAddress(const struct addrinfo &); + IPAddress(const char*); + /// Default destructor. + ~IPAddress(); + /*@}*/ + + /** @name Assignment Operators */ + /*@{*/ + IPAddress& operator =(const IPAddress &s); + IPAddress& operator =(IPAddress *s); + + IPAddress& operator =(struct sockaddr_in const &s); + + IPAddress& operator =(struct in_addr const &s); +#if USE_IPV6 + + IPAddress& operator =(struct in6_addr const &s); + + IPAddress& operator =(struct sockaddr_in6 const &s); +#endif + + bool operator =(const struct hostent &s); + bool operator =(const struct addrinfo &s); + bool operator =(const char *s); + /*@}*/ + + /** @name Boolean Operators */ + /*@{*/ + bool operator ==(IPAddress const &s) const; + bool operator !=(IPAddress const &s) const; + bool operator >=(IPAddress const &rhs) const; + bool operator <=(IPAddress const &rhs) const; + bool operator >(IPAddress const &rhs) const; + bool operator <(IPAddress const &rhs) const; + +public: + /* methods */ + + /** Test whether content can be used as an IPv4 address + * \retval true if content was received as an IPv4 address + * \retval true if content was received as an IPv4-Mapped address + * \retval false if content was received as a non-mapped IPv6 native address. + */ + bool IsIPv4() const; + + /** Test whether content can be used as an IPv6 address. + * \retval true if --enable-ipv6 has been compiled. + * \retval false if --disable-ipv6 has been compiled. + * \retval false if --with-ipv6-split-stack has been compiled AND content is IPv4-mapped. + */ + bool IsIPv6() const; + + /** Test whether content can be used as a Socket address. + * \retval true if address AND port are both set + * \retval true if content was received as a Socket address with port + * \retval false if port in unset (zero) + */ + bool IsSockAddr() const; + + /** Content-neutral test for whether the specific IP case ANY_ADDR is stored. + * This is the default content of a new undefined IPAddress object. + * \retval true IPv4 0.0.0.0 + * \retval true IPv6 :: + * \retval false anything else. + */ + bool IsAnyAddr() const; + + /** Content-neutral test for whether the specific IP case NO_ADDR is stored. + * \retval true IPv4 255.255.255.255 + * \retval true IPv6 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff + * \retval false anything else. + */ + bool IsNoAddr() const; + + /** Content-neutral test for whether the specific IP case LOCALHOST is stored. + * This is the default content of a new undefined IPAddress object. + * \retval true IPv4 127.0.0.1 + * \retval true IPv6 ::1 + * \retval false anything else. + */ + bool IsLocalhost() const; + + /*@}*/ + + /** Retrieve the Port if stored. + * \retval 0 Port is unset or an error occured. + * \retval n Port associated with this address in host native -endian. + */ + u_short GetPort() const; + + /** Set the Port value for an address. + * Replaces any previously existing Port value. + * \param port Port being assigned in host native -endian. + * \retval 0 Port is unset or an error occured. + * \retval n Port associated with this address in host native -endian. + */ + u_short SetPort(u_short port); + + /// Set object to contain the specific IP case ANY_ADDR (format-neutral). + /// see IsAnyAddr() for more detail. + void SetAnyAddr(); + + /// Set object to contain the specific IP case NO_ADDR (format-neutral). + /// see \link IsNoAddr() for more detail. + void SetNoAddr(); + + /// Set object to contain the specific IP case LOCALHOST (format-neutral). + /// see \link IsLocalhost() for more detail. + void SetLocalhost(); + + /// Fast reset of the stored content to what would be after default constructor. + void SetEmpty(); + + /** Require an IPv4-only address for this usage. + * Converts the object to prefer only IPv4 output. + \retval true Content can be IPv4 + \retval false Content CANNOT be IPv4 + */ + bool SetIPv4(); + + /** + * Valid results IF and only IF the stored IP address is actually a network bitmask + * \retval N number of bits which are set in the bitmask stored. + */ + int GetCIDR(); + + /** Apply a mask to the stored address. + * \param mask Netmask format to be bit-mask-AND'd over the stored address. + */ + const int ApplyMask(const IPAddress &mask); + + /** Apply a mask to the stored address. + * CIDR will be converted appropriate to map the stored content. + * \param cidr CIDR Mask being applied. As an integer in host format. + * \param mtype Type of CIDR mask being applied (AF_INET or AF_INET6) + */ + bool ApplyMask(const unsigned int cidr, int mtype = AF_UNSPEC); + + + /** Return the ASCII equivalent of the address + * Semantically equivalent to the IPv4 inet_ntoa() + * eg. 127.0.0.1 (IPv4) or ::1 (IPv6) + * But for memory safety it requires a buffer as input + * instead of producing one magically. + * If buffer is not large enough the data is truncated silently. + * \param buf Allocated buffer to write address to + * \param len byte length of buffer available for writing. + * \param force (optional) require the IPA in a specific format. + * \return pointer to buffer received. + */ + char* NtoA(char *buf, unsigned int len, int force = AF_UNSPEC) const; + + /** Return the ASCII equivalent of the address:port combination + * Provides a URL formatted version of the content. + * If buffer is not large enough the data is truncated silently. + * eg. 127.0.0.1:80 (IPv4) or [::1]:80 (IPv6) + * \param buf Allocated buffer to write address:port to + * \param len byte length of buffer available for writing. + * \return pointer to buffer received. + */ + char* ToURL(char *buf, unsigned int len) const; + + /** Return a properly hostname formatted copy of the address + * Provides a URL formatted version of the content. + * If buffer is not large enough the data is truncated silently. + * eg. 127.0.0.1 (IPv4) or [::1] (IPv6) + * \param buf Allocated buffer to write address to + * \param len byte length of buffer available for writing. + * \return pointer to buffer received. + */ + unsigned int ToHostname(char *buf, const unsigned int len) const; + + /** \fn bool GetReverseString(char buf[], IPAddressType show_format) + * Convert the content into a Reverse-DNS string. + * The buffer sent MUST be allocated large enough to hold the resulting string. + * Name truncation will occur if buf does not have enough space. + * The constant MAX_IPSTRLEN is defined to provide for sizing arrays correctly. + * \param show_format may be one of: AF_INET, AF_INET6 for the format of rDNS string wanted. + * \param buf buffer to receive the text string output. + */ + bool GetReverseString(char buf[], int show_format = AF_UNSPEC) const; + + /** Test how two IP relate to each other. + * \retval 0 IP are equal + * \retval 1 IP rhs is greater (numerically) than that stored. + * \retval -1 IP rhs is less (numerically) than that stored. + */ + int matchIPAddr(const IPAddress &rhs) const; + + /** + * Get RFC 3493 addrinfo structure from the IPAddress data + * for protocol-neutral socket operations. + * Should be passed a NULL pointer of type struct addrinfo* it will + * allocate memory for the structures involved. (see FreeAddrInfo to clear). + * Defaults to a TCP streaming socket, if other values (such as UDP) are needed + * the caller MUST override these default settings. + * Some situations may also require an actual call to the system getaddrinfo() + * to pull relevant OS details for the socket. + *\par + * IPAddress allocated objects MUST be destructed by IPAddress::FreeAddrInfo + * System getaddrinfo() allocated objects MUST be freed with system freeaddrinfo() + *\par + * Some OS require that IPv4 addresses are pre-mapped by the client. + * The configure option --with-ipv4-mapping=yes will enable this. + * + * \param ai structure to be filled out. + * \param force a specific sockaddr type is needed. default: don't care. + */ +#if IPV6_SPECIAL_V4MAPPING + void GetAddrInfo(struct addrinfo *&ai, int force = AF_INET6) const; +#else + void GetAddrInfo(struct addrinfo *&ai, int force = AF_UNSPEC) const; +#endif + + /** + * Equivalent to the sysem call freeaddrinfo() but for IPAddress allocated data + */ + void FreeAddrInfo(struct addrinfo *&ai) const; + + /** + * Initializes an empty addrinfo properly for use. + * It is intended for use in cases such as getsockopt() where the addrinfo is + * about to be changed and the stored details may not match the new ones coming. + * \param ai addrinfo struct to be initialized as AF_UNSPEC with large address buffer + */ + void InitAddrInfo(struct addrinfo *&ai) const; + + /** + * Lookup a Host by Name. Equivalent to system call gethostbyname(char*) + * \param s The textual FQDN of teh host being located. + * \retval true lookup was successful and an IPA was located. + * \retval false lookup failed or FQDN has no IP associated. + */ + bool GetHostByName(const char *s); + +public: + /* FIXME: When C => C++ conversion is done will be fully private. + * Legacy Transition Methods. + * These are here solely to simplify the transition + * when moving from converted code to unconverted + * these functions can be used to convert this object + * and pull out the data needed by the unconverted code + * they are intentionaly hard to use, use GetAddrInfo() instead. + * these functiosn WILL NOT be in the final public API after transition. + */ + + /// \deprecated Deprecated for public use. Use IPAddress::GetAddrInfo() + void GetSockAddr(struct sockaddr_in &) const; + + /// \deprecated Deprecated for public use. Use IPAddress::GetAddrInfo() + bool GetInAddr(struct in_addr &) const; /* false if could not convert IPv6 down to IPv4 */ +#if USE_IPV6 + + /// \deprecated Deprecated for public use. Use IPAddress::GetAddrInfo() + void GetSockAddr(struct sockaddr_in6 &) const; + + /// \deprecated Deprecated for public use. Use IPAddress::GetAddrInfo() + void GetInAddr(struct in6_addr &) const; +#endif + +private: + /* Conversion for dual-type internals */ + + bool GetReverseString4(char buf[], const struct in_addr &) const; + + void check4Mapped(); + +#if USE_IPV6 + + bool GetReverseString6(char buf[], const struct in6_addr &) const; + + void Map4to6(const struct in_addr &src, struct in6_addr &dest) const; + + void Map6to4(const struct in6_addr &src, struct in_addr &dest) const; +#endif + + // Worker behind GetHostName and char* converters + bool LookupHostIP(const char *s, bool nodns); + + /* variables */ +#if USE_IPV6 + + struct sockaddr_in6 m_SocketAddr; +#else + + struct sockaddr_in m_SocketAddr; +#endif +}; + +inline std::ostream & +operator << (std::ostream &os, const IPAddress &ipa) +{ + char buf[MAX_IPSTRLEN]; + os << ipa.ToURL(buf,MAX_IPSTRLEN); + return os; +} + + +// Macros for Old IPv4-Only code that still needs to use IN_ADDR +#define ANY_ADDR (struct in_addr)0x00000000 +#define NO_ADDR (struct_in_addr)0xFFFFFFFF +#define IN_ADDR in_addr + +// WAS _sockaddr_in_list in an earlier incarnation +/* INET6 : this could possibly be an addrinfo structure now IFF it needs to be in a generic raw form. */ +class IPAddress_list +{ +public: + IPAddress_list() { next = NULL; }; + ~IPAddress_list() { if(next) delete next; next = NULL; }; + + IPAddress s; + IPAddress_list *next; +}; + + +#endif /* _INC_IPADDRESS_H */ diff --git a/lib/IPAddress.cc b/lib/IPAddress.cc new file mode 100644 index 0000000000..43af63bfca --- /dev/null +++ b/lib/IPAddress.cc @@ -0,0 +1,1200 @@ +/* + * $Id: IPAddress.cc,v 1.1 2007/12/14 04:29:22 amosjeffries Exp $ + * + * DEBUG: section 14 IP Storage and Handling + * AUTHOR: Amos Jeffries + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * the Regents of the University of California. Please see the + * COPYRIGHT file for full details. Squid incorporates software + * developed and/or copyrighted by other sources. Please see the + * CREDITS file for full details. + * + * This IPAddress code is copyright (C) 2007 by Treehouse Networks Ltd + * of New Zealand. It is published and Lisenced as an extension of + * squid under the same conditions as the main squid application. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "config.h" + +#include "IPAddress.h" + +#if HAVE_ASSERT_H +#include +#endif +#if HAVE_STDLIB_H +#include +#endif +#if HAVE_STRING_H +#include +#endif +#if HAVE_ARPA_INET_H +#include /* inet_ntoa() */ +#endif + +#include "util.h" + +#ifdef INET6 +#error "INET6 defined but has been deprecated! Try running bootstrap and configure again." +#endif + +/* We want to use the debug routines when running as module of squid. */ +/* otherwise fallback to printf if those are not available. */ +#ifndef SQUID_DEBUG +# define debugs(a,b,c) // drop. +#else +#warning "IPAddress built with Debugs!!" +# include "../src/Debug.h" +#endif + +#if !USE_IPV6 +// So there are some places where I will drop to using Macros too. +// At least I can restrict them to this file so they don't corrupt the app with C code. +# define sin6_addr sin_addr +# define sin6_port sin_port +# define sin6_family sin_family +#undef s6_addr +# define s6_addr s_addr +#endif + +static const unsigned int STRLEN_IP4A = 16; // aaa.bbb.ccc.ddd\0 +static const unsigned int STRLEN_IP4R = 28; // ddd.ccc.bbb.aaa.in-addr.arpa.\0 +static const unsigned int STRLEN_IP4S = 21; // ddd.ccc.bbb.aaa:ppppp\0 +static const unsigned int MAX_IP4_STRLEN = STRLEN_IP4R; +static const unsigned int STRLEN_IP6A = 42; // [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]/0 +static const unsigned int STRLEN_IP6R = 75; // f.f.f.f f.f.f.f f.f.f.f f.f.f.f f.f.f.f f.f.f.f f.f.f.f f.f.f.f ipv6.arpa./0 +static const unsigned int STRLEN_IP6S = 48; // [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:00000/0 +static const unsigned int MAX_IP6_STRLEN = STRLEN_IP6R; + + +/* Debugging only. Dump the address content when a fatal assert is encountered. */ +#if USE_IPV6 +#define IASSERT(a,b) \ + if(!b){ printf("assert \"%s\" at line %d\n", a, __LINE__); \ + printf("IPAddress invalid? with IsIPv4()=%c, IsIPv6()=%c\n",(IsIPv4()?'T':'F'),(IsIPv6()?'T':'F')); \ + printf("ADDRESS:"); \ + for(unsigned int i = 0; i < sizeof(m_SocketAddr.sin6_addr); i++) { \ + printf(" %x", m_SocketAddr.sin6_addr.s6_addr[i]); \ + } printf("\n"); assert(b); \ + } +#else +#define IASSERT(a,b) \ + if(!b){ printf("assert \"%s\" at line %d\n", a, __LINE__); \ + printf("IPAddress invalid? with IsIPv4()=%c, IsIPv6()=%c\n",(IsIPv4()?'T':'F'),(IsIPv6()?'T':'F')); \ + printf("ADDRESS: %x\n", (unsigned int)m_SocketAddr.sin_addr.s_addr); \ + assert(b); \ + } +#endif + +IPAddress::IPAddress() +{ + SetEmpty(); +} + +IPAddress::~IPAddress() +{ + memset(this,0,sizeof(IPAddress)); +} + +int +IPAddress::GetCIDR() +{ + uint8_t shift,byte; + uint8_t bit,caught; + int len = 0; +#if USE_IPV6 + uint8_t *ptr= m_SocketAddr.sin6_addr.s6_addr; +#else + uint8_t *ptr= (uint8_t *)&m_SocketAddr.sin_addr.s_addr; +#endif + + /* Let's scan all the bits from Most Significant to Least */ + /* Until we find an "0" bit. Then, we return */ + shift=0; + +#if USE_IPV6 + /* return IPv4 CIDR for any Mapped address */ + /* Thus only check the mapped bit */ + + if( !IsIPv6() ) { + shift = 12; + } + +#endif + + for (;shift 128) + return false; + + if (cidr > 32 && mtype == AF_INET) + return false; + + clearbits = (uint8_t)( (mtype==AF_INET6?128:32) -cidr); + + // short-cut + if(clearbits == 0) + return true; + +#if USE_IPV6 + + p = (uint8_t*)(&m_SocketAddr.sin6_addr) + 15; + +#else + + p = (uint8_t*)(&m_SocketAddr.sin_addr) + 3; + +#endif + + for (; clearbits>0 && p >= (uint8_t*)&m_SocketAddr.sin6_addr ; p-- ) { + if(clearbits < 8) { + *p &= ((0xFF << clearbits) & 0xFF); + clearbits = 0; + } else { + *p &= 0x00; + clearbits -= 8; + } + } + + return true; +} + +bool IPAddress::IsSockAddr() const +{ + return (m_SocketAddr.sin6_port != 0); +} + +bool IPAddress::IsIPv4() const +{ +#if USE_IPV6 + + return IsAnyAddr() || IsNoAddr() || +#if IPV6_SPECIAL_LOCALHOST + IsLocalhost() || +#endif + ( m_SocketAddr.sin6_addr.s6_addr32[0] == htonl(0x00000000) && + m_SocketAddr.sin6_addr.s6_addr32[1] == htonl(0x00000000) && + m_SocketAddr.sin6_addr.s6_addr32[2] == htonl(0x0000FFFF) + ); + +#else + return true; // enforce IPv4 in IPv4-only mode. +#endif +} + +bool IPAddress::IsIPv6() const +{ +#if USE_IPV6 + + return IsAnyAddr() || IsNoAddr() || +#if IPV6_SPECIAL_LOCALHOST + IsLocalhost() || +#endif + !( m_SocketAddr.sin6_addr.s6_addr32[0] == htonl(0x00000000) && + m_SocketAddr.sin6_addr.s6_addr32[1] == htonl(0x00000000) && + m_SocketAddr.sin6_addr.s6_addr32[2] == htonl(0x0000FFFF) + ); +#else + return false; // enforce IPv4 in IPv4-only mode. +#endif +} + +bool IPAddress::IsAnyAddr() const +{ +#if USE_IPV6 + return m_SocketAddr.sin6_addr.s6_addr32[0] == 0 + && m_SocketAddr.sin6_addr.s6_addr32[1] == 0 + && m_SocketAddr.sin6_addr.s6_addr32[2] == 0 + && m_SocketAddr.sin6_addr.s6_addr32[3] == 0 + ; +#else + + return (INADDR_ANY == m_SocketAddr.sin_addr.s_addr); +#endif +} + +/// NOTE: Does NOT clear the Port stored. Ony the Address and Type. +void IPAddress::SetAnyAddr() +{ +#if USE_IPV6 + memset(&m_SocketAddr.sin6_addr, 0, sizeof(struct in6_addr) ); +#else + memset(&m_SocketAddr.sin_addr, 0, sizeof(struct in_addr) ); +#endif +} + +/// NOTE: completely empties the IPAddress structure. Address, Port, Type, everything. +void IPAddress::SetEmpty() +{ + memset(&m_SocketAddr, 0, sizeof(m_SocketAddr) ); +} + +bool IPAddress::SetIPv4() +{ +#if USE_IPV6 + +#if !IPV6_SPECIAL_LOCALHOST + if( IsLocalhost() ) { + m_SocketAddr.sin6_addr.s6_addr32[2] = htonl(0xffff); + m_SocketAddr.sin6_addr.s6_addr32[3] = htonl(0x7F000001); + return true; + } +#endif + + if( IsAnyAddr() ) { + m_SocketAddr.sin6_addr.s6_addr32[2] = htonl(0xffff); + return true; + } + + if( IsIPv4()) + return true; + + // anything non-IPv4 and non-convertable is BAD. + return false; +#else + return true; // Always IPv4 in IPv4-only builds. +#endif +} + +bool IPAddress::IsLocalhost() const +{ +#if USE_IPV6 + return m_SocketAddr.sin6_addr.s6_addr32[0] == 0 + && m_SocketAddr.sin6_addr.s6_addr32[1] == 0 + && m_SocketAddr.sin6_addr.s6_addr32[2] == 0 + && m_SocketAddr.sin6_addr.s6_addr32[3] == htonl(0x1) + +#if !IPV6_SPECIAL_LOCALHOST + || m_SocketAddr.sin6_addr.s6_addr32[0] == 0 + && m_SocketAddr.sin6_addr.s6_addr32[1] == 0 + && m_SocketAddr.sin6_addr.s6_addr32[2] == htonl(0xffff) + && m_SocketAddr.sin6_addr.s6_addr32[3] == htonl(0x7F000001) +#endif + ; +#else + + return (htonl(0x7F000001) == m_SocketAddr.sin_addr.s_addr); +#endif +} + +void IPAddress::SetLocalhost() +{ +#if USE_IPV6 + SetAnyAddr(); + m_SocketAddr.sin6_addr.s6_addr[15] = 0x1; + m_SocketAddr.sin6_family = AF_INET6; + +#else + m_SocketAddr.sin_addr.s_addr = htonl(0x7F000001); + m_SocketAddr.sin_family = AF_INET; +#endif +} + +bool IPAddress::IsNoAddr() const +{ + // IFF the address == 0xff..ff (all ones) +#if USE_IPV6 + return m_SocketAddr.sin6_addr.s6_addr32[0] == 0xFFFFFFFF + && m_SocketAddr.sin6_addr.s6_addr32[1] == 0xFFFFFFFF + && m_SocketAddr.sin6_addr.s6_addr32[2] == 0xFFFFFFFF + && m_SocketAddr.sin6_addr.s6_addr32[3] == 0xFFFFFFFF + ; +#else + + return 0xFFFFFFFF == m_SocketAddr.sin_addr.s_addr; +#endif +} + +void IPAddress::SetNoAddr() +{ +#if USE_IPV6 + memset(&m_SocketAddr.sin6_addr, 0xFFFFFFFF, sizeof(struct in6_addr) ); + m_SocketAddr.sin6_family = AF_INET6; +#else + memset(&m_SocketAddr.sin_addr, 0xFFFFFFFF, sizeof(struct in_addr) ); + m_SocketAddr.sin_family = AF_INET; +#endif +} + +#if USE_IPV6 + +bool IPAddress::GetReverseString6(char buf[MAX_IPSTRLEN], const struct in6_addr &dat) const +{ + char *p = buf; + unsigned char const *r = dat.s6_addr; + + /* RFC1886 says: */ + /* 4321:0:1:2:3:4:567:89ab */ + /* must be sent */ + /* b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.int. */ + + /* Work from the binary field. Anything else may have representation changes. */ + /* The sin6_port and sin6_addr members shall be in network byte order. */ + + /* Compile Err: 'Too many arguments for format. */ + + for(int i = 15; i >= 0; i--, p+=4) + { + snprintf(p, 5, "%x.%x.", ((r[i])&0xf), (((r[i])>>4)&0xf) ); + } + + /* RFC3152 says: */ + /* ip6.int is now deprecated TLD, use ip6.arpa instead. */ + snprintf(p,10,"ip6.arpa."); + + return true; +} + +#endif + +bool IPAddress::GetReverseString4(char buf[MAX_IPSTRLEN], const struct in_addr &dat) const +{ + unsigned int i = (unsigned int) ntohl(dat.s_addr); + snprintf(buf, 32, "%u.%u.%u.%u.in-addr.arpa.", + i & 255, + (i >> 8) & 255, + (i >> 16) & 255, + (i >> 24) & 255); + return true; +} + +bool IPAddress::GetReverseString(char buf[MAX_IPSTRLEN], int show_type) const +{ + + if(show_type == AF_UNSPEC) { +#if USE_IPV6 + show_type = IsIPv6() ? AF_INET6 : AF_INET ; +#else + show_type = AF_INET; +#endif + } + + if (show_type == AF_INET && IsIPv4()) { +#if USE_IPV6 + + return GetReverseString4(buf, *(struct in_addr*)&m_SocketAddr.sin6_addr.s6_addr32[3] ); + } else if ( show_type == AF_INET6 && IsIPv6() ) { + return GetReverseString6(buf, m_SocketAddr.sin6_addr); +#else + return GetReverseString4(buf, m_SocketAddr.sin_addr); +#endif + } + + debugs(14,0, "Unable to convert '" << NtoA(buf,MAX_IPSTRLEN) << "' to the rDNS type requested."); + + buf[0] = '\0'; + + return false; +} + +IPAddress& IPAddress::operator =(const IPAddress &s) +{ + memcpy(this, &s, sizeof(IPAddress)); + return *this; +}; + +IPAddress::IPAddress(const char*s) +{ + SetEmpty(); + operator=(s); +} + +bool IPAddress::operator =(const char* s) +{ + return LookupHostIP(s, true); +} + +bool IPAddress::GetHostByName(const char* s) +{ + return LookupHostIP(s, false); +} + +bool IPAddress::LookupHostIP(const char *s, bool nodns) +{ + int err = 0; + + short port = 0; + + struct addrinfo *res = NULL; + + struct addrinfo want; + + memset(&want, 0, sizeof(struct addrinfo)); + if(nodns) { + want.ai_flags = AI_NUMERICHOST; // prevent actual DNS lookups! + } +#if !USE_IPV6 + want.ai_family = AF_INET; +#endif + + if ( (err = xgetaddrinfo(s, NULL, &want, &res)) != 0) { + debugs(14,1, HERE << "Given Bad IP '" << s << "': " << xgai_strerror(err) ); + /* free the memory xgetaddrinfo() dynamically allocated. */ + if(res) { + xfreeaddrinfo(res); + res = NULL; + } + return false; + } + + /* + * NP: =(sockaddr_*) may alter the port. we don't want that. + * all we have been given as input was an IPA. + */ + port = GetPort(); + operator=(*res); + SetPort(port); + + /* free the memory xgetaddrinfo() dynamically allocated. */ + xfreeaddrinfo(res); + + res = NULL; + + return true; +} + +IPAddress::IPAddress(struct sockaddr_in const &s) +{ + SetEmpty(); + operator=(s); +}; + +IPAddress& IPAddress::operator =(struct sockaddr_in const &s) +{ +#if USE_IPV6 + Map4to6((const in_addr)s.sin_addr, m_SocketAddr.sin6_addr); + m_SocketAddr.sin6_port = s.sin_port; + m_SocketAddr.sin6_family = AF_INET6; +#else + + memcpy(&m_SocketAddr, &s, sizeof(struct sockaddr_in)); +#endif + + /* maintain stored family values properly */ + check4Mapped(); + + return *this; +}; + +void IPAddress::check4Mapped() +{ + // obsolete. + // TODO use this NOW to set the sin6_family properly on exporting. not on import. +} + +#if USE_IPV6 +IPAddress::IPAddress(sockaddr_in6 const &s) +{ + SetEmpty(); + operator=(s); +}; + +IPAddress& IPAddress::operator =(sockaddr_in6 const &s) +{ + memcpy(&m_SocketAddr, &s, sizeof(struct sockaddr_in6)); + + /* maintain address family properly */ + check4Mapped(); + return *this; +}; + +#endif + +IPAddress::IPAddress(in_addr const &s) +{ + SetEmpty(); + operator=(s); +}; + +IPAddress& IPAddress::operator =(struct in_addr const &s) +{ +#if USE_IPV6 + Map4to6((const in_addr)s, m_SocketAddr.sin6_addr); + m_SocketAddr.sin6_family = AF_INET6; + +#else + + memcpy(&m_SocketAddr.sin_addr, &s, sizeof(struct in_addr)); + +#endif + + /* maintain stored family type properly */ + check4Mapped(); + + return *this; +}; + +#if USE_IPV6 + +IPAddress::IPAddress(struct in6_addr const &s) +{ + SetEmpty(); + operator=(s); +}; + +IPAddress& IPAddress::operator =(struct in6_addr const &s) +{ + + memcpy(&m_SocketAddr.sin6_addr, &s, sizeof(struct in6_addr)); + m_SocketAddr.sin6_family = AF_INET6; + + /* maintain address family type properly */ + check4Mapped(); + + return *this; +}; + +#endif + +IPAddress::IPAddress(const IPAddress &s) +{ + SetEmpty(); + operator=(s); +} + +IPAddress::IPAddress(IPAddress *s) +{ + SetEmpty(); + operator=(s); +} + +IPAddress& IPAddress::operator =(IPAddress *s) +{ + IPAddress *tmp = NULL; + if(!s) return *this; + tmp = dynamic_cast(s); + if(!tmp) return *this; + return operator=(*tmp); +} + +IPAddress::IPAddress(const struct hostent &s) +{ + SetEmpty(); + operator=(s); +} + +bool IPAddress::operator =(const struct hostent &s) +{ + + struct in_addr* ipv4 = NULL; + + struct in6_addr* ipv6 = NULL; + + //struct hostent { + // char *h_name; /* official name of host */ + // char **h_aliases; /* alias list */ + // int h_addrtype; /* host address type */ + // int h_length; /* length of address */ + // char **h_addr_list; /* list of addresses */ + //} + + switch(s.h_addrtype) + { + + case AF_INET: + ipv4 = (in_addr*)(s.h_addr_list[0]); + /* this */ + operator=(*ipv4); + break; + + case AF_INET6: + ipv6 = (in6_addr*)(s.h_addr_list[0]); +#if USE_IPV6 + /* this */ + operator=(*ipv6); +#else + + debugs(14,1, HERE << "Discarded IPv6 Address. Protocol disabled."); + + // FIXME see if there is another address in the list that might be usable ?? + return false; +#endif + + break; + + default: + IASSERT("false",false); + return false; + } + + return true; +} + +IPAddress::IPAddress(const struct addrinfo &s) +{ + SetEmpty(); + operator=(s); +} + +bool IPAddress::operator =(const struct addrinfo &s) +{ + + struct sockaddr_in* ipv4 = NULL; + + struct sockaddr_in6* ipv6 = NULL; + + //struct addrinfo { + // int ai_flags; /* input flags */ + // int ai_family; /* protocol family for socket */ + // int ai_socktype; /* socket type */ + // int ai_protocol; /* protocol for socket */ + // socklen_t ai_addrlen; /* length of socket-address */ + // struct sockaddr *ai_addr; /* socket-address for socket */ + // char *ai_canonname; /* canonical name for service location */ + // struct addrinfo *ai_next; /* pointer to next in list */ + //} + + switch(s.ai_family) + { + + case AF_INET: + ipv4 = (sockaddr_in*)(s.ai_addr); + /* this */ + assert(ipv4); + operator=(*ipv4); + break; + + case AF_INET6: + ipv6 = (sockaddr_in6*)(s.ai_addr); +#if USE_IPV6 + /* this */ + assert(ipv6); + operator=(*ipv6); +#else + + debugs(14,1, HERE << "Discarded IPv6 Address. Protocol disabled."); + + // see if there is another address in the list that might be usable ?? + + if (s.ai_next) + return operator=(*s.ai_next); + else + return false; + +#endif + break; + + case AF_UNSPEC: + default: + // attempt to handle partially initialised addrinfo. + // such as those where data only comes from getsockopt() + if(s.ai_addr != NULL) { +#if USE_IPV6 + if(s.ai_addrlen == sizeof(struct sockaddr_in6)) { + operator=(*((struct sockaddr_in6*)s.ai_addr)); + return true; + } + else +#endif + if(s.ai_addrlen == sizeof(struct sockaddr_in)) { + operator=(*((struct sockaddr_in*)s.ai_addr)); + return true; + } + } + return false; + } + + return true; +} + +void IPAddress::GetAddrInfo(struct addrinfo *&dst, int force) const +{ + if(dst == NULL) + { + dst = new addrinfo; + } + + memset(dst, 0, sizeof(struct addrinfo)); + + // set defaults + dst->ai_flags = AI_NUMERICHOST; + + if(dst->ai_socktype == 0) + dst->ai_socktype = SOCK_STREAM; + + if(dst->ai_socktype == SOCK_STREAM // implies TCP + && dst->ai_protocol == 0) + dst->ai_protocol = IPPROTO_TCP; + + if(dst->ai_socktype == SOCK_DGRAM // implies UDP + && dst->ai_protocol == 0) + dst->ai_protocol = IPPROTO_UDP; + +#if USE_IPV6 + if( force == AF_INET6 || force == AF_UNSPEC && IsIPv6() ) + { + dst->ai_addr = (struct sockaddr*)new sockaddr_in6; + + memset(dst->ai_addr,0,sizeof(struct sockaddr_in6)); + + GetSockAddr(*((struct sockaddr_in6*)dst->ai_addr)); + + dst->ai_addrlen = sizeof(struct sockaddr_in6); + + dst->ai_family = ((struct sockaddr_in6*)dst->ai_addr)->sin6_family; + dst->ai_protocol = IPPROTO_IPV6; + } else +#endif + if( force == AF_INET || force == AF_UNSPEC && IsIPv4() ) + { + + dst->ai_addr = (struct sockaddr*)new sockaddr_in; + + memset(dst->ai_addr,0,sizeof(struct sockaddr_in)); + + GetSockAddr(*((struct sockaddr_in*)dst->ai_addr)); + + dst->ai_addrlen = sizeof(struct sockaddr_in); + + dst->ai_family = ((struct sockaddr_in*)dst->ai_addr)->sin_family; + } else + { + IASSERT("false",false); + } +} + +void IPAddress::InitAddrInfo(struct addrinfo *&ai) const +{ + if(ai == NULL) { + ai = new addrinfo; + memset(ai,0,sizeof(struct addrinfo)); + } + + // remove any existing data. + if(ai->ai_addr) delete ai->ai_addr; + + ai->ai_addr = (struct sockaddr*)new sockaddr_in6; + memset(ai->ai_addr, 0, sizeof(struct sockaddr_in6)); + + ai->ai_addrlen = sizeof(struct sockaddr_in6); + +} + +void IPAddress::FreeAddrInfo(struct addrinfo *&ai) const +{ + if(ai == NULL) return; + + if(ai->ai_addr) delete ai->ai_addr; + + ai->ai_addr = NULL; + + ai->ai_addrlen = 0; + + // NP: name fields are NOT allocated at present. + delete ai; + + ai = NULL; +} + +int IPAddress::matchIPAddr(const IPAddress &rhs) const +{ +#if USE_IPV6 + uint8_t *l = (uint8_t*)m_SocketAddr.sin6_addr.s6_addr; + uint8_t *r = (uint8_t*)rhs.m_SocketAddr.sin6_addr.s6_addr; +#else + uint8_t *l = (uint8_t*)&m_SocketAddr.sin_addr.s_addr; + uint8_t *r = (uint8_t*)&rhs.m_SocketAddr.sin_addr.s_addr; +#endif + + // loop a byte-wise compare + // NP: match MUST be R-to-L : L-to-R produces inconsistent gt/lt results at varying CIDR + // expected difference on CIDR is gt/eq or lt/eq ONLY. + for(unsigned int i = 0 ; i < sizeof(m_SocketAddr.sin6_addr) ; i++) { + + if(l[i] < r[i]) + return -1; + + if(l[i] > r[i]) + return 1; + } + + return 0; +} + +bool IPAddress::operator ==(const IPAddress &s) const +{ + return (0 == matchIPAddr(s)); +} + +bool IPAddress::operator !=(const IPAddress &s) const +{ + return ! ( operator==(s) ); +} + +bool IPAddress::operator <=(const IPAddress &rhs) const +{ + if(IsAnyAddr() && !rhs.IsAnyAddr()) + return true; + + return (matchIPAddr(rhs) <= 0); +} + +bool IPAddress::operator >=(const IPAddress &rhs) const +{ + if(IsNoAddr() && !rhs.IsNoAddr()) + return true; + + return ( matchIPAddr(rhs) >= 0); +} + +bool IPAddress::operator >(const IPAddress &rhs) const +{ + if(IsNoAddr() && !rhs.IsNoAddr()) + return true; + + return ( matchIPAddr(rhs) > 0); +} + +bool IPAddress::operator <(const IPAddress &rhs) const +{ + if(IsNoAddr() && !rhs.IsNoAddr()) + return true; + + return ( matchIPAddr(rhs) < 0); +} + +u_short IPAddress::GetPort() const +{ + return ntohs( m_SocketAddr.sin6_port ); +} + +u_short IPAddress::SetPort(u_short prt) +{ + m_SocketAddr.sin6_port = htons(prt); + + return prt; +} + +/** + * NtoA Given a buffer writes a readable ascii version of the IPA and/or port stored + * + * Buffer must be of a size large enough to hold the converted address. + * This size is provided in the form of a global defined variable MAX_IPSTRLEN + * Should a buffer shorter be provided the string result will be truncated + * at the length of the available buffer. + * + * A copy of the buffer is also returned for simple immediate display. + */ +char* IPAddress::NtoA(char* buf, const unsigned int blen, int force) const +{ + // Ensure we have a buffer. + if(buf == NULL) { + return NULL; + } + + /* some external code may have blindly memset a parent. */ + /* thats okay, our default is known */ + if( IsAnyAddr() ) { +#if USE_IPV6 + memcpy(buf,"::\0", xmin(3,blen)); +#else + memcpy(buf,"0.0.0.0\0", xmin(8,blen)); +#endif + return buf; + } + + memset(buf,0,blen); // clear buffer before write + + /* Pure-IPv6 CANNOT be displayed in IPv4 format. */ + /* However IPv4 CAN. */ + if( force == AF_INET && !IsIPv4() ) { + if( IsIPv6() ) { + memcpy(buf, "{!IPv4}\0", xmin(8,blen)); + } + return buf; + } + +#if USE_IPV6 + if( force == AF_INET6 || (force == AF_UNSPEC && IsIPv6()) ) { + + xinet_ntop(AF_INET6, &m_SocketAddr.sin6_addr, buf, blen); + + } else if ( force == AF_INET || (force == AF_UNSPEC && IsIPv4()) ) { + + struct in_addr tmp; + GetInAddr(tmp); + xinet_ntop(AF_INET, &tmp, buf, blen); +#else + if ( force == AF_UNSPEC || (force == AF_INET && IsIPv4()) ) { + xinet_ntop(AF_INET, &m_SocketAddr.sin_addr, buf, blen); +#endif + } else { + debugs(14,0,"WARNING: Corrupt IP Address details OR required to display in unknown format (" << + force << "). accepted={" << AF_UNSPEC << "," << AF_INET << "," << AF_INET6 << "}"); + fprintf(stderr,"WARNING: Corrupt IP Address details OR required to display in unknown format (%d). accepted={%d,%d,%d} ", + force, AF_UNSPEC, AF_INET, AF_INET6); + memcpy(buf,"dead:beef::\0", xmin(13,blen)); + assert(false); + } + + return buf; +} + +unsigned int IPAddress::ToHostname(char *buf, const unsigned int blen) const +{ + char *p = buf; + + if(IsIPv6() && blen > 0) { + *p = '['; + p++; + } + + /* 7 being space for [,], and port */ + if( IsIPv6() ) + NtoA(p, blen-7, AF_INET6); + else + NtoA(p, blen-7, AF_INET); + + // find the end of the new string + while(*p != '\0' && p < buf+blen) + p++; + + if(IsIPv6() && p < buf+blen-1) { + *p = ']'; + p++; + } + + /* terminate just in case. */ + *p = '\0'; + + /* return size of buffer now used */ + return (p - buf); +} + +char* IPAddress::ToURL(char* buf, unsigned int blen) const +{ + char *p = buf; + + // Ensure we have a buffer. + + if(buf == NULL) { + return NULL; + } + + p += ToHostname(p, blen); + + if(m_SocketAddr.sin6_port > 0 && p < buf+blen-6) { + /* 6 is max length of expected ':port' (short int) */ + snprintf(p, 6,":%d", GetPort() ); + } + + // force a null-terminated string + buf[blen] = '\0'; + + return buf; +} + +void IPAddress::GetSockAddr(struct sockaddr_in &buf) const +{ +#if USE_IPV6 + + if( IsIPv4() ) + { + buf.sin_family = AF_INET; + buf.sin_port = m_SocketAddr.sin6_port; + Map6to4( m_SocketAddr.sin6_addr, buf.sin_addr); + } else + { + debugs(14,1, HERE << "IPAddress::GetSockAddr : Cannot convert non-IPv4 to IPv4."); + + memset(&buf,0xFFFFFFFF,sizeof(struct sockaddr_in)); + assert(false); + } + +#else + + memcpy(&buf, &m_SocketAddr, sizeof(struct sockaddr_in)); + + if(buf.sin_family == 0) + { + buf.sin_family = AF_INET; + } + +#endif +} + +#if USE_IPV6 + +void IPAddress::GetSockAddr(struct sockaddr_in6 &buf) const +{ + memcpy(&buf, &m_SocketAddr, sizeof(struct sockaddr_in6)); + /* maintain address family. It may have changed inside us. */ + buf.sin6_family = AF_INET6; +} + +#endif + +#if USE_IPV6 + +void IPAddress::Map4to6(const struct in_addr &in, struct in6_addr &out) const +{ + /* check for special cases */ + + if( in.s_addr == 0x00000000) + { + /* ANYADDR */ + + memset(&out, 0, sizeof(struct in6_addr)); + } else if( in.s_addr == 0xFFFFFFFF) + { + /* NOADDR */ + + out.s6_addr32[0] = 0xFFFFFFFF; + out.s6_addr32[1] = 0xFFFFFFFF; + out.s6_addr32[2] = 0xFFFFFFFF; + out.s6_addr32[3] = 0xFFFFFFFF; + +#if IPV6_SPECIAL_LOCALHOST + } else if( in.s_addr == htonl(0x7F000001)) + { + /* LOCALHOST */ + + memset(&out, 0, sizeof(struct in6_addr)); + out.s6_addr32[3] = htonl(0x1); +#endif + + } else + { + /* general */ + + memset(&out, 0, sizeof(struct in6_addr)); + out.s6_addr32[2] = htonl(0xFFFF); + out.s6_addr32[3] = in.s_addr; + } +} + +void IPAddress::Map6to4(const struct in6_addr &in, struct in_addr &out) const +{ + /* ANYADDR */ + /* NOADDR */ + /* general */ + + memset(&out, 0, sizeof(struct in_addr)); + out.s_addr = in.s6_addr32[3]; + +#if IPV6_SPECIAL_LOCALHOST + /* LOCALHOST */ + + if( IsLocalhost() ) + { + out.s_addr = htonl(0x7F000001); + } +#endif + +} + +#endif + +#if USE_IPV6 +void IPAddress::GetInAddr(in6_addr &buf) const +{ + memcpy(&buf, &m_SocketAddr.sin6_addr, sizeof(struct in6_addr)); +} + +#endif + +bool IPAddress::GetInAddr(struct in_addr &buf) const +{ + +#if USE_IPV6 + if( IsIPv4() ) { + Map6to4((const in6_addr)m_SocketAddr.sin6_addr, buf); + return true; + } +#else + + if( IsIPv4() ) { + memcpy(&buf, &m_SocketAddr.sin_addr, sizeof(struct in_addr)); + return true; + } +#endif + + // default: + // non-compatible IPv6 Pure Address + + debugs(14,1, HERE << "IPAddress::GetInAddr : Cannot convert non-IPv4 to IPv4. IPA=" << *this); + memset(&buf,0xFFFFFFFF,sizeof(struct in_addr)); + assert(false); + return false; +} diff --git a/lib/Makefile.am b/lib/Makefile.am index 04e6125178..c8744d7dc9 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in # -# $Id: Makefile.am,v 1.30 2007/12/04 13:22:54 hno Exp $ +# $Id: Makefile.am,v 1.31 2007/12/14 04:29:22 amosjeffries Exp $ # DIST_SUBDIRS = libTrie @@ -58,6 +58,7 @@ libmiscutil_a_SOURCES = \ hash.c \ heap.c \ html_quote.c \ + IPAddress.cc \ iso3307.c \ md5.c \ radix.c \ @@ -97,7 +98,12 @@ INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include @SQUID_CPPUNIT_INC@ TESTS=$(check_PROGRAMS) check_PROGRAMS=tests/testAll -tests_testAll_SOURCES= tests/testArray.cc tests/testMain.cc tests/testArray.h \ +tests_testAll_SOURCES= \ + tests/testArray.h \ + tests/testArray.cc \ + tests/testIPAddress.h \ + tests/testIPAddress.cc \ + tests/testMain.cc \ $(XPROF_STATS_SOURCE) \ $(WIN32SRC) \ util.c assert.c diff --git a/lib/tests/testIPAddress.cc b/lib/tests/testIPAddress.cc new file mode 100644 index 0000000000..77098118ce --- /dev/null +++ b/lib/tests/testIPAddress.cc @@ -0,0 +1,674 @@ +#include + +#include +#include +#include +#include + +#include "config.h" +#include "testIPAddress.h" +#include "IPAddress.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( testIPAddress ); + + +void +testIPAddress::testDefaults() +{ + IPAddress anIPA; + + /* test stored values */ + CPPUNIT_ASSERT( anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( anIPA.IsIPv4() ); + CPPUNIT_ASSERT( !anIPA.IsSockAddr() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , anIPA.GetPort() ); +#if USE_IPV6 + CPPUNIT_ASSERT( anIPA.IsIPv6() ); +#else + CPPUNIT_ASSERT( !anIPA.IsIPv6() ); +#endif +} + +void +testIPAddress::testInAddrConstructor() +{ + struct in_addr inval; + struct in_addr outval; + + inval.s_addr = htonl(0xC0A8640C); + outval.s_addr = htonl(0x00000000); + + IPAddress anIPA(inval); + + /* test stored values */ + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( anIPA.IsIPv4() ); + CPPUNIT_ASSERT( !anIPA.IsIPv6() ); + CPPUNIT_ASSERT( !anIPA.IsSockAddr() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , anIPA.GetPort() ); + anIPA.GetInAddr(outval); + CPPUNIT_ASSERT( memcmp(&inval, &outval, sizeof(struct in_addr)) == 0 ); +} + +#if USE_IPV6 +void +testIPAddress::testInAddr6Constructor() +{ + struct in6_addr inval; + struct in6_addr outval = IN6ADDR_ANY_INIT; + + inval.s6_addr32[0] = htonl(0xC0A8640C); + inval.s6_addr32[1] = htonl(0xFFFFFFFF); + inval.s6_addr32[2] = htonl(0xFFFFFFFF); + inval.s6_addr32[3] = htonl(0xFFFFFFFF); + + IPAddress anIPA(inval); + + /* test stored values */ + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( !anIPA.IsIPv4() ); + CPPUNIT_ASSERT( anIPA.IsIPv6() ); + CPPUNIT_ASSERT( !anIPA.IsSockAddr() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , anIPA.GetPort() ); + anIPA.GetInAddr(outval); + CPPUNIT_ASSERT( memcmp( &inval, &outval, sizeof(struct in6_addr)) == 0 ); +} +#endif + +void +testIPAddress::testSockAddrConstructor() +{ + struct sockaddr_in insock; + struct sockaddr_in outsock; + + memset(&insock, 0, sizeof(struct sockaddr_in)); + memset(&outsock, 0, sizeof(struct sockaddr_in)); + + insock.sin_family = AF_INET; + insock.sin_port = htons(80); + insock.sin_addr.s_addr = htonl(0xC0A8640C); + + IPAddress anIPA((const struct sockaddr_in)insock); + + /* test stored values */ + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( anIPA.IsIPv4() ); + CPPUNIT_ASSERT( !anIPA.IsIPv6() ); + CPPUNIT_ASSERT( anIPA.IsSockAddr() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 80 , anIPA.GetPort() ); + anIPA.GetSockAddr(outsock); + CPPUNIT_ASSERT( memcmp( &insock, &outsock, sizeof(struct sockaddr_in)) == 0 ); +} + +#if USE_IPV6 +void +testIPAddress::testSockAddr6Constructor() +{ + struct sockaddr_in6 insock; + struct sockaddr_in6 outsock; + + memset(&insock, 0, sizeof(struct sockaddr_in6)); + memset(&outsock, 0, sizeof(struct sockaddr_in6)); + + insock.sin6_family = AF_INET6; + insock.sin6_port = htons(80); + insock.sin6_addr.s6_addr32[0] = htonl(0xFFFFFFFF); + insock.sin6_addr.s6_addr32[1] = htonl(0x00000000); + insock.sin6_addr.s6_addr32[2] = htonl(0x0000FFFF); + insock.sin6_addr.s6_addr32[3] = htonl(0xC0A8640C); + + IPAddress anIPA((const struct sockaddr_in6)insock); + + /* test stored values */ + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( !anIPA.IsIPv4() ); + CPPUNIT_ASSERT( anIPA.IsIPv6() ); + CPPUNIT_ASSERT( anIPA.IsSockAddr() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 80 , anIPA.GetPort() ); + anIPA.GetSockAddr(outsock); + CPPUNIT_ASSERT( memcmp( &insock, &outsock, sizeof(struct sockaddr_in6)) == 0 ); +} +#endif + + +void +testIPAddress::testCopyConstructor() +{ + struct sockaddr_in insock; + struct sockaddr_in outsock; + + memset(&insock, 0, sizeof(struct sockaddr_in)); + memset(&outsock, 0, sizeof(struct sockaddr_in)); + + insock.sin_family = AF_INET; + insock.sin_port = htons(80); + insock.sin_addr.s_addr = htonl(0xC0A8640C); + + IPAddress inIPA(insock); + IPAddress outIPA(inIPA); + + + /* test stored values */ + CPPUNIT_ASSERT( !outIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !outIPA.IsNoAddr() ); + CPPUNIT_ASSERT( outIPA.IsIPv4() ); + CPPUNIT_ASSERT( !outIPA.IsIPv6() ); + CPPUNIT_ASSERT( outIPA.IsSockAddr() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 80 , outIPA.GetPort() ); + outIPA.GetSockAddr(outsock); + CPPUNIT_ASSERT( memcmp( &insock, &outsock, sizeof(struct sockaddr_in)) == 0 ); +} + +void +testIPAddress::testHostentConstructor() +{ + struct hostent *hp = NULL; + struct in_addr outval; + struct in_addr expectval; + + expectval.s_addr = htonl(0xC0A8640C); + + hp = gethostbyname("192.168.100.12"); + CPPUNIT_ASSERT( hp != NULL /* gethostbyname failure.*/ ); + + IPAddress anIPA(*hp); + + /* test stored values */ + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( anIPA.IsIPv4() ); + CPPUNIT_ASSERT( !anIPA.IsIPv6() ); + CPPUNIT_ASSERT( !anIPA.IsSockAddr() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , anIPA.GetPort() ); + anIPA.GetInAddr(outval); + CPPUNIT_ASSERT( memcmp( &expectval, &outval, sizeof(struct in_addr)) == 0 ); +} + +void +testIPAddress::testStringConstructor() +{ + struct in_addr outval; + struct in_addr expectval; + + expectval.s_addr = htonl(0xC0A8640C); + + IPAddress anIPA = "192.168.100.12"; + + /* test stored values */ + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( anIPA.IsIPv4() ); + CPPUNIT_ASSERT( !anIPA.IsIPv6() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , anIPA.GetPort() ); + CPPUNIT_ASSERT( !anIPA.IsSockAddr() ); + anIPA.GetInAddr(outval); + CPPUNIT_ASSERT( memcmp( &expectval, &outval, sizeof(struct in_addr)) == 0 ); + +#if USE_IPV6 + struct in6_addr expectv6; + struct in6_addr outval6; + + expectv6.s6_addr32[0] = htonl(0x20000800); + expectv6.s6_addr32[1] = htonl(0x00000000); + expectv6.s6_addr32[2] = htonl(0x00000000); + expectv6.s6_addr32[3] = htonl(0x00000045); + + IPAddress bnIPA = "2000:800::45"; + + /* test stored values */ + CPPUNIT_ASSERT( !bnIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !bnIPA.IsNoAddr() ); + CPPUNIT_ASSERT( !bnIPA.IsIPv4() ); + CPPUNIT_ASSERT( bnIPA.IsIPv6() ); + CPPUNIT_ASSERT( !bnIPA.IsSockAddr() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , bnIPA.GetPort() ); + bnIPA.GetInAddr(outval6); + CPPUNIT_ASSERT( memcmp( &expectv6, &outval6, sizeof(struct in6_addr)) == 0 ); + + /* test IPv6 as an old netmask format. This is invalid but sometimes use. */ + IPAddress cnIPA = "ffff:ffff:fff0::"; + + expectv6.s6_addr32[0] = htonl(0xFFFFFFFF); + expectv6.s6_addr32[1] = htonl(0xFFF00000); + expectv6.s6_addr32[2] = htonl(0x00000000); + expectv6.s6_addr32[3] = htonl(0x00000000); + + /* test stored values */ + CPPUNIT_ASSERT( !cnIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !cnIPA.IsNoAddr() ); + CPPUNIT_ASSERT( !cnIPA.IsIPv4() ); + CPPUNIT_ASSERT( cnIPA.IsIPv6() ); + CPPUNIT_ASSERT( !cnIPA.IsSockAddr() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , cnIPA.GetPort() ); + cnIPA.GetInAddr(outval6); + CPPUNIT_ASSERT( memcmp( &expectv6, &outval6, sizeof(struct in6_addr)) == 0 ); +#endif +} + +void +testIPAddress::testSetEmpty() +{ + IPAddress anIPA; + struct in_addr inval; + + inval.s_addr = htonl(0xC0A8640C); + + anIPA = inval; + + /* test stored values before empty */ + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( anIPA.IsIPv4() ); + CPPUNIT_ASSERT( !anIPA.IsIPv6() ); + CPPUNIT_ASSERT( !anIPA.IsSockAddr() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , anIPA.GetPort() ); + + anIPA.SetEmpty(); + + /* test stored values after empty */ + CPPUNIT_ASSERT( anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( anIPA.IsIPv4() ); +#if USE_IPV6 + CPPUNIT_ASSERT( anIPA.IsIPv6() ); +#else + CPPUNIT_ASSERT( !anIPA.IsIPv6() ); +#endif + CPPUNIT_ASSERT( !anIPA.IsSockAddr() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , anIPA.GetPort() ); +} + +void +testIPAddress::testBooleans() +{ + IPAddress lhsIPA; + IPAddress rhsIPA; + struct in_addr valLow; + struct in_addr valHigh; + + valLow.s_addr = htonl(0xC0A8640C); + valHigh.s_addr = htonl(0xC0A8640F); + + /* test equality */ + lhsIPA = valLow; + rhsIPA = valLow; + CPPUNIT_ASSERT( lhsIPA.matchIPAddr(rhsIPA) == 0 ); + CPPUNIT_ASSERT( ( lhsIPA == rhsIPA ) ); + CPPUNIT_ASSERT( !( lhsIPA != rhsIPA ) ); + CPPUNIT_ASSERT( ( lhsIPA >= rhsIPA ) ); + CPPUNIT_ASSERT( !( lhsIPA > rhsIPA ) ); + CPPUNIT_ASSERT( ( lhsIPA <= rhsIPA ) ); + CPPUNIT_ASSERT( !( lhsIPA < rhsIPA ) ); + + /* test inequality (less than) */ + lhsIPA = valLow; + rhsIPA = valHigh; + CPPUNIT_ASSERT( lhsIPA.matchIPAddr(rhsIPA) < 0 ); + CPPUNIT_ASSERT( !( lhsIPA == rhsIPA ) ); + CPPUNIT_ASSERT( ( lhsIPA != rhsIPA ) ); + CPPUNIT_ASSERT( !( lhsIPA >= rhsIPA ) ); + CPPUNIT_ASSERT( !( lhsIPA > rhsIPA ) ); + CPPUNIT_ASSERT( ( lhsIPA <= rhsIPA ) ); + CPPUNIT_ASSERT( ( lhsIPA < rhsIPA ) ); + + /* test inequality (greater than) */ + lhsIPA = valHigh; + rhsIPA = valLow; + CPPUNIT_ASSERT( lhsIPA.matchIPAddr(rhsIPA) > 0 ); + CPPUNIT_ASSERT( !( lhsIPA == rhsIPA ) ); + CPPUNIT_ASSERT( ( lhsIPA != rhsIPA ) ); + CPPUNIT_ASSERT( ( lhsIPA >= rhsIPA ) ); + CPPUNIT_ASSERT( ( lhsIPA > rhsIPA ) ); + CPPUNIT_ASSERT( !( lhsIPA <= rhsIPA ) ); + CPPUNIT_ASSERT( !( lhsIPA < rhsIPA ) ); + +} + +void +testIPAddress::testNtoA() +{ + struct in_addr inval; + char buf[MAX_IPSTRLEN]; + IPAddress anIPA; + + anIPA.SetAnyAddr(); + + /* test AnyAddr display values */ +#if USE_IPV6 + CPPUNIT_ASSERT( memcmp("::", anIPA.NtoA(buf,MAX_IPSTRLEN), 2) == 0 ); +#else + CPPUNIT_ASSERT( memcmp("0.0.0.0",anIPA.NtoA(buf,MAX_IPSTRLEN), 7) == 0 ); +#endif + + inval.s_addr = htonl(0xC0A8640C); + anIPA = inval; + + /* test IP display */ + CPPUNIT_ASSERT( memcmp("192.168.100.12",anIPA.NtoA(buf,MAX_IPSTRLEN), 14) == 0 ); + + anIPA.SetNoAddr(); + + /* test NoAddr display values */ +#if USE_IPV6 + CPPUNIT_ASSERT( memcmp("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",anIPA.NtoA(buf,MAX_IPSTRLEN), 39) == 0 ); +#else + CPPUNIT_ASSERT( memcmp("255.255.255.255",anIPA.NtoA(buf,MAX_IPSTRLEN), 15) == 0 ); +#endif +} + +void +testIPAddress::testToURL_fromInAddr() +{ + char buf[MAX_IPSTRLEN]; buf[0] = '\0'; + struct in_addr inval; + + inval.s_addr = htonl(0xC0A8640C); + + IPAddress anIPA(inval); + + /* test values */ + anIPA.ToURL(buf,MAX_IPSTRLEN); + CPPUNIT_ASSERT( memcmp("192.168.100.12", buf, 14) == 0 ); + +#if USE_IPV6 + + /* test output when constructed from in6_addr with IPv6 */ + struct in6_addr ip6val; + + ip6val.s6_addr32[0] = htonl(0xC0A8640C); + ip6val.s6_addr32[1] = htonl(0xFFFFFFFF); + ip6val.s6_addr32[2] = htonl(0xFFFFFFFF); + ip6val.s6_addr32[3] = htonl(0xFFFFFFFF); + + IPAddress bnIPA(ip6val); + + bnIPA.ToURL(buf,MAX_IPSTRLEN); + CPPUNIT_ASSERT( memcmp("[c0a8:640c:ffff:ffff:ffff:ffff:ffff:ffff]", buf, 41) == 0 ); + +#endif +} + +void +testIPAddress::testToURL_fromSockAddr() +{ + struct sockaddr_in sock; + sock.sin_addr.s_addr = htonl(0xC0A8640C); + sock.sin_port = htons(80); + sock.sin_family = AF_INET; + IPAddress anIPA(sock); + char buf[MAX_IPSTRLEN]; + + /* test values */ + anIPA.ToURL(buf,MAX_IPSTRLEN); + CPPUNIT_ASSERT( memcmp("192.168.100.12:80", buf, 17) == 0 ); + +#if USE_IPV6 + + /* test output when constructed from in6_addr with IPv6 */ + struct sockaddr_in6 ip6val; + + ip6val.sin6_addr.s6_addr32[0] = htonl(0xC0A8640C); + ip6val.sin6_addr.s6_addr32[1] = htonl(0xFFFFFFFF); + ip6val.sin6_addr.s6_addr32[2] = htonl(0xFFFFFFFF); + ip6val.sin6_addr.s6_addr32[3] = htonl(0xFFFFFFFF); + ip6val.sin6_port = htons(80); + ip6val.sin6_family = AF_INET6; + + IPAddress bnIPA(ip6val); + + bnIPA.ToURL(buf,MAX_IPSTRLEN); + CPPUNIT_ASSERT( memcmp("[c0a8:640c:ffff:ffff:ffff:ffff:ffff:ffff]:80", buf, 44) == 0 ); + +#endif + +} + +void +testIPAddress::testGetReverseString() +{ + char buf[MAX_IPSTRLEN]; + + struct in_addr ipv4val; + ipv4val.s_addr = htonl(0xC0A8640C); + + IPAddress v4IPA(ipv4val); + + /* test IPv4 output */ + v4IPA.GetReverseString(buf); + CPPUNIT_ASSERT( memcmp("12.100.168.192.in-addr.arpa.",buf, 28) == 0 ); + + v4IPA.GetReverseString(buf,AF_INET); + CPPUNIT_ASSERT( memcmp("12.100.168.192.in-addr.arpa.",buf, 28) == 0 ); + + v4IPA.GetReverseString(buf,AF_INET6); + CPPUNIT_ASSERT( memcmp("",buf, 1) == 0 ); + + +#if USE_IPV6 + struct in6_addr ip6val; + + ip6val.s6_addr32[0] = htonl(0xC0A8640C); + ip6val.s6_addr32[1] = htonl(0xFFFFFFFF); + ip6val.s6_addr32[2] = htonl(0xFFFFFFFF); + ip6val.s6_addr32[3] = htonl(0xFFFFFFFF); + + IPAddress v6IPA(ip6val); + + /* test IPv6 output */ + v6IPA.GetReverseString(buf); + CPPUNIT_ASSERT( memcmp("f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.c.0.4.6.8.a.0.c.ip6.arpa.",buf,73) == 0 ); + +#endif + +} + +void +testIPAddress::testMasking() +{ + char buf[MAX_IPSTRLEN]; + IPAddress anIPA; + IPAddress maskIPA; + + /* Test Basic CIDR Routine */ + anIPA.SetAnyAddr(); + CPPUNIT_ASSERT_EQUAL( 0 ,anIPA.GetCIDR() ); + + anIPA.SetNoAddr(); +#if USE_IPV6 + CPPUNIT_ASSERT_EQUAL( 128 , anIPA.GetCIDR() ); +#else + CPPUNIT_ASSERT_EQUAL( 32 , anIPA.GetCIDR() ); +#endif + + /* Test Numeric ApplyCIDR */ + anIPA.SetNoAddr(); + CPPUNIT_ASSERT( !anIPA.ApplyMask(129) ); +#if !USE_IPV6 + CPPUNIT_ASSERT( !anIPA.ApplyMask(33) ); +#endif + CPPUNIT_ASSERT( anIPA.ApplyMask(31) ); + CPPUNIT_ASSERT_EQUAL( 31 , anIPA.GetCIDR() ); + + anIPA.SetNoAddr(); + anIPA.ApplyMask(31, AF_INET); +#if USE_IPV6 + CPPUNIT_ASSERT_EQUAL( 127 , anIPA.GetCIDR() ); +#else + CPPUNIT_ASSERT_EQUAL( 31 , anIPA.GetCIDR() ); +#endif + +#if USE_IPV6 + anIPA.SetNoAddr(); + anIPA.ApplyMask(80,AF_INET6); + CPPUNIT_ASSERT_EQUAL( 80 , anIPA.GetCIDR() ); + /* BUG Check: test values by display. */ + CPPUNIT_ASSERT( anIPA.NtoA(buf,MAX_IPSTRLEN) != NULL ); + CPPUNIT_ASSERT( memcmp("ffff:ffff:ffff:ffff:ffff::", buf, 26) == 0 ); +#endif + + /* Test Network Bitmask from IPAddress */ + anIPA.SetNoAddr(); + maskIPA = "255.255.240.0"; + CPPUNIT_ASSERT_EQUAL( 20 , maskIPA.GetCIDR() ); + anIPA.ApplyMask(maskIPA); + CPPUNIT_ASSERT_EQUAL( 20 , anIPA.GetCIDR() ); + + /* BUG Check: test values memory after masking. */ + struct in_addr btest; + CPPUNIT_ASSERT( anIPA.IsIPv4() ); + CPPUNIT_ASSERT( !anIPA.IsIPv6() ); + anIPA.GetInAddr(btest); + CPPUNIT_ASSERT_EQUAL( (uint32_t)htonl(0xFFFFF000) , btest.s_addr ); + + /* BUG Check failing test. Masked values for display. */ + CPPUNIT_ASSERT( memcmp("255.255.240.0",anIPA.NtoA(buf,MAX_IPSTRLEN), 13) == 0 ); + + +#if USE_IPV6 + anIPA.SetNoAddr(); + maskIPA.SetNoAddr(); + + /* IPv6 masks MUST be CIDR representations. */ + /* however as with IPv4 they can technically be represented as a bitmask */ + maskIPA = "ffff:ffff:fff0::"; + CPPUNIT_ASSERT( !maskIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !maskIPA.IsNoAddr() ); + anIPA.ApplyMask(maskIPA); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT_EQUAL( 44 , anIPA.GetCIDR() ); + + anIPA.SetNoAddr(); + maskIPA.SetNoAddr(); + + /* IPv4 masks represented in IPv6 as IPv4 bitmasks. */ + maskIPA = "::ffff:ffff:f000"; + CPPUNIT_ASSERT( !maskIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !maskIPA.IsNoAddr() ); + CPPUNIT_ASSERT( maskIPA.IsIPv4() ); + CPPUNIT_ASSERT( !maskIPA.IsIPv6() ); + anIPA.ApplyMask(maskIPA); + CPPUNIT_ASSERT( !maskIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !maskIPA.IsNoAddr() ); + CPPUNIT_ASSERT( maskIPA.IsIPv4() ); + CPPUNIT_ASSERT( !maskIPA.IsIPv6() ); + CPPUNIT_ASSERT_EQUAL( 20 , anIPA.GetCIDR() ); +#endif + +} + +void +testIPAddress::testAddrInfo() +{ + struct addrinfo *expect; + struct addrinfo *ipval = NULL; + struct addrinfo hints; + + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_NUMERICHOST; + + IPAddress anIP = "127.0.0.1"; + + /* assert this just to check that getaddrinfo is working properly */ + CPPUNIT_ASSERT( xgetaddrinfo("127.0.0.1", NULL, &hints, &expect ) == 0 ); + + anIP.GetAddrInfo(ipval); + + /* display a byte-by-byte hex comparison of the addr cores */ + unsigned int *p; + p = (unsigned int*)expect; + printf("\nADDRINFO: %x %x %x %x %x %x ", + p[0],p[1],p[2],p[3],p[4],p[5] ); + + p = (unsigned int*)ipval; + printf("\nADDRINFO: %x %x %x %x %x %x ", + p[0],p[1],p[2],p[3],p[4],p[5] ); + printf("\n"); + + // check the addrinfo object core. (BUT not the two ptrs at the tail) + CPPUNIT_ASSERT( memcmp( expect, ipval, sizeof(struct addrinfo)-(sizeof(void*)*3) ) == 0 ); + // check the sockaddr it points to. + CPPUNIT_ASSERT_EQUAL( expect->ai_addrlen, ipval->ai_addrlen ); + CPPUNIT_ASSERT( memcmp( expect, ipval, expect->ai_addrlen ) == 0 ); + + xfreeaddrinfo(expect); +} + +void +testIPAddress::testBugNullingDisplay() +{ + // Weird Bug: address set to empty during string conversion somewhere. + // initial string gets created and returned OK. + // but at the end of the process m_SocketAddr is left NULL'ed + + char ntoabuf[MAX_IPSTRLEN]; + char hostbuf[MAX_IPSTRLEN]; + char urlbuf[MAX_IPSTRLEN]; + + struct in_addr outval; + struct in_addr expectval; + + expectval.s_addr = htonl(0xC0A8640C); + + IPAddress anIPA = "192.168.100.12"; + + + /* test stored values */ + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( anIPA.IsIPv4() ); + CPPUNIT_ASSERT( !anIPA.IsIPv6() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , anIPA.GetPort() ); + CPPUNIT_ASSERT( !anIPA.IsSockAddr() ); + anIPA.GetInAddr(outval); + CPPUNIT_ASSERT( memcmp( &expectval, &outval, sizeof(struct in_addr)) == 0 ); + + + /* POKE NtoA display function to see what it is doing */ + anIPA.NtoA(ntoabuf,MAX_IPSTRLEN); + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + /* test stored values */ + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( anIPA.IsIPv4() ); + CPPUNIT_ASSERT( !anIPA.IsIPv6() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , anIPA.GetPort() ); + CPPUNIT_ASSERT( !anIPA.IsSockAddr() ); + anIPA.GetInAddr(outval); + CPPUNIT_ASSERT( memcmp( &expectval, &outval, sizeof(struct in_addr)) == 0 ); + + + + /* POKE ToHostname display function to see what it is doing */ + anIPA.ToHostname(hostbuf,MAX_IPSTRLEN); + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + /* test stored values */ + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( anIPA.IsIPv4() ); + CPPUNIT_ASSERT( !anIPA.IsIPv6() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , anIPA.GetPort() ); + CPPUNIT_ASSERT( !anIPA.IsSockAddr() ); + anIPA.GetInAddr(outval); + CPPUNIT_ASSERT( memcmp( &expectval, &outval, sizeof(struct in_addr)) == 0 ); + + + /* POKE ToURL display function to see what it is doing */ + anIPA.ToURL(urlbuf,MAX_IPSTRLEN); + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + /* test stored values */ + CPPUNIT_ASSERT( !anIPA.IsAnyAddr() ); + CPPUNIT_ASSERT( !anIPA.IsNoAddr() ); + CPPUNIT_ASSERT( anIPA.IsIPv4() ); + CPPUNIT_ASSERT( !anIPA.IsIPv6() ); + CPPUNIT_ASSERT_EQUAL( (u_short) 0 , anIPA.GetPort() ); + CPPUNIT_ASSERT( !anIPA.IsSockAddr() ); + anIPA.GetInAddr(outval); + CPPUNIT_ASSERT( memcmp( &expectval, &outval, sizeof(struct in_addr)) == 0 ); + +} diff --git a/lib/tests/testIPAddress.h b/lib/tests/testIPAddress.h new file mode 100644 index 0000000000..740aca095b --- /dev/null +++ b/lib/tests/testIPAddress.h @@ -0,0 +1,70 @@ +#ifndef SQUID_SRC_TEST_IPADDRESS_H +#define SQUID_SRC_TEST_IPADDRESS_H + +#include + +/* + * test the IP storage type + */ + +class testIPAddress : public CPPUNIT_NS::TestFixture +{ + CPPUNIT_TEST_SUITE( testIPAddress ); + CPPUNIT_TEST( testDefaults ); + CPPUNIT_TEST( testInAddrConstructor ); +#if USE_IPV6 + CPPUNIT_TEST( testInAddr6Constructor ); +#endif + CPPUNIT_TEST( testSockAddrConstructor ); +#if USE_IPV6 + CPPUNIT_TEST( testSockAddr6Constructor ); +#endif + CPPUNIT_TEST( testHostentConstructor ); + CPPUNIT_TEST( testStringConstructor ); + CPPUNIT_TEST( testCopyConstructor ); + CPPUNIT_TEST( testSetEmpty ); + CPPUNIT_TEST( testBooleans ); + CPPUNIT_TEST( testAddrInfo ); + CPPUNIT_TEST( testNtoA ); + CPPUNIT_TEST( testToURL_fromInAddr ); + CPPUNIT_TEST( testToURL_fromSockAddr ); + CPPUNIT_TEST( testGetReverseString ); + CPPUNIT_TEST( testMasking ); + + CPPUNIT_TEST( testBugNullingDisplay ); + CPPUNIT_TEST_SUITE_END(); + +public: + +protected: + void testDefaults(); + + void testInAddrConstructor(); +#if USE_IPV6 + void testInAddr6Constructor(); +#endif + void testSockAddrConstructor(); +#if USE_IPV6 + void testSockAddr6Constructor(); +#endif + void testHostentConstructor(); + void testStringConstructor(); + void testCopyConstructor(); + + void testSetEmpty(); + void testBooleans(); + + void testAddrInfo(); + + void testNtoA(); + void testToURL_fromInAddr(); + void testToURL_fromSockAddr(); + void testGetReverseString(); + void testMasking(); + + // bugs. + void testBugNullingDisplay(); + +}; + +#endif /* SQUID_SRC_TEST_IPADDRESS_H */