Parse an IP Address: Difference between revisions
Content added Content deleted
m (Reformatted to reduce line count) |
(Added C/POSIX solution) |
||
Line 464: | Line 464: | ||
addr: 00000000000000000000000000000000 |
addr: 00000000000000000000000000000000 |
||
port: 80 |
port: 80 |
||
</pre> |
|||
===POSIX=== |
|||
{{libheader|POSIX}} |
|||
The task is a bit easier on POSIX platforms where we can use the function inet_pton |
|||
to parse IP addresses. This approach will also work on Microsoft Windows with some |
|||
minor changes. |
|||
<lang c>#include <arpa/inet.h> |
|||
#include <stdbool.h> |
|||
#include <stdio.h> |
|||
#include <stdint.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
typedef struct ip_address_tag { |
|||
union { |
|||
uint8_t address_v6[16]; |
|||
uint32_t address_v4; |
|||
} address; |
|||
uint16_t family; |
|||
uint16_t port; |
|||
} ip_address_t; |
|||
bool parse_ipv4_address(const char* input, ip_address_t* result) { |
|||
struct in_addr addr; |
|||
if (inet_pton(AF_INET, input, &addr) == 1) { |
|||
result->family = AF_INET; |
|||
result->address.address_v4 = ntohl(addr.s_addr); |
|||
result->port = 0; |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
bool parse_ipv6_address(const char* input, ip_address_t* result) { |
|||
struct in6_addr addr; |
|||
if (inet_pton(AF_INET6, input, &addr) == 1) { |
|||
result->family = AF_INET6; |
|||
memcpy(result->address.address_v6, addr.s6_addr, 16); |
|||
result->port = 0; |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
uint16_t parse_port_number(const char* str) { |
|||
char* eptr; |
|||
unsigned long port = strtoul(str, &eptr, 10); |
|||
if (port > 0 && *eptr == '\0' && port <= UINT16_MAX) |
|||
return (uint16_t)port; |
|||
return 0; |
|||
} |
|||
// |
|||
// Parse an IP address and port from the given input string. |
|||
// Returns false if the input is not valid. |
|||
// |
|||
// Valid formats are: |
|||
// [ipv6_address]:port |
|||
// ipv4_address:port |
|||
// ipv4_address |
|||
// ipv6_address |
|||
// |
|||
bool parse_address(const char* input, ip_address_t* result) { |
|||
char* ptr = strrchr(input, ':'); |
|||
if (ptr != NULL && ptr > input) { |
|||
uint16_t port = parse_port_number(ptr + 1); |
|||
if (port > 0) { |
|||
bool success = false; |
|||
char* copy = strdup(input); |
|||
if (copy == NULL) |
|||
return false; |
|||
int index = ptr - input; |
|||
copy[index] = '\0'; |
|||
if (copy[index - 1] == ']' && copy[0] == '[') { |
|||
copy[index - 1] = '\0'; |
|||
if (parse_ipv6_address(copy + 1, result)) |
|||
success = true; |
|||
} else if (parse_ipv4_address(copy, result)) { |
|||
success = true; |
|||
} |
|||
free(copy); |
|||
if (success) { |
|||
result->port = port; |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
return parse_ipv6_address(input, result) |
|||
|| parse_ipv4_address(input, result); |
|||
} |
|||
void test_parse_address(const char* input) { |
|||
printf("input: %s\n", input); |
|||
ip_address_t result; |
|||
if (parse_address(input, &result)) { |
|||
printf("address family: %s\n", |
|||
result.family == AF_INET ? "IPv4" : "IPv6"); |
|||
if (result.family == AF_INET) |
|||
printf("address: %X", result.address.address_v4); |
|||
else if (result.family == AF_INET6) { |
|||
printf("address: "); |
|||
for (int i = 0; i < 16; ++i) |
|||
printf("%02X", (unsigned int)result.address.address_v6[i]); |
|||
} |
|||
printf("\n"); |
|||
if (result.port > 0) |
|||
printf("port: %hu\n", result.port); |
|||
else |
|||
printf("port not specified\n"); |
|||
} else { |
|||
printf("Parsing failed.\n"); |
|||
} |
|||
printf("\n"); |
|||
} |
|||
int main() { |
|||
test_parse_address("127.0.0.1"); |
|||
test_parse_address("127.0.0.1:80"); |
|||
test_parse_address("::ffff:127.0.0.1"); |
|||
test_parse_address("::1"); |
|||
test_parse_address("[::1]:80"); |
|||
test_parse_address("1::80"); |
|||
test_parse_address("2605:2700:0:3::4713:93e3"); |
|||
test_parse_address("[2605:2700:0:3::4713:93e3]:80"); |
|||
return 0; |
|||
}</lang> |
|||
{{out}} |
|||
<pre> |
|||
input: 127.0.0.1 |
|||
address family: IPv4 |
|||
address: 7F000001 |
|||
port not specified |
|||
input: 127.0.0.1:80 |
|||
address family: IPv4 |
|||
address: 7F000001 |
|||
port: 80 |
|||
input: ::ffff:127.0.0.1 |
|||
address family: IPv6 |
|||
address: 00000000000000000000FFFF7F000001 |
|||
port not specified |
|||
input: ::1 |
|||
address family: IPv6 |
|||
address: 00000000000000000000000000000001 |
|||
port not specified |
|||
input: [::1]:80 |
|||
address family: IPv6 |
|||
address: 00000000000000000000000000000001 |
|||
port: 80 |
|||
input: 1::80 |
|||
address family: IPv6 |
|||
address: 00010000000000000000000000000080 |
|||
port not specified |
|||
input: 2605:2700:0:3::4713:93e3 |
|||
address family: IPv6 |
|||
address: 260527000000000300000000471393E3 |
|||
port not specified |
|||
input: [2605:2700:0:3::4713:93e3]:80 |
|||
address family: IPv6 |
|||
address: 260527000000000300000000471393E3 |
|||
port: 80 |
|||
</pre> |
</pre> |
||