[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