[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