[squid-users] [squid 7.2] Segmentation fault
David Touzeau
david at articatech.com
Mon Oct 20 23:42:41 UTC 2025
Hi, on debian 13, have a segmentation fault
squid -v
Squid Cache: Version 7.2-VCS
Service Name: squid
This binary uses OpenSSL 3.5.1 1 Jul 2025. configure options:
'--prefix=/usr' '--build=x86_64-linux-gnu' '--includedir=/include'
'--mandir=/share/man' '--infodir=/share/info' '--localstatedir=/var'
'--libexecdir=/lib/squid3' '--disable-maintainer-mode'
'--disable-dependency-tracking' '--datadir=/usr/share/squid3'
'--sysconfdir=/etc/squid3' '--enable-gnuregex'
'--enable-removal-policy=heap' '--enable-follow-x-forwarded-for'
'--enable-removal-policies=lru,heap' '--enable-arp-acl'
'--enable-truncate' '--with-large-files' '--with-pthreads'
'--enable-esi' '--enable-storeio=aufs,diskd,ufs,rock'
'--enable-x-accelerator-vary' '--with-dl--enable-linux-netfilter'
'--with-netfilter-conntrack' '--enable-wccpv2' '--enable-eui'
'--enable-auth' '--enable-auth-basic' '--enable-snmp' '--enable-icmp'
'--enable-auth-digest' '--enable-log-daemon-helpers'
'--enable-url-rewrite-helpers' '--enable-auth-ntlm'
'--with-default-user=squid' '--enable-icap-client'
'--disable-cache-digests' '--enable-poll' '--enable-epoll'
'--enable-async-io=128' '--enable-zph-qos' '--enable-delay-pools'
'--enable-http-violations' '--enable-url-maps' '--enable-ecap'
'--enable-ssl' '--with-openssl' '--enable-ssl-crtd'
'--enable-xmalloc-statistics' '--enable-ident-lookups'
'--with-filedescriptors=163840' '--with-aufs-threads=128'
'--disable-arch-native' '--without-gnutls'
'--with-logdir=/var/log/squid' '--with-pidfile=/var/run/squid/squid.pid'
'--with-swapdir=/var/cache/squid' 'CXXFLAGS=-O1 -g -fsanitize=address
-fno-omit-frame-pointer -Wno-error=maybe-uninitialized'
'build_alias=x86_64-linux-gnu' 'PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:'
2025/10/20 22:40:14| Initializing IP Cache...
=================================================================
==284891==ERROR: AddressSanitizer: new-delete-type-mismatch on
0x503000147bb0 in thread T0:
object passed to delete has wrong type:
size of the allocated type: 28 bytes;
size of the deallocated type: 16 bytes.
#0 0x7f55bd2f6678 in operator delete(void*, unsigned long)
../../../../src/libsanitizer/asan/asan_new_delete.cpp:164
#1 0x55bd86e79254 in Ip::Address::FreeAddr(addrinfo*&)
/root/squid-7.2.patched/src/ip/Address.cc:710
#2 0x55bd86d6c923 in comm_local_port(int)
/root/squid-7.2.patched/src/comm.cc:194
#3 0x55bd86667a94 in Dns::Init()
/root/squid-7.2.patched/src/dns_internal.cc:1594
#4 0x55bd869fe2ec in mainInitialize
/root/squid-7.2.patched/src/main.cc:1149
#5 0x55bd869fe2ec in SquidMain(int, char**)
/root/squid-7.2.patched/src/main.cc:1665
#6 0x55bd869ff6fe in SquidMainSafe
/root/squid-7.2.patched/src/main.cc:1351
#7 0x55bd869ff6fe in main /root/squid-7.2.patched/src/main.cc:1339
#8 0x7f55bc233ca7 (/usr/lib/x86_64-linux-gnu/libc.so.6+0x29ca7)
(BuildId: def5460e3cee00bfee25b429c97bcc4853e5b3a8)
#9 0x7f55bc233d64 in __libc_start_main
(/usr/lib/x86_64-linux-gnu/libc.so.6+0x29d64) (BuildId:
def5460e3cee00bfee25b429c97bcc4853e5b3a8)
#10 0x55bd86605960 in _start (/usr/sbin/squid+0x36f960) (BuildId:
610a22a0102aabfd95125cac39325f2ee96de29b)
0x503000147bb0 is located 0 bytes inside of 28-byte region
[0x503000147bb0,0x503000147bcc)
allocated by thread T0 here:
#0 0x7f55bd2f5778 in operator new(unsigned long)
../../../../src/libsanitizer/asan/asan_new_delete.cpp:95
#1 0x55bd86e79106 in Ip::Address::InitAddr(addrinfo*&)
/root/squid-7.2.patched/src/ip/Address.cc:698
#2 0x55bd86d6c6ab in comm_local_port(int)
/root/squid-7.2.patched/src/comm.cc:184
#3 0x55bd86667a94 in Dns::Init()
/root/squid-7.2.patched/src/dns_internal.cc:1594
#4 0x55bd869fe2ec in mainInitialize
/root/squid-7.2.patched/src/main.cc:1149
#5 0x55bd869fe2ec in SquidMain(int, char**)
/root/squid-7.2.patched/src/main.cc:1665
#6 0x55bd869ff6fe in SquidMainSafe
/root/squid-7.2.patched/src/main.cc:1351
#7 0x55bd869ff6fe in main /root/squid-7.2.patched/src/main.cc:1339
#8 0x7f55bc233ca7 (/usr/lib/x86_64-linux-gnu/libc.so.6+0x29ca7)
(BuildId: def5460e3cee00bfee25b429c97bcc4853e5b3a8)
SUMMARY: AddressSanitizer: new-delete-type-mismatch
../../../../src/libsanitizer/asan/asan_new_delete.cpp:164 in operator
delete(void*, unsigned long)
==284891==HINT: if you don't care about these errors you may set
ASAN_OPTIONS=new_delete_type_mismatch=0
==284891==ABORTING
root at artica-appliance:~#
=================================================================
==284891==ERROR: AddressSanitizer: new-delete-type-mismatch on
0x503000147bb0 in thread T0:
object passed to delete has wrong type:
size of the allocated type: 28 bytes;
size of the deallocated type: 16 bytes.
#0 0x7f55bd2f6678 in operator delete(void*, unsigned long)
../../../../src/libsanitizer/asan/asan_new_delete.cpp:164
#1 0x55bd86e79254 in Ip::Address::FreeAddr(addrinfo*&)
/root/squid-7.2.patched/src/ip/Address.cc:710
#2 0x55bd86d6c923 in comm_local_port(int)
/root/squid-7.2.patched/src/comm.cc:194
#3 0x55bd86667a94 in Dns::Init()
/root/squid-7.2.patched/src/dns_internal.cc:1594
#4 0x55bd869fe2ec in mainInitialize
/root/squid-7.2.patched/src/main.cc:1149
#5 0x55bd869fe2ec in SquidMain(int, char**)
/root/squid-7.2.patched/src/main.cc:1665
#6 0x55bd869ff6fe in SquidMainSafe
/root/squid-7.2.patched/src/main.cc:1351
#7 0x55bd869ff6fe in main /root/squid-7.2.patched/src/main.cc:1339
#8 0x7f55bc233ca7 (/usr/lib/x86_64-linux-gnu/libc.so.6+0x29ca7)
(BuildId: def5460e3cee00bfee25b429c97bcc4853e5b3a8)
#9 0x7f55bc233d64 in __libc_start_main
(/usr/lib/x86_64-linux-gnu/libc.so.6+0x29d64) (BuildId:
def5460e3cee00bfee25b429c97bcc4853e5b3a8)
#10 0x55bd86605960 in _start (/usr/sbin/squid+0x36f960) (BuildId:
610a22a0102aabfd95125cac39325f2ee96de29b)
0x503000147bb0 is located 0 bytes inside of 28-byte region
[0x503000147bb0,0x503000147bcc)
allocated by thread T0 here:
#0 0x7f55bd2f5778 in operator new(unsigned long)
../../../../src/libsanitizer/asan/asan_new_delete.cpp:95
#1 0x55bd86e79106 in Ip::Address::InitAddr(addrinfo*&)
/root/squid-7.2.patched/src/ip/Address.cc:698
#2 0x55bd86d6c6ab in comm_local_port(int)
/root/squid-7.2.patched/src/comm.cc:184
#3 0x55bd86667a94 in Dns::Init()
/root/squid-7.2.patched/src/dns_internal.cc:1594
#4 0x55bd869fe2ec in mainInitialize
/root/squid-7.2.patched/src/main.cc:1149
#5 0x55bd869fe2ec in SquidMain(int, char**)
/root/squid-7.2.patched/src/main.cc:1665
#6 0x55bd869ff6fe in SquidMainSafe
/root/squid-7.2.patched/src/main.cc:1351
#7 0x55bd869ff6fe in main /root/squid-7.2.patched/src/main.cc:1339
#8 0x7f55bc233ca7 (/usr/lib/x86_64-linux-gnu/libc.so.6+0x29ca7)
(BuildId: def5460e3cee00bfee25b429c97bcc4853e5b3a8)
SUMMARY: AddressSanitizer: new-delete-type-mismatch
../../../../src/libsanitizer/asan/asan_new_delete.cpp:164 in operator
delete(void*, unsigned long)
==284891==HINT: if you don't care about these errors you may set
ASAN_OPTIONS=new_delete_type_mismatch=0
==284891==ABORTING
I’m not exactly sure why I’m hitting this bug, but it comes from a 6.14
→ 7.2 configuration upgrade.
I patched the functions |Ip::Address::getAddrInfo|,
|Ip::Address::FreeAddr|, and |Ip::Address::InitAddr| to get Squid working.
I’m attaching the modified |Address.cc| that makes it run, but there may
be something upstream that forced me to change the code (most likely a
|malloc|-related issue).
--
David Touzeau - Artica Tech France
Development team, level 3 support
----------------------------------
P: +33 6 58 44 69 46
www:https://wiki.articatech.com
www:http://articatech.net
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squid-cache.org/pipermail/squid-users/attachments/20251021/6dc26010/attachment-0001.htm>
-------------- next part --------------
/*
* Copyright (C) 1996-2025 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 <cassert>
#include <cstring>
#if HAVE_ARPA_INET_H
/* for inet_ntoa() */
#include <arpa/inet.h>
#endif
#if HAVE_WS2TCPIP_H
// Windows IPv6 definitions
#include <ws2tcpip.h>
#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>
Ip::Address::Parse(const char * const raw)
{
Address tmp;
// TODO: Merge with lookupHostIP() after removing DNS lookups from Ip.
if (tmp.lookupHostIP(raw, true))
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<sizeof(mSocketAddr_.sin6_addr) ; ++shift) {
ipbyte= *(ptr+shift);
if (ipbyte == 0xFF) {
len += 8;
continue ; /* A short-cut */
}
for (caught = 0, bit= 7 ; !caught && (bit <= 7); --bit) {
caught = ((ipbyte & 0x80) == 0x00); /* Found a '0' at 'bit' ? */
if (!caught)
++len;
ipbyte <<= 1;
}
if (caught)
break; /* We have found the most significant "0" bit. */
}
return len;
}
int
Ip::Address::applyMask(Ip::Address const &mask_addr)
{
uint32_t *p1 = (uint32_t*)(&mSocketAddr_.sin6_addr);
uint32_t const *p2 = (uint32_t const *)(&mask_addr.mSocketAddr_.sin6_addr);
unsigned int blen = sizeof(mSocketAddr_.sin6_addr)/sizeof(uint32_t);
unsigned int changes = 0;
for (unsigned int i = 0; i < blen; ++i) {
if ((p1[i] & p2[i]) != p1[i])
++changes;
p1[i] &= p2[i];
}
return changes;
}
void
Ip::Address::turnMaskedBitsOn(const Address &mask)
{
const auto addressWords = reinterpret_cast<uint32_t*>(&mSocketAddr_.sin6_addr);
const auto maskWords = reinterpret_cast<const uint32_t*>(&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<uint8_t>(0xfd) ||
mSocketAddr_.sin6_addr.s6_addr[0] == static_cast<uint8_t>(0xfc);
}
bool
Ip::Address::isSiteLocalAuto() const
{
return mSocketAddr_.sin6_addr.s6_addr[11] == static_cast<uint8_t>(0xff) &&
mSocketAddr_.sin6_addr.s6_addr[12] == static_cast<uint8_t>(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
{
// allocate struct if needed
if (!dst) {
dst = new addrinfo;
std::memset(dst, 0, sizeof(*dst));
} else {
// drop any previous buffer we may have left there
if (dst->ai_addr) {
std::free(dst->ai_addr);
dst->ai_addr = nullptr;
}
std::memset(dst, 0, sizeof(*dst));
}
// defaults
#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 && dst->ai_protocol == 0)
dst->ai_protocol = IPPROTO_TCP;
if (dst->ai_socktype == SOCK_DGRAM && dst->ai_protocol == 0)
dst->ai_protocol = IPPROTO_UDP;
if (force == AF_INET6 || (force == AF_UNSPEC && isIPv6())) {
dst->ai_family = AF_INET6;
dst->ai_addrlen = sizeof(sockaddr_in6);
dst->ai_addr = static_cast<sockaddr*>(std::malloc(dst->ai_addrlen));
if (!dst->ai_addr) throw std::bad_alloc();
std::memset(dst->ai_addr, 0, dst->ai_addrlen);
auto *sa6 = reinterpret_cast<sockaddr_in6*>(dst->ai_addr);
sa6->sin6_family = AF_INET6;
getSockAddr(*sa6); // fill from this Address
} else if (force == AF_INET || (force == AF_UNSPEC && isIPv4())) {
dst->ai_family = AF_INET;
dst->ai_addrlen = sizeof(sockaddr_in);
dst->ai_addr = static_cast<sockaddr*>(std::malloc(dst->ai_addrlen));
if (!dst->ai_addr) throw std::bad_alloc();
std::memset(dst->ai_addr, 0, dst->ai_addrlen);
auto *sa4 = reinterpret_cast<sockaddr_in*>(dst->ai_addr);
sa4->sin_family = AF_INET;
getSockAddr(*sa4); // fill from this Address
} else {
IASSERT("false", false);
}
}
void
Ip::Address::InitAddr(struct addrinfo *&ai)
{
if (!ai) {
ai = new addrinfo;
std::memset(ai, 0, sizeof(*ai));
// If caller did not preset the family, pick a default (IPv4 is safest)
ai->ai_family = AF_INET;
}
// drop any previous buffer we may have allocated
if (ai->ai_addr) {
std::free(ai->ai_addr);
ai->ai_addr = nullptr;
}
int fam = ai->ai_family;
if (fam != AF_INET && fam != AF_INET6) {
// fallback to IPv4 if unspecified/unknown
fam = AF_INET;
ai->ai_family = AF_INET;
}
if (fam == AF_INET6) {
ai->ai_addrlen = sizeof(sockaddr_in6);
ai->ai_addr = static_cast<sockaddr*>(std::malloc(ai->ai_addrlen));
if (!ai->ai_addr) throw std::bad_alloc();
std::memset(ai->ai_addr, 0, ai->ai_addrlen);
reinterpret_cast<sockaddr_in6*>(ai->ai_addr)->sin6_family = AF_INET6;
} else {
ai->ai_addrlen = sizeof(sockaddr_in);
ai->ai_addr = static_cast<sockaddr*>(std::malloc(ai->ai_addrlen));
if (!ai->ai_addr) throw std::bad_alloc();
std::memset(ai->ai_addr, 0, ai->ai_addrlen);
reinterpret_cast<sockaddr_in*>(ai->ai_addr)->sin_family = AF_INET;
}
// leave these neutral; caller can set if needed
ai->ai_socktype = ai->ai_socktype; // no change
ai->ai_protocol = ai->ai_protocol; // no change
}
void
Ip::Address::FreeAddr(struct addrinfo *&ai)
{
if (!ai)
return;
if (ai->ai_addr) {
std::free(ai->ai_addr);
ai->ai_addr = nullptr;
}
ai->ai_addrlen = 0;
// Canonical name fields are not allocated by us, so nothing else to free
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<unsigned int>(3),blen));
else if (isIPv4())
memcpy(buf,"0.0.0.0\0", min(static_cast<unsigned int>(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<unsigned int>(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<unsigned int>(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;
}
More information about the squid-users
mailing list