NetTunnelWindows/NetTunnelSDK/misc/ipcalc.cpp

538 lines
16 KiB
C++
Raw Permalink Normal View History

#include "pch.h"
#include <strsafe.h>
#include <winsock2.h>
#include <ws2ipdef.h>
#include <ws2tcpip.h>
#include <spdlog/spdlog.h>
#include "usrerr.h"
#include "misc.h"
#include "tunnel.h"
static const TCHAR *p2_table(unsigned pow) {
static const TCHAR *pow2[] = {
TEXT("1"),
TEXT("2"),
TEXT("4"),
TEXT("8"),
TEXT("16"),
TEXT("32"),
TEXT("64"),
TEXT("128"),
TEXT("256"),
TEXT("512"),
TEXT("1024"),
TEXT("2048"),
TEXT("4096"),
TEXT("8192"),
TEXT("16384"),
TEXT("32768"),
TEXT("65536"),
TEXT("131072"),
TEXT("262144"),
TEXT("524288"),
TEXT("1048576"),
TEXT("2097152"),
TEXT("4194304"),
TEXT("8388608"),
TEXT("16777216"),
TEXT("33554432"),
TEXT("67108864"),
TEXT("134217728"),
TEXT("268435456"),
TEXT("536870912"),
TEXT("1073741824"),
TEXT("2147483648"),
TEXT("4294967296"),
TEXT("8589934592"),
TEXT("17179869184"),
TEXT("34359738368"),
TEXT("68719476736"),
TEXT("137438953472"),
TEXT("274877906944"),
TEXT("549755813888"),
TEXT("1099511627776"),
TEXT("2199023255552"),
TEXT("4398046511104"),
TEXT("8796093022208"),
TEXT("17592186044416"),
TEXT("35184372088832"),
TEXT("70368744177664"),
TEXT("140737488355328"),
TEXT("281474976710656"),
TEXT("562949953421312"),
TEXT("1125899906842624"),
TEXT("2251799813685248"),
TEXT("4503599627370496"),
TEXT("9007199254740992"),
TEXT("18014398509481984"),
TEXT("36028797018963968"),
TEXT("72057594037927936"),
TEXT("144115188075855872"),
TEXT("288230376151711744"),
TEXT("576460752303423488"),
TEXT("1152921504606846976"),
TEXT("2305843009213693952"),
TEXT("4611686018427387904"),
TEXT("9223372036854775808"),
TEXT("18446744073709551616"),
TEXT("36893488147419103232"),
TEXT("73786976294838206464"),
TEXT("147573952589676412928"),
TEXT("295147905179352825856"),
TEXT("590295810358705651712"),
TEXT("1180591620717411303424"),
TEXT("2361183241434822606848"),
TEXT("4722366482869645213696"),
TEXT("9444732965739290427392"),
TEXT("18889465931478580854784"),
TEXT("37778931862957161709568"),
TEXT("75557863725914323419136"),
TEXT("151115727451828646838272"),
TEXT("302231454903657293676544"),
TEXT("604462909807314587353088"),
TEXT("1208925819614629174706176"),
TEXT("2417851639229258349412352"),
TEXT("4835703278458516698824704"),
TEXT("9671406556917033397649408"),
TEXT("19342813113834066795298816"),
TEXT("38685626227668133590597632"),
TEXT("77371252455336267181195264"),
TEXT("154742504910672534362390528"),
TEXT("309485009821345068724781056"),
TEXT("618970019642690137449562112"),
TEXT("1237940039285380274899124224"),
TEXT("2475880078570760549798248448"),
TEXT("4951760157141521099596496896"),
TEXT("9903520314283042199192993792"),
TEXT("19807040628566084398385987584"),
TEXT("39614081257132168796771975168"),
TEXT("79228162514264337593543950336"),
TEXT("158456325028528675187087900672"),
TEXT("316912650057057350374175801344"),
TEXT("633825300114114700748351602688"),
TEXT("1267650600228229401496703205376"),
TEXT("2535301200456458802993406410752"),
TEXT("5070602400912917605986812821504"),
TEXT("10141204801825835211973625643008"),
TEXT("20282409603651670423947251286016"),
TEXT("40564819207303340847894502572032"),
TEXT("81129638414606681695789005144064"),
TEXT("162259276829213363391578010288128"),
TEXT("324518553658426726783156020576256"),
TEXT("649037107316853453566312041152512"),
TEXT("1298074214633706907132624082305024"),
TEXT("2596148429267413814265248164610048"),
TEXT("5192296858534827628530496329220096"),
TEXT("10384593717069655257060992658440192"),
TEXT("20769187434139310514121985316880384"),
TEXT("41538374868278621028243970633760768"),
TEXT("83076749736557242056487941267521536"),
TEXT("166153499473114484112975882535043072"),
TEXT("332306998946228968225951765070086144"),
TEXT("664613997892457936451903530140172288"),
TEXT("1329227995784915872903807060280344576"),
TEXT("2658455991569831745807614120560689152"),
TEXT("5316911983139663491615228241121378304"),
TEXT("10633823966279326983230456482242756608"),
TEXT("21267647932558653966460912964485513216"),
TEXT("42535295865117307932921825928971026432"),
TEXT("85070591730234615865843651857942052864"),
TEXT("170141183460469231731687303715884105728"),
};
if (pow <= 127) {
return pow2[pow];
}
return TEXT("");
}
static int vasprintf(TCHAR **strp, const TCHAR *fmt, va_list ap) {
// _vscprintf tells you how big the buffer needs to be
const int len = _vscprintf(fmt, ap);
if (len == -1) {
return -1;
}
const size_t size = static_cast<size_t>(len) + 1;
const auto str = static_cast<TCHAR *>(malloc(size));
if (!str) {
return -1;
}
// _vsprintf_s is the "secure" version of vsprintf
const int r = vsprintf_s(str, len + 1, fmt, ap);
if (r == -1) {
free(str);
return -1;
}
*strp = str;
return r;
}
static int asprintf(TCHAR **strp, const TCHAR *fmt, ...) {
va_list ap;
va_start(ap, fmt);
const int r = vasprintf(strp, fmt, ap);
va_end(ap);
return r;
}
static int bit_count(unsigned int i) {
int c = 0;
unsigned int seen_one = 0;
while (i > 0) {
if (i & 1) {
seen_one = 1;
c++;
} else {
if (seen_one) {
return -1;
}
}
i >>= 1;
}
return c;
}
/**
* @brief creates a netmask from a specified number of bits
* This function converts a prefix length to a netmask. As CIDR (classless
* internet domain internet domain routing) has taken off, more an more IP
* addresses are being specified in the format address/prefix
* (i.e. 192.168.2.3/24, with a corresponding netmask 255.255.255.0). If you
* need to see what netmask corresponds to the prefix part of the address, this
* is the function. See also @ref mask2prefix.
* @param prefix prefix is the number of bits to create a mask for.
* @return a network mask, in network byte order.
*/
unsigned int prefix2mask(int prefix) {
if (prefix) {
return htonl(~((1 << (32 - prefix)) - 1));
} else {
return htonl(0);
}
}
/**
* @brief calculates the number of bits masked off by a netmask.
* This function calculates the significant bits in an IP address as specified by
* a netmask. See also @ref prefix2mask.
* @param mask is the netmask, specified as an struct in_addr in network byte order.
* @return the number of significant bits.
*/
int mask2prefix(IN_ADDR mask) {
return bit_count(ntohl(mask.s_addr));
}
static int ipv4_mask_to_int(const char *prefix) {
int ret;
IN_ADDR in;
ret = inet_pton(AF_INET, prefix, &in);
if (ret == 0) {
return -1;
}
return mask2prefix(in);
}
/**
* @brief calculate broadcast address given an IP address and a prefix length.
* @param addr an IP address in network byte order.
* @param prefix a prefix length.
* @return the calculated broadcast address for the network, in network byte order.
*/
static IN_ADDR calc_broadcast(IN_ADDR addr, int prefix) {
IN_ADDR mask;
IN_ADDR broadcast;
mask.s_addr = prefix2mask(prefix);
memset(&broadcast, 0, sizeof(broadcast));
broadcast.s_addr = (addr.s_addr & mask.s_addr) | ~mask.s_addr;
return broadcast;
}
/**
* @brief calculates the network address for a specified address and prefix.
* @param addr an IP address, in network byte order
* @param prefix the network prefix
* @return the base address of the network that addr is associated with, in
* network byte order.
*/
static IN_ADDR calc_network(IN_ADDR addr, int prefix) {
IN_ADDR mask;
IN_ADDR network;
mask.s_addr = prefix2mask(prefix);
memset(&network, 0, sizeof(network));
network.s_addr = addr.s_addr & mask.s_addr;
return network;
}
static TCHAR *ipv4_prefix_to_hosts(TCHAR *hosts, unsigned hosts_size, unsigned prefix) {
if (prefix >= 31) {
StringCbPrintf(hosts, hosts_size, TEXT("%s"), p2_table(32 - prefix));
} else {
unsigned int tmp;
tmp = (1 << (32 - prefix)) - 2;
StringCbPrintf(hosts, hosts_size, TEXT("%u"), tmp);
}
return hosts;
}
static int str_to_prefix(int *ipv6, const char *prefixStr, unsigned fix) {
int prefix;
if (!(*ipv6) && strchr(prefixStr, '.')) { /* prefix is 255.x.x.x */
prefix = ipv4_mask_to_int(prefixStr);
} else {
prefix = strtol(prefixStr, nullptr, 10);
}
if (fix && (prefix > 32 && !(*ipv6))) {
*ipv6 = 1;
}
if (prefix < 0 || (((*ipv6) && prefix > 128) || (!(*ipv6) && prefix > 32))) {
return -1;
}
return prefix;
}
static int GetIpV4Info(const TCHAR *pIpStr, int prefix, PIP_INFO pInfo, unsigned int flags) {
IN_ADDR ip, netmask, network, broadcast, minhost, maxhost;
TCHAR namebuf[INET_ADDRSTRLEN + 1];
TCHAR *ipStr = _strdup(pIpStr);
memset(pInfo, 0, sizeof(*pInfo));
if (inet_pton(AF_INET, ipStr, &ip) <= 0) {
SPDLOG_ERROR(TEXT("ipcalc: bad IPv4 address: {0}"), ipStr);
free(ipStr);
return -ERR_UN_SUPPORT;
}
/* Handle CIDR entries such as 172/8 */
if (prefix >= 0) {
auto tmp = const_cast<TCHAR *>(ipStr);
int i;
for (i = 3; i > 0; i--) {
tmp = strchr(tmp, '.');
if (!tmp) {
break;
} else {
tmp++;
}
}
tmp = nullptr;
for (; i > 0; i--) {
if (asprintf(&tmp, "%s.0", ipStr) == -1) {
SPDLOG_ERROR(TEXT("Memory allocation failure"));
free(ipStr);
return -ERR_MALLOC_MEMORY;
}
ipStr = tmp;
}
} else { // assume good old days classful Internet
prefix = 32;
}
if (prefix > 32) {
SPDLOG_ERROR(TEXT("ipcalc: bad IPv4 prefix: {0}"), prefix);
free(ipStr);
return -ERR_UN_SUPPORT;
}
if (inet_ntop(AF_INET, &ip, namebuf, sizeof(namebuf)) == 0) {
SPDLOG_ERROR(TEXT("ipcalc: error calculating the IPv4 network"));
free(ipStr);
return -ERR_UN_SUPPORT;
}
StringCbCopy(pInfo->ip, MAX_IP_LEN, namebuf);
netmask.s_addr = prefix2mask(prefix);
memset(namebuf, '\0', sizeof(namebuf));
if (inet_ntop(AF_INET, &netmask, namebuf, INET_ADDRSTRLEN) == nullptr) {
SPDLOG_ERROR(TEXT("inet_ntop error"));
free(ipStr);
return -ERR_UN_SUPPORT;
}
StringCbCopy(pInfo->netmask, MAX_IP_LEN, namebuf);
pInfo->prefix = prefix;
broadcast = calc_broadcast(ip, prefix);
memset(namebuf, '\0', sizeof(namebuf));
if (inet_ntop(AF_INET, &broadcast, namebuf, INET_ADDRSTRLEN) == nullptr) {
SPDLOG_ERROR(TEXT("inet_ntop error"));
free(ipStr);
return -ERR_UN_SUPPORT;
}
StringCbCopy(pInfo->broadcast, MAX_IP_LEN, namebuf);
network = calc_network(ip, prefix);
memset(namebuf, '\0', sizeof(namebuf));
if (inet_ntop(AF_INET, &network, namebuf, INET_ADDRSTRLEN) == nullptr) {
SPDLOG_ERROR(TEXT("inet_ntop error"));
free(ipStr);
return -ERR_UN_SUPPORT;
}
StringCbCopy(pInfo->network, MAX_IP_LEN, namebuf);
if (prefix < 32) {
memcpy(&minhost, &network, sizeof(minhost));
if (prefix <= 30) {
minhost.s_addr = htonl(ntohl(minhost.s_addr) | 1);
}
if (inet_ntop(AF_INET, &minhost, namebuf, INET_ADDRSTRLEN) == nullptr) {
SPDLOG_ERROR(TEXT("inet_ntop error"));
free(ipStr);
return -ERR_UN_SUPPORT;
}
StringCbCopy(pInfo->hostmin, MAX_IP_LEN, namebuf);
memcpy(&maxhost, &network, sizeof(minhost));
maxhost.s_addr |= ~netmask.s_addr;
if (prefix <= 30) {
maxhost.s_addr = htonl(ntohl(maxhost.s_addr) - 1);
}
if (inet_ntop(AF_INET, &maxhost, namebuf, sizeof(namebuf)) == 0) {
SPDLOG_ERROR(TEXT("ipcalc: error calculating the IPv4 network"));
free(ipStr);
return -ERR_UN_SUPPORT;
}
StringCbCopy(pInfo->hostmax, MAX_IP_LEN, namebuf);
} else {
StringCbCopy(pInfo->hostmin, MAX_IP_LEN, pInfo->network);
StringCbCopy(pInfo->hostmax, MAX_IP_LEN, pInfo->network);
}
ipv4_prefix_to_hosts(pInfo->hosts, sizeof(pInfo->hosts), prefix);
free(ipStr);
return ERR_SUCCESS;
}
/**
* @brief IPv4
* @param[in] pIpStr IPv4 '/' CIDR以及子网掩码 example: 192.168.1.32/24, 192.168.1.32/255.255.255.0
* @param[out] pInfo
* @return 0: 0 @see USER_ERRNO
* - -ERR_INPUT_PARAMS
* - -ERR_UN_SUPPORT
* - -ERR_MALLOC_MEMORY
* - ERR_SUCCESS
*/
int GetIpV4InfoFromCIDR(const TCHAR *pIpStr, PIP_INFO pInfo) {
int ret, prefix, familyIPv6 = 0;
TCHAR *prefixStr;
TCHAR *ipStr = _strdup(pIpStr);
if (pIpStr == nullptr || lstrlen(pIpStr) < MIN_IP_LEN || lstrlen(pIpStr) >= MAX_IP_LEN) {
SPDLOG_ERROR(TEXT("Input pIpStr format error: {}."), pIpStr);
return -ERR_INPUT_PARAMS;
}
if (pInfo == nullptr) {
SPDLOG_ERROR(TEXT("Input pInfo is NULL."));
return -ERR_INPUT_PARAMS;
}
if (strchr(ipStr, '/') != nullptr) {
prefixStr = static_cast<TCHAR *>(strchr(ipStr, '/'));
*prefixStr = '\0'; /* fix up ipStr */
prefixStr++;
} else {
SPDLOG_ERROR(TEXT("Input pIpStr isn't CIDR format: {}."), pIpStr);
free(ipStr);
return -ERR_INPUT_PARAMS;
}
if (strchr(prefixStr, '.') != nullptr) {
prefix = ipv4_mask_to_int(prefixStr);
} else {
prefix = str_to_prefix(&familyIPv6, prefixStr, 0);
}
ret = GetIpV4Info(ipStr, prefix, pInfo, 0);
free(ipStr);
return ret;
}
/**
* @brief IPv4
* @param[in] pIpStr IPv4
* @param[in] pNetmask IPv4子网掩码
* @param[out] pInfo
* @return 0 @see USER_ERRNO
* - -ERR_INPUT_PARAMS
* - -ERR_UN_SUPPORT
* - -ERR_MALLOC_MEMORY
* - ERR_SUCCESS
*/
int GetIpV4InfoFromNetmask(const TCHAR *pIpStr, const TCHAR *pNetmask, PIP_INFO pInfo) {
int prefix;
if (pIpStr == nullptr || lstrlen(pIpStr) < MIN_IP_LEN || lstrlen(pIpStr) >= MAX_IP_LEN) {
SPDLOG_ERROR(TEXT("Input pIpStr format error: {}."), pIpStr);
return -ERR_INPUT_PARAMS;
}
if (pNetmask == nullptr || lstrlen(pNetmask) < MIN_IP_LEN || lstrlen(pNetmask) >= MAX_IP_LEN) {
SPDLOG_ERROR(TEXT("Input pNetmask format error: {}."), pNetmask);
return -ERR_INPUT_PARAMS;
}
if (pInfo == nullptr) {
SPDLOG_ERROR(TEXT("Input pInfo is NULL."));
return -ERR_INPUT_PARAMS;
}
prefix = ipv4_mask_to_int(pNetmask);
return GetIpV4Info(pIpStr, prefix, pInfo, 0);
}
int GetIpV4InfoFromHostname(int family, const char *host, PIP_INFO pInfo) {
addrinfo *res, *rp;
addrinfo hints {};
int err;
static char ipname[64];
void *addr;
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
err = getaddrinfo(host, nullptr, &hints, &res);
if (err != 0) {
return -ERR_INPUT_PARAMS;
}
for (rp = res; rp != nullptr; rp = rp->ai_next) {
if (rp->ai_family == AF_INET) {
addr = (&reinterpret_cast<sockaddr_in *>(rp->ai_addr)->sin_addr);
} else {
addr = (&reinterpret_cast<sockaddr_in6 *>(rp->ai_addr)->sin6_addr);
}
if (inet_ntop(rp->ai_family, addr, ipname, sizeof(ipname)) != nullptr) {
freeaddrinfo(res);
StringCbCopy(pInfo->hostip, MAX_IP_LEN, ipname);
return ERR_SUCCESS;
}
}
freeaddrinfo(res);
return ERR_ITEM_EXISTS;
}