lokinet/llarp/net/net.cpp
Thomas Winget 4c630e0437 Large collection of changes to make android work
- Previous android java and jni code updated to work, but with much love
  still needed to make it work nicely, e.g. handling when the VPN is
  turned off.

- DNS handling refactored to allow android to intercept and handle DNS
  requests as we can't set the system DNS to use a high port
  (and apparently Chrome ignores system DNS settings anyway)

- add packet router structure to allow separate handling of specific
  intercepted traffic, e.g. UDP traffic to port 53 gets handled by our
  DNS handler rather than being naively forwarded as exit traffic.

- For now, android lokinet is exit-only and hard-coded to use exit.loki
  as its exit.  The exit will be configurable before release, but
  allowing to not use exit-only mode is more of a challenge.

- some old gitignore remnants which were matching to things we don't
  want them to (and are no longer relevant) removed

- some minor changes to CI configuration
2021-03-02 13:18:22 -05:00

677 lines
17 KiB
C++

#include <net/net.hpp>
#include <net/net_if.hpp>
#include <stdexcept>
#ifdef ANDROID
#include <android/ifaddrs.h>
#endif
#ifndef _WIN32
#include <arpa/inet.h>
#ifndef ANDROID
#include <ifaddrs.h>
#endif
#endif
#include <net/ip.hpp>
#include <net/ip_range.hpp>
#include <util/logging/logger.hpp>
#include <util/str.hpp>
#if ANDROID
#include <android/ifaddrs.h>
#else
#ifndef _WIN32
#include <ifaddrs.h>
#endif
#endif
#include <cstdio>
#include <list>
bool
operator==(const sockaddr& a, const sockaddr& b)
{
if (a.sa_family != b.sa_family)
return false;
switch (a.sa_family)
{
case AF_INET:
return *((const sockaddr_in*)&a) == *((const sockaddr_in*)&b);
case AF_INET6:
return *((const sockaddr_in6*)&a) == *((const sockaddr_in6*)&b);
default:
return false;
}
}
bool
operator<(const sockaddr_in6& a, const sockaddr_in6& b)
{
return a.sin6_addr < b.sin6_addr || a.sin6_port < b.sin6_port;
}
bool
operator<(const in6_addr& a, const in6_addr& b)
{
return memcmp(&a, &b, sizeof(in6_addr)) < 0;
}
bool
operator==(const in6_addr& a, const in6_addr& b)
{
return memcmp(&a, &b, sizeof(in6_addr)) == 0;
}
bool
operator==(const sockaddr_in& a, const sockaddr_in& b)
{
return a.sin_port == b.sin_port && a.sin_addr.s_addr == b.sin_addr.s_addr;
}
bool
operator==(const sockaddr_in6& a, const sockaddr_in6& b)
{
return a.sin6_port == b.sin6_port && a.sin6_addr == b.sin6_addr;
}
#ifdef _WIN32
#include <assert.h>
#include <errno.h>
#include <iphlpapi.h>
#include <strsafe.h>
// current strategy: mingw 32-bit builds call an inlined version of the function
// microsoft c++ and mingw 64-bit builds call the normal function
#define DEFAULT_BUFFER_SIZE 15000
// in any case, we still need to implement some form of
// getifaddrs(3) with compatible semantics on NT...
// daemon.ini section [bind] will have something like
// [bind]
// Ethernet=1090
// inside, since that's what we use in windows to refer to
// network interfaces
struct llarp_nt_ifaddrs_t
{
struct llarp_nt_ifaddrs_t* ifa_next; /* Pointer to the next structure. */
char* ifa_name; /* Name of this network interface. */
unsigned int ifa_flags; /* Flags as from SIOCGIFFLAGS ioctl. */
struct sockaddr* ifa_addr; /* Network address of this interface. */
struct sockaddr* ifa_netmask; /* Netmask of this interface. */
};
// internal struct
struct _llarp_nt_ifaddrs_t
{
struct llarp_nt_ifaddrs_t _ifa;
char _name[256];
struct sockaddr_storage _addr;
struct sockaddr_storage _netmask;
};
static inline void*
_llarp_nt_heap_alloc(const size_t n_bytes)
{
/* Does not appear very safe with re-entrant calls on XP */
return HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, n_bytes);
}
static inline void
_llarp_nt_heap_free(void* mem)
{
HeapFree(GetProcessHeap(), 0, mem);
}
#define llarp_nt_new0(struct_type, n_structs) \
((struct_type*)malloc((size_t)sizeof(struct_type) * (size_t)(n_structs)))
int
llarp_nt_sockaddr_pton(const char* src, struct sockaddr* dst)
{
struct addrinfo hints;
struct addrinfo* result = nullptr;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_NUMERICHOST;
const int status = getaddrinfo(src, nullptr, &hints, &result);
if (!status)
{
memcpy(dst, result->ai_addr, result->ai_addrlen);
freeaddrinfo(result);
return 1;
}
return 0;
}
/* NB: IP_ADAPTER_INFO size varies size due to sizeof (time_t), the API assumes
* 4-byte datatype whilst compiler uses an 8-byte datatype. Size can be forced
* with -D_USE_32BIT_TIME_T with side effects to everything else.
*
* Only supports IPv4 addressing similar to SIOCGIFCONF socket option.
*
* Interfaces that are not "operationally up" will return the address 0.0.0.0,
* this includes adapters with static IP addresses but with disconnected cable.
* This is documented under the GetIpAddrTable API. Interface status can only
* be determined by the address, a separate flag is introduced with the
* GetAdapterAddresses API.
*
* The IPv4 loopback interface is not included.
*
* Available in Windows 2000 and Wine 1.0.
*/
static bool
_llarp_nt_getadaptersinfo(struct llarp_nt_ifaddrs_t** ifap)
{
DWORD dwRet;
ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE;
PIP_ADAPTER_INFO pAdapterInfo = nullptr;
PIP_ADAPTER_INFO pAdapter = nullptr;
/* loop to handle interfaces coming online causing a buffer overflow
* between first call to list buffer length and second call to enumerate.
*/
for (unsigned i = 3; i; i--)
{
#ifdef DEBUG
fprintf(stderr, "IP_ADAPTER_INFO buffer length %lu bytes.\n", ulOutBufLen);
#endif
pAdapterInfo = (IP_ADAPTER_INFO*)_llarp_nt_heap_alloc(ulOutBufLen);
dwRet = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
if (ERROR_BUFFER_OVERFLOW == dwRet)
{
_llarp_nt_heap_free(pAdapterInfo);
pAdapterInfo = nullptr;
}
else
{
break;
}
}
switch (dwRet)
{
case ERROR_SUCCESS: /* NO_ERROR */
break;
case ERROR_BUFFER_OVERFLOW:
errno = ENOBUFS;
if (pAdapterInfo)
_llarp_nt_heap_free(pAdapterInfo);
return false;
default:
errno = dwRet;
#ifdef DEBUG
fprintf(stderr, "system call failed: %lu\n", GetLastError());
#endif
if (pAdapterInfo)
_llarp_nt_heap_free(pAdapterInfo);
return false;
}
/* count valid adapters */
int n = 0, k = 0;
for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next)
{
for (IP_ADDR_STRING* pIPAddr = &pAdapter->IpAddressList; pIPAddr; pIPAddr = pIPAddr->Next)
{
/* skip null adapters */
if (strlen(pIPAddr->IpAddress.String) == 0)
continue;
++n;
}
}
#ifdef DEBUG
fprintf(stderr, "GetAdaptersInfo() discovered %d interfaces.\n", n);
#endif
/* contiguous block for adapter list */
struct _llarp_nt_ifaddrs_t* ifa = llarp_nt_new0(struct _llarp_nt_ifaddrs_t, n);
struct _llarp_nt_ifaddrs_t* ift = ifa;
int val = 0;
/* now populate list */
for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next)
{
for (IP_ADDR_STRING* pIPAddr = &pAdapter->IpAddressList; pIPAddr; pIPAddr = pIPAddr->Next)
{
/* skip null adapters */
if (strlen(pIPAddr->IpAddress.String) == 0)
continue;
/* address */
ift->_ifa.ifa_addr = (struct sockaddr*)&ift->_addr;
val = llarp_nt_sockaddr_pton(pIPAddr->IpAddress.String, ift->_ifa.ifa_addr);
assert(1 == val);
/* name */
#ifdef DEBUG
fprintf(stderr, "name:%s IPv4 index:%lu\n", pAdapter->AdapterName, pAdapter->Index);
#endif
ift->_ifa.ifa_name = ift->_name;
StringCchCopyN(ift->_ifa.ifa_name, 128, pAdapter->AdapterName, 128);
/* flags: assume up, broadcast and multicast */
ift->_ifa.ifa_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST;
if (pAdapter->Type == MIB_IF_TYPE_LOOPBACK)
ift->_ifa.ifa_flags |= IFF_LOOPBACK;
/* netmask */
ift->_ifa.ifa_netmask = (sockaddr*)&ift->_netmask;
val = llarp_nt_sockaddr_pton(pIPAddr->IpMask.String, ift->_ifa.ifa_netmask);
assert(1 == val);
/* next */
if (k++ < (n - 1))
{
ift->_ifa.ifa_next = (struct llarp_nt_ifaddrs_t*)(ift + 1);
ift = (struct _llarp_nt_ifaddrs_t*)(ift->_ifa.ifa_next);
}
else
{
ift->_ifa.ifa_next = nullptr;
}
}
}
if (pAdapterInfo)
_llarp_nt_heap_free(pAdapterInfo);
*ifap = (struct llarp_nt_ifaddrs_t*)ifa;
return true;
}
// an implementation of if_nametoindex(3) based on GetAdapterIndex(2)
// with a fallback to GetAdaptersAddresses(2) commented out for now
// unless it becomes evident that the first codepath fails in certain
// edge cases?
static unsigned
_llarp_nt_nametoindex(const char* ifname)
{
ULONG ifIndex;
DWORD dwRet;
char szAdapterName[256];
if (!ifname)
return 0;
StringCchCopyN(szAdapterName, sizeof(szAdapterName), ifname, 256);
dwRet = GetAdapterIndex((LPWSTR)szAdapterName, &ifIndex);
if (!dwRet)
return ifIndex;
else
return 0;
}
// the emulated getifaddrs(3) itself.
static bool
llarp_nt_getifaddrs(struct llarp_nt_ifaddrs_t** ifap)
{
assert(nullptr != ifap);
#ifdef DEBUG
fprintf(stderr, "llarp_nt_getifaddrs (ifap:%p error:%p)\n", (void*)ifap, (void*)errno);
#endif
return _llarp_nt_getadaptersinfo(ifap);
}
static void
llarp_nt_freeifaddrs(struct llarp_nt_ifaddrs_t* ifa)
{
if (!ifa)
return;
free(ifa);
}
// emulated if_nametoindex(3)
static unsigned
llarp_nt_if_nametoindex(const char* ifname)
{
if (!ifname)
return 0;
return _llarp_nt_nametoindex(ifname);
}
// fix up names for win32
#define ifaddrs llarp_nt_ifaddrs_t
#define getifaddrs llarp_nt_getifaddrs
#define freeifaddrs llarp_nt_freeifaddrs
#define if_nametoindex llarp_nt_if_nametoindex
#endif
// jeff's original code
bool
llarp_getifaddr(const char* ifname, int af, struct sockaddr* addr)
{
ifaddrs* ifa = nullptr;
bool found = false;
socklen_t sl = sizeof(sockaddr_in6);
if (af == AF_INET)
sl = sizeof(sockaddr_in);
#ifndef _WIN32
if (getifaddrs(&ifa) == -1)
#else
if (!strcmp(ifname, "lo") || !strcmp(ifname, "lo0"))
{
if (addr)
{
sockaddr_in* lo = (sockaddr_in*)addr;
lo->sin_family = af;
lo->sin_port = 0;
inet_pton(af, "127.0.0.1", &lo->sin_addr);
}
return true;
}
if (!getifaddrs(&ifa))
#endif
return false;
ifaddrs* i = ifa;
while (i)
{
if (i->ifa_addr)
{
// llarp::LogInfo(__FILE__, "scanning ", i->ifa_name, " af: ",
// std::to_string(i->ifa_addr->sa_family));
if (std::string_view{i->ifa_name} == std::string_view{ifname} && i->ifa_addr->sa_family == af)
{
// can't do this here
// llarp::Addr a(*i->ifa_addr);
// if(!a.isPrivate())
//{
// llarp::LogInfo(__FILE__, "found ", ifname, " af: ", af);
if (addr)
{
memcpy(addr, i->ifa_addr, sl);
if (af == AF_INET6)
{
// set scope id
auto* ip6addr = (sockaddr_in6*)addr;
ip6addr->sin6_scope_id = if_nametoindex(ifname);
ip6addr->sin6_flowinfo = 0;
}
}
found = true;
break;
}
//}
}
i = i->ifa_next;
}
if (ifa)
freeifaddrs(ifa);
return found;
}
namespace llarp
{
static void
IterAllNetworkInterfaces(std::function<void(ifaddrs* const)> visit)
{
ifaddrs* ifa = nullptr;
#ifndef _WIN32
if (getifaddrs(&ifa) == -1)
#else
if (!getifaddrs(&ifa))
#endif
return;
ifaddrs* i = ifa;
while (i)
{
visit(i);
i = i->ifa_next;
}
if (ifa)
freeifaddrs(ifa);
}
namespace net
{
std::string
LoopbackInterfaceName()
{
const auto loopback = IPRange::FromIPv4(127, 0, 0, 0, 8);
std::string ifname;
IterAllNetworkInterfaces([&ifname, loopback](ifaddrs* const i) {
if (i->ifa_addr and i->ifa_addr->sa_family == AF_INET)
{
llarp::nuint32_t addr{((sockaddr_in*)i->ifa_addr)->sin_addr.s_addr};
if (loopback.Contains(xntohl(addr)))
{
ifname = i->ifa_name;
}
}
});
if (ifname.empty())
{
throw std::runtime_error(
"we have no ipv4 loopback interface for some ungodly reason, yeah idk fam");
}
return ifname;
}
} // namespace net
bool
GetBestNetIF(std::string& ifname, int af)
{
bool found = false;
IterAllNetworkInterfaces([&](ifaddrs* i) {
if (found)
return;
if (i->ifa_addr)
{
if (i->ifa_addr->sa_family == af)
{
llarp::SockAddr a(*i->ifa_addr);
llarp::IpAddress ip(a);
if (!ip.isBogon())
{
ifname = i->ifa_name;
found = true;
}
}
}
});
return found;
}
// TODO: ipv6?
std::optional<IPRange>
FindFreeRange()
{
std::list<IPRange> currentRanges;
IterAllNetworkInterfaces([&](ifaddrs* i) {
if (i && i->ifa_addr)
{
const auto fam = i->ifa_addr->sa_family;
if (fam != AF_INET)
return;
auto* addr = (sockaddr_in*)i->ifa_addr;
auto* mask = (sockaddr_in*)i->ifa_netmask;
nuint32_t ifaddr{addr->sin_addr.s_addr};
nuint32_t ifmask{mask->sin_addr.s_addr};
#ifdef _WIN32
// do not delete, otherwise GCC will do horrible things to this lambda
LogDebug("found ", ifaddr, " with mask ", ifmask);
#endif
if (addr->sin_addr.s_addr)
// skip unconfig'd adapters (windows passes these through the unix-y
// wrapper)
currentRanges.emplace_back(
IPRange{net::ExpandV4(xntohl(ifaddr)), net::ExpandV4(xntohl(ifmask))});
}
});
auto ownsRange = [&currentRanges](IPRange range) -> bool {
for (const auto& ownRange : currentRanges)
{
if (ownRange.Contains(range))
return true;
}
return false;
};
// generate possible ranges to in order of attempts
std::list<IPRange> possibleRanges;
for (byte_t oct = 16; oct < 32; ++oct)
{
possibleRanges.emplace_back(IPRange::FromIPv4(172, oct, 0, 1, 16));
}
for (byte_t oct = 0; oct < 255; ++oct)
{
possibleRanges.emplace_back(IPRange::FromIPv4(10, oct, 0, 1, 16));
}
for (byte_t oct = 0; oct < 255; ++oct)
{
possibleRanges.emplace_back(IPRange::FromIPv4(192, 168, oct, 1, 24));
}
// for each possible range pick the first one we don't own
for (const auto& range : possibleRanges)
{
if (not ownsRange(range))
return range;
}
return std::nullopt;
}
std::optional<std::string>
FindFreeTun()
{
int num = 0;
while (num < 255)
{
std::stringstream ifname_ss;
ifname_ss << "lokitun" << num;
std::string iftestname = ifname_ss.str();
bool found = llarp_getifaddr(iftestname.c_str(), AF_INET, nullptr);
if (!found)
{
return iftestname;
}
num++;
}
return std::nullopt;
}
std::optional<SockAddr>
GetInterfaceAddr(const std::string& ifname, int af)
{
sockaddr_storage s;
sockaddr* sptr = (sockaddr*)&s;
sptr->sa_family = af;
if (!llarp_getifaddr(ifname.c_str(), af, sptr))
return std::nullopt;
return SockAddr{*sptr};
}
std::optional<huint128_t>
GetInterfaceIPv6Address(std::string ifname)
{
sockaddr_storage s;
sockaddr* sptr = (sockaddr*)&s;
sptr->sa_family = AF_INET6;
if (!llarp_getifaddr(ifname.c_str(), AF_INET6, sptr))
return std::nullopt;
llarp::SockAddr addr{*sptr};
return addr.asIPv6();
}
bool
AllInterfaces(int af, SockAddr& result)
{
if (af == AF_INET)
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(0);
result = SockAddr{addr};
return true;
}
if (af == AF_INET6)
{
sockaddr_in6 addr6;
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(0);
addr6.sin6_addr = IN6ADDR_ANY_INIT;
result = SockAddr{addr6};
return true;
}
return false;
}
bool
IsBogon(const in6_addr& addr)
{
#if defined(TESTNET)
(void)addr;
return false;
#else
if (!ipv6_is_mapped_ipv4(addr))
{
static in6_addr zero = {};
if (addr == zero)
return true;
return false;
}
return IsIPv4Bogon(
ipaddr_ipv4_bits(addr.s6_addr[12], addr.s6_addr[13], addr.s6_addr[14], addr.s6_addr[15]));
#endif
}
bool
IsBogon(const huint128_t ip)
{
const nuint128_t netIP{ntoh128(ip.h)};
in6_addr addr{};
std::copy_n((const uint8_t*)&netIP.n, 16, &addr.s6_addr[0]);
return IsBogon(addr);
}
bool
IsBogonRange(const in6_addr& host, const in6_addr&)
{
// TODO: implement me
return IsBogon(host);
}
#if !defined(TESTNET)
static constexpr std::array bogonRanges = {IPRange::FromIPv4(0, 0, 0, 0, 8),
IPRange::FromIPv4(10, 0, 0, 0, 8),
IPRange::FromIPv4(21, 0, 0, 0, 8),
IPRange::FromIPv4(100, 64, 0, 0, 10),
IPRange::FromIPv4(127, 0, 0, 0, 8),
IPRange::FromIPv4(169, 254, 0, 0, 16),
IPRange::FromIPv4(172, 16, 0, 0, 12),
IPRange::FromIPv4(192, 0, 0, 0, 24),
IPRange::FromIPv4(192, 0, 2, 0, 24),
IPRange::FromIPv4(192, 88, 99, 0, 24),
IPRange::FromIPv4(192, 168, 0, 0, 16),
IPRange::FromIPv4(198, 18, 0, 0, 15),
IPRange::FromIPv4(198, 51, 100, 0, 24),
IPRange::FromIPv4(203, 0, 113, 0, 24),
IPRange::FromIPv4(224, 0, 0, 0, 4),
IPRange::FromIPv4(240, 0, 0, 0, 4)};
bool
IsIPv4Bogon(const huint32_t& addr)
{
for (const auto& bogon : bogonRanges)
{
if (bogon.Contains(addr))
{
return true;
}
}
return false;
}
#else
bool
IsIPv4Bogon(const huint32_t&)
{
return false;
}
#endif
} // namespace llarp