LibC: Implement {get,free}ifaddrs
This commit is contained in:
@@ -1,15 +1,148 @@
|
|||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <ifaddrs.h>
|
#include <ifaddrs.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#include <BAN/Assert.h>
|
static int selector(const dirent* dirent)
|
||||||
|
{
|
||||||
|
if (strcmp(dirent->d_name, "lo") == 0)
|
||||||
|
return 1;
|
||||||
|
if (strncmp(dirent->d_name, "eth", 3) == 0)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int comparator(const dirent** d1, const dirent** d2)
|
||||||
|
{
|
||||||
|
if (strcmp((*d1)->d_name, "lo"))
|
||||||
|
return -1;
|
||||||
|
if (strcmp((*d2)->d_name, "lo"))
|
||||||
|
return +1;
|
||||||
|
return alphasort(d1, d2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_ifaddrs(ifaddrs* ifa)
|
||||||
|
{
|
||||||
|
if (ifa->ifa_name)
|
||||||
|
free(ifa->ifa_name);
|
||||||
|
if (ifa->ifa_addr)
|
||||||
|
free(ifa->ifa_addr);
|
||||||
|
if (ifa->ifa_netmask)
|
||||||
|
free(ifa->ifa_netmask);
|
||||||
|
if (ifa->ifa_ifu.ifu_broadaddr)
|
||||||
|
free(ifa->ifa_ifu.ifu_broadaddr);
|
||||||
|
if (ifa->ifa_data)
|
||||||
|
free(ifa->ifa_data);
|
||||||
|
free(ifa);
|
||||||
|
}
|
||||||
|
|
||||||
|
static sockaddr* dup_sockaddr(const sockaddr* saddr)
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
switch (saddr->sa_family)
|
||||||
|
{
|
||||||
|
case AF_INET:
|
||||||
|
size = sizeof(sockaddr_in);
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
size = sizeof(sockaddr_in6);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr* result = static_cast<sockaddr*>(malloc(size));
|
||||||
|
if (result == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
memcpy(result, saddr, size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ifaddrs* open_ifaddrs(const char* path)
|
||||||
|
{
|
||||||
|
int fd = open(path, O_RDONLY);
|
||||||
|
if (fd == -1)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
ifaddrs* ifa = static_cast<ifaddrs*>(calloc(1, sizeof(ifaddrs)));
|
||||||
|
if (ifa == nullptr)
|
||||||
|
goto open_ifaddrs_error;
|
||||||
|
|
||||||
|
ifreq ifreq;
|
||||||
|
|
||||||
|
if (ioctl(fd, SIOCGIFNAME, &ifreq) == -1)
|
||||||
|
goto open_ifaddrs_error;
|
||||||
|
ifa->ifa_name = strdup(ifreq.ifr_name);
|
||||||
|
if (ifa->ifa_name == nullptr)
|
||||||
|
goto open_ifaddrs_error;
|
||||||
|
|
||||||
|
if (ioctl(fd, SIOCGIFFLAGS, &ifreq) == -1)
|
||||||
|
goto open_ifaddrs_error;
|
||||||
|
ifa->ifa_flags = ifreq.ifr_flags;
|
||||||
|
|
||||||
|
if (ioctl(fd, SIOCGIFADDR, &ifreq) == -1)
|
||||||
|
goto open_ifaddrs_error;
|
||||||
|
ifa->ifa_addr = dup_sockaddr(&ifreq.ifr_addr);
|
||||||
|
if (ifa->ifa_addr == nullptr)
|
||||||
|
goto open_ifaddrs_error;
|
||||||
|
|
||||||
|
if (ioctl(fd, SIOCGIFNETMASK, &ifreq) == -1)
|
||||||
|
goto open_ifaddrs_error;
|
||||||
|
ifa->ifa_netmask = dup_sockaddr(&ifreq.ifr_netmask);
|
||||||
|
if (ifa->ifa_netmask == nullptr)
|
||||||
|
goto open_ifaddrs_error;
|
||||||
|
|
||||||
|
// TODO: broadcast/point-to-point
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return ifa;
|
||||||
|
|
||||||
|
open_ifaddrs_error:
|
||||||
|
if (ifa != nullptr)
|
||||||
|
free_ifaddrs(ifa);
|
||||||
|
close(fd);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
int getifaddrs(struct ifaddrs** ifap)
|
int getifaddrs(struct ifaddrs** ifap)
|
||||||
{
|
{
|
||||||
(void)ifap;
|
dirent** namelist;
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
|
const int count = scandir("/dev", &namelist, selector, comparator);
|
||||||
|
if (count == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*ifap = nullptr;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
char path[5 + sizeof(namelist[i]->d_name)];
|
||||||
|
sprintf(path, "/dev/%s", namelist[i]->d_name);
|
||||||
|
|
||||||
|
ifaddrs* ifa = open_ifaddrs(path);
|
||||||
|
if (ifa == nullptr)
|
||||||
|
continue;
|
||||||
|
*ifap = ifa;
|
||||||
|
ifap = &ifa->ifa_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
free(namelist[i]);
|
||||||
|
free(namelist);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeifaddrs(struct ifaddrs* ifa)
|
void freeifaddrs(struct ifaddrs* ifa)
|
||||||
{
|
{
|
||||||
(void)ifa;
|
while (ifa)
|
||||||
ASSERT_NOT_REACHED();
|
{
|
||||||
|
auto* next = ifa->ifa_next;
|
||||||
|
free_ifaddrs(ifa);
|
||||||
|
ifa = next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user