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