X-Git-Url: http://git.ozlabs.org/?a=blobdiff_plain;f=ccan%2Fnet%2Ftest%2Frun-bind.c;fp=ccan%2Fnet%2Ftest%2Frun-bind.c;h=9fb2081db3396a128027b984625d8c9350bb15a2;hb=81f2204ceaec3bc99442344c87e2872747c2100a;hp=0000000000000000000000000000000000000000;hpb=8216fa0be7d9bf39774a192d52fef955f6bd7cd8;p=ccan diff --git a/ccan/net/test/run-bind.c b/ccan/net/test/run-bind.c new file mode 100644 index 00000000..9fb2081d --- /dev/null +++ b/ccan/net/test/run-bind.c @@ -0,0 +1,212 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + +#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(); +}