Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 13 Jul 2012 11:46:05 +0200
From:      Matthias Apitz <guru@unixarea.de>
To:        freebsd-questions@freebsd.org
Subject:   Re: IPv6 && getaddrinfo(3C)
Message-ID:  <20120713094604.GA1282@tiny.Sisis.de>
In-Reply-To: <201207130201.q6D21oTp045911@mail.r-bonomi.com>
References:  <80BE761E-42B1-4111-ACEC-0C31E43D105D@lafn.org> <201207130201.q6D21oTp045911@mail.r-bonomi.com>

next in thread | previous in thread | raw e-mail | index | archive | help
El día Thursday, July 12, 2012 a las 09:01:50PM -0500, Robert Bonomi escribió:

> > >  req.ai_flags = AI_ADDRCONFIG|AI_NUMERICHOST; 
> > >  req.ai_family = AF_INET6;	/* Same as AF_INET6. */ 
> 
> Isn't the setting of 'req.ai_family', above, going to guarantee that
> something that "looks like"  an IPv4 address will not be considered valid?
> 
> After all, what *POSSIBLE* _IPv6_info_ is there about an IPv4 address?
> 
> Per the manpage example, try PF_UNSPEC.

With PF_UNSPEC it works fine now, thanks for the hint; I'm attaching the
code for the client and as well one for a server creating LISTEN on IPv6
and IPv4 at the same time and handling the connections on both ports;

HIH

	matthias


/* IPv6 client code using getaddrinfo */

#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>


main(int argc, char **argv)
{

	struct addrinfo	req, *ans;
	int	code, s, n;
	char buf[1024];

	memset(&req, 0, sizeof(req));
	req.ai_flags = 0;               /* may be restricted to AI_ADDRCONFIG|AI_NUMERICHOST|... */
	/* req.ai_family = AF_INET6;	/* validates only AF_INET6 */
	/* req.ai_family = AF_INET;	/* validates only AF_INET, i.e. IPv4 */
	req.ai_family = PF_UNSPEC;	/* validates IPv4 and IPv6. */
	req.ai_socktype = SOCK_STREAM;

	/* Use protocol TCP */

	req.ai_protocol = IPPROTO_TCP;  /* 0: any, IPPROTO_UDP: UDP */

	printf("host: %s\n", argv[1]);
	if ((code = getaddrinfo(argv[1], "ssh", &req, &ans)) != 0) {
		fprintf(stderr, "ssh: getaddrinfo failed code %d: %s\n", code, gai_strerror(code));
		exit(1);
	}
	 
	/* 'ans' must contain at least one addrinfo, use the first */ 
	
	s = socket(ans->ai_family, ans->ai_socktype, ans->ai_protocol);
	if (s < 0) {
		perror("ssh: socket");
		exit(3);
	}

	/* Connect does the bind for us */
	
	if (connect(s, ans->ai_addr, ans->ai_addrlen) < 0) {
		perror("ssh: connect");
		exit(5);
	}

	/* just for test: read in SSH' good morning message */

	n = read(s, buf, 1024);
	printf ("read: %s", buf);
	
	/*
	 Free answers after use
	 */ 
	freeaddrinfo(ans);

	exit(0);
}





/* IPv6 server code using getaddrinfo */

#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <stdarg.h>

#include <poll.h>


void doit()
{
	printf("child forked end ended\n");
}

main(int argc, char **argv)
{
	struct sockaddr_in6 from;
	struct addrinfo	req, *ans, *ans2;
	int    code, sockFd1, sockFd2, len;

	/* Set ai_flags to AI_PASSIVE to indicate that return addres s is suitable for bind() */

	memset(&req, 0, sizeof(req));
	req.ai_flags = AI_PASSIVE;
	req.ai_family = PF_UNSPEC;          /* IPv6+IPv4: PF_UNSPEC, IPv4: PF_INET */
	req.ai_socktype = SOCK_STREAM;
	req.ai_protocol = IPPROTO_TCP;

#define SLNP "3025"

	if ((code = getaddrinfo(NULL, SLNP, &req, &ans)) != 0) {
		fprintf(stderr, "SLNP (%s): getaddrinfo failed code %d: %s\n", SLNP, code, gai_strerror(code));
		exit(1);
	}

	/* 'ans' must contain at least one addrinfo and we use the first. */
	/* it seems(!) that 1st one is the IPv6 when we use PF_UNSPEC */

	if( (sockFd1 = socket(ans->ai_family, ans->ai_socktype, ans->ai_protocol)) < 0) {
		perror("socket");
		exit(-1);
	}

	if (bind(sockFd1, ans->ai_addr, ans->ai_addrlen) < 0) {
		perror("bind");
		close(sockFd1);
		exit(-1);
	}

	/* create the 1st LISTEN */

	printf("1st (IPv6) LISTEN...\n");
	listen(sockFd1, 5);

	/* if there is a 2nd addrinfo provided by getaddrinfo(3C) and we will create 2nd socket... */

	ans2 = NULL;
	if( ans->ai_next != NULL )
		ans2 = ans->ai_next;
	
	sockFd2 = -1;       /* set to -1 to be used as this in poll, see below */
	if( ans2 != NULL )     {
	    if( (sockFd2 = socket(ans2->ai_family, ans2->ai_socktype, ans2->ai_protocol)) < 0) {
		perror("socket");
		exit(-1);
	    }
	    if (bind(sockFd2, ans2->ai_addr, ans2->ai_addrlen) < 0) {
		perror("bind");
		close(sockFd2);
		exit(-1);
	    }
	    printf("2nd (IPv4) LISTEN...\n");
	    listen(sockFd2, 5);
	}


	for (;;) {
		int newsockFd, len = sizeof(from), readyFd, polled;
		struct pollfd fds[2];

		/* we poll both fds for events and accept the one which is ready */

		fds[0].fd = sockFd1;
		fds[0].events = POLLIN | POLLPRI;
		fds[0].revents = 0;
		fds[1].fd = sockFd2;    /* will not be poll'ed if -1 */
		fds[1].events = POLLIN | POLLPRI;
		fds[1].revents = 0;

		polled = poll(fds, 2, INFTIM);
		printf("poll fds ready: (IPv6) %d (IPv4) %d\n", fds[0].revents, fds[1].revents);
		
		/* this is rather dirty, because both fds could be ready and this would always prefer the 2nd fd */

		if( fds[0].revents )
			readyFd = fds[0].fd;
		if( fds[1].revents )
			readyFd = fds[1].fd;

		newsockFd = accept(readyFd, (struct sockaddr *)&from, &len);
		if (newsockFd < 0) {
			perror("accept");
			exit(-1);
		} 
		
		if (fork() == 0) {
			close(readyFd);
			(void) doit(newsockFd, &from);
			exit(0);
		} 
		close(newsockFd);
	}

	 /* Free answers after use */
	 
	freeaddrinfo(ans);
	exit(0);
}
-- 
Matthias Apitz
e <guru@unixarea.de> - w http://www.unixarea.de/
UNIX since V7 on PDP-11, UNIX on mainframe since ESER 1055 (IBM /370)
UNIX on x86 since SVR4.2 UnixWare 2.1.2, FreeBSD since 2.2.5



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120713094604.GA1282>