17#include <netinet/in.h>
28 if (strlen(ip) == 0) {
33 if (strlen(ip) > 256) {
43 int has_leading_zero = 0;
45 for (
const char *p = ip; *p !=
'\0'; p++) {
48 if (digits == 0 || value > 255 || (has_leading_zero && digits > 1))
54 }
else if (*p >=
'0' && *p <=
'9') {
56 if (digits == 0 && *p ==
'0') {
60 value = value * 10 + (*p -
'0');
62 if (digits > 3 || value > 255)
70 if (digits == 0 || value > 255 || (has_leading_zero && digits > 1))
83 if (strlen(ip) == 0) {
88 if (strlen(ip) > 256) {
96 if (strcmp(ip,
"::") == 0)
100 if (strchr(ip,
':') == NULL)
104 size_t len = strlen(ip);
105 if (len > 0 && ip[0] ==
':' && (len < 2 || ip[1] !=
':'))
107 if (len > 0 && ip[len - 1] ==
':' && (len < 2 || ip[len - 2] !=
':'))
111 int has_ipv4_mapped = (strchr(ip,
'.') != NULL);
115 int double_colon_count = 0;
117 int segment_count = 0;
118 int last_was_colon = 0;
122 if (last_was_colon) {
123 double_colon_count++;
124 if (double_colon_count > 1)
127 if (segment_len > 0) {
134 }
else if (*p ==
'.') {
138 }
else if ((*p >=
'0' && *p <=
'9') || (*p >=
'a' && *p <=
'f') || (*p >=
'A' && *p <=
'F')) {
148 if (segment_len > 0) {
149 if (!has_ipv4_mapped && segment_len > 4)
156 if (!has_ipv4_mapped) {
157 if (double_colon_count == 0 && segment_count != 8)
159 if (segment_count > 8)
168 if (!input || !output || output_size == 0)
171 size_t input_len = strlen(input);
175 const char *start = input;
176 const char *end = input + input_len;
180 if (input_len < 2 || *(end - 1) !=
']')
186 if (start < end && *start ==
'[')
188 }
else if (*(end - 1) ==
']') {
193 size_t len = (size_t)(end - start);
196 if (len >= output_size)
199 memcpy(output, start, len);
206 if (!addr || !output || output_size == 0) {
210 const void *ip_ptr = NULL;
212 if (family == AF_INET) {
213 const struct sockaddr_in *addr_in = (
const struct sockaddr_in *)addr;
214 ip_ptr = &addr_in->sin_addr;
215 }
else if (family == AF_INET6) {
216 const struct sockaddr_in6 *addr_in6 = (
const struct sockaddr_in6 *)addr;
217 ip_ptr = &addr_in6->sin6_addr;
222 if (inet_ntop(family, ip_ptr, output, (socklen_t)output_size) == NULL) {
231 if (!ip || !output || output_size == 0) {
235 if (strlen(ip) == 0) {
239 if (strlen(ip) > 256) {
246 if (strchr(ip,
':') != NULL) {
248 size_t written =
SAFE_SNPRINTF(output, output_size,
"[%s]:%u", ip, port);
249 if (written >= output_size) {
254 size_t written =
SAFE_SNPRINTF(output, output_size,
"%s:%u", ip, port);
255 if (written >= output_size) {
265static int is_valid_hostname(
const char *hostname) {
266 if (!hostname || strlen(hostname) == 0 || strlen(hostname) > 253) {
271 size_t len = strlen(hostname);
272 if (hostname[0] ==
'-' || hostname[0] ==
'.' || hostname[len - 1] ==
'-' || hostname[len - 1] ==
'.') {
278 for (
size_t i = 0; i < len; i++) {
279 char c = hostname[i];
282 if (label_len == 0 || label_len > 63) {
286 }
else if ((c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9') || c ==
'-') {
288 if (label_len > 63) {
297 if (label_len == 0 || label_len > 63) {
306 if (!input || !ip_output || !port_output || ip_output_size == 0) {
311 if (strlen(input) == 0) {
316 if (strlen(input) > 256) {
317 char input_buffer[256];
318 SAFE_STRNCPY(input_buffer, input,
sizeof(input_buffer));
324 if (input[0] ==
'[') {
326 const char *bracket_end = strchr(input,
']');
331 size_t ip_len = (size_t)(bracket_end - input - 1);
332 if (ip_len >= ip_output_size)
335 memcpy(ip_output, input + 1, ip_len);
336 ip_output[ip_len] =
'\0';
339 if (bracket_end[1] ==
':') {
350 const char *colon = strrchr(input,
':');
355 size_t ip_len = (size_t)(colon - input);
356 if (ip_len >= ip_output_size)
359 memcpy(ip_output, input, ip_len);
360 ip_output[ip_len] =
'\0';
364 if (strchr(ip_output,
':') != NULL)
381 if (!input || !address_output || !port_output || address_output_size == 0) {
386 size_t input_len = strlen(input);
387 if (input_len == 0) {
392 if (input_len > 256) {
399 if (input[0] ==
'[') {
400 const char *bracket_end = strchr(input,
']');
407 size_t addr_len = (size_t)(bracket_end - input - 1);
412 if (addr_len >= address_output_size) {
417 memcpy(address_output, input + 1, addr_len);
418 address_output[addr_len] =
'\0';
427 if (bracket_end[1] ==
'\0') {
429 *port_output = default_port;
430 }
else if (bracket_end[1] ==
':') {
447 if (strlen(input) >= address_output_size) {
451 SAFE_STRNCPY(address_output, input, address_output_size);
452 *port_output = default_port;
458 const char *last_colon = strrchr(input,
':');
460 if (last_colon == NULL) {
462 if (strlen(input) >= address_output_size) {
473 SAFE_STRNCPY(address_output, input, address_output_size);
474 *port_output = default_port;
479 size_t addr_len = (size_t)(last_colon - input);
484 if (addr_len >= address_output_size) {
489 memcpy(address_output, input, addr_len);
490 address_output[addr_len] =
'\0';
493 if (strchr(address_output,
':') != NULL) {
495 address_output, last_colon + 1, input);
500 if (!
is_valid_ipv4(address_output) && !is_valid_hostname(address_output)) {
#define SAFE_STRNCPY(dst, src, size)
#define SAFE_SNPRINTF(buffer, buffer_size,...)
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
asciichat_error_t format_ip_with_port(const char *ip, uint16_t port, char *output, size_t output_size)
Format IP address with port number.
int is_valid_ipv4(const char *ip)
Check if a string is a valid IPv4 address.
int parse_ip_with_port(const char *input, char *ip_output, size_t ip_output_size, uint16_t *port_output)
Parse IP address and port from string.
int is_valid_ipv6(const char *ip)
Check if a string is a valid IPv6 address.
int parse_address_with_optional_port(const char *input, char *address_output, size_t address_output_size, uint16_t *port_output, uint16_t default_port)
Parse address with optional port from string.
int parse_ipv6_address(const char *input, char *output, size_t output_size)
Parse IPv6 address, removing brackets if present.
asciichat_error_t format_ip_address(int family, const struct sockaddr *addr, char *output, size_t output_size)
Format IP address from socket address structure.
🌍 IP Address Parsing and Formatting Utilities
asciichat_error_t parse_port(const char *str, uint16_t *out_port)
Parse port number (1-65535) from string.
🔤 String Manipulation and Shell Escaping Utilities