ccan: Correct some poor conventions in _info includes
[ccan] / ccan / net / test / run.c
1 #include <ccan/net/net.h>
2 #include <ccan/net/net.c>
3 #include <ccan/tap/tap.h>
4 #include <sys/types.h>
5 #include <sys/wait.h>
6 #include <stdio.h>
7 #include <err.h>
8
9 static int server(int protocol, int type)
10 {
11         int sock;
12         union {
13                 struct sockaddr addr;
14                 struct sockaddr_in ipv4;
15                 struct sockaddr_in6 ipv6;
16         } addr;
17         socklen_t addlen = sizeof(addr);
18
19         sock = socket(protocol, type, 0);
20         if (sock < 0)
21                 return -1;
22
23         /* Bind to free port. */
24         if (listen(sock, 0) != 0)
25                 return -1;
26
27         /* Figure out what port it gave us. */
28         getsockname(sock, &addr.addr, &addlen);
29         fflush(stdout);
30
31         if (fork() == 0) {
32                 int ret, fd;
33
34                 alarm(3);
35                 fd = accept(sock, NULL, 0);
36                 if (fd < 0)
37                         err(1, "Accepting from socket %i", sock);
38
39                 ret = write(fd, "Yay!", strlen("Yay!"));
40                 if (ret != strlen("Yay!"))
41                         err(1, "Write returned %i", ret);
42                 exit(0);
43         }
44         close(sock);
45         return ntohs(protocol == AF_INET
46                      ? addr.ipv4.sin_port : addr.ipv6.sin6_port);
47 }
48
49 /* Get a localhost on ipv4 and IPv6.  Fake it if we can. */
50 static struct addrinfo* double_addr_lookup(char* buf, bool *fake_double)
51 {
52         struct addrinfo *addr, *addr2;
53
54         addr = net_client_lookup("localhost", buf, AF_UNSPEC, SOCK_STREAM);
55         if (!addr)
56                 return addr;
57
58         /* If we only got one, we need to fake up the other one. */
59         if (addr->ai_next) {
60                 addr2 = addr->ai_next;
61                 *fake_double = false;
62         } else {
63                 /* OK, IPv4 only? */
64                 if (addr->ai_family == AF_INET) {
65                         /* These are the names I found on my Ubuntu system. */
66                         addr2 = net_client_lookup("ip6-localhost", buf,
67                                                   AF_UNSPEC, SOCK_STREAM);
68                         if (!addr2)
69                                 addr2 = net_client_lookup("localhost6", buf,
70                                                           AF_UNSPEC,
71                                                           SOCK_STREAM);
72                         if (!addr2)
73                                 addr2 = net_client_lookup("::1", buf,
74                                                           AF_UNSPEC,
75                                                           SOCK_STREAM);
76                 } else if (addr->ai_family == AF_INET6)
77                         /* IPv6 only?  This is a guess... */
78                         addr2 = net_client_lookup("ip4-localhost", buf,
79                                                   AF_UNSPEC, SOCK_STREAM);
80
81                 /* Perhaps no support on this system?  Go ahead with one. */
82                 if (!addr2) {
83                         *fake_double = false;
84                         return addr;
85                 }
86
87                 *fake_double = true;
88                 addr->ai_next = addr2;
89         }
90
91         /* More than two? */
92         if (addr2->ai_next)
93                 return NULL;
94         /* One IPv4 and one IPv6? */
95         if (addr->ai_family == AF_INET && addr2->ai_family == AF_INET6)
96                 return addr;
97         if (addr->ai_family == AF_INET6 && addr2->ai_family == AF_INET)
98                 return addr;
99         return NULL;
100 }
101
102 static void double_addr_free(struct addrinfo* addr, bool fake_double)
103 {
104         if (fake_double) {
105                 freeaddrinfo(addr->ai_next);
106                 addr->ai_next = NULL;
107         }
108         freeaddrinfo(addr);
109 }
110
111 int main(void)
112 {
113         struct addrinfo *addr;
114         int fd, status;
115         struct sockaddr saddr;
116         socklen_t slen;
117         char buf[20];
118         int port;
119         bool fake_double;
120
121         plan_tests(14);
122
123         port = server(AF_INET, SOCK_STREAM);
124         if (port == -1) {
125                 /* No IPv4 support?  Maybe one day this will happen! */
126                 if (errno == EAFNOSUPPORT)
127                         skip(6, "No IPv4 socket support");
128                 else
129                         fail("Could not create IPv4 listening socket: %s",
130                              strerror(errno));
131         } else {
132                 sprintf(buf, "%u", port);
133
134                 addr = double_addr_lookup(buf, &fake_double);
135                 ok1(addr);
136                 fd = net_connect(addr);
137                 ok1(fd >= 0);
138
139                 slen = sizeof(saddr);
140                 ok1(getsockname(fd, &saddr, &slen) == 0);
141                 diag("family = %d", saddr.sa_family);
142                 ok1(saddr.sa_family == AF_INET);
143                 status = read(fd, buf, sizeof(buf));
144                 ok(status == strlen("Yay!"),
145                    "Read returned %i (%s)", status, strerror(errno));
146                 ok1(strncmp(buf, "Yay!", strlen("Yay!")) == 0);
147                 close(fd);
148                 double_addr_free(addr, fake_double);
149         }
150
151         port = server(AF_INET6, SOCK_STREAM);
152         if (port == -1) {
153                 /* No IPv6 support? */
154                 if (errno == EAFNOSUPPORT)
155                         skip(6, "No IPv6 socket support");
156                 else
157                         fail("Could not create IPv6 listening socket: %s",
158                              strerror(errno));
159         } else {
160                 sprintf(buf, "%u", port);
161
162                 addr = double_addr_lookup(buf, &fake_double);
163                 ok1(addr);
164                 fd = net_connect(addr);
165                 ok1(fd >= 0);
166
167                 slen = sizeof(saddr);
168                 ok1(getsockname(fd, &saddr, &slen) == 0);
169                 ok1(saddr.sa_family == AF_INET6);
170                 status = read(fd, buf, sizeof(buf));
171                 ok(status == strlen("Yay!"),
172                    "Read returned %i (%s)", status, strerror(errno));
173                 ok1(strncmp(buf, "Yay!", strlen("Yay!")) == 0);
174                 close(fd);
175                 double_addr_free(addr, fake_double);
176         }
177
178         wait(&status);
179         ok1(WIFEXITED(status));
180         ok1(WEXITSTATUS(status) == 0);
181
182         /* This exits depending on whether all tests passed */
183         return exit_status();
184 }