#include <stdio.h>
#include <err.h>
-static unsigned int server(int protocol, int type)
+static int server(int protocol, int type)
{
int sock;
union {
socklen_t addlen = sizeof(addr);
sock = socket(protocol, type, 0);
+ if (sock < 0)
+ return -1;
/* Bind to free port. */
- listen(sock, 0);
+ if (listen(sock, 0) != 0)
+ return -1;
/* Figure out what port it gave us. */
getsockname(sock, &addr.addr, &addlen);
? addr.ipv4.sin_port : addr.ipv6.sin6_port);
}
-int main(void)
+/* Get a localhost on ipv4 and IPv6. Fake it if we can. */
+static struct addrinfo* double_addr_lookup(char* buf, bool *fake_double)
{
struct addrinfo *addr, *addr2;
+
+ addr = net_client_lookup("localhost", buf, AF_UNSPEC, SOCK_STREAM);
+ if (!addr)
+ return addr;
+
+ /* If we only got one, we need to fake up the other one. */
+ if (addr->ai_next) {
+ addr2 = addr->ai_next;
+ *fake_double = false;
+ } else {
+ /* OK, IPv4 only? */
+ if (addr->ai_family == AF_INET) {
+ /* These are the names I found on my Ubuntu system. */
+ addr2 = net_client_lookup("ip6-localhost", buf,
+ AF_UNSPEC, SOCK_STREAM);
+ if (!addr2)
+ addr2 = net_client_lookup("localhost6", buf,
+ AF_UNSPEC,
+ SOCK_STREAM);
+ if (!addr2)
+ addr2 = net_client_lookup("::1", buf,
+ AF_UNSPEC,
+ SOCK_STREAM);
+ } else if (addr->ai_family == AF_INET6)
+ /* IPv6 only? This is a guess... */
+ addr2 = net_client_lookup("ip4-localhost", buf,
+ AF_UNSPEC, SOCK_STREAM);
+
+ /* Perhaps no support on this system? Go ahead with one. */
+ if (!addr2) {
+ *fake_double = false;
+ return addr;
+ }
+
+ *fake_double = true;
+ addr->ai_next = addr2;
+ }
+
+ /* More than two? */
+ if (addr2->ai_next)
+ return NULL;
+ /* One IPv4 and one IPv6? */
+ if (addr->ai_family == AF_INET && addr2->ai_family == AF_INET6)
+ return addr;
+ if (addr->ai_family == AF_INET6 && addr2->ai_family == AF_INET)
+ return addr;
+ return NULL;
+}
+
+static void double_addr_free(struct addrinfo* addr, bool fake_double)
+{
+ if (fake_double) {
+ freeaddrinfo(addr->ai_next);
+ addr->ai_next = NULL;
+ }
+ freeaddrinfo(addr);
+}
+
+int main(void)
+{
+ struct addrinfo *addr;
int fd, status;
struct sockaddr saddr;
- socklen_t slen = sizeof(saddr);
+ socklen_t slen;
char buf[20];
- unsigned int port;
+ int port;
+ bool fake_double;
- plan_tests(16);
- port = server(AF_INET, SOCK_STREAM);
- sprintf(buf, "%u", port);
+ plan_tests(14);
- addr = net_client_lookup("localhost", buf, AF_UNSPEC, SOCK_STREAM);
- addr2 = net_client_lookup("ip6-localhost", buf,
- AF_UNSPEC, SOCK_STREAM);
- ok1(addr);
- ok1(addr2);
- /* Join them as if they were from one lookup. */
- addr->ai_next = addr2;
-
- fd = net_connect(addr);
- ok1(fd >= 0);
-
- ok1(getsockname(fd, &saddr, &slen) == 0);
- ok1(saddr.sa_family == AF_INET);
- status = read(fd, buf, sizeof(buf));
- ok(status == strlen("Yay!"),
- "Read returned %i (%s)", status, strerror(errno));
- ok1(strncmp(buf, "Yay!", strlen("Yay!")) == 0);
- close(fd);
- addr->ai_next = NULL;
- freeaddrinfo(addr);
- freeaddrinfo(addr2);
+ port = server(AF_INET, SOCK_STREAM);
+ if (port == -1) {
+ /* No IPv4 support? Maybe one day this will happen! */
+ if (errno == EAFNOSUPPORT)
+ skip(6, "No IPv4 socket support");
+ else
+ fail("Could not create IPv4 listening socket: %s",
+ strerror(errno));
+ } else {
+ sprintf(buf, "%u", port);
+
+ addr = double_addr_lookup(buf, &fake_double);
+ ok1(addr);
+ fd = net_connect(addr);
+ ok1(fd >= 0);
+
+ slen = sizeof(saddr);
+ ok1(getsockname(fd, &saddr, &slen) == 0);
+ diag("family = %d", saddr.sa_family);
+ ok1(saddr.sa_family == AF_INET);
+ status = read(fd, buf, sizeof(buf));
+ ok(status == strlen("Yay!"),
+ "Read returned %i (%s)", status, strerror(errno));
+ ok1(strncmp(buf, "Yay!", strlen("Yay!")) == 0);
+ close(fd);
+ double_addr_free(addr, fake_double);
+ }
port = server(AF_INET6, SOCK_STREAM);
- sprintf(buf, "%u", port);
-
- addr = net_client_lookup("localhost", buf, AF_UNSPEC, SOCK_STREAM);
- addr2 = net_client_lookup("ip6-localhost", buf,
- AF_UNSPEC, SOCK_STREAM);
- ok1(addr);
- ok1(addr2);
- /* Join them as if they were from one lookup. */
- addr->ai_next = addr2;
-
- fd = net_connect(addr);
- ok1(fd >= 0);
-
- ok1(getsockname(fd, &saddr, &slen) == 0);
- ok1(saddr.sa_family == AF_INET6);
- status = read(fd, buf, sizeof(buf));
- ok(status == strlen("Yay!"),
- "Read returned %i (%s)", status, strerror(errno));
- ok1(strncmp(buf, "Yay!", strlen("Yay!")) == 0);
- close(fd);
- addr->ai_next = NULL;
- freeaddrinfo(addr);
- freeaddrinfo(addr2);
+ if (port == -1) {
+ /* No IPv6 support? */
+ if (errno == EAFNOSUPPORT)
+ skip(6, "No IPv6 socket support");
+ else
+ fail("Could not create IPv6 listening socket: %s",
+ strerror(errno));
+ } else {
+ sprintf(buf, "%u", port);
+
+ addr = double_addr_lookup(buf, &fake_double);
+ ok1(addr);
+ fd = net_connect(addr);
+ ok1(fd >= 0);
+
+ slen = sizeof(saddr);
+ ok1(getsockname(fd, &saddr, &slen) == 0);
+ ok1(saddr.sa_family == AF_INET6);
+ status = read(fd, buf, sizeof(buf));
+ ok(status == strlen("Yay!"),
+ "Read returned %i (%s)", status, strerror(errno));
+ ok1(strncmp(buf, "Yay!", strlen("Yay!")) == 0);
+ close(fd);
+ double_addr_free(addr, fake_double);
+ }
wait(&status);
ok1(WIFEXITED(status));