Skip to content

Commit

Permalink
Fixed local ip lookup on macOS GitHub instances.
Browse files Browse the repository at this point in the history
myIpAddress() and myIpAddressEx should be enumerating the network interfaces
and returning the local addresses with getifaddrs/GetAdapterAddresses.

Where as, dnsResolve() and dnsResolveEx() enumerates resolved host
names using getaddrinfo.
  • Loading branch information
nmoinvaz committed Dec 12, 2023
1 parent 75e368c commit 4a4febb
Show file tree
Hide file tree
Showing 5 changed files with 466 additions and 397 deletions.
168 changes: 118 additions & 50 deletions net_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#ifdef _WIN32
# include <winsock2.h>
Expand All @@ -16,104 +17,171 @@
# include <unistd.h>
#endif

#include "net_adapter.h"
#include "util.h"

typedef struct address_list {
int32_t family;
int32_t max_addrs;
char *string;
size_t string_len;
size_t max_string;
} address_list;

// Calculate the max length of the address string
static bool my_ip_address_list_length(void *user_data, net_adapter_s *adapter) {
address_list *list = (address_list *)user_data;

Check warning on line 33 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L32-L33

Added lines #L32 - L33 were not covered by tests
// Use different length depending on the address type
list->max_string += ((list->family == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN);
// Add room for semi-colon separators
list->max_string += 2;
return true;
}

Check warning on line 39 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L37-L39

Added lines #L37 - L39 were not covered by tests

// Copy each localhost address into a string buffer seperated by semi-colons
static bool my_ip_address_list_populate(void *user_data, net_adapter_s *adapter) {
address_list *list = (address_list *)user_data;

Check warning on line 43 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L42-L43

Added lines #L42 - L43 were not covered by tests

if (!list->max_addrs || !adapter->is_connected || !*adapter->ip)
return true;

Check warning on line 46 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L46

Added line #L46 was not covered by tests

#ifdef _WIN32
if (!adapter->is_dhcp_v4)
return true;
#endif

if (list->family == AF_INET || list->family == AF_UNSPEC) {
char ip_str[INET_ADDRSTRLEN] = {0};
inet_ntop(AF_INET, adapter->ip, ip_str, sizeof(ip_str));
strncat(list->string + list->string_len, ip_str, list->max_string - list->string_len - 1);
list->string_len += strlen(ip_str);
}

Check warning on line 58 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L54-L58

Added lines #L54 - L58 were not covered by tests

if (adapter->is_ipv6 && (list->family == AF_INET6 || list->family == AF_UNSPEC)) {
// Append semi-colon separator
if (list->max_string - list->string_len > 1) {
list->string[list->string_len++] = ';';
list->string[list->string_len] = 0;
}

Check warning on line 65 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L63-L65

Added lines #L63 - L65 were not covered by tests

char ipv6_str[INET6_ADDRSTRLEN] = {0};
inet_ntop(AF_INET6, adapter->ipv6, ipv6_str, sizeof(ipv6_str));
strncat(list->string + list->string_len, ipv6_str, list->max_string - list->string_len - 1);
list->string_len += strlen(ipv6_str);
}

Check warning on line 71 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L67-L71

Added lines #L67 - L71 were not covered by tests

list->max_addrs--;

Check warning on line 73 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L73

Added line #L73 was not covered by tests

// Append semi-colon separator
if (list->max_addrs && list->max_string - list->string_len > 1) {
list->string[list->string_len++] = ';';
list->string[list->string_len] = 0;
}
return true;
}

Check warning on line 81 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L77-L81

Added lines #L77 - L81 were not covered by tests

// Enumerate network adapters and get localhost addresses with a filter
static char *my_ip_address_filter(int32_t family, int32_t max_addrs) {
address_list list = {family, max_addrs, NULL, 0, 1};
int32_t err = 0;

Check warning on line 86 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L84-L86

Added lines #L84 - L86 were not covered by tests

if (!net_adapter_enum(&list, my_ip_address_list_length))
return NULL;

Check warning on line 89 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L89

Added line #L89 was not covered by tests

// Allocate buffer for the return string
list.string = (char *)calloc(1, list.max_string);

Check warning on line 92 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L92

Added line #L92 was not covered by tests
if (list.string) {
if (!net_adapter_enum(&list, my_ip_address_list_populate)) {
free(list.string);
list.string = NULL;
}
}

Check warning on line 98 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L95-L98

Added lines #L95 - L98 were not covered by tests

return list.string;
}

Check warning on line 101 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L100-L101

Added lines #L100 - L101 were not covered by tests

// Get local IPv4 address for localhost
char *my_ip_address(void) {
return my_ip_address_filter(AF_INET, 1);

Check warning on line 105 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L104-L105

Added lines #L104 - L105 were not covered by tests
}

// Get local IPv6 and IPv6 addresses for localhost
char *my_ip_address_ex(void) {
return my_ip_address_filter(AF_UNSPEC, UINT8_MAX);

Check warning on line 110 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L109-L110

Added lines #L109 - L110 were not covered by tests
}

// Resolve a host name to its addresses with a filter and custom separator
static char *dns_resolve_filter(const char *host, int32_t family, uint8_t max_addrs, int32_t *error) {
char name[HOST_MAX] = {0};
address_list list = {family, max_addrs, NULL, 0, 1};

Check warning on line 115 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L115

Added line #L115 was not covered by tests
struct addrinfo hints = {0};
struct addrinfo *address_info = NULL;
struct addrinfo *address = NULL;
char *ai_string = NULL;
size_t ai_string_len = 0;
int32_t err = 0;

// If no host supplied, then use local machine name
if (!host) {
err = gethostname(name, sizeof(name));
if (err != 0)
goto dns_resolve_error;
} else {
// Otherwise copy the host provided
strncat(name, host, sizeof(name) - 1);
*error = EINVAL;
return NULL;

Check warning on line 123 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L122-L123

Added lines #L122 - L123 were not covered by tests
}

hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = PF_UNSPEC;
hints.ai_family = family;

Check warning on line 126 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L126

Added line #L126 was not covered by tests
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

err = getaddrinfo(name, NULL, &hints, &address_info);
if (err != EAI_NONAME) {
if (err != 0)
goto dns_resolve_error;

// Name is already an IP address
freeaddrinfo(address_info);
return strdup(name);
}

hints.ai_flags = 0;
err = getaddrinfo(name, NULL, &hints, &address_info);
err = getaddrinfo(host, NULL, &hints, &address_info);

Check warning on line 129 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L129

Added line #L129 was not covered by tests
if (err != 0)
goto dns_resolve_error;

// Calculate the length of the return string
size_t max_ai_string = 1;
address = address_info;
while (address) {
// Use different length depending on the address type
if (address->ai_family == AF_INET)
max_ai_string += INET_ADDRSTRLEN;
else
max_ai_string += INET6_ADDRSTRLEN;

list.max_string += (address->ai_family == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN;
// Add room for semi-colon separator
max_ai_string++;
list.max_string++;

Check warning on line 139 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L139

Added line #L139 was not covered by tests
address = address->ai_next;
}

// Allocate buffer for the return string
ai_string = (char *)calloc(1, max_ai_string);
if (!ai_string)
list.string = (char *)calloc(1, list.max_string);

Check warning on line 144 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L144

Added line #L144 was not covered by tests
if (!list.string)
goto dns_resolve_error;

// Enumerate each address
address = address_info;
while (address && max_addrs) {
while (address && list.max_addrs) {
// Only copy addresses that match the family filter
if (family == AF_UNSPEC || address->ai_family == family) {
if (list.family == AF_UNSPEC || address->ai_family == list.family) {
// Ensure there is room to copy something into return string buffer
if (ai_string_len >= max_ai_string)
if (list.string_len >= list.max_string)
break;

// Copy address name into return string
err = getnameinfo(address->ai_addr, (socklen_t)address->ai_addrlen, ai_string + ai_string_len,
(uint32_t)(max_ai_string - ai_string_len), NULL, 0, NI_NUMERICHOST);
if (err != 0)
goto dns_resolve_error;
// Convert address name into a numeric host string
err = getnameinfo(address->ai_addr, (socklen_t)address->ai_addrlen, list.string + list.string_len,
(uint32_t)(list.max_string - list.string_len), NULL, 0, NI_NUMERICHOST);

Check warning on line 159 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L158-L159

Added lines #L158 - L159 were not covered by tests
if (err != 0) {
continue;

Check warning on line 161 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L161

Added line #L161 was not covered by tests
}

max_addrs--;
list.max_addrs--;

Check warning on line 164 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L164

Added line #L164 was not covered by tests

// Append semi-colon separator
ai_string_len = strlen(ai_string);
if (max_addrs && address->ai_next && ai_string_len + 1 < max_ai_string) {
ai_string[ai_string_len++] = ';';
ai_string[ai_string_len] = 0;
list.string_len = strlen(list.string);

Check warning on line 167 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L167

Added line #L167 was not covered by tests
if (list.max_addrs && address->ai_next && list.string_len + 1 < list.max_string) {
list.string[list.string_len++] = ';';
list.string[list.string_len] = 0;

Check warning on line 170 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L169-L170

Added lines #L169 - L170 were not covered by tests
}
}

address = address->ai_next;
}

if (err != 0)
if (err != 0 && list.string_len == 0)
goto dns_resolve_error;

return ai_string;
return list.string;

Check warning on line 180 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L180

Added line #L180 was not covered by tests

dns_resolve_error:

free(ai_string);
free(list.string);

Check warning on line 184 in net_util.c

View check run for this annotation

Codecov / codecov/patch

net_util.c#L184

Added line #L184 was not covered by tests

if (address_info)
freeaddrinfo(address_info);
Expand Down
48 changes: 27 additions & 21 deletions net_util.h
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
#pragma once

#ifdef __cplusplus
extern "C" {
#endif

// Resolve a host name to it an IPv4 address
char *dns_resolve(const char *host, int32_t *error);

// Resolve a host name to its addresses
char *dns_resolve_ex(const char *host, int32_t *error);

// Check if the ipv4 address matches the cidr notation range
bool is_ipv4_in_cidr_range(const char *ip, const char *cidr);

// Check if the ipv6 address matches the cidr notation range
bool is_ipv6_in_cidr_range(const char *ip, const char *cidr);

#ifdef __cplusplus
}
#endif
#pragma once

#ifdef __cplusplus
extern "C" {
#endif

// Get local IPv4 address for localhost
char *my_ip_address(void);

// Get local IPv6 and IPv6 addresses for localhost
char *my_ip_address_ex(void);

// Resolve a host name to it an IPv4 address
char *dns_resolve(const char *host, int32_t *error);

// Resolve a host name to its IPv6 and IPv6 addresses
char *dns_resolve_ex(const char *host, int32_t *error);

// Check if the ipv4 address matches the cidr notation range
bool is_ipv4_in_cidr_range(const char *ip, const char *cidr);

// Check if the ipv6 address matches the cidr notation range
bool is_ipv6_in_cidr_range(const char *ip, const char *cidr);

#ifdef __cplusplus
}
#endif
Loading

0 comments on commit 4a4febb

Please sign in to comment.