From 185a9fa221753d9e7add96c8f7d5acb1d7cf5dfc Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sun, 26 Jul 2020 00:06:09 +0200 Subject: [PATCH] Support IPv6 shorthand and IPv4-mapped addresses in inet_{pton,ntop}(3). --- libc/arpa/inet/inet_ntop.c | 59 ++++++++++++++++++++++++++++-------- libc/arpa/inet/inet_pton.c | 61 ++++++++++++++++++++++++++++++++++---- 2 files changed, 103 insertions(+), 17 deletions(-) diff --git a/libc/arpa/inet/inet_ntop.c b/libc/arpa/inet/inet_ntop.c index 0197eb1d..7d1dacd5 100644 --- a/libc/arpa/inet/inet_ntop.c +++ b/libc/arpa/inet/inet_ntop.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Jonas 'Sortie' Termansen. + * Copyright (c) 2016, 2020 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,8 +21,8 @@ #include #include -#include #include +#include const char* inet_ntop(int af, const void* restrict src, @@ -42,18 +42,53 @@ const char* inet_ntop(int af, } else if ( af == AF_INET6 ) { - // TODO: Support for :: syntax. - // TODO: Support for x:x:x:x:x:x:d.d.d.d syntax. + // TODO: When should x:x:x:x:x:x:d.d.d.d notation be used? const unsigned char* ip = (const unsigned char*) src; - int len = snprintf(dst, size, "%x:%x:%x:%x:%x:%x:%x:%x", - ip[0] << 8 | ip[1], ip[2] << 8 | ip[3], - ip[4] << 8 | ip[5], ip[6] << 8 | ip[7], - ip[8] << 8 | ip[9], ip[10] << 8 | ip[11], - ip[12] << 8 | ip[13], ip[14] << 8 | ip[15]); - if ( len < 0 ) - return NULL; - if ( size <= (size_t) len ) + size_t longest_zeroes_offset = 0; + size_t longest_zeroes_length = 0; + size_t current_zeroes_offset = 0; + size_t current_zeroes_length = 0; + for ( size_t i = 0; i < 8; i++ ) + { + const unsigned char* data = ip + i * 2; + if ( !data[0] && !data[1] ) + { + current_zeroes_length++; + if ( longest_zeroes_length < current_zeroes_length ) + { + longest_zeroes_offset = current_zeroes_offset; + longest_zeroes_length = current_zeroes_length; + } + } + else + { + current_zeroes_offset = i + 1; + current_zeroes_length = 0; + } + } + char buffer[INET6_ADDRSTRLEN]; + size_t offset = 0; + for ( size_t i = 0; i < 8; i++ ) + { + const unsigned char* data = ip + i * 2; + if ( i == longest_zeroes_offset && 2 <= longest_zeroes_length ) + { + buffer[offset++] = ':'; + buffer[offset++] = ':'; + i += longest_zeroes_length - 1; + } + else + { + if ( offset && buffer[offset - 1] != ':' ) + buffer[offset++] = ':'; + offset += snprintf(buffer + offset, sizeof(buffer) - offset, + "%x", data[0] << 8 | data[1]); + } + } + buffer[offset] = '\0'; + if ( size <= (size_t) offset ) return errno = ENOSPC, (const char*) NULL; + memcpy(dst, buffer, offset + 1); return dst; } else diff --git a/libc/arpa/inet/inet_pton.c b/libc/arpa/inet/inet_pton.c index 1751af0f..73fb2d21 100644 --- a/libc/arpa/inet/inet_pton.c +++ b/libc/arpa/inet/inet_pton.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Jonas 'Sortie' Termansen. + * Copyright (c) 2016, 2020 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -61,15 +61,55 @@ int inet_pton(int af, const char* restrict src, void* restrict dst) { unsigned char ip[16]; size_t index = 0; - // TODO: Support for :: syntax. - // TODO: Support for x:x:x:x:x:x:d.d.d.d syntax. - for ( int i = 0; i < 8; i++ ) + int compressed_at = -1; + int i; + for ( i = 0; i < 8; i++ ) { - if ( i && src[index++] != ':' ) + if ( compressed_at == -1 && + src[index + 0] == ':' && + src[index + 1] == ':' ) + { + index += 2; + compressed_at = i; + } + else if ( !src[index] ) + break; + else if ( i && src[index++] != ':' ) return 0; int num = 0; for ( int j = 0; j < 4; j++ ) { + if ( src[index] == '.' && + ((compressed_at == -1 && i == 6) || + (0 < compressed_at && i <= 6)) ) + { + index -= j; + for ( int n = 0; n < 4; n++ ) + { + if ( n && src[index++] != '.' ) + return 0; + num = 0; + for ( int m = 0; m < 3; m++ ) + { + if ( !m && src[index] == '0' ) + { + index++; + break; + } + if ( '0' <= src[index] && src[index] <= '9' ) + num = num * 10 + src[index++] - '0'; + else if ( !m ) + return 0; + else + break; + } + if ( 255 < num ) + return 0; + ip[2 * i + n] = num; + } + i += 2; + goto done; + } int dgt; if ( '0' <= src[index] && src[index] <= '9' ) dgt = src[index] - '0'; @@ -89,8 +129,19 @@ int inet_pton(int af, const char* restrict src, void* restrict dst) ip[2 * i + 0] = num >> 8 & 0xFF; ip[2 * i + 1] = num >> 0 & 0xFF; } +done: if ( src[index] ) return 0; + if ( 0 <= compressed_at ) + { + if ( i == 8 ) + return 0; + memmove(ip + 16 - 2 * (i - compressed_at), ip + 2 * compressed_at, + (i - compressed_at) * 2); + memset(ip + 2 * compressed_at, 0, (8 - i) * 2); + } + else if ( i < 8 ) + return 0; memcpy(dst, ip, sizeof(ip)); return 1; }