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>