blob: ca95a68714a1b1d640b81783e3971f1bc497b2a4 [file] [log] [blame]
// Copyright 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "android/base/network/NetworkUtils.h"
#include "android/base/Log.h"
#include "android/base/StringFormat.h"
#include "android/base/system/System.h"
#include <limits.h>
#ifdef __APPLE__
#include <sys/socket.h>
#endif
#ifndef _WIN32
#include <net/if.h>
#endif
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include "android/base/memory/LazyInstance.h"
#include "android/base/sockets/Winsock.h"
#include "android/base/system/Win32UnicodeString.h"
#include <iphlpapi.h>
#include <unordered_map>
#include <string>
#endif
// Set to 1 to display debug messages.
#define DEBUG 0
namespace android {
namespace base {
/* $OpenBSD: inet_pton.c,v 1.8 2010/05/06 15:47:14 claudio Exp $ */
/* $OpenBSD: inet_ntop.c,v 1.10 2014/05/17 18:16:14 tedu Exp $ */
/* Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
#define INADDRSZ 4
#define IN6ADDRSZ 16
#define INT16SZ 2
using u_char = unsigned char;
using u_int = unsigned int;
bool netStringToIpv4(const char* src, uint8_t* dst) {
static const char digits[] = "0123456789";
int saw_digit, octets, ch;
u_char tmp[INADDRSZ], *tp;
saw_digit = 0;
octets = 0;
*(tp = tmp) = 0;
while ((ch = *src++) != '\0') {
const char* pch;
if ((pch = strchr(digits, ch)) != NULL) {
u_int val = *tp * 10 + (pch - digits);
if (val > 255)
return (0);
if (!saw_digit) {
if (++octets > 4)
return (0);
saw_digit = 1;
}
*tp = val;
} else if (ch == '.' && saw_digit) {
if (octets == 4)
return (0);
*++tp = 0;
saw_digit = 0;
} else
return (0);
}
if (octets < 4)
return (0);
memcpy(dst, tmp, INADDRSZ);
return (1);
}
bool netStringToIpv6(const char* src, uint8_t* dst) {
static const char xdigits_l[] = "0123456789abcdef",
xdigits_u[] = "0123456789ABCDEF";
u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
const char *xdigits, *curtok;
int ch, saw_xdigit, count_xdigit;
u_int val;
memset((tp = tmp), '\0', IN6ADDRSZ);
endp = tp + IN6ADDRSZ;
colonp = NULL;
/* Leading :: requires some special handling. */
if (*src == ':')
if (*++src != ':')
return (0);
curtok = src;
saw_xdigit = count_xdigit = 0;
val = 0;
while ((ch = *src++) != '\0') {
const char* pch;
if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
pch = strchr((xdigits = xdigits_u), ch);
if (pch != NULL) {
if (count_xdigit >= 4)
return (0);
val <<= 4;
val |= (pch - xdigits);
if (val > 0xffff)
return (0);
saw_xdigit = 1;
count_xdigit++;
continue;
}
if (ch == ':') {
curtok = src;
if (!saw_xdigit) {
if (colonp)
return (0);
colonp = tp;
continue;
} else if (*src == '\0') {
return (0);
}
if (tp + INT16SZ > endp)
return (0);
*tp++ = (u_char)(val >> 8) & 0xff;
*tp++ = (u_char)val & 0xff;
saw_xdigit = 0;
count_xdigit = 0;
val = 0;
continue;
}
if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
netStringToIpv4(curtok, tp) > 0) {
tp += INADDRSZ;
saw_xdigit = 0;
count_xdigit = 0;
break; /* '\0' was seen by inet_pton4(). */
}
return (0);
}
if (saw_xdigit) {
if (tp + INT16SZ > endp)
return (0);
*tp++ = (u_char)(val >> 8) & 0xff;
*tp++ = (u_char)val & 0xff;
}
if (colonp != NULL) {
/*
* Since some memmove()'s erroneously fail to handle
* overlapping regions, we'll do the shift by hand.
*/
const int n = tp - colonp;
int i;
if (tp == endp)
return (0);
for (i = 1; i <= n; i++) {
endp[-i] = colonp[n - i];
colonp[n - i] = 0;
}
tp = endp;
}
if (tp != endp)
return (0);
memcpy(dst, tmp, IN6ADDRSZ);
return (1);
}
using android::base::System;
namespace {
#ifdef _WIN32
// Technical note:
//
// This uses GetInterfaceInfo() to retrieve the table of network interfaces
// that have IPv4 enabled, even though this feature would normally be used
// for IPv6-enabled ones. For now just assume these are the same interfaces.
//
// TODO: Find the right Win32 API to list the IPv6-enabled interfaces.
//
struct SystemNetworkInterfaceNameResolver
: public NetworkInterfaceNameResolver {
// Default constructor.
SystemNetworkInterfaceNameResolver() { refresh(); }
// Find the index for an interface named |src|. Return -1 if not found.
virtual int queryInterfaceName(const char* src) override {
// Avoid calling GetInterfaceInfo() on each query.
if ((System::get()->getUnixTimeUs() - mLastTimestamp) >
kRefreshTimeoutUs) {
refresh();
}
auto it = mMap.find(std::string(src));
if (it == mMap.end()) {
return -1;
}
return it->second;
}
private:
using InterfaceMap = std::unordered_map<std::string, int>;
// Call GetInterfaceInfo() and refresh the map of interface names.
void refresh() {
InterfaceMap map;
mLastTimestamp = System::get()->getUnixTimeUs();
std::string buffer;
ULONG outBufLen = 0;
DWORD ret = GetInterfaceInfo(NULL, &outBufLen);
if (ret == ERROR_INSUFFICIENT_BUFFER) {
buffer.resize(static_cast<size_t>(outBufLen));
ret = GetInterfaceInfo(
reinterpret_cast<IP_INTERFACE_INFO*>(&buffer[0]),
&outBufLen);
}
if (ret != NO_ERROR) {
LOG(ERROR) << "Cannot read network interface table";
return;
}
auto info = reinterpret_cast<IP_INTERFACE_INFO*>(&buffer[0]);
for (int i = 0; i < info->NumAdapters; ++i) {
std::string name =
Win32UnicodeString::convertToUtf8(info->Adapter[i].Name);
#if DEBUG
LOG(INFO) << StringFormat("Network interface %2d [%s]",
info->Adapter[i].Index, name.c_str());
#endif
map.insert(std::make_pair(
std::move(name), static_cast<int>(info->Adapter[i].Index)));
}
mMap.swap(map);
}
InterfaceMap mMap;
System::Duration mLastTimestamp = 0;
static constexpr System::Duration kRefreshTimeoutUs = 2000000LL;
};
#else // !_WIN32
class SystemNetworkInterfaceNameResolver : public NetworkInterfaceNameResolver {
public:
virtual int queryInterfaceName(const char* src) override {
int ret = if_nametoindex(src);
return (ret == 0) ? -1 : ret;
}
};
#endif // !_WIN32
NetworkInterfaceNameResolver* sResolver = nullptr;
} // namespace
int netStringToInterfaceIndex(const char* name) {
// If |name| is a decimal number, don't try to lookup the name.
if (name[0] >= '0' && name[0] <= '9') {
char* end = nullptr;
long val = strtol(name, &end, 10);
if (*end != '\0' || val < 0 || val > INT_MAX) {
// Invalid decimal number.
return -1;
}
return static_cast<int>(val);
}
// Otherwise, use the current system resolver.
NetworkInterfaceNameResolver* resolver = sResolver;
if (!resolver) {
sResolver = resolver = new SystemNetworkInterfaceNameResolver();
}
return resolver->queryInterfaceName(name);
}
NetworkInterfaceNameResolver* netSetNetworkInterfaceNameResolverForTesting(
NetworkInterfaceNameResolver* resolver) {
NetworkInterfaceNameResolver* old = sResolver;
sResolver = resolver;
return old;
}
} // namespace base
} // namepsace android