/* * Copyright (C) 1996-2023 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ /* DEBUG: section 14 IP Storage and Handling */ #include "squid.h" #include "debug/Stream.h" #include "ip/Address.h" #include "ip/tools.h" #include "util.h" #include #include #if HAVE_ARPA_INET_H /* for inet_ntoa() */ #include #endif #if HAVE_WS2TCPIP_H // Windows IPv6 definitions #include #endif // some OS (ie WIndows) define IN6_ADDR_EQUAL instead #if !defined(IN6_ARE_ADDR_EQUAL) && _SQUID_WINDOWS_ #define IN6_ARE_ADDR_EQUAL IN6_ADDR_EQUAL #endif /* Debugging only. Dump the address content when a fatal assert is encountered. */ #define IASSERT(a,b) \ if(!(b)){ printf("assert \"%s\" at line %d\n", a, __LINE__); \ printf("Ip::Address invalid? with isIPv4()=%c, isIPv6()=%c\n",(isIPv4()?'T':'F'),(isIPv6()?'T':'F')); \ printf("ADDRESS:"); \ for(unsigned int i = 0; i < sizeof(mSocketAddr_.sin6_addr); ++i) { \ printf(" %x", mSocketAddr_.sin6_addr.s6_addr[i]); \ } printf("\n"); assert(b); \ } std::optional Ip::Address::Parse(const char * const raw) { Address tmp; // TODO: Merge with lookupHostIP() after removing DNS lookups from Ip. if (tmp.lookupHostIP(raw, false)) return tmp; return std::nullopt; } int Ip::Address::cidr() const { uint8_t shift,ipbyte; uint8_t bit,caught; int len = 0; const uint8_t *ptr= mSocketAddr_.sin6_addr.s6_addr; /* Let's scan all the bits from Most Significant to Least */ /* Until we find an "0" bit. Then, we return */ shift=0; /* return IPv4 CIDR for any Mapped address */ /* Thus only check the mapped bit */ if ( !isIPv6() ) { shift = 12; } for (; shift(&mSocketAddr_.sin6_addr); const auto maskWords = reinterpret_cast(&mask.mSocketAddr_.sin6_addr); const auto len = sizeof(mSocketAddr_.sin6_addr)/sizeof(uint32_t); for (size_t i = 0; i < len; ++i) addressWords[i] |= ~maskWords[i]; } void Ip::Address::applyClientMask(const Address &mask) { if (!isLocalhost() && isIPv4()) (void)applyMask(mask); } bool Ip::Address::applyMask(const unsigned int cidrMask, int mtype) { uint8_t clearbits = 0; uint8_t* p = nullptr; // validation and short-cuts. if (cidrMask > 128) return false; if (cidrMask > 32 && mtype == AF_INET) return false; if (cidrMask == 0) { /* CIDR /0 is NoAddr regardless of the IPv4/IPv6 protocol */ setNoAddr(); return true; } clearbits = (uint8_t)( (mtype==AF_INET6?128:32) - cidrMask); // short-cut if (clearbits == 0) return true; p = (uint8_t*)(&mSocketAddr_.sin6_addr) + 15; for (; clearbits>0 && p >= (uint8_t*)&mSocketAddr_.sin6_addr ; --p ) { if (clearbits < 8) { *p &= ((0xFF << clearbits) & 0xFF); clearbits = 0; } else { *p &= 0x00; clearbits -= 8; } } return true; } bool Ip::Address::isSockAddr() const { return (mSocketAddr_.sin6_port != 0); } bool Ip::Address::isIPv4() const { return IN6_IS_ADDR_V4MAPPED( &mSocketAddr_.sin6_addr ); } bool Ip::Address::isIPv6() const { return !isIPv4(); } bool Ip::Address::isAnyAddr() const { return IN6_IS_ADDR_UNSPECIFIED(&mSocketAddr_.sin6_addr) || IN6_ARE_ADDR_EQUAL(&mSocketAddr_.sin6_addr, &v4_anyaddr); } /// NOTE: Does NOT clear the Port stored. Only the Address and Type. void Ip::Address::setAnyAddr() { memset(&mSocketAddr_.sin6_addr, 0, sizeof(struct in6_addr) ); } /// NOTE: completely empties the Ip::Address structure. Address, Port, Type, everything. void Ip::Address::setEmpty() { memset(&mSocketAddr_, 0, sizeof(mSocketAddr_) ); } #if _SQUID_AIX_ // Bug 2885 comment 78 explains. // In short AIX has a different netinet/in.h union definition const struct in6_addr Ip::Address::v4_localhost = {{{ 0x00000000, 0x00000000, 0x0000ffff, 0x7f000001 }}}; const struct in6_addr Ip::Address::v4_anyaddr = {{{ 0x00000000, 0x00000000, 0x0000ffff, 0x00000000 }}}; const struct in6_addr Ip::Address::v4_noaddr = {{{ 0x00000000, 0x00000000, 0x0000ffff, 0xffffffff }}}; const struct in6_addr Ip::Address::v6_noaddr = {{{ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }}}; #else const struct in6_addr Ip::Address::v4_localhost = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01 } } }; const struct in6_addr Ip::Address::v4_anyaddr = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } }; const struct in6_addr Ip::Address::v4_noaddr = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } }; const struct in6_addr Ip::Address::v6_noaddr = {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } }; #endif bool Ip::Address::setIPv4() { if ( isLocalhost() ) { mSocketAddr_.sin6_addr = v4_localhost; return true; } if ( isAnyAddr() ) { mSocketAddr_.sin6_addr = v4_anyaddr; return true; } if ( isNoAddr() ) { mSocketAddr_.sin6_addr = v4_noaddr; return true; } if ( isIPv4()) return true; // anything non-IPv4 and non-convertable is BAD. return false; } bool Ip::Address::isLocalhost() const { return IN6_IS_ADDR_LOOPBACK( &mSocketAddr_.sin6_addr ) || IN6_ARE_ADDR_EQUAL( &mSocketAddr_.sin6_addr, &v4_localhost ); } void Ip::Address::setLocalhost() { if (Ip::EnableIpv6) { mSocketAddr_.sin6_addr = in6addr_loopback; mSocketAddr_.sin6_family = AF_INET6; } else { mSocketAddr_.sin6_addr = v4_localhost; mSocketAddr_.sin6_family = AF_INET; } } bool Ip::Address::isSiteLocal6() const { // RFC 4193 the site-local allocated range is fc00::/7 // with fd00::/8 as the only currently allocated range (so we test it first). // BUG: as of 2010-02 Linux and BSD define IN6_IS_ADDR_SITELOCAL() to check for fec::/10 return mSocketAddr_.sin6_addr.s6_addr[0] == static_cast(0xfd) || mSocketAddr_.sin6_addr.s6_addr[0] == static_cast(0xfc); } bool Ip::Address::isSiteLocalAuto() const { return mSocketAddr_.sin6_addr.s6_addr[11] == static_cast(0xff) && mSocketAddr_.sin6_addr.s6_addr[12] == static_cast(0xfe); } bool Ip::Address::isNoAddr() const { // IFF the address == 0xff..ff (all ones) return IN6_ARE_ADDR_EQUAL( &mSocketAddr_.sin6_addr, &v6_noaddr ) || IN6_ARE_ADDR_EQUAL( &mSocketAddr_.sin6_addr, &v4_noaddr ); } void Ip::Address::setNoAddr() { memset(&mSocketAddr_.sin6_addr, 0xFF, sizeof(struct in6_addr) ); mSocketAddr_.sin6_family = AF_INET6; } bool Ip::Address::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; } bool Ip::Address::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 Ip::Address::getReverseString(char buf[MAX_IPSTRLEN], int show_type) const { if (show_type == AF_UNSPEC) { show_type = isIPv6() ? AF_INET6 : AF_INET ; } if (show_type == AF_INET && isIPv4()) { struct in_addr* tmp = (struct in_addr*)&mSocketAddr_.sin6_addr.s6_addr[12]; return getReverseString4(buf, *tmp); } else if ( show_type == AF_INET6 && isIPv6() ) { return getReverseString6(buf, mSocketAddr_.sin6_addr); } debugs(14, DBG_CRITICAL, "ERROR: Unable to convert '" << toStr(buf,MAX_IPSTRLEN) << "' to the rDNS type requested."); buf[0] = '\0'; return false; } Ip::Address::Address(const char*s) { setEmpty(); lookupHostIP(s, true); } bool Ip::Address::operator =(const char* s) { return lookupHostIP(s, true); } bool Ip::Address::GetHostByName(const char* s) { return lookupHostIP(s, false); } bool Ip::Address::lookupHostIP(const char *s, bool nodns) { struct addrinfo want; memset(&want, 0, sizeof(struct addrinfo)); if (nodns) { want.ai_flags = AI_NUMERICHOST; // prevent actual DNS lookups! } int err = 0; struct addrinfo *res = nullptr; if ( (err = getaddrinfo(s, nullptr, &want, &res)) != 0) { debugs(14,3, "Given Non-IP '" << s << "': " << gai_strerror(err) ); /* free the memory getaddrinfo() dynamically allocated. */ if (res) freeaddrinfo(res); return false; } struct addrinfo *resHead = res; // we need to free the whole list later if (!Ip::EnableIpv6) { // if we are IPv6-disabled, use first-IPv4 instead of first-IP. struct addrinfo *maybeIpv4 = res; while (maybeIpv4) { if (maybeIpv4->ai_family == AF_INET) break; maybeIpv4 = maybeIpv4->ai_next; } if (maybeIpv4 != nullptr) res = maybeIpv4; // else IPv6-only host, let the caller deal with first-IP anyway. } /* * NP: =(sockaddr_*) may alter the port. we don't want that. * all we have been given as input was an IPA. */ short portSaved = port(); operator=(*res); port(portSaved); /* free the memory getaddrinfo() dynamically allocated. */ freeaddrinfo(resHead); return true; } Ip::Address::Address(struct sockaddr_in const &s) { setEmpty(); operator=(s); }; Ip::Address & Ip::Address::operator =(struct sockaddr_in const &s) { map4to6((const in_addr)s.sin_addr, mSocketAddr_.sin6_addr); mSocketAddr_.sin6_port = s.sin_port; mSocketAddr_.sin6_family = AF_INET6; return *this; }; Ip::Address & Ip::Address::operator =(const struct sockaddr_storage &s) { /* some AF_* magic to tell socket types apart and what we need to do */ if (s.ss_family == AF_INET6) { memmove(&mSocketAddr_, &s, sizeof(struct sockaddr_in6)); } else { // convert it to our storage mapping. struct sockaddr_in *sin = (struct sockaddr_in*)&s; mSocketAddr_.sin6_port = sin->sin_port; map4to6( sin->sin_addr, mSocketAddr_.sin6_addr); } return *this; }; Ip::Address::Address(struct sockaddr_in6 const &s) { setEmpty(); operator=(s); }; Ip::Address & Ip::Address::operator =(struct sockaddr_in6 const &s) { memmove(&mSocketAddr_, &s, sizeof(struct sockaddr_in6)); return *this; }; Ip::Address::Address(struct in_addr const &s) { setEmpty(); operator=(s); }; Ip::Address & Ip::Address::operator =(struct in_addr const &s) { map4to6((const in_addr)s, mSocketAddr_.sin6_addr); mSocketAddr_.sin6_family = AF_INET6; return *this; }; Ip::Address::Address(struct in6_addr const &s) { setEmpty(); operator=(s); }; Ip::Address & Ip::Address::operator =(struct in6_addr const &s) { memmove(&mSocketAddr_.sin6_addr, &s, sizeof(struct in6_addr)); mSocketAddr_.sin6_family = AF_INET6; return *this; }; Ip::Address::Address(const struct hostent &s) { setEmpty(); operator=(s); } bool Ip::Address::operator =(const struct hostent &s) { struct in_addr* ipv4 = nullptr; struct in6_addr* ipv6 = nullptr; //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]); /* this */ operator=(*ipv6); break; default: IASSERT("false",false); return false; } return true; } Ip::Address::Address(const struct addrinfo &s) { setEmpty(); operator=(s); } bool Ip::Address::operator =(const struct addrinfo &s) { struct sockaddr_in* ipv4 = nullptr; struct sockaddr_in6* ipv6 = nullptr; //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); /* this */ assert(ipv6); operator=(*ipv6); break; case AF_UNSPEC: default: // attempt to handle partially initialised addrinfo. // such as those where data only comes from getsockopt() if (s.ai_addr != nullptr) { if (s.ai_addrlen == sizeof(struct sockaddr_in6)) { operator=(*((struct sockaddr_in6*)s.ai_addr)); return true; } else if (s.ai_addrlen == sizeof(struct sockaddr_in)) { operator=(*((struct sockaddr_in*)s.ai_addr)); return true; } } return false; } return true; } void Ip::Address::getAddrInfo(struct addrinfo *&dst, int force) const { if (dst == nullptr) { dst = new addrinfo; } memset(dst, 0, sizeof(struct addrinfo)); // set defaults // Mac OS X does not emit a flag indicating the output is numeric (IP address) #if _SQUID_APPLE_ dst->ai_flags = 0; #else dst->ai_flags = AI_NUMERICHOST; #endif 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 (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; #if 0 /** * Enable only if you must and please report to squid-dev if you find a need for this. * * Vista may need this to cope with dual-stack (unsetting IP6_V6ONLY). * http://msdn.microsoft.com/en-us/library/ms738574(VS.85).aspx * Linux appears to only do some things when its present. * (93) Bad Protocol * FreeBSD dies horribly when using dual-stack with it set. * (43) Protocol not supported */ dst->ai_protocol = IPPROTO_IPV6; #endif } else 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 Ip::Address::InitAddr(struct addrinfo *&ai) { if (ai == nullptr) { 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 Ip::Address::FreeAddr(struct addrinfo *&ai) { if (ai == nullptr) return; if (ai->ai_addr) delete ai->ai_addr; ai->ai_addr = nullptr; ai->ai_addrlen = 0; // NP: name fields are NOT allocated at present. delete ai; ai = nullptr; } int Ip::Address::matchIPAddr(const Ip::Address &rhs) const { uint8_t *l = (uint8_t*)mSocketAddr_.sin6_addr.s6_addr; uint8_t *r = (uint8_t*)rhs.mSocketAddr_.sin6_addr.s6_addr; // 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(mSocketAddr_.sin6_addr) ; ++i) { if (l[i] < r[i]) return -1; if (l[i] > r[i]) return 1; } return 0; } int Ip::Address::compareWhole(const Ip::Address &rhs) const { return memcmp(this, &rhs, sizeof(*this)); } bool Ip::Address::operator ==(const Ip::Address &s) const { return (0 == matchIPAddr(s)); } bool Ip::Address::operator !=(const Ip::Address &s) const { return ! ( operator==(s) ); } bool Ip::Address::operator <=(const Ip::Address &rhs) const { if (isAnyAddr() && !rhs.isAnyAddr()) return true; return (matchIPAddr(rhs) <= 0); } bool Ip::Address::operator >=(const Ip::Address &rhs) const { if (isNoAddr() && !rhs.isNoAddr()) return true; return ( matchIPAddr(rhs) >= 0); } bool Ip::Address::operator >(const Ip::Address &rhs) const { if (isNoAddr() && !rhs.isNoAddr()) return true; return ( matchIPAddr(rhs) > 0); } bool Ip::Address::operator <(const Ip::Address &rhs) const { if (isAnyAddr() && !rhs.isAnyAddr()) return true; return ( matchIPAddr(rhs) < 0); } unsigned short Ip::Address::port() const { return ntohs( mSocketAddr_.sin6_port ); } unsigned short Ip::Address::port(unsigned short prt) { mSocketAddr_.sin6_port = htons(prt); return prt; } char * Ip::Address::toStr(char* buf, const unsigned int blen, int force) const { // Ensure we have a buffer. if (buf == nullptr) { return nullptr; } /* some external code may have blindly memset a parent. */ /* that's okay, our default is known */ if ( isAnyAddr() ) { if (isIPv6()) memcpy(buf,"::\0", min(static_cast(3),blen)); else if (isIPv4()) memcpy(buf,"0.0.0.0\0", min(static_cast(8),blen)); 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", min(static_cast(8),blen)); } return buf; } if ( force == AF_INET6 || (force == AF_UNSPEC && isIPv6()) ) { inet_ntop(AF_INET6, &mSocketAddr_.sin6_addr, buf, blen); } else if ( force == AF_INET || (force == AF_UNSPEC && isIPv4()) ) { struct in_addr tmp; getInAddr(tmp); inet_ntop(AF_INET, &tmp, buf, blen); } else { debugs(14, DBG_CRITICAL, "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", min(static_cast(13),blen)); assert(false); } return buf; } unsigned int Ip::Address::toHostStr(char *buf, const unsigned int blen) const { char *p = buf; if (isIPv6() && blen > 0) { *p = '['; ++p; } /* 8 being space for [ ] : and port digits */ if ( isIPv6() ) toStr(p, blen-8, AF_INET6); else toStr(p, blen-8, 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 * Ip::Address::toUrl(char* buf, unsigned int blen) const { char *p = buf; // Ensure we have a buffer. if (buf == nullptr) { return nullptr; } p += toHostStr(p, blen); if (mSocketAddr_.sin6_port > 0 && p <= (buf+blen-7) ) { // ':port' (short int) needs at most 6 bytes plus 1 for 0-terminator snprintf(p, 7, ":%d", port() ); } // force a null-terminated string buf[blen-1] = '\0'; return buf; } bool Ip::Address::fromHost(const char *host) { setEmpty(); if (!host) return false; if (host[0] != '[') return lookupHostIP(host, true); // no brackets /* unwrap a bracketed [presumably IPv6] address, presumably without port */ const char *start = host + 1; if (!*start) return false; // missing address after an opening bracket // XXX: Check that there is a closing bracket and no trailing garbage. char *tmp = xstrdup(start); // XXX: Slow. TODO: Bail on huge strings and use an on-stack buffer. tmp[strlen(tmp)-1] = '\0'; // XXX: Wasteful: xstrdup() just did strlen(). const bool result = lookupHostIP(tmp, true); xfree(tmp); return result; } void Ip::Address::getSockAddr(struct sockaddr_storage &addr, const int family) const { struct sockaddr_in *sin = nullptr; if ( family == AF_INET && !isIPv4()) { // TODO INET6: caller using the wrong socket type! debugs(14, DBG_CRITICAL, "ERROR: Ip::Address::getSockAddr : Cannot convert non-IPv4 to IPv4. from " << *this); assert(false); } if ( family == AF_INET6 || (family == AF_UNSPEC && isIPv6()) ) { struct sockaddr_in6 *ss6 = (struct sockaddr_in6*)&addr; getSockAddr(*ss6); } else if ( family == AF_INET || (family == AF_UNSPEC && isIPv4()) ) { sin = (struct sockaddr_in*)&addr; getSockAddr(*sin); } else { IASSERT("false",false); } } void Ip::Address::getSockAddr(struct sockaddr_in &buf) const { if ( isIPv4() ) { buf.sin_family = AF_INET; buf.sin_port = mSocketAddr_.sin6_port; map6to4( mSocketAddr_.sin6_addr, buf.sin_addr); } else { debugs(14, DBG_CRITICAL, "ERROR: Ip::Address::getSockAddr : Cannot convert non-IPv4 to IPv4. from " << *this ); memset(&buf,0xFFFFFFFF,sizeof(struct sockaddr_in)); assert(false); } #if HAVE_SIN_LEN_IN_SAI /* not all OS have this field, BUT when they do it can be a problem if set wrong */ buf.sin_len = sizeof(struct sockaddr_in); #endif } void Ip::Address::getSockAddr(struct sockaddr_in6 &buf) const { memmove(&buf, &mSocketAddr_, sizeof(struct sockaddr_in6)); /* maintain address family. It may have changed inside us. */ buf.sin6_family = AF_INET6; #if HAVE_SIN6_LEN_IN_SAI /* not all OS have this field, BUT when they do it can be a problem if set wrong */ buf.sin6_len = sizeof(struct sockaddr_in6); #endif } void Ip::Address::map4to6(const struct in_addr &in, struct in6_addr &out) const { /* check for special cases */ if ( in.s_addr == 0x00000000) { /* ANYADDR */ out = v4_anyaddr; } else if ( in.s_addr == 0xFFFFFFFF) { /* NOADDR */ out = v4_noaddr; } else { /* general */ out = v4_anyaddr; out.s6_addr[12] = ((uint8_t *)&in.s_addr)[0]; out.s6_addr[13] = ((uint8_t *)&in.s_addr)[1]; out.s6_addr[14] = ((uint8_t *)&in.s_addr)[2]; out.s6_addr[15] = ((uint8_t *)&in.s_addr)[3]; } } void Ip::Address::map6to4(const struct in6_addr &in, struct in_addr &out) const { /* ANYADDR */ /* NOADDR */ /* general */ memset(&out, 0, sizeof(struct in_addr)); ((uint8_t *)&out.s_addr)[0] = in.s6_addr[12]; ((uint8_t *)&out.s_addr)[1] = in.s6_addr[13]; ((uint8_t *)&out.s_addr)[2] = in.s6_addr[14]; ((uint8_t *)&out.s_addr)[3] = in.s6_addr[15]; } void Ip::Address::getInAddr(struct in6_addr &buf) const { memmove(&buf, &mSocketAddr_.sin6_addr, sizeof(struct in6_addr)); } bool Ip::Address::getInAddr(struct in_addr &buf) const { if ( isIPv4() ) { map6to4(mSocketAddr_.sin6_addr, buf); return true; } // default: // non-compatible IPv6 Pure Address debugs(14, DBG_IMPORTANT, "ERROR: Ip::Address::getInAddr : Cannot convert non-IPv4 to IPv4. IPA=" << *this); memset(&buf,0xFFFFFFFF,sizeof(struct in_addr)); assert(false); return false; }