aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Common/isLocalAddress.cpp
blob: 7569c6fc14eb1b806260a43d353a3ed3317948ea (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <Common/isLocalAddress.h>

#include <ifaddrs.h>
#include <cstring>
#include <optional>
#include <base/types.h>
#include <Common/Exception.h>
#include <Poco/Net/IPAddress.h>
#include <Poco/Net/SocketAddress.h>


namespace DB
{

namespace ErrorCodes
{
    extern const int SYSTEM_ERROR;
}

namespace
{

struct NetworkInterfaces
{
    ifaddrs * ifaddr;
    NetworkInterfaces()
    {
        if (getifaddrs(&ifaddr) == -1)
        {
            throwFromErrno("Cannot getifaddrs", ErrorCodes::SYSTEM_ERROR);
        }
    }

    bool hasAddress(const Poco::Net::IPAddress & address) const
    {
        ifaddrs * iface;
        for (iface = ifaddr; iface != nullptr; iface = iface->ifa_next)
        {
            /// Point-to-point (VPN) addresses may have NULL ifa_addr
            if (!iface->ifa_addr)
                continue;

            auto family = iface->ifa_addr->sa_family;
            std::optional<Poco::Net::IPAddress> interface_address;
            switch (family)
            {
                /// We interested only in IP-addresses
                case AF_INET:
                {
                    interface_address.emplace(*(iface->ifa_addr));
                    break;
                }
                case AF_INET6:
                {
                    interface_address.emplace(&reinterpret_cast<const struct sockaddr_in6*>(iface->ifa_addr)->sin6_addr, sizeof(struct in6_addr));
                    break;
                }
                default:
                    continue;
            }

            /** Compare the addresses without taking into account `scope`.
              * Theoretically, this may not be correct - depends on `route` setting
              *  - through which interface we will actually access the specified address.
              */
            if (interface_address->length() == address.length()
                && 0 == memcmp(interface_address->addr(), address.addr(), address.length()))
                return true;
        }
        return false;
    }

    ~NetworkInterfaces()
    {
        freeifaddrs(ifaddr);
    }
};

}


bool isLocalAddress(const Poco::Net::IPAddress & address)
{
    /** 127.0.0.1 is treat as local address unconditionally.
      * ::1 is also treat as local address unconditionally.
      *
      * 127.0.0.{2..255} are not treat as local addresses, because they are used in tests
      *  to emulate distributed queries across localhost.
      *
      * But 127.{0,1}.{0,1}.{0,1} are treat as local addresses,
      *  because they are used in Debian for localhost.
      */
    if (address.isLoopback())
    {
        if (address.family() == Poco::Net::AddressFamily::IPv4)
        {
            /// The address is located in memory in big endian form (network byte order).
            const unsigned char * digits = static_cast<const unsigned char *>(address.addr());

            if (digits[0] == 127
                && digits[1] <= 1
                && digits[2] <= 1
                && digits[3] <= 1)
            {
                return true;
            }
        }
        else if (address.family() == Poco::Net::AddressFamily::IPv6)
        {
            return true;
        }
    }

    NetworkInterfaces interfaces;
    return interfaces.hasAddress(address);
}


bool isLocalAddress(const Poco::Net::SocketAddress & address, UInt16 clickhouse_port)
{
    return clickhouse_port == address.port() && isLocalAddress(address.host());
}


size_t getHostNameDifference(const std::string & local_hostname, const std::string & host)
{
    /// FIXME should we replace it with Levenstein distance? (we already have it in NamePrompter)
    size_t hostname_difference = 0;
    for (size_t i = 0; i < std::min(local_hostname.length(), host.length()); ++i)
        if (local_hostname[i] != host[i])
            ++hostname_difference;
    return hostname_difference;
}

}