getifaddrs: less malloc

This commit is contained in:
rofl0r
2013-04-05 22:06:35 +02:00
parent b3792c8891
commit c82f53f67c

View File

@ -12,17 +12,27 @@
#include <unistd.h> #include <unistd.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
static struct ifaddrs* list_add(struct ifaddrs** list, struct ifaddrs** head, char* ifname) typedef union {
struct sockaddr_in6 v6;
struct sockaddr_in v4;
} soa;
typedef struct ifaddrs_storage {
struct ifaddrs ifa;
soa addr;
soa netmask;
soa dst;
char name[IFNAMSIZ+1];
} stor;
#define next ifa.ifa_next
static stor* list_add(stor** list, stor** head, char* ifname)
{ {
struct ifaddrs* curr = calloc(1, sizeof(struct ifaddrs)); stor* curr = calloc(1, sizeof(stor));
if(curr) { if(curr) {
curr->ifa_name = strdup(ifname); strcpy(curr->name, ifname);
if(!curr->ifa_name) { curr->ifa.ifa_name = curr->name;
free(curr); if(*head) (*head)->next = (struct ifaddrs*) curr;
curr = 0;
goto out;
}
if(*head) (*head)->ifa_next = curr;
*head = curr; *head = curr;
if(!*list) *list = curr; if(!*list) *list = curr;
} }
@ -32,40 +42,21 @@ static struct ifaddrs* list_add(struct ifaddrs** list, struct ifaddrs** head, ch
void freeifaddrs(struct ifaddrs *ifp) void freeifaddrs(struct ifaddrs *ifp)
{ {
struct ifaddrs *head = ifp; stor *head = (stor *) ifp;
while(head) { while(head) {
free(head->ifa_name);
free(head->ifa_addr);
free(head->ifa_netmask);
free(head->ifa_ifu.ifu_dstaddr);
free(head->ifa_data);
void *p = head; void *p = head;
head = head->ifa_next; head = (stor *) head->next;
free(p); free(p);
} }
} }
static struct sockaddr *sockaddr_in_dup(struct sockaddr_in *src)
{
struct sockaddr_in *nu = malloc(sizeof(struct sockaddr_in));
if(nu) *nu = *src;
return (struct sockaddr*) nu;
}
static struct sockaddr *sockaddr_in6_dup(struct sockaddr_in6 *src)
{
struct sockaddr_in6 *nu = malloc(sizeof(struct sockaddr_in6));
if(nu) *nu = *src;
return (struct sockaddr*) nu;
}
static void ipv6netmask(unsigned prefix_length, struct sockaddr_in6 *sa) static void ipv6netmask(unsigned prefix_length, struct sockaddr_in6 *sa)
{ {
// FIXME: left for bit-wizard rich // FIXME: left for bit-wizard rich
memset(&sa->sin6_addr, -1, sizeof(sa->sin6_addr)); memset(&sa->sin6_addr, -1, sizeof(sa->sin6_addr));
} }
static void dealwithipv6(struct ifaddrs **list, struct ifaddrs** head) static void dealwithipv6(stor **list, stor** head)
{ {
FILE* f = fopen("/proc/net/if_inet6", "r"); FILE* f = fopen("/proc/net/if_inet6", "r");
/* 00000000000000000000000000000001 01 80 10 80 lo /* 00000000000000000000000000000001 01 80 10 80 lo
@ -94,16 +85,18 @@ static void dealwithipv6(struct ifaddrs **list, struct ifaddrs** head)
struct sockaddr_in6 sa = {0}; struct sockaddr_in6 sa = {0};
if(1 == inet_pton(AF_INET6, v6conv, &sa.sin6_addr)) { if(1 == inet_pton(AF_INET6, v6conv, &sa.sin6_addr)) {
sa.sin6_family = AF_INET6; sa.sin6_family = AF_INET6;
struct ifaddrs* curr = list_add(list, head, name); stor* curr = list_add(list, head, name);
if(!curr) goto out; if(!curr) goto out;
curr->ifa_addr = sockaddr_in6_dup(&sa); curr->addr.v6 = sa;
curr->ifa.ifa_addr = (struct sockaddr*) &curr->addr;
ipv6netmask(c, &sa); ipv6netmask(c, &sa);
curr->ifa_netmask = sockaddr_in6_dup(&sa); curr->netmask.v6 = sa;
curr->ifa.ifa_netmask = (struct sockaddr*) &curr->netmask;
/* find ipv4 struct with the same interface name to copy flags */ /* find ipv4 struct with the same interface name to copy flags */
struct ifaddrs* scan = *list; stor* scan = *list;
for(;scan && strcmp(name, scan->ifa_name);scan=scan->ifa_next); for(;scan && strcmp(name, scan->name);scan=(stor*)scan->next);
if(scan) curr->ifa_flags=scan->ifa_flags; if(scan) curr->ifa.ifa_flags = scan->ifa.ifa_flags;
else curr->ifa_flags = 0; else curr->ifa.ifa_flags = 0;
} else errno = 0; } else errno = 0;
} }
} }
@ -114,12 +107,13 @@ static void dealwithipv6(struct ifaddrs **list, struct ifaddrs** head)
int getifaddrs(struct ifaddrs **ifap) int getifaddrs(struct ifaddrs **ifap)
{ {
FILE* f = fopen("/proc/net/dev", "r"); FILE* f = fopen("/proc/net/dev", "r");
if(!f) return -1;
/* the alternative to parsing /proc.. seems to be iterating /* the alternative to parsing /proc.. seems to be iterating
through the interfaces using an index number in ifreq.ifr_ifindex through the interfaces using an index number in ifreq.ifr_ifindex
until we get some error code back. the kernel will fill ifr_name field until we get some error code back. the kernel will fill ifr_name field
for valid ifindices (SIOCGIFINDEX) */ for valid ifindices (SIOCGIFINDEX) */
if(!f) return -1; stor *list = 0, *head = 0;
struct ifaddrs *list = 0, *head = 0;
char* line; char linebuf[512]; char* line; char linebuf[512];
while((line = fgets(linebuf, sizeof linebuf, f))) { while((line = fgets(linebuf, sizeof linebuf, f))) {
@ -129,7 +123,7 @@ int getifaddrs(struct ifaddrs **ifap)
if(line > start && *line == ':') { if(line > start && *line == ':') {
// found interface // found interface
*line = 0; *line = 0;
struct ifaddrs* curr = list_add(&list, &head, start); stor* curr = list_add(&list, &head, start);
if(!curr) { if(!curr) {
fclose(f); fclose(f);
goto err2; goto err2;
@ -145,47 +139,50 @@ int getifaddrs(struct ifaddrs **ifap)
if(-1 == ioctl(sock, SIOCGIFCONF, &conf)) goto err; if(-1 == ioctl(sock, SIOCGIFCONF, &conf)) goto err;
else { else {
size_t reqitems = conf.ifc_len / sizeof(struct ifreq); size_t reqitems = conf.ifc_len / sizeof(struct ifreq);
for(head = list; head; head=head->ifa_next) { for(head = list; head; head = (stor*)head->next) {
size_t i; size_t i;
for(i = 0; i < reqitems; i++) { for(i = 0; i < reqitems; i++) {
// get SIOCGIFADDR of active interfaces. // get SIOCGIFADDR of active interfaces.
if(!strcmp(reqs[i].ifr_name, head->ifa_name)) { if(!strcmp(reqs[i].ifr_name, head->name)) {
head->ifa_addr = sockaddr_in_dup((struct sockaddr_in*) &reqs[i].ifr_addr); head->addr.v4 = *(struct sockaddr_in*)&reqs[i].ifr_addr;
head->ifa.ifa_addr = (struct sockaddr*) &head->addr;
break; break;
} }
} }
struct ifreq req; struct ifreq req;
snprintf(req.ifr_name, sizeof req.ifr_name, "%s", head->ifa_name); snprintf(req.ifr_name, sizeof req.ifr_name, "%s", head->name);
if(-1 == ioctl(sock, SIOCGIFFLAGS, &req)) goto err; if(-1 == ioctl(sock, SIOCGIFFLAGS, &req)) goto err;
head->ifa_flags = req.ifr_flags; head->ifa.ifa_flags = req.ifr_flags;
if(head->ifa_addr) { if(head->ifa.ifa_addr) {
/* or'ing flags with IFF_LOWER_UP on active interfaces to mimic glibc */ /* or'ing flags with IFF_LOWER_UP on active interfaces to mimic glibc */
head->ifa_flags |= IFF_LOWER_UP; head->ifa.ifa_flags |= IFF_LOWER_UP;
if(-1 == ioctl(sock, SIOCGIFNETMASK, &req)) goto err; if(-1 == ioctl(sock, SIOCGIFNETMASK, &req)) goto err;
head->ifa_netmask = sockaddr_in_dup((struct sockaddr_in*) &req.ifr_netmask); head->netmask.v4 = *(struct sockaddr_in*)&req.ifr_netmask;
head->ifa.ifa_netmask = (struct sockaddr*) &head->netmask;
if(head->ifa_flags & IFF_POINTOPOINT) { if(head->ifa.ifa_flags & IFF_POINTOPOINT) {
if(-1 == ioctl(sock, SIOCGIFDSTADDR, &req)) goto err; if(-1 == ioctl(sock, SIOCGIFDSTADDR, &req)) goto err;
head->ifa_ifu.ifu_dstaddr = sockaddr_in_dup((struct sockaddr_in*) &req.ifr_dstaddr); head->dst.v4 = *(struct sockaddr_in*)&req.ifr_dstaddr;
} else { } else {
if(-1 == ioctl(sock, SIOCGIFBRDADDR, &req)) goto err; if(-1 == ioctl(sock, SIOCGIFBRDADDR, &req)) goto err;
head->ifa_ifu.ifu_broadaddr = sockaddr_in_dup((struct sockaddr_in*) &req.ifr_broadaddr); head->dst.v4 = *(struct sockaddr_in*)&req.ifr_broadaddr;
} }
head->ifa.ifa_ifu.ifu_dstaddr = (struct sockaddr*) &head->dst;
} }
} }
} }
close(sock); close(sock);
void* last = 0; void* last = 0;
for(head = list; head; head=head->ifa_next) last=head; for(head = list; head; head=(stor*)head->next) last=head;
head = last; head = last;
dealwithipv6(&list, &head); dealwithipv6(&list, &head);
*ifap = list; *ifap = (struct ifaddrs*) list;
return 0; return 0;
err: err:
close(sock); close(sock);
err2: err2:
freeifaddrs(list); freeifaddrs((struct ifaddrs*) list);
return -1; return -1;
} }