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