]>
Commit | Line | Data |
---|---|---|
a39ff7e2 A |
1 | /* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */ |
2 | ||
3 | #include <darwintest.h> | |
4 | #include <stdio.h> | |
5 | #include <unistd.h> | |
6 | #include <stdlib.h> | |
7 | #include <string.h> | |
8 | #include <sys/socket.h> | |
9 | #include <netinet/in.h> | |
10 | #include <arpa/inet.h> | |
11 | #include <errno.h> | |
12 | #include <pthread.h> | |
13 | #include <stdbool.h> | |
cb323159 A |
14 | #include <TargetConditionals.h> |
15 | ||
16 | T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true)); | |
a39ff7e2 A |
17 | |
18 | static bool debug; | |
19 | ||
20 | static int | |
21 | sock_open_common(int pf, int type) | |
22 | { | |
0a7de745 | 23 | int s; |
a39ff7e2 A |
24 | |
25 | s = socket(pf, type, 0); | |
26 | T_QUIET; | |
27 | T_ASSERT_POSIX_SUCCESS(s, "socket(%d, %d, 0)", pf, type); | |
0a7de745 | 28 | return s; |
a39ff7e2 A |
29 | } |
30 | ||
31 | static int | |
32 | sock_open(int type) | |
33 | { | |
0a7de745 | 34 | return sock_open_common(PF_INET, type); |
a39ff7e2 A |
35 | } |
36 | ||
37 | static int | |
38 | sock_bind(int s, int port) | |
39 | { | |
0a7de745 | 40 | struct sockaddr_in sin = { |
a39ff7e2 A |
41 | .sin_len = sizeof(sin), |
42 | .sin_family = AF_INET, | |
43 | }; | |
44 | ||
45 | sin.sin_port = htons(port); | |
0a7de745 | 46 | return bind(s, (const struct sockaddr *)&sin, sizeof(sin)); |
a39ff7e2 A |
47 | } |
48 | ||
49 | static int | |
50 | sockv6_open(int type) | |
51 | { | |
0a7de745 | 52 | return sock_open_common(PF_INET6, type); |
a39ff7e2 A |
53 | } |
54 | ||
55 | static int | |
56 | sockv6_bind(int s, int port) | |
57 | { | |
0a7de745 | 58 | struct sockaddr_in6 sin6 = { |
a39ff7e2 A |
59 | .sin6_len = sizeof(sin6), |
60 | .sin6_family = AF_INET6, | |
61 | }; | |
62 | ||
63 | sin6.sin6_port = htons(port); | |
0a7de745 | 64 | return bind(s, (const struct sockaddr *)&sin6, sizeof(sin6)); |
a39ff7e2 A |
65 | } |
66 | ||
67 | static uint16_t | |
68 | sock_get_port(int sockfd) | |
69 | { | |
0a7de745 A |
70 | int error; |
71 | uint16_t p; | |
72 | union sockaddr_in_4_6 sin; | |
73 | socklen_t sin_len; | |
a39ff7e2 A |
74 | |
75 | sin_len = sizeof(sin); | |
76 | bzero(&sin, sin_len); | |
77 | error = getsockname(sockfd, (struct sockaddr *)&sin, &sin_len); | |
78 | T_QUIET; | |
79 | T_EXPECT_POSIX_ZERO(error, "getsockname(%d)", sockfd); | |
80 | if (error != 0) { | |
0a7de745 | 81 | return 0; |
a39ff7e2 A |
82 | } |
83 | switch (sin.sa.sa_family) { | |
84 | case AF_INET: | |
85 | p = sin.sin.sin_port; | |
86 | break; | |
87 | case AF_INET6: | |
88 | p = sin.sin6.sin6_port; | |
89 | break; | |
90 | default: | |
91 | T_ASSERT_FAIL("unknown address family %d\n", | |
0a7de745 | 92 | sin.sa.sa_family); |
a39ff7e2 A |
93 | p = 0; |
94 | break; | |
95 | } | |
0a7de745 | 96 | return p; |
a39ff7e2 A |
97 | } |
98 | ||
99 | typedef struct { | |
0a7de745 A |
100 | bool v6; |
101 | int socket_count; | |
102 | int * socket_list; | |
a39ff7e2 A |
103 | } SocketInfo, * SocketInfoRef; |
104 | ||
105 | static void | |
106 | bind_sockets(SocketInfoRef info, const char * msg) | |
107 | { | |
108 | for (int i = 0; i < info->socket_count; i++) { | |
0a7de745 A |
109 | int error; |
110 | uint16_t port; | |
a39ff7e2 A |
111 | |
112 | if (info->v6) { | |
113 | error = sockv6_bind(info->socket_list[i], 0); | |
0a7de745 | 114 | } else { |
a39ff7e2 A |
115 | error = sock_bind(info->socket_list[i], 0); |
116 | } | |
117 | port = sock_get_port(info->socket_list[i]); | |
118 | if (debug) { | |
119 | T_LOG( "%s: fd %d port is %d error %d", | |
0a7de745 | 120 | msg, info->socket_list[i], ntohs(port), error); |
a39ff7e2 A |
121 | } |
122 | } | |
123 | return; | |
124 | } | |
125 | ||
126 | static void * | |
127 | second_thread(void * arg) | |
128 | { | |
0a7de745 | 129 | SocketInfoRef info = (SocketInfoRef)arg; |
a39ff7e2 A |
130 | |
131 | bind_sockets(info, "second"); | |
0a7de745 | 132 | return NULL; |
a39ff7e2 A |
133 | } |
134 | ||
135 | static void | |
136 | multithreaded_bind_test(bool v6, int socket_count) | |
137 | { | |
0a7de745 A |
138 | int error; |
139 | SocketInfo info; | |
140 | int socket_list[socket_count]; | |
141 | pthread_t thread; | |
a39ff7e2 A |
142 | |
143 | info.v6 = v6; | |
144 | for (int i = 0; i < socket_count; i++) { | |
145 | if (v6) { | |
146 | socket_list[i] = sockv6_open(SOCK_STREAM); | |
147 | } else { | |
148 | socket_list[i] = sock_open(SOCK_STREAM); | |
149 | } | |
150 | } | |
151 | info.socket_count = socket_count; | |
152 | info.socket_list = socket_list; | |
153 | error = pthread_create(&thread, NULL, second_thread, &info); | |
154 | T_QUIET; | |
155 | T_ASSERT_POSIX_ZERO(error, "pthread_create"); | |
156 | ||
157 | /* compete with second thread */ | |
158 | bind_sockets(&info, "main"); | |
159 | error = pthread_join(thread, NULL); | |
160 | T_QUIET; | |
161 | T_ASSERT_POSIX_ZERO(error, "pthread_join"); | |
162 | ||
163 | for (int i = 0; i < socket_count; i++) { | |
0a7de745 | 164 | error = close(socket_list[i]); |
a39ff7e2 A |
165 | T_QUIET; |
166 | T_ASSERT_POSIX_ZERO(error, "close socket %d", socket_list[i]); | |
167 | } | |
168 | } | |
169 | ||
170 | static void | |
171 | run_multithreaded_bind_test(int number_of_runs, bool v6, int socket_count) | |
172 | { | |
cb323159 A |
173 | #if TARGET_OS_BRIDGE |
174 | T_SKIP("Not enough memory to handle this test"); | |
175 | #else /* TARGET_OS_BRIDGE */ | |
a39ff7e2 A |
176 | for (int i = 0; i < number_of_runs; i++) { |
177 | multithreaded_bind_test(v6, socket_count); | |
178 | } | |
179 | T_PASS("multithreaded_bind_test %s", v6 ? "IPv6" : "IPv4"); | |
cb323159 | 180 | #endif /* TARGET_OS_BRIDGE */ |
a39ff7e2 A |
181 | } |
182 | ||
183 | T_DECL(socket_bind_35685803, | |
0a7de745 A |
184 | "multithreaded bind IPv4 socket as root", |
185 | T_META_ASROOT(false), | |
186 | T_META_CHECK_LEAKS(false)) | |
a39ff7e2 A |
187 | { |
188 | run_multithreaded_bind_test(100, false, 100); | |
189 | } | |
190 | ||
191 | T_DECL(socket_bind_35685803_root, | |
0a7de745 A |
192 | "multithreaded bind IPv4 socket", |
193 | T_META_ASROOT(true)) | |
a39ff7e2 A |
194 | { |
195 | run_multithreaded_bind_test(100, false, 100); | |
196 | } | |
197 | ||
198 | T_DECL(socket_bind_35685803_v6, | |
0a7de745 A |
199 | "multithreaded bind IPv6 socket as root", |
200 | T_META_ASROOT(false), | |
201 | T_META_CHECK_LEAKS(false)) | |
a39ff7e2 A |
202 | { |
203 | run_multithreaded_bind_test(100, true, 100); | |
204 | } | |
205 | ||
206 | T_DECL(socket_bind_35685803_v6_root, | |
0a7de745 A |
207 | "multithreaded bind IPv6 socket", |
208 | T_META_ASROOT(true)) | |
a39ff7e2 A |
209 | { |
210 | run_multithreaded_bind_test(100, true, 100); | |
211 | } |