RosettaCodeData/Task/Parse-an-IP-Address/C/parse-an-ip-address-3.c
2023-07-01 13:44:08 -04:00

120 lines
3.4 KiB
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;
}