[ccan] [PATCH 2/2] net: Extend to allow convenient binding to a specified local address

David Gibson david at gibson.dropbear.id.au
Mon Sep 23 21:37:02 EST 2013


Currently the server/bind side functions in the net module always bind to
the "wildcard" address listening on all interfaces / local addresses.  This
patch adds a new net_server_lookup_addr() function to also allow listening
on only a specified local address.

Signed-off-by: David Gibson <david at gibson.dropbear.id.au>
---
 ccan/net/net.c           |  11 ++--
 ccan/net/net.h           |  42 +++++++++++++++
 ccan/net/test/run-bind.c | 138 ++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 179 insertions(+), 12 deletions(-)

diff --git a/ccan/net/net.c b/ccan/net/net.c
index e84380f..8877452 100644
--- a/ccan/net/net.c
+++ b/ccan/net/net.c
@@ -151,19 +151,20 @@ out:
 	return sockfd;
 }
 
-struct addrinfo *net_server_lookup(const char *service,
-				   int family,
-				   int socktype)
+struct addrinfo *net_server_lookup_addr(const char *addr,
+					const char *service,
+					int family,
+					int socktype)
 {
 	struct addrinfo *res, hints;
 
 	memset(&hints, 0, sizeof(hints));
 	hints.ai_family = family;
 	hints.ai_socktype = socktype;
-	hints.ai_flags = AI_PASSIVE;
+	hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
 	hints.ai_protocol = 0;
 
-	if (getaddrinfo(NULL, service, &hints, &res) != 0)
+	if (getaddrinfo(addr, service, &hints, &res) != 0)
 		return NULL;
 
 	return res;
diff --git a/ccan/net/net.h b/ccan/net/net.h
index 9ad6de5..a14b6c8 100644
--- a/ccan/net/net.h
+++ b/ccan/net/net.h
@@ -1,6 +1,9 @@
 /* Licensed under BSD-MIT - see LICENSE file for details */
 #ifndef CCAN_NET_H
 #define CCAN_NET_H
+
+#include <stdio.h>
+
 /**
  * net_client_lookup - look up a network name to connect to.
  * @hostname: the name to look up
@@ -48,6 +51,37 @@ struct addrinfo *net_client_lookup(const char *hostname,
  */
 int net_connect(const struct addrinfo *addrinfo);
 
+
+/**
+ * net_server_lookup_addr - look up a local address and service name to bind to.
+ * @address: local address to use
+ * @service: the service to look up
+ * @family: Usually AF_UNSPEC, otherwise AF_INET or AF_INET6.
+ * @socktype: SOCK_DGRAM or SOCK_STREAM.
+ *
+ * This will do a synchronous lookup of a given name, returning a linked list
+ * of results, or NULL on error.  You should use freeaddrinfo() to free it.
+ *
+ * Example:
+ *	#include <sys/types.h>
+ *	#include <sys/socket.h>
+ *	#include <stdio.h>
+ *	#include <netdb.h>
+ *	#include <err.h>
+ *	...
+ *	struct addrinfo *addr;
+ *
+ *	// Get address(es) to bind for our service.
+ *	addr = net_server_lookup_addr("127.0.0.1", "8888",
+ *                                    AF_UNSPEC, SOCK_STREAM);
+ *	if (!addr)
+ *		errx(1, "Failed to look up 8888 on 127.0.0.1 to bind to");
+ */
+struct addrinfo *net_server_lookup_addr(const char *addr,
+					const char *service,
+					int family,
+					int socktype);
+
 /**
  * net_server_lookup - look up a service name to bind to.
  * @service: the service to look up
@@ -71,6 +105,14 @@ int net_connect(const struct addrinfo *addrinfo);
  *	if (!addr)
  *		errx(1, "Failed to look up 8888 to bind to");
  */
+static inline struct addrinfo *net_server_lookup(const char *service,
+						 int family,
+						 int socktype)
+{
+	return net_server_lookup_addr(NULL, service, family, socktype);
+}
+
+
 struct addrinfo *net_server_lookup(const char *service,
 				   int family,
 				   int socktype);
diff --git a/ccan/net/test/run-bind.c b/ccan/net/test/run-bind.c
index 9fb2081..f61284f 100644
--- a/ccan/net/test/run-bind.c
+++ b/ccan/net/test/run-bind.c
@@ -29,7 +29,7 @@ static int my_setsockopt(int sockfd, int level, int optname,
 
 #define TEST_PORT "65001"
 
-static void do_connect(int family, int type)
+static void do_connect(const char *host, int family, int type)
 {
 	int fd, ret;
 	struct addrinfo *addr;
@@ -37,7 +37,7 @@ static void do_connect(int family, int type)
 
 	/* Just in case... */
 	alarm(5);
-	addr = net_client_lookup(NULL, TEST_PORT, family, type);
+	addr = net_client_lookup(host, TEST_PORT, family, type);
 	fd = net_connect(addr);
 	if (fd < 0)
 		err(1, "Failed net_connect");
@@ -86,7 +86,7 @@ int main(void)
 	} remote_addr;
 	socklen_t addlen = sizeof(remote_addr);
 
-	plan_tests(35);
+	plan_tests(73);
 
 	/* Simple TCP test. */
 	addr = net_server_lookup(TEST_PORT, AF_UNSPEC, SOCK_STREAM);
@@ -97,7 +97,67 @@ int main(void)
 	if (!fork()) {
 		for (i = 0; i < num_fds; i++)
 			close(fds[i]);
-		do_connect(AF_UNSPEC, SOCK_STREAM);
+		do_connect(NULL, AF_UNSPEC, SOCK_STREAM);
+		exit(0);
+	}
+
+	i = wait_for_readable(fds, num_fds);
+	ok1(i < num_fds);
+	fd = accept(fds[i], NULL, NULL);
+	ok1(fd >= 0);
+
+	ret = read(fd, buf, strlen("Yay!"));
+	ok1(ret == strlen("Yay!"));
+	ok1(memcmp(buf, "Yay!", ret) == 0);
+	ret = write(fd, "metoo", strlen("metoo"));
+	ok1(ret == strlen("metoo"));
+	ok1(wait(&status) != -1);
+	ok1(WIFEXITED(status));
+	ok1(WEXITSTATUS(status) == 0);
+	close(fd);
+	for (i = 0; i < num_fds; i++)
+		close(fds[i]);
+
+	/* TCP test with specified bind address (IPv4) */
+	addr = net_server_lookup_addr("127.0.0.1", TEST_PORT,
+				      AF_UNSPEC, SOCK_STREAM);
+	ok1(addr);
+	num_fds = net_bind(addr, fds);
+	ok1(num_fds == 1);
+
+	if (!fork()) {
+		close(fds[0]);
+		do_connect("127.0.0.1", AF_UNSPEC, SOCK_STREAM);
+		exit(0);
+	}
+
+	i = wait_for_readable(fds, num_fds);
+	ok1(i < num_fds);
+	fd = accept(fds[i], NULL, NULL);
+	ok1(fd >= 0);
+
+	ret = read(fd, buf, strlen("Yay!"));
+	ok1(ret == strlen("Yay!"));
+	ok1(memcmp(buf, "Yay!", ret) == 0);
+	ret = write(fd, "metoo", strlen("metoo"));
+	ok1(ret == strlen("metoo"));
+	ok1(wait(&status) != -1);
+	ok1(WIFEXITED(status));
+	ok1(WEXITSTATUS(status) == 0);
+	close(fd);
+	for (i = 0; i < num_fds; i++)
+		close(fds[i]);
+
+	/* TCP test with specified bind address (IPv6) */
+	addr = net_server_lookup_addr("::1", TEST_PORT,
+				      AF_UNSPEC, SOCK_STREAM);
+	ok1(addr);
+	num_fds = net_bind(addr, fds);
+	ok1(num_fds == 1);
+
+	if (!fork()) {
+		close(fds[0]);
+		do_connect("::1", AF_UNSPEC, SOCK_STREAM);
 		exit(0);
 	}
 
@@ -127,7 +187,71 @@ int main(void)
 	if (!fork()) {
 		for (i = 0; i < num_fds; i++)
 			close(fds[i]);
-		do_connect(AF_UNSPEC, SOCK_DGRAM);
+		do_connect(NULL, AF_UNSPEC, SOCK_DGRAM);
+		exit(0);
+	}
+
+	i = wait_for_readable(fds, num_fds);
+	ok1(i < num_fds);
+	fd = fds[i];
+
+	ret = recvfrom(fd, buf, strlen("Yay!"), 0,
+		       (void *)&remote_addr, &addlen);
+	ok1(ret == strlen("Yay!"));
+	ok1(memcmp(buf, "Yay!", ret) == 0);
+	ret = sendto(fd, "metoo", strlen("metoo"), 0,
+		     (void *)&remote_addr, addlen);
+	ok1(ret == strlen("metoo"));
+	ok1(wait(&status) >= 0);
+	ok1(WIFEXITED(status));
+	ok1(WEXITSTATUS(status) == 0);
+	close(fd);
+	for (i = 0; i < num_fds; i++)
+		close(fds[i]);
+
+	/* UDP test with specified bind address (IPv4) */
+	addr = net_server_lookup_addr("127.0.0.1", TEST_PORT,
+				      AF_UNSPEC, SOCK_DGRAM);
+	ok1(addr);
+	num_fds = net_bind(addr, fds);
+	ok1(num_fds == 1);
+
+	if (!fork()) {
+		for (i = 0; i < num_fds; i++)
+			close(fds[i]);
+		do_connect("127.0.01", AF_UNSPEC, SOCK_DGRAM);
+		exit(0);
+	}
+
+	i = wait_for_readable(fds, num_fds);
+	ok1(i < num_fds);
+	fd = fds[i];
+
+	ret = recvfrom(fd, buf, strlen("Yay!"), 0,
+		       (void *)&remote_addr, &addlen);
+	ok1(ret == strlen("Yay!"));
+	ok1(memcmp(buf, "Yay!", ret) == 0);
+	ret = sendto(fd, "metoo", strlen("metoo"), 0,
+		     (void *)&remote_addr, addlen);
+	ok1(ret == strlen("metoo"));
+	ok1(wait(&status) >= 0);
+	ok1(WIFEXITED(status));
+	ok1(WEXITSTATUS(status) == 0);
+	close(fd);
+	for (i = 0; i < num_fds; i++)
+		close(fds[i]);
+
+	/* UDP test with specified bind address (IPv6) */
+	addr = net_server_lookup_addr("::1", TEST_PORT,
+				      AF_UNSPEC, SOCK_DGRAM);
+	ok1(addr);
+	num_fds = net_bind(addr, fds);
+	ok1(num_fds == 1);
+
+	if (!fork()) {
+		for (i = 0; i < num_fds; i++)
+			close(fds[i]);
+		do_connect("::1", AF_UNSPEC, SOCK_DGRAM);
 		exit(0);
 	}
 
@@ -168,8 +292,8 @@ int main(void)
 		if (!fork()) {
 			for (i = 0; i < num_fds; i++)
 				close(fds[i]);
-			do_connect(AF_INET, SOCK_STREAM);
-			do_connect(AF_INET6, SOCK_STREAM);
+			do_connect(NULL, AF_INET, SOCK_STREAM);
+			do_connect(NULL, AF_INET6, SOCK_STREAM);
 			exit(0);
 		}
 
-- 
1.8.3.1



More information about the ccan mailing list