You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

130 lines
2.9 KiB
C

// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file sys/ip_common.c
*
* Cross-system Internet Protocol functionality implementation.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#include "sys/ip.h"
#include "sys/endian.h"
#include "numlib.h"
#include <string.h>
char *Ipv4_AdrToString(const Ipv4adr *adr, char *dest)
{
char *ptr = dest;
ptr = Utoa(adr->bytes[0], ptr); *ptr++ = '.';
ptr = Utoa(adr->bytes[1], ptr); *ptr++ = '.';
ptr = Utoa(adr->bytes[2], ptr); *ptr++ = '.';
ptr = Utoa(adr->bytes[3], ptr);
return ptr;
}
char *Ipv6_AdrToString(const Ipv6adr *adr, char *dest)
{
char *ptr = dest;
if (IS_IPV6_V4MAPPED(adr)) {
// IPv4 mapped to IPv6 (starts with 0:0:0:0:0:0:ffff)
memcpy(ptr, "::ffff:", 7);
ptr += 7;
ptr = Utoa(adr->bytes[0], ptr); *ptr++ = '.';
ptr = Utoa(adr->bytes[1], ptr); *ptr++ = '.';
ptr = Utoa(adr->bytes[2], ptr); *ptr++ = '.';
ptr = Utoa(adr->bytes[3], ptr);
} else {
// Regular IPv6
ptr = Xtoa(beswap16(adr->words[0]), ptr); *ptr++ = ':';
ptr = Xtoa(beswap16(adr->words[1]), ptr); *ptr++ = ':';
ptr = Xtoa(beswap16(adr->words[2]), ptr); *ptr++ = ':';
ptr = Xtoa(beswap16(adr->words[3]), ptr); *ptr++ = ':';
ptr = Xtoa(beswap16(adr->words[4]), ptr); *ptr++ = ':';
ptr = Xtoa(beswap16(adr->words[5]), ptr); *ptr++ = ':';
ptr = Xtoa(beswap16(adr->words[6]), ptr); *ptr++ = ':';
ptr = Xtoa(beswap16(adr->words[7]), ptr);
// Replace longest /(^0|:)[:0]{2,}/ with "::"
unsigned i, j, n, best, max;
for (i = best = 0, max = 2; dest[i] != '\0'; i++) {
if (i > 0 && dest[i] != ':')
continue;
// Count the number of consecutive 0 in this substring
n = 0;
for (j = i; dest[j] != '\0'; j++) {
if (dest[j] != ':' && dest[j] != '0')
break;
n++;
}
// Store the position if this is the best we've seen so far
if (n > max) {
best = i;
max = n;
}
}
ptr = dest + i;
if (max > 3) {
// Compress the string
dest[best] = ':';
dest[best + 1] = ':';
unsigned len = i - best - max;
memmove(&dest[best + 2], &dest[best + max], len + 1);
ptr = &dest[best + 2 + len];
}
}
return ptr;
}
char *Ip_AdrToString(const Ipadr *addr, char *dest)
{
switch (addr->family) {
case IP4: return Ipv4_AdrToString(&addr->v4, dest);
case IP6: return Ipv6_AdrToString(&addr->v6, dest);
default: return NULL;
}
}
static Boolean Ip_IsStringIpv4(const char *s)
{
// A well formed IPv4 address contains one dot in chars [1,3]
// x.
// xx.
// xxx.
for (int i = 1; i < 4; i++) {
if (s[i-1] == '\0')
return FALSE;
if (s[i] == '.')
return TRUE;
}
return FALSE;
}
Judgement Ip_StringToAdr(const char *s, Ipadr *dest)
{
if (Ip_IsStringIpv4(s)) {
if (Ipv4_StringToAdr(s, &dest->v4) != OK)
return NG;
dest->family = IP4;
} else {
if (Ipv6_StringToAdr(s, &dest->v6) != OK)
return NG;
dest->family = IP6;
}
return OK;
}