Description: Implement capability for IPv6. Migration to 'getaddrinfo()' and 'struct sockaddr_storage' make both address families AF_INET and AF_INET6 viable. . The preferred form of a port is as a string value in getaddrinfo(), so named ports are no possible, alongside numerical ports. . The goto statement is left because the previous code enforced a similar construct. It should really be removed. Author: Mats Erik Andersson Forwarded: no Last-Updated: 2010-03-03 Index: netris-0.52/inet.c =================================================================== --- netris-0.52.orig/inet.c +++ netris-0.52/inet.c @@ -49,32 +49,60 @@ ExtFunc void InitNet(void) ExtFunc int WaitForConnection(char *portStr) { - struct sockaddr_in addr; - struct hostent *host; - int sockListen; + struct sockaddr_storage addr; + struct sockaddr_in *sa4 = (struct sockaddr_in *) &addr; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &addr; + struct hostent *host = NULL; + struct addrinfo hints, *ai, *aiptr; + char portStrDef[12]; + int sockListen, status; socklen_t addrLen; - short port; int val1; struct linger val2; - if (portStr) - port = atoi(portStr); /* XXX Error checking */ - else - port = DEFAULT_PORT; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(port); - sockListen = socket(AF_INET, SOCK_STREAM, 0); - if (sockListen < 0) + if (!portStr || !strlen(portStr)) { + snprintf(portStrDef, sizeof(portStrDef), "%u", DEFAULT_PORT); + portStr = portStrDef; + } + /* XXX Error checking of port string. */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + if ( (status = getaddrinfo(NULL, portStr, &hints, &ai)) ) { + fprintf(stderr, "getaddrinfo() failed: %s\n", + gai_strerror(status)); + die("getaddrinfo"); + } + + for (aiptr = ai; aiptr; aiptr = aiptr->ai_next) { + if ( (sockListen = socket(aiptr->ai_family, + aiptr->ai_socktype, + aiptr->ai_protocol)) + < 0 ) + continue; + + val1 = 1; + setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, + (void *)&val1, sizeof(val1)); + val1 = 0; + setsockopt(sockListen, IPPROTO_IPV6, IPV6_V6ONLY, + (void *)&val1, sizeof(val1)); + + if ( bind(sockListen, aiptr->ai_addr, aiptr->ai_addrlen) + == 0 ) + if ( listen(sockListen, 1) >= 0 ) + break; + + close(sockListen); + } + + freeaddrinfo(ai); + if (aiptr == NULL) die("socket"); - val1 = 1; - setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, - (void *)&val1, sizeof(val1)); - if (bind(sockListen, (struct sockaddr *)&addr, sizeof(addr)) < 0) - die("bind"); - if (listen(sockListen, 1) < 0) - die("listen"); + addrLen = sizeof(addr); sock = accept(sockListen, (struct sockaddr *)&addr, &addrLen); if (sock < 0) @@ -86,13 +114,18 @@ ExtFunc int WaitForConnection(char *port (void *)&val2, sizeof(val2)); netGen.fd = sock; strcpy(opponentHost, "???"); - if (addr.sin_family == AF_INET) { - host = gethostbyaddr((void *)&addr.sin_addr, - sizeof(struct in_addr), AF_INET); - if (host) { - strncpy(opponentHost, host->h_name, sizeof(opponentHost)-1); - opponentHost[sizeof(opponentHost)-1] = 0; - } + switch (addr.ss_family) { + case AF_INET6: + host = gethostbyaddr((void *)&sa6->sin6_addr, + sizeof(struct in6_addr), addr.ss_family); + break; + case AF_INET: + host = gethostbyaddr((void *)&sa4->sin_addr, + sizeof(struct in_addr), addr.ss_family); + } + if (host) { + strncpy(opponentHost, host->h_name, sizeof(opponentHost)-1); + opponentHost[sizeof(opponentHost)-1] = 0; } AddEventGen(&netGen); isServer = 1; @@ -101,36 +134,54 @@ ExtFunc int WaitForConnection(char *port ExtFunc int InitiateConnection(char *hostStr, char *portStr) { - struct sockaddr_in addr; - struct hostent *host; - short port; - int mySock; - - if (portStr) - port = atoi(portStr); /* XXX Error checking */ - else - port = DEFAULT_PORT; - host = gethostbyname(hostStr); - if (!host) - die("gethostbyname"); - assert(host->h_addrtype == AF_INET); - strncpy(opponentHost, host->h_name, sizeof(opponentHost)-1); - opponentHost[sizeof(opponentHost)-1] = 0; - again: - memset(&addr, 0, sizeof(addr)); - addr.sin_family = host->h_addrtype; - memcpy(&addr.sin_addr, host->h_addr, host->h_length); - addr.sin_port = htons(port); - mySock = socket(AF_INET, SOCK_STREAM, 0); - if (mySock < 0) - die("socket"); - if (connect(mySock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - if (errno != ECONNREFUSED) - die("connect"); + struct addrinfo hints, *ai, *aiptr; + char portStrDef[12]; + int mySock, status; + + if (!portStr || !strlen(portStr)) { + snprintf(portStrDef, sizeof(portStrDef), "%u", DEFAULT_PORT); + portStr = portStrDef; + } + /* XXX Error checking of port string. */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME; + + if ( (status = getaddrinfo(hostStr, portStr, &hints, &ai)) ) { + fprintf(stderr, "getaddrinfo() failed: %s\n", + gai_strerror(status)); + die("getaddrinfo"); + } + + for (aiptr = ai; aiptr; aiptr = aiptr->ai_next) { +again: + if ( (mySock = socket(aiptr->ai_family, aiptr->ai_socktype, + aiptr->ai_protocol)) + < 0 ) + continue; + while ( (status = connect(mySock, aiptr->ai_addr, + aiptr->ai_addrlen)) < 0 + && errno == ECONNREFUSED ) { + close(mySock); + sleep(1); + goto again; + } + if (status >= 0) + break; + /* Failure to connect. */ close(mySock); - sleep(1); - goto again; } + + if (aiptr == NULL) { + freeaddrinfo(ai); + die("socket/connect"); + } + + strncpy(opponentHost, aiptr->ai_canonname, sizeof(opponentHost)-1); + opponentHost[sizeof(opponentHost)-1] = 0; + freeaddrinfo(ai); netGen.fd = sock = mySock; AddEventGen(&netGen); return 0;