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

David Gibson david at gibson.dropbear.id.au
Tue Sep 24 18:37:07 EST 2013


On Tue, Sep 24, 2013 at 02:12:20PM +0930, Paul 'Rusty' Russell wrote:
> 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.

Well.  The interface I have in mind here does take an explicit address
(well, optionally) in which case, yes, you'll only get either an IPv4
or an IPv6 address.

I did think of taking an interface name and binding to all addresses
(v4 or v6) on that interface as a possible extension.  But for the use
I have in mind, I really do want to specify a single address.

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

I'm not sure what you mean here.

> 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);
> >  		}
> >  
> 

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.ozlabs.org/pipermail/ccan/attachments/20130924/251ec095/attachment-0001.sig>


More information about the ccan mailing list