about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/network/getifaddrs.c107
1 files changed, 52 insertions, 55 deletions
diff --git a/src/network/getifaddrs.c b/src/network/getifaddrs.c
index d96d1094..50eaee80 100644
--- a/src/network/getifaddrs.c
+++ b/src/network/getifaddrs.c
@@ -12,17 +12,27 @@
 #include <unistd.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) {
-		curr->ifa_name = strdup(ifname);
-		if(!curr->ifa_name) {
-			free(curr);
-			curr = 0;
-			goto out;
-		}
-		if(*head) (*head)->ifa_next = curr;
+		strcpy(curr->name, ifname);
+		curr->ifa.ifa_name = curr->name;
+		if(*head) (*head)->next = (struct ifaddrs*) curr;
 		*head = 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)
 {
-	struct ifaddrs *head = ifp;
+	stor *head = (stor *) ifp;
 	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;
-		head = head->ifa_next;
+		head = (stor *) head->next;
 		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)
 {
 	// FIXME: left for bit-wizard rich
 	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");
 	/* 00000000000000000000000000000001 01 80 10 80 lo
@@ -94,16 +85,18 @@ static void dealwithipv6(struct ifaddrs **list, struct ifaddrs** head)
 			struct sockaddr_in6 sa = {0};
 			if(1 == inet_pton(AF_INET6, v6conv, &sa.sin6_addr)) {
 				sa.sin6_family = AF_INET6;
-				struct ifaddrs* curr = list_add(list, head, name);
+				stor* curr = list_add(list, head, name);
 				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);
-				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 */
-				struct ifaddrs* scan = *list;
-				for(;scan && strcmp(name, scan->ifa_name);scan=scan->ifa_next);
-				if(scan) curr->ifa_flags=scan->ifa_flags;
-				else curr->ifa_flags = 0;
+				stor* scan = *list;
+				for(;scan && strcmp(name, scan->name);scan=(stor*)scan->next);
+				if(scan) curr->ifa.ifa_flags = scan->ifa.ifa_flags;
+				else curr->ifa.ifa_flags = 0;
 			} else errno = 0;
 		}
 	}
@@ -114,12 +107,13 @@ static void dealwithipv6(struct ifaddrs **list, struct ifaddrs** head)
 int getifaddrs(struct ifaddrs **ifap)
 {
 	FILE* f = fopen("/proc/net/dev", "r");
+	if(!f) return -1;
+
 	/* the alternative to parsing /proc.. seems to be iterating
 	   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
 	   for valid ifindices (SIOCGIFINDEX) */
-	if(!f) return -1;
-	struct ifaddrs *list = 0, *head = 0;
+	stor *list = 0, *head = 0;
 
 	char* line; char linebuf[512];
 	while((line = fgets(linebuf, sizeof linebuf, f))) {
@@ -129,7 +123,7 @@ int getifaddrs(struct ifaddrs **ifap)
 		if(line > start && *line == ':') {
 			// found interface
 			*line = 0;
-			struct ifaddrs* curr = list_add(&list, &head, start);
+			stor* curr = list_add(&list, &head, start);
 			if(!curr) {
 				fclose(f);
 				goto err2;
@@ -145,47 +139,50 @@ int getifaddrs(struct ifaddrs **ifap)
 	if(-1 == ioctl(sock, SIOCGIFCONF, &conf)) goto err;
 	else {
 		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;
 			for(i = 0; i < reqitems; i++) {
 				// get SIOCGIFADDR of active interfaces.
-				if(!strcmp(reqs[i].ifr_name, head->ifa_name)) {
-					head->ifa_addr = sockaddr_in_dup((struct sockaddr_in*) &reqs[i].ifr_addr);
+				if(!strcmp(reqs[i].ifr_name, head->name)) {
+					head->addr.v4 = *(struct sockaddr_in*)&reqs[i].ifr_addr;
+					head->ifa.ifa_addr = (struct sockaddr*) &head->addr;
 					break;
 				}
 			}
 			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;
 
-			head->ifa_flags = req.ifr_flags;
-			if(head->ifa_addr) {
+			head->ifa.ifa_flags = req.ifr_flags;
+			if(head->ifa.ifa_addr) {
 				/* 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;
-				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;
-					head->ifa_ifu.ifu_dstaddr = sockaddr_in_dup((struct sockaddr_in*) &req.ifr_dstaddr);
+					head->dst.v4 = *(struct sockaddr_in*)&req.ifr_dstaddr;
 				} else {
 					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);
 	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;
 	dealwithipv6(&list, &head);
-	*ifap = list;
+	*ifap = (struct ifaddrs*) list;
 	return 0;
 	err:
 	close(sock);
 	err2:
-	freeifaddrs(list);
+	freeifaddrs((struct ifaddrs*) list);
 	return -1;
 }