Added support for shortened form ipv6 addresses

supported:
    1.2.3.4
    1:2:3:4:5:6:7:8
    1:2::7:8
    ::

currently not supported:
    1:2:3:4:5:6:1.2.3.4
Christopher Haster 2016-03-13 19:30:30 -05:00 committed by Russ Butler
parent 525c7b361c
commit e762b04f64
1 changed files with 63 additions and 33 deletions

View File

@ -19,7 +19,8 @@
#include <string.h>
#include "mbed.h"
static bool address_is_ipv4(const char *addr)
static bool ipv4_is_valid(const char *addr)
{
int i = 0;
@ -38,7 +39,7 @@ static bool address_is_ipv4(const char *addr)
return true;
}
static bool address_is_ipv6(const char *addr)
static bool ipv6_is_valid(const char *addr)
{
// Check each digit for [0-9a-fA-F:]
for (int i = 0; addr[i]; i++) {
@ -53,20 +54,57 @@ static bool address_is_ipv6(const char *addr)
return true;
}
static void address_to_ipv4(uint8_t *bytes, const char *addr)
static void ipv4_from_address(uint8_t *bytes, const char *addr)
{
sscanf(addr, "%hhd.%hhd.%hhd.%hhd", &bytes[0], &bytes[1], &bytes[2], &bytes[3]);
sscanf(addr, "%hhu.%hhu.%hhu.%hhu", &bytes[0], &bytes[1], &bytes[2], &bytes[3]);
}
static void address_to_ipv6(uint8_t *bytes, const char *addr)
{
// TODO support short form (::1, 2001::ffee:100a)
// Use a more intellegent algorithm
uint16_t shorts[NSAPI_IPv6_BYTES/2];
sscanf(addr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
&shorts[0], &shorts[1], &shorts[2], &shorts[3],
&shorts[4], &shorts[5], &shorts[6], &shorts[7]);
static int ipv6_scan_chunk(uint16_t *shorts, const char *chunk) {
int count = 0;
int i = 0;
for (; count < NSAPI_IPv6_BYTES/2; count++) {
int scanned = sscanf(&chunk[i], "%hx", &shorts[count]);
if (scanned < 1) {
return count;
}
for (; chunk[i] != ':'; i++) {
if (!chunk[i]) {
return count+1;
}
}
i++;
}
return count;
}
static void ipv6_from_address(uint8_t *bytes, const char *addr)
{
// Start with zeroed address
uint16_t shorts[NSAPI_IPv6_BYTES/2];
memset(shorts, 0, sizeof shorts);
int suffix = 0;
// Find double colons and scan suffix
for (int i = 0; addr[i]; i++) {
if (addr[i] == ':' && addr[i+1] == ':') {
suffix = ipv6_scan_chunk(shorts, &addr[i+2]);
break;
}
}
// Move suffix to end
memmove(&shorts[NSAPI_IPv6_BYTES/2-suffix], &shorts[0],
suffix*sizeof(uint16_t));
// Scan prefix
ipv6_scan_chunk(shorts, &addr[0]);
// Flip bytes
for (int i = 0; i < NSAPI_IPv6_BYTES/2; i++) {
bytes[2*i+0] = (uint8_t)(shorts[i] >> 8);
bytes[2*i+1] = (uint8_t)(shorts[i] >> 0);
@ -80,31 +118,23 @@ static void ipv4_to_address(char *addr, const uint8_t *bytes)
static void ipv6_to_address(char *addr, const uint8_t *bytes)
{
int pos = 0;
for (int i = 0; i < NSAPI_IPv6_BYTES; i+=2) {
int ret = sprintf(&addr[pos], "%02x%02x", bytes[i], bytes[i+1]);
if (ret < 0) {
memset(addr, 0, NSAPI_IPv6_SIZE + 1);
return;
}
pos += ret;
addr[pos++] = ':';
for (int i = 0; i < NSAPI_IPv6_BYTES/2; i++) {
sprintf(&addr[5*i], "%02x%02x", bytes[2*i], bytes[2*i+1]);
addr[5*i+4] = ':';
}
pos -= 1; // Overwrite last ':'
addr[pos++] = '\0';
MBED_ASSERT(NSAPI_IPv6_SIZE == pos);
addr[NSAPI_IPv6_SIZE-1] = '\0';
}
SocketAddress::SocketAddress(NetworkInterface *iface, const char *host, uint16_t port)
{
// Check for valid IP addresses
if (host && address_is_ipv4(host)) {
if (host && ipv4_is_valid(host)) {
_ip_version = NSAPI_IPv4;
address_to_ipv4(_ip_bytes, host);
} else if (host && address_is_ipv6(host)) {
ipv4_from_address(_ip_bytes, host);
} else if (host && ipv6_is_valid(host)) {
_ip_version = NSAPI_IPv6;
address_to_ipv4(_ip_bytes, host);
ipv4_from_address(_ip_bytes, host);
} else {
// DNS lookup
int err = iface->gethostbyname(this, host);
@ -140,12 +170,12 @@ void SocketAddress::set_ip_address(const char *addr)
{
_ip_address[0] = '\0';
if (addr && address_is_ipv4(addr)) {
if (addr && ipv4_is_valid(addr)) {
_ip_version = NSAPI_IPv4;
address_to_ipv4(_ip_bytes, addr);
} else if (addr && address_is_ipv6(addr)) {
ipv4_from_address(_ip_bytes, addr);
} else if (addr && ipv6_is_valid(addr)) {
_ip_version = NSAPI_IPv6;
address_to_ipv6(_ip_bytes, addr);
ipv6_from_address(_ip_bytes, addr);
} else {
_ip_version = NSAPI_IPv4;
memset(_ip_bytes, 0, NSAPI_IPv4_BYTES);