/* IPv6 server code using getaddrinfo */ #include #include #include #include #include #include #include #include #include #include #include #define PORT "39999" void doit(int fd) { printf("child forked ...\n"); sleep(300); printf("child ended\n"); } printaddr(struct addrinfo * ans) { if (ans->ai_family == PF_INET6) { struct sockaddr_in6 *sa; char str[INET6_ADDRSTRLEN]; sa = (struct sockaddr_in6 *) ans->ai_addr; inet_ntop(AF_INET6, &(sa->sin6_addr), str, INET6_ADDRSTRLEN); printf("family: %d (PF_INET6) sin6_addr: [%s]\n", PF_INET6, str); } if (ans->ai_family == PF_INET) { struct sockaddr_in *sa; char str[INET_ADDRSTRLEN]; sa = (struct sockaddr_in *) ans->ai_addr; inet_ntop(AF_INET, &(sa->sin_addr), str, INET_ADDRSTRLEN); printf("family: %d (PF_INET) sin_addr: [%s]\n", PF_INET, str); } } main(int argc, char **argv) { struct addrinfo req, *ans, *ans2; int code, sockFd1, sockFd2, len; int dummy = 1; /* * man getaddrinfo says: * * int getaddrinfo(const char *hostname, const char *servname, * const struct addrinfo *hints, struct addrinfo **res); * * The getaddrinfo() function is used to get a list of IP addresses and port * numbers for host hostname and service servname. It is a replacement for * and provides more flexibility than the gethostbyname(3) and * getservbyname(3) functions. * * The hostname and servname arguments are either pointers to NUL-terminated * strings or the null pointer. An acceptable value for hostname is either * a valid host name or a numeric host address string consisting of a dotted * decimal IPv4 address or an IPv6 address. The servname is either a deci- * mal port number or a service name listed in services(5). At least one of * hostname and servname must be non-null. */ /* 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; if ((code = getaddrinfo(NULL, PORT, &req, &ans)) != 0) { fprintf(stderr, "SLNP (%s): getaddrinfo failed code %d: %s\n", PORT, 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 if we use PF_UNSPEC */ if( (sockFd1 = socket(ans->ai_family, ans->ai_socktype, ans->ai_protocol)) < 0) { perror("socket"); exit(-1); } /* * set options for socket */ setsockopt(sockFd1, SOL_SOCKET, SO_REUSEADDR, (char *) &dummy, sizeof(dummy)); #ifdef IPV6_V6ONLY /* * see also: http://stackoverflow.com/questions/7474066/how-to-listen-on-all-ipv6-addresses-using-c-sockets-api */ if (ans->ai_family == PF_INET6) setsockopt(sockFd1, IPPROTO_IPV6, IPV6_V6ONLY, &dummy, sizeof(dummy)); #endif printaddr(ans); if (bind(sockFd1, ans->ai_addr, ans->ai_addrlen) < 0) { perror("bind"); close(sockFd1); exit(-1); } /* create the 1st LISTEN with backlog 5 */ printf("LISTEN: %s ...\n", (ans->ai_family == PF_INET6)?"IPv6":"IPv4"); listen(sockFd1, 5); /* if there is a 2nd addrinfo provided by getaddrinfo(3C), 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); } /* * set options for socket */ setsockopt(sockFd2, SOL_SOCKET, SO_REUSEADDR, (char *) &dummy, sizeof(dummy)); #ifdef IPV6_V6ONLY /* * see also: http://stackoverflow.com/questions/7474066/how-to-listen-on-all-ipv6-addresses-using-c-sockets-api */ if (ans2->ai_family == PF_INET6) setsockopt(sockFd2, IPPROTO_IPV6, IPV6_V6ONLY, &dummy, sizeof(dummy)); #endif printaddr(ans2); if (bind(sockFd2, ans2->ai_addr, ans2->ai_addrlen) < 0) { perror("bind"); close(sockFd2); exit(-1); } printf("LISTEN: %s ...\n", (ans2->ai_family == PF_INET6)?"IPv6":"IPv4"); listen(sockFd2, 5); } for (;;) { struct sockaddr_storage from; int newsockFd, len = sizeof(from), readyFd, polled; struct pollfd fds[2]; struct hostent *origin; /* 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, -1 /* 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 (from.ss_family == PF_INET6) { printf("from.ss_family is: PF_INET6\n"); struct sockaddr_in6 *p = &from; char straddr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &(p->sin6_addr), straddr, sizeof(straddr)); printf("origin: %s\n", straddr); } else if (from.ss_family == PF_INET) { struct sockaddr_in *p = &from; printf("from.ss_family is: PF_INET\n"); printf("origin: %s\n", inet_ntoa(p->sin_addr)); } else printf("from.ss_family is: unknow\n"); /* check the origin addr */ /* printf("origin: %s\n", inet_ntoa(from.sin_addr)); */ if (newsockFd < 0) { perror("accept"); exit(-1); } if (fork() == 0) { close(fds[0].fd); close(fds[1].fd); (void) doit(newsockFd); exit(0); } close(newsockFd); } /* Free answers after use */ freeaddrinfo(ans); exit(0); }