]> git.ozlabs.org Git - ccan/blobdiff - ccan/net/test/run-bind.c
net: add server support.
[ccan] / ccan / net / test / run-bind.c
diff --git a/ccan/net/test/run-bind.c b/ccan/net/test/run-bind.c
new file mode 100644 (file)
index 0000000..9fb2081
--- /dev/null
@@ -0,0 +1,212 @@
+#include <ccan/net/net.h>
+#include <ccan/tap/tap.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+static int ipv6_only;
+
+#ifdef IPV6_V6ONLY
+static int my_setsockopt(int sockfd, int level, int optname,
+                        const void *optval, socklen_t optlen)
+{
+       int ret;
+       setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
+                  sizeof(ipv6_only));
+       ret = setsockopt(sockfd, level, optname, optval, optlen);
+       return ret;
+}
+#define setsockopt my_setsockopt
+#endif
+
+#include <ccan/net/net.c>
+
+#define TEST_PORT "65001"
+
+static void do_connect(int family, int type)
+{
+       int fd, ret;
+       struct addrinfo *addr;
+       char buf[8];
+
+       /* Just in case... */
+       alarm(5);
+       addr = net_client_lookup(NULL, TEST_PORT, family, type);
+       fd = net_connect(addr);
+       if (fd < 0)
+               err(1, "Failed net_connect");
+       freeaddrinfo(addr);
+
+       ret = write(fd, "Yay!", strlen("Yay!"));
+       if (ret != strlen("Yay!"))
+               err(1, "Write returned %i", ret);
+       ret = read(fd, buf, sizeof(buf));
+       if (ret != 5)
+               err(1, "Read returned %i", ret);
+       if (memcmp(buf, "metoo", ret) != 0)
+               err(1, "Read returned '%.*s'", ret, buf);
+       close(fd);
+}
+
+static int wait_for_readable(int fds[2], int num_fds)
+{
+       int i, max_fd = -1;
+       fd_set set;
+
+       FD_ZERO(&set);
+       for (i = 0; i < num_fds; i++) {
+               if (fds[i] > max_fd)
+                       max_fd = fds[i];
+               FD_SET(fds[i], &set);
+       }
+
+       select(max_fd+1, &set, NULL, NULL, NULL);
+       for (i = 0; i < num_fds; i++) {
+               if (FD_ISSET(fds[i], &set))
+                       return i;
+       }
+       return num_fds+1;
+}
+
+int main(void)
+{
+       struct addrinfo *addr;
+       int fds[2], num_fds, i, fd, status, ret;
+       char buf[20];
+       union {
+               struct sockaddr addr;
+               struct sockaddr_in ipv4;
+               struct sockaddr_in6 ipv6;
+       } remote_addr;
+       socklen_t addlen = sizeof(remote_addr);
+
+       plan_tests(35);
+
+       /* Simple TCP test. */
+       addr = net_server_lookup(TEST_PORT, AF_UNSPEC, SOCK_STREAM);
+       ok1(addr);
+       num_fds = net_bind(addr, fds);
+       ok1(num_fds == 1 || num_fds == 2);
+
+       if (!fork()) {
+               for (i = 0; i < num_fds; i++)
+                       close(fds[i]);
+               do_connect(AF_UNSPEC, SOCK_STREAM);
+               exit(0);
+       }
+
+       i = wait_for_readable(fds, num_fds);
+       ok1(i < num_fds);
+       fd = accept(fds[i], NULL, NULL);
+       ok1(fd >= 0);
+
+       ret = read(fd, buf, strlen("Yay!"));
+       ok1(ret == strlen("Yay!"));
+       ok1(memcmp(buf, "Yay!", ret) == 0);
+       ret = write(fd, "metoo", strlen("metoo"));
+       ok1(ret == strlen("metoo"));
+       ok1(wait(&status) != -1);
+       ok1(WIFEXITED(status));
+       ok1(WEXITSTATUS(status) == 0);
+       close(fd);
+       for (i = 0; i < num_fds; i++)
+               close(fds[i]);
+
+       /* Simple UDP test. */
+       addr = net_server_lookup(TEST_PORT, AF_UNSPEC, SOCK_DGRAM);
+       ok1(addr);
+       num_fds = net_bind(addr, fds);
+       ok1(num_fds == 1 || num_fds == 2);
+
+       if (!fork()) {
+               for (i = 0; i < num_fds; i++)
+                       close(fds[i]);
+               do_connect(AF_UNSPEC, SOCK_DGRAM);
+               exit(0);
+       }
+
+       i = wait_for_readable(fds, num_fds);
+       ok1(i < num_fds);
+       fd = fds[i];
+
+       ret = recvfrom(fd, buf, strlen("Yay!"), 0,
+                      (void *)&remote_addr, &addlen);
+       ok1(ret == strlen("Yay!"));
+       ok1(memcmp(buf, "Yay!", ret) == 0);
+       ret = sendto(fd, "metoo", strlen("metoo"), 0,
+                    (void *)&remote_addr, addlen);
+       ok1(ret == strlen("metoo"));
+       ok1(wait(&status) >= 0);
+       ok1(WIFEXITED(status));
+       ok1(WEXITSTATUS(status) == 0);
+       close(fd);
+       for (i = 0; i < num_fds; i++)
+               close(fds[i]);
+
+/* This seems like a Linux-only extension */
+#ifdef IPV6_V6ONLY
+       /* Try to force separate sockets for IPv4/IPv6, if we can. */
+       if (addr->ai_next)
+               ipv6_only = true;
+#endif
+
+       if (ipv6_only) {
+               int j;
+
+               addr = net_server_lookup(TEST_PORT, AF_UNSPEC, SOCK_STREAM);
+               ok1(addr);
+               num_fds = net_bind(addr, fds);
+               ok1(num_fds == 2);
+               freeaddrinfo(addr);
+
+               if (!fork()) {
+                       for (i = 0; i < num_fds; i++)
+                               close(fds[i]);
+                       do_connect(AF_INET, SOCK_STREAM);
+                       do_connect(AF_INET6, SOCK_STREAM);
+                       exit(0);
+               }
+
+               i = wait_for_readable(fds, num_fds);
+               ok1(i < num_fds);
+               fd = accept(fds[i], NULL, NULL);
+               ok1(fd >= 0);
+
+               ret = read(fd, buf, strlen("Yay!"));
+               ok1(ret == strlen("Yay!"));
+               ok1(memcmp(buf, "Yay!", ret) == 0);
+               ret = write(fd, "metoo", strlen("metoo"));
+               ok1(ret == strlen("metoo"));
+               close(fd);
+
+               j = wait_for_readable(fds, num_fds);
+               ok1(j < num_fds);
+               ok1(j != i);
+               fd = accept(fds[j], NULL, NULL);
+               ok1(fd >= 0);
+
+               ret = read(fd, buf, strlen("Yay!"));
+               ok1(ret == strlen("Yay!"));
+               ok1(memcmp(buf, "Yay!", ret) == 0);
+               ret = write(fd, "metoo", strlen("metoo"));
+               ok1(ret == strlen("metoo"));
+
+               ok1(wait(&status) >= 0);
+               ok1(WIFEXITED(status));
+               ok1(WEXITSTATUS(status) == 0);
+               close(fd);
+       } else
+               skip(16, "No support for IPv6-only binding");
+
+       for (i = 0; i < num_fds; i++)
+               close(fds[i]);
+
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}