]>
Commit | Line | Data |
---|---|---|
527f9951 A |
1 | /* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */ |
2 | ||
3 | #include <darwintest.h> | |
4 | #include <poll.h> | |
5 | #include <sys/socket.h> | |
6 | #include <unistd.h> | |
7 | #include <netinet/in.h> | |
8 | #include <arpa/inet.h> | |
9 | #include <errno.h> | |
10 | #include <TargetConditionals.h> | |
11 | ||
12 | static int | |
13 | sockv6_open(void) | |
14 | { | |
15 | int s; | |
16 | ||
17 | s = socket(AF_INET6, SOCK_DGRAM, 0); | |
18 | T_QUIET; | |
19 | T_ASSERT_POSIX_SUCCESS(s, "socket(AF_INET6, SOCK_DGRAM, 0)"); | |
20 | return (s); | |
21 | } | |
22 | ||
23 | static int | |
24 | sockv6_bind(int s, in_port_t port) | |
25 | { | |
26 | struct sockaddr_in6 sin6; | |
27 | ||
28 | bzero(&sin6, sizeof(sin6)); | |
29 | sin6.sin6_len = sizeof(sin6); | |
30 | sin6.sin6_family = AF_INET6; | |
31 | sin6.sin6_port = port; | |
32 | return (bind(s, (const struct sockaddr *)&sin6, sizeof(sin6))); | |
33 | } | |
34 | ||
35 | static void | |
36 | sockv6_set_v6only(int s) | |
37 | { | |
38 | int on = 1; | |
39 | int ret; | |
40 | ||
41 | ret = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); | |
42 | T_QUIET; | |
43 | T_ASSERT_POSIX_SUCCESS(ret, "setsockopt(%d, IPV6_ONLY)", s); | |
44 | } | |
45 | ||
46 | static bool | |
47 | alloc_and_bind_ports(in_port_t port_start, in_port_t port_end, | |
48 | int bind_attempts) | |
49 | { | |
50 | int bound_count = 0; | |
51 | bool success = true; | |
52 | ||
53 | for (in_port_t i = port_start; success && i <= port_end; i++) { | |
54 | int s6 = -1; | |
55 | int s6_other = -1; | |
56 | int ret; | |
57 | ||
58 | s6 = sockv6_open(); | |
59 | sockv6_set_v6only(s6); | |
60 | if (sockv6_bind(s6, i) != 0) { | |
61 | /* find the next available port */ | |
62 | goto loop_done; | |
63 | } | |
64 | s6_other = sockv6_open(); | |
65 | ret = sockv6_bind(s6_other, i); | |
66 | T_WITH_ERRNO; | |
67 | T_QUIET; | |
68 | T_ASSERT_TRUE(ret != 0, "socket %d bind %d", s6_other, i); | |
69 | /* | |
70 | * After bind fails, try binding to a different port. | |
71 | * For non-root user, this will panic without the fix for | |
72 | * <rdar://problem/35243417>. | |
73 | */ | |
74 | if (sockv6_bind(s6_other, i + 1) == 0) { | |
75 | bound_count++; | |
76 | if (bound_count >= bind_attempts) { | |
77 | break; | |
78 | } | |
79 | } | |
80 | loop_done: | |
81 | if (s6 >= 0) { | |
82 | close(s6); | |
83 | } | |
84 | if (s6_other >= 0) { | |
85 | close(s6_other); | |
86 | } | |
87 | } | |
88 | T_ASSERT_TRUE(bound_count == bind_attempts, | |
89 | "number of successful binds %d (out of %d)", | |
90 | bound_count, bind_attempts); | |
91 | return (success); | |
92 | } | |
93 | ||
94 | ||
95 | T_DECL(socket_bind_35243417, | |
96 | "bind IPv6 only UDP socket, then bind IPv6 socket.", | |
97 | T_META_ASROOT(false), | |
98 | T_META_CHECK_LEAKS(false)) | |
99 | { | |
100 | #if TARGET_OS_WATCH | |
101 | T_SKIP("socket_bind_35243417 can't run on watch."); | |
102 | #else | |
103 | alloc_and_bind_ports(1, 65534, 10); | |
104 | #endif | |
105 | } | |
106 | ||
107 | T_DECL(socket_bind_35243417_root, | |
108 | "bind IPv6 only UDP socket, then bind IPv6 socket.", | |
109 | T_META_ASROOT(true)) | |
110 | { | |
111 | #if TARGET_OS_WATCH | |
112 | T_SKIP("socket_bind_35243417_root can't run on watch."); | |
113 | #else | |
114 | alloc_and_bind_ports(1, 65534, 10); | |
115 | #endif | |
116 | } |