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

Rusty Russell rusty at rustcorp.com.au
Tue Sep 24 14:42:20 EST 2013


David Gibson <david at gibson.dropbear.id.au> writes:
> 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>

Gah, getaddrinfo() is a terrible interface.

What you really want to do is specify an interface to use (ie. loopback
in your examples).  By specifying addr as "127.0.0.1" it's unclear to me
if we'll see any IPv6 addresses.

Yet if we really want to join to struct addrinfo, the user can't use
freeaddrinfo() on it: we need to supply a free function.  I guess we'll
do that, too.

If this is what you want, I think I'll code up
"net_local_server_lookup()" which does two lookups and joins them, and
then add a net_addrinfo_free() function.

Or am I missing something?
Rusty.



> ---
>  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