IPv6 && getaddrinfo(3C)
Matthias Apitz
guru at unixarea.de
Fri Jul 13 09:46:08 UTC 2012
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 at 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
More information about the freebsd-questions
mailing list