1 /*
2 * Copyright © 2019 Manuel Stoeckl
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26 #include "common.h"
27 #include "shadow.h"
28
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <signal.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/socket.h>
36 #include <time.h>
37 #include <unistd.h>
38
shadow_sync(struct fd_translation_map * src_map,struct fd_translation_map * dst_map)39 static int shadow_sync(struct fd_translation_map *src_map,
40 struct fd_translation_map *dst_map)
41 {
42 struct transfer_queue queue;
43 memset(&queue, 0, sizeof(queue));
44 pthread_mutex_init(&queue.async_recv_queue.lock, NULL);
45
46 read_readable_pipes(src_map);
47
48 for (struct shadow_fd_link *lcur = src_map->link.l_next,
49 *lnxt = lcur->l_next;
50 lcur != &src_map->link;
51 lcur = lnxt, lnxt = lcur->l_next) {
52 struct shadow_fd *sfd = (struct shadow_fd *)lcur;
53 collect_update(NULL, sfd, &queue, false);
54 /* collecting updates can reset `remote_can_X` state, so
55 * garbage collect the sfd */
56 destroy_shadow_if_unreferenced(sfd);
57 }
58 for (int i = 0; i < queue.end; i++) {
59 if (queue.vecs[i].iov_len < 8) {
60 cleanup_transfer_queue(&queue);
61 wp_error("Invalid message");
62 return -1;
63 }
64 const uint32_t *header =
65 (const uint32_t *)queue.vecs[i].iov_base;
66 struct bytebuf msg;
67 msg.data = queue.vecs[i].iov_base;
68 msg.size = transfer_size(header[0]);
69 if (apply_update(dst_map, NULL, NULL, transfer_type(header[0]),
70 (int32_t)header[1], &msg) == -1) {
71 wp_error("Update failed");
72 cleanup_transfer_queue(&queue);
73 return -1;
74 }
75 }
76 flush_writable_pipes(dst_map);
77
78 int nt = queue.end;
79 cleanup_transfer_queue(&queue);
80 return nt;
81 }
82
create_pseudo_pipe(bool can_read,bool can_write,bool half_open_socket,int * spec_end,int * opp_end)83 static int create_pseudo_pipe(bool can_read, bool can_write,
84 bool half_open_socket, int *spec_end, int *opp_end)
85 {
86 bool pipe_possible = can_read != can_write;
87 int pipe_fds[2];
88 if (half_open_socket || !pipe_possible) {
89 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) == -1) {
90 wp_error("Socketpair failed");
91 return -1;
92 }
93 if (!can_read) {
94 shutdown(pipe_fds[0], SHUT_RD);
95 }
96 if (!can_write) {
97 shutdown(pipe_fds[0], SHUT_WR);
98 }
99 } else {
100 if (pipe(pipe_fds) == -1) {
101 wp_error("Pipe failed");
102 return -1;
103 }
104 if (can_write) {
105 int tmp = pipe_fds[0];
106 pipe_fds[0] = pipe_fds[1];
107 pipe_fds[1] = tmp;
108 }
109 }
110 *spec_end = pipe_fds[0];
111 *opp_end = pipe_fds[1];
112 return 0;
113 }
114
fd_is_readable(int fd)115 static char fd_is_readable(int fd)
116 {
117 int flags = fcntl(fd, F_GETFL, 0);
118 if (flags == -1) {
119 wp_error("fctnl F_GETFL failed!");
120 return '?';
121 }
122 flags = flags & O_ACCMODE;
123 return (flags == O_RDONLY || flags == O_RDWR) ? 'R' : 'n';
124 }
125
fd_is_writable(int fd)126 static char fd_is_writable(int fd)
127 {
128 int flags = fcntl(fd, F_GETFL, 0);
129 if (flags == -1) {
130 wp_error("fctnl F_GETFL failed!");
131 return '?';
132 }
133 flags = flags & O_ACCMODE;
134 return (flags == O_WRONLY || flags == O_RDWR) ? 'W' : 'n';
135 }
136
print_pipe_state(const char * desc,struct pipe_state * p)137 static void print_pipe_state(const char *desc, struct pipe_state *p)
138 {
139 printf("%s state: %c %c %c %c%s\n", desc, p->can_read ? 'R' : 'n',
140 p->can_write ? 'W' : 'n',
141 p->remote_can_read ? 'R' : 'n',
142 p->remote_can_write ? 'W' : 'n',
143 p->pending_w_shutdown ? " shutdownWpending" : "");
144 }
145
test_pipe_mirror(bool close_src,bool can_read,bool can_write,bool half_open_socket,bool interpret_as_force_iw)146 static bool test_pipe_mirror(bool close_src, bool can_read, bool can_write,
147 bool half_open_socket, bool interpret_as_force_iw)
148 {
149 if (can_read == can_write && half_open_socket) {
150 return true;
151 }
152 printf("\nTesting:%s%s%s%s%s\n", can_read ? " read" : "",
153 can_write ? " write" : "",
154 half_open_socket ? " socket" : "",
155 interpret_as_force_iw ? " force_iw" : "",
156 close_src ? " close_src" : " close_dst");
157 int spec_end, opp_end, anti_end = -1;
158 if (create_pseudo_pipe(can_read, can_write, half_open_socket, &spec_end,
159 &opp_end) == -1) {
160 return false;
161 }
162
163 struct fd_translation_map src_map;
164 setup_translation_map(&src_map, false);
165
166 struct fd_translation_map dst_map;
167 setup_translation_map(&dst_map, true);
168
169 bool success = true;
170
171 /* Step 1: replicate */
172 struct shadow_fd *src_shadow = translate_fd(&src_map, NULL, spec_end,
173 FDC_PIPE, 0, NULL, false, interpret_as_force_iw);
174 shadow_decref_transfer(src_shadow);
175 int rid = src_shadow->remote_id;
176 if (shadow_sync(&src_map, &dst_map) == -1) {
177 success = false;
178 goto cleanup;
179 }
180 struct shadow_fd *dst_shadow = get_shadow_for_rid(&dst_map, rid);
181 if (!dst_shadow) {
182 printf("Failed to create remote shadow structure\n");
183 success = false;
184 goto cleanup;
185 }
186 anti_end = dup(dst_shadow->fd_local);
187 shadow_decref_transfer(dst_shadow);
188
189 if (set_nonblocking(anti_end) == -1 || set_nonblocking(opp_end) == -1) {
190 printf("Failed to make user fds nonblocking\n");
191 success = false;
192 goto cleanup;
193 }
194 printf("spec %c %c %c %c | opp %c %c | anti %c %c\n",
195 can_read ? 'R' : 'n', can_write ? 'W' : 'n',
196 fd_is_readable(spec_end), fd_is_writable(spec_end),
197 fd_is_readable(opp_end), fd_is_writable(opp_end),
198 fd_is_readable(anti_end), fd_is_writable(anti_end));
199
200 print_pipe_state("dst", &dst_shadow->pipe);
201 print_pipe_state("src", &src_shadow->pipe);
202
203 /* Step 2: transfer tests */
204 for (int i = 0; i < 4; i++) {
205 bool from_src = i % 2;
206
207 /* Smaller than a pipe buffer, so writing should always succeed
208 */
209 char buf[4096];
210 memset(buf, rand(), sizeof(buf));
211
212 int write_fd = from_src ? opp_end : anti_end;
213 int read_fd = from_src ? anti_end : opp_end;
214 const char *target = from_src ? "src" : "dst";
215 const char *antitarget = from_src ? "dst" : "src";
216
217 if (fd_is_writable(write_fd) != 'W') {
218 /* given proper replication, the reverse end should
219 * be readable */
220 continue;
221 }
222
223 int amt = max(rand() % 4096, 1);
224 ssize_t ret = write(write_fd, buf, (size_t)amt);
225 if (ret == amt) {
226 struct shadow_fd *mod_sfd =
227 from_src ? src_shadow : dst_shadow;
228 mod_sfd->pipe.readable = true;
229
230 /* Write successful */
231 if (shadow_sync(from_src ? &src_map : &dst_map,
232 from_src ? &dst_map : &src_map) ==
233 -1) {
234 success = false;
235 goto cleanup;
236 }
237
238 bool believe_read = can_read && !interpret_as_force_iw;
239 bool expect_transfer_fail =
240 (from_src && !believe_read) ||
241 (!from_src && !can_write);
242
243 // todo: try multiple sync cycles (?)
244 ssize_t rr = read(read_fd, buf, 4096);
245 bool tf_pass = rr == amt;
246 if (!expect_transfer_fail) {
247 /* on some systems, pipe is bidirectional,
248 * making some additional transfers succeed.
249 * This is fine. */
250 success = success && tf_pass;
251 }
252 const char *resdesc = tf_pass != expect_transfer_fail
253 ? "expected"
254 : "unexpected";
255 if (tf_pass) {
256 printf("Send packet to %s, and received it from %s, %s\n",
257 target, antitarget, resdesc);
258 } else {
259 printf("Failed to receive packet from %s, %d %zd %s, %s\n",
260 antitarget, read_fd, rr,
261 strerror(errno), resdesc);
262 }
263 }
264 }
265
266 /* Step 3: close one end, and verify that the other end is closed */
267 // TODO: test partial shutdowns as well, all 2^4 cases for a single
268 // cycle; and test epipe closing by queuing additional data
269 struct shadow_fd *cls_shadow = close_src ? src_shadow : dst_shadow;
270 if (close_src) {
271 checked_close(opp_end);
272 opp_end = -1;
273 } else {
274 checked_close(anti_end);
275 anti_end = -1;
276 }
277
278 bool shutdown_deletes = (cls_shadow->pipe.can_read &&
279 !cls_shadow->pipe.can_write);
280 /* Special cases, which aren't very important */
281 shutdown_deletes |= (interpret_as_force_iw &&
282 !cls_shadow->pipe.can_write && close_src);
283
284 cls_shadow->pipe.readable = cls_shadow->pipe.can_read;
285 cls_shadow->pipe.writable = cls_shadow->pipe.can_write;
286
287 if (shadow_sync(close_src ? &src_map : &dst_map,
288 close_src ? &dst_map : &src_map) == -1) {
289 success = false;
290 goto cleanup;
291 }
292 bool deleted_shadows = true;
293 if (dst_map.link.l_next != &dst_map.link) {
294 print_pipe_state("dst", &dst_shadow->pipe);
295 deleted_shadows = false;
296 }
297 if (src_map.link.l_next != &src_map.link) {
298 print_pipe_state("src", &src_shadow->pipe);
299 deleted_shadows = false;
300 }
301
302 bool correct_teardown = deleted_shadows == shutdown_deletes;
303 success = success && correct_teardown;
304 printf("Deleted shadows: %c (expected %c)\n",
305 deleted_shadows ? 'Y' : 'n',
306 shutdown_deletes ? 'Y' : 'n');
307
308 printf("Test: %s\n", success ? "pass" : "FAIL");
309 cleanup:
310 if (anti_end != -1) {
311 checked_close(anti_end);
312 }
313 if (opp_end != -1) {
314 checked_close(opp_end);
315 }
316 cleanup_translation_map(&src_map);
317 cleanup_translation_map(&dst_map);
318
319 return success;
320 }
321
322 log_handler_func_t log_funcs[2] = {NULL, test_log_handler};
main(int argc,char ** argv)323 int main(int argc, char **argv)
324 {
325 (void)argc;
326 (void)argv;
327
328 struct sigaction act;
329 act.sa_handler = SIG_IGN;
330 sigemptyset(&act.sa_mask);
331 act.sa_flags = 0;
332 if (sigaction(SIGPIPE, &act, NULL) == -1) {
333 printf("Sigaction failed\n");
334 return EXIT_SUCCESS;
335 }
336
337 srand(0);
338 bool all_success = true;
339 for (uint32_t bits = 0; bits < 32; bits++) {
340 bool pass = test_pipe_mirror(bits & 1, bits & 2, bits & 4,
341 bits & 8, bits & 16);
342 all_success = all_success && pass;
343 }
344 printf("\nSuccess: %c\n", all_success ? 'Y' : 'n');
345 return all_success ? EXIT_SUCCESS : EXIT_FAILURE;
346 }
347