9a9b91c940
It's finally done.. Signed-off-by: kaguya <vpshinomiya@protonmail.com>
308 lines
7.4 KiB
C++
308 lines
7.4 KiB
C++
|
|
#include <arpa/inet.h>
|
|
#include <bits/ensure.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <mlibc/bitutil.hpp>
|
|
#include <mlibc/debug.hpp>
|
|
|
|
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
|
|
const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
|
|
|
|
uint32_t htonl(uint32_t x) {
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
return mlibc::bit_util<uint32_t>::byteswap(x);
|
|
#else
|
|
return x;
|
|
#endif
|
|
}
|
|
uint16_t htons(uint16_t x) {
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
return mlibc::bit_util<uint16_t>::byteswap(x);
|
|
#else
|
|
return x;
|
|
#endif
|
|
}
|
|
uint32_t ntohl(uint32_t x) {
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
return mlibc::bit_util<uint32_t>::byteswap(x);
|
|
#else
|
|
return x;
|
|
#endif
|
|
}
|
|
uint16_t ntohs(uint16_t x) {
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
return mlibc::bit_util<uint16_t>::byteswap(x);
|
|
#else
|
|
return x;
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// IPv4 address manipulation.
|
|
// ----------------------------------------------------------------------------
|
|
in_addr_t inet_addr(const char *p) {
|
|
struct in_addr a;
|
|
if(!inet_aton(p, &a))
|
|
return -1;
|
|
return a.s_addr;
|
|
}
|
|
|
|
in_addr_t inet_network(const char *) {
|
|
__ensure(!"Not implemented");
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
char *inet_ntoa(struct in_addr addr) {
|
|
// string: xxx.yyy.zzz.aaa
|
|
// 4 * 3 + 3 + 1 = 12 + 4 = 16
|
|
thread_local static char buffer[16];
|
|
uint32_t proper = htonl(addr.s_addr);
|
|
snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d",
|
|
(proper >> 24) & 0xff, ((proper >> 16) & 0xff),
|
|
(proper >> 8) & 0xff, proper & 0xff);
|
|
return buffer;
|
|
}
|
|
int inet_aton(const char *string, struct in_addr *dest) {
|
|
int array[4];
|
|
int i = 0;
|
|
char *end;
|
|
|
|
for (; i < 4; i++) {
|
|
array[i] = strtoul(string, &end, 0);
|
|
if (*end && *end != '.')
|
|
return 0;
|
|
if (!*end)
|
|
break;
|
|
string = end + 1;
|
|
}
|
|
|
|
switch (i) {
|
|
case 0:
|
|
dest->s_addr = htonl(array[0]);
|
|
break;
|
|
case 1:
|
|
if (array[0] > 255 || array[1] > 0xffffff)
|
|
return 0;
|
|
dest->s_addr = htonl((array[0] << 24) | array[1]);
|
|
break;
|
|
case 2:
|
|
if (array[0] > 255 || array[1] > 255 ||
|
|
array[2] > 0xffff)
|
|
return 0;
|
|
dest->s_addr = htonl((array[0] << 24) | (array[1] << 16) |
|
|
array[2]);
|
|
break;
|
|
case 3:
|
|
if (array[0] > 255 || array[1] > 255 ||
|
|
array[2] > 255 || array[3] > 255)
|
|
return 0;
|
|
dest->s_addr = htonl((array[0] << 24) | (array[1] << 16) |
|
|
(array[2] << 8) | array[3]);
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Generic IP address manipulation.
|
|
// ----------------------------------------------------------------------------
|
|
const char *inet_ntop(int af, const void *__restrict src, char *__restrict dst,
|
|
socklen_t size) {
|
|
switch (af) {
|
|
case AF_INET: {
|
|
auto source = reinterpret_cast<const struct in_addr*>(src);
|
|
uint32_t addr = ntohl(source->s_addr);
|
|
if (snprintf(dst, size, "%d.%d.%d.%d",
|
|
(addr >> 24) & 0xff,
|
|
(addr >> 16) & 0xff,
|
|
(addr >> 8) & 0xff,
|
|
addr & 0xff) < (int)size)
|
|
return dst;
|
|
break;
|
|
}
|
|
case AF_INET6: {
|
|
auto source = reinterpret_cast<const struct in6_addr*>(src);
|
|
size_t cur_zeroes_off = 0;
|
|
size_t cur_zeroes_len = 0;
|
|
size_t max_zeroes_off = 0;
|
|
size_t max_zeroes_len = 0;
|
|
|
|
/* we look for the largest block of zeroed quartet(s) */
|
|
for(size_t i = 0; i < 8; i++) {
|
|
auto ptr = source->s6_addr + (i * 2);
|
|
if(!ptr[0] && !ptr[1]) {
|
|
cur_zeroes_len++;
|
|
if(max_zeroes_len < cur_zeroes_len) {
|
|
max_zeroes_len = cur_zeroes_len;
|
|
max_zeroes_off = cur_zeroes_off;
|
|
}
|
|
} else {
|
|
/* advance the offset to the next quartet to check */
|
|
cur_zeroes_len = 0;
|
|
cur_zeroes_off = i + 1;
|
|
}
|
|
}
|
|
|
|
size_t off = 0;
|
|
for(size_t i = 0; i < 8; i++) {
|
|
auto ptr = source->s6_addr + (i * 2);
|
|
|
|
/* if we are at the beginning of the largest block of zeroed quartets, place "::" */
|
|
if(i == max_zeroes_off && max_zeroes_len >= 2) {
|
|
if(off < size) {
|
|
dst[off++] = ':';
|
|
}
|
|
if(off < size) {
|
|
dst[off++] = ':';
|
|
}
|
|
i += max_zeroes_len - 1;
|
|
|
|
continue;
|
|
}
|
|
|
|
/* place a colon if we're not at the beginning of the string and it is not already there */
|
|
if(off && dst[off - 1] != ':') {
|
|
if(off < size) {
|
|
dst[off++] = ':';
|
|
}
|
|
}
|
|
|
|
off += snprintf(dst + off, size - off, "%x", ptr[0] << 8 | ptr[1]);
|
|
}
|
|
|
|
dst[off] = 0;
|
|
|
|
return dst;
|
|
}
|
|
default:
|
|
errno = EAFNOSUPPORT;
|
|
return nullptr;
|
|
}
|
|
|
|
errno = ENOSPC;
|
|
return nullptr;
|
|
}
|
|
|
|
int inet_pton(int af, const char *__restrict src, void *__restrict dst) {
|
|
switch (af) {
|
|
case AF_INET: {
|
|
uint8_t array[4] = {};
|
|
for (int i = 0; i < 4; i++) {
|
|
char *end;
|
|
long int value = strtol(src, &end, 10);
|
|
if (value > 255)
|
|
return 0;
|
|
if (*end != '\0' && *end != '.')
|
|
return 0;
|
|
src = end + 1;
|
|
array[i] = value;
|
|
}
|
|
auto addr = reinterpret_cast<struct in_addr*>(dst);
|
|
uint32_t ip = (array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3];
|
|
addr->s_addr = htonl(ip);
|
|
break;
|
|
}
|
|
case AF_INET6: {
|
|
size_t i = 0;
|
|
uint16_t array[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
|
frg::optional<size_t> doubleColonOffset = frg::null_opt;
|
|
|
|
auto reservedRange = [&]() -> bool {
|
|
if(i && ((doubleColonOffset && doubleColonOffset.value()) || !doubleColonOffset)) {
|
|
return (ntohs(array[0]) >> 8) == 0;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
for(; i < 8; i++) {
|
|
char *end = nullptr;
|
|
auto value = strtol(src, &end, 16);
|
|
|
|
if (value > UINT16_MAX)
|
|
return 0;
|
|
if(end[0] != '\0' && end[0] != ':' && end[0] != '.')
|
|
return 0;
|
|
|
|
if(end[0] == '.' && reservedRange() && i < 7) {
|
|
char *ipv4end = nullptr;
|
|
auto value0 = strtol(src, &ipv4end, 10);
|
|
if(ipv4end[0] != '.' || value0 > UINT8_MAX || src == ipv4end)
|
|
return 0;
|
|
src = ipv4end + 1;
|
|
auto value1 = strtol(src, &ipv4end, 10);
|
|
if(ipv4end[0] != '.' || value1 > UINT8_MAX || src == ipv4end)
|
|
return 0;
|
|
array[i++] = htons((value0 << 8) | value1);
|
|
src = ipv4end + 1;
|
|
|
|
auto value2 = strtol(src, &ipv4end, 10);
|
|
if(ipv4end[0] != '.' || value2 > UINT8_MAX || src == ipv4end)
|
|
return 0;
|
|
src = ipv4end + 1;
|
|
auto value3 = strtol(src, &ipv4end, 10);
|
|
if(value3 > UINT8_MAX || src == ipv4end)
|
|
return 0;
|
|
array[i] = htons((value2 << 8) | value3);
|
|
break;
|
|
} else if(end[0] == ':' && end[1] == ':') {
|
|
if(doubleColonOffset)
|
|
return 0;
|
|
doubleColonOffset = i + 1;
|
|
src = end + 2;
|
|
} else if(end[0] == ':' || end[0] == '\0') {
|
|
src = end + 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
array[i] = htons(value);
|
|
|
|
if(end[0] == '\0')
|
|
break;
|
|
}
|
|
|
|
auto addr = reinterpret_cast<struct in6_addr *>(dst);
|
|
|
|
if(doubleColonOffset) {
|
|
size_t suffix = i - doubleColonOffset.value() + 1;
|
|
memset(addr->s6_addr, 0, 16);
|
|
|
|
for(size_t j = 0; j < doubleColonOffset.value(); j++) {
|
|
addr->s6_addr16[j] = array[j];
|
|
}
|
|
|
|
for(size_t j = 0; j < suffix; j++) {
|
|
addr->s6_addr16[8 - suffix + j] = array[doubleColonOffset.value() + j];
|
|
}
|
|
} else {
|
|
for(size_t j = 0; j < 8; j++) {
|
|
addr->s6_addr16[j] = array[j];
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
errno = EAFNOSUPPORT;
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
struct in_addr inet_makeaddr(in_addr_t, in_addr_t) {
|
|
__ensure(!"Not implemented");
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
in_addr_t inet_netof(struct in_addr) {
|
|
__ensure(!"Not implemented");
|
|
__builtin_unreachable();
|
|
}
|