Line data Source code
1 : /*
2 : * Unix SMB/CIFS implementation.
3 : *
4 : * Copyright (C) 2022 Andrew Bartlett <abartlet@samba.org>
5 : * Copyright (C) 2021 Andreas Schneider <asn@samba.org>
6 : *
7 : * This program is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU General Public License as published by
9 : * the Free Software Foundation; either version 3 of the License, or
10 : * (at your option) any later version.
11 : *
12 : * This program is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU General Public License
18 : * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <stdarg.h>
22 : #include <stddef.h>
23 : #include <stdint.h>
24 : #include <setjmp.h>
25 : #include <cmocka.h>
26 : #include "includes.h"
27 : #include "system/network.h"
28 : #include "socketpair_tcp.h"
29 : #include "tsocket.h"
30 :
31 : enum socket_pair_selector {
32 : SOCKET_SERVER = 0,
33 : SOCKET_CLIENT = 1,
34 : };
35 :
36 : struct socket_pair {
37 : struct tevent_context *ev;
38 : int socket_server;
39 : int socket_client;
40 :
41 : /* for tstream tests */
42 : int rc;
43 : int sys_errno;
44 : int expected_errno;
45 : struct timeval endtime;
46 : size_t max_loops;
47 : size_t num_loops;
48 : };
49 :
50 : /* If this is too large, we get EPIPE rather than EAGAIN */
51 : static const uint8_t TEST_STRING[128] = { 0 };
52 :
53 1 : static int sigpipe_setup(void **state)
54 : {
55 1 : BlockSignals(true, SIGPIPE);
56 1 : return 0;
57 : }
58 :
59 11 : static int setup_socketpair_tcp_context(void **state)
60 : {
61 11 : int fd[2];
62 11 : struct socket_pair *sp = talloc_zero(NULL, struct socket_pair);
63 11 : assert_non_null(sp);
64 :
65 : /* Set up a socketpair over TCP to test with */
66 11 : assert_return_code(socketpair_tcp(fd), errno);
67 :
68 11 : sp->socket_server = fd[SOCKET_SERVER];
69 11 : sp->socket_client = fd[SOCKET_CLIENT];
70 :
71 11 : sp->ev = tevent_context_init(sp);
72 11 : assert_non_null(sp->ev);
73 :
74 11 : *state = sp;
75 11 : return 0;
76 : }
77 :
78 3 : static int setup_socketpair_context(void **state)
79 : {
80 3 : int fd[2];
81 3 : struct socket_pair *sp = talloc_zero(NULL, struct socket_pair);
82 3 : assert_non_null(sp);
83 :
84 : /* Set up a socketpair over TCP to test with */
85 3 : assert_return_code(socketpair(AF_UNIX, SOCK_STREAM, 0, fd), errno);
86 :
87 3 : sp->socket_server = fd[SOCKET_SERVER];
88 3 : sp->socket_client = fd[SOCKET_CLIENT];
89 :
90 3 : sp->ev = tevent_context_init(sp);
91 3 : assert_non_null(sp->ev);
92 :
93 3 : *state = sp;
94 3 : return 0;
95 : }
96 :
97 14 : static int teardown_socketpair_context(void **state)
98 : {
99 14 : struct socket_pair *sp = *state;
100 14 : struct socket_pair sp_save = *sp;
101 :
102 14 : TALLOC_FREE(sp);
103 :
104 : /*
105 : * Close these after the TALLOC_FREE() to allow clean shutdown
106 : * of epoll() in tstream
107 : */
108 14 : if (sp_save.socket_client != -1) {
109 10 : close(sp_save.socket_client);
110 : }
111 14 : if (sp_save.socket_server != -1) {
112 7 : close(sp_save.socket_server);
113 : }
114 14 : return 0;
115 : }
116 :
117 :
118 : /* Test socket behaviour */
119 1 : static void test_simple_socketpair(void **state) {
120 :
121 1 : struct socket_pair *sp = *state;
122 :
123 1 : char buf[sizeof(TEST_STRING)];
124 :
125 1 : assert_int_equal(write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING)),
126 : sizeof(TEST_STRING));
127 1 : assert_int_equal(read(sp->socket_client, buf, sizeof(buf)),
128 : sizeof(buf));
129 :
130 :
131 1 : }
132 :
133 : /* Test socket behaviour */
134 1 : static void test_read_client_after_close_server_socket(void **state) {
135 :
136 1 : struct socket_pair *sp = *state;
137 1 : int rc;
138 1 : char buf[sizeof(TEST_STRING)];
139 :
140 1 : rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
141 1 : assert_return_code(rc, errno);
142 1 : assert_int_equal(rc, sizeof(TEST_STRING));
143 :
144 1 : assert_return_code(close(sp->socket_server), 0);
145 :
146 1 : rc = read(sp->socket_client, buf, sizeof(buf));
147 :
148 1 : assert_return_code(rc, errno);
149 1 : assert_int_equal(rc, sizeof(buf));
150 1 : }
151 :
152 1 : static void test_write_server_after_close_client_socket(void **state) {
153 :
154 1 : struct socket_pair *sp = *state;
155 1 : int rc;
156 :
157 1 : assert_return_code(close(sp->socket_client), 0);
158 1 : sp->socket_client = -1;
159 :
160 1 : rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
161 1 : assert_return_code(rc, errno);
162 1 : assert_int_equal(rc, sizeof(TEST_STRING));
163 1 : }
164 :
165 11 : static void test_fill_socket(int sock)
166 : {
167 11 : size_t num_busy = 0;
168 165030 : int rc;
169 :
170 165030 : while (true) {
171 165030 : rc = write(sock, TEST_STRING, sizeof(TEST_STRING));
172 165030 : if (rc == -1 && errno == EAGAIN) {
173 : /*
174 : * This makes sure we write until we get a whole second
175 : * only with EAGAIN every 50 ms (20 times)
176 : *
177 : * Otherwise the tests are not reliable...
178 : */
179 231 : num_busy++;
180 231 : if (num_busy > 20) {
181 : break;
182 : }
183 220 : smb_msleep(50);
184 220 : continue;
185 : }
186 : /* try again next time */
187 : num_busy = 0;
188 : }
189 :
190 11 : assert_int_equal(rc, -1);
191 11 : assert_int_equal(errno, EAGAIN);
192 11 : }
193 :
194 1 : static void test_big_write_server(void **state) {
195 :
196 1 : struct socket_pair *sp = *state;
197 1 : int rc;
198 :
199 1 : rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
200 1 : assert_return_code(rc, errno);
201 1 : assert_int_equal(rc, sizeof(TEST_STRING));
202 :
203 1 : rc = set_blocking(sp->socket_server, 0);
204 1 : assert_return_code(rc, errno);
205 :
206 1 : test_fill_socket(sp->socket_server);
207 1 : }
208 :
209 1 : static void test_big_write_server_close_write(void **state) {
210 :
211 1 : struct socket_pair *sp = *state;
212 1 : int rc;
213 :
214 1 : rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
215 1 : assert_return_code(rc, errno);
216 1 : assert_int_equal(rc, sizeof(TEST_STRING));
217 :
218 1 : rc = set_blocking(sp->socket_server, 0);
219 1 : assert_return_code(rc, errno);
220 :
221 1 : test_fill_socket(sp->socket_server);
222 :
223 1 : assert_return_code(close(sp->socket_client), 0);
224 1 : sp->socket_client = -1;
225 :
226 1 : rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
227 1 : assert_int_equal(errno, ECONNRESET);
228 :
229 1 : }
230 :
231 1 : static void test_big_write_server_shutdown_wr_write(void **state) {
232 :
233 1 : struct socket_pair *sp = *state;
234 1 : int rc;
235 :
236 1 : rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
237 1 : assert_return_code(rc, errno);
238 1 : assert_int_equal(rc, sizeof(TEST_STRING));
239 :
240 1 : rc = set_blocking(sp->socket_server, 0);
241 1 : assert_return_code(rc, errno);
242 :
243 1 : test_fill_socket(sp->socket_server);
244 :
245 1 : assert_return_code(shutdown(sp->socket_client, SHUT_WR), 0);
246 1 : sp->socket_client = -1;
247 :
248 1 : rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
249 1 : assert_int_equal(rc, -1);
250 1 : assert_int_equal(errno, EAGAIN);
251 1 : }
252 :
253 1 : static void test_big_write_server_shutdown_rd_write(void **state) {
254 :
255 1 : struct socket_pair *sp = *state;
256 1 : int rc;
257 :
258 1 : rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
259 1 : assert_return_code(rc, errno);
260 1 : assert_int_equal(rc, sizeof(TEST_STRING));
261 :
262 1 : rc = set_blocking(sp->socket_server, 0);
263 1 : assert_return_code(rc, errno);
264 :
265 1 : test_fill_socket(sp->socket_server);
266 :
267 1 : assert_return_code(shutdown(sp->socket_client, SHUT_RD), 0);
268 1 : sp->socket_client = -1;
269 :
270 1 : rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
271 1 : assert_int_equal(rc, -1);
272 1 : assert_int_equal(errno, EAGAIN);
273 1 : }
274 :
275 7 : static void test_call_writev_done(struct tevent_req *subreq)
276 : {
277 7 : struct socket_pair *sp =
278 7 : tevent_req_callback_data(subreq,
279 : struct socket_pair);
280 7 : int rc;
281 :
282 7 : rc = tstream_writev_recv(subreq, &sp->sys_errno);
283 7 : TALLOC_FREE(subreq);
284 :
285 7 : sp->rc = rc;
286 7 : }
287 :
288 4 : static void test_tstream_server_spin_client_shutdown(struct socket_pair *sp)
289 : {
290 4 : int rc;
291 :
292 4 : rc = shutdown(sp->socket_client, SHUT_WR);
293 4 : assert_return_code(rc, errno);
294 : /*
295 : * It should only take a few additional loop to realise that this socket is
296 : * in CLOSE_WAIT
297 : */
298 4 : sp->max_loops = sp->num_loops + 2;
299 4 : sp->expected_errno = ECONNRESET;
300 4 : }
301 :
302 4 : static void test_tstream_server_spin_client_write(struct socket_pair *sp)
303 : {
304 4 : int rc;
305 4 : int timeout = 5000;
306 :
307 4 : sp->endtime = timeval_current_ofs_msec(timeout);
308 :
309 4 : rc = write(sp->socket_client, TEST_STRING, sizeof(TEST_STRING));
310 4 : assert_return_code(rc, errno);
311 4 : sp->expected_errno = ETIMEDOUT;
312 4 : }
313 :
314 1 : static void test_tstream_server_spin_client_tcp_user_timeout(struct socket_pair *sp)
315 : {
316 1 : int rc;
317 1 : int timeout = 5000;
318 :
319 1 : rc = setsockopt(sp->socket_server, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout, sizeof(timeout));
320 1 : assert_return_code(rc, errno);
321 :
322 1 : rc = write(sp->socket_client, TEST_STRING, sizeof(TEST_STRING));
323 1 : assert_return_code(rc, errno);
324 1 : sp->expected_errno = ETIMEDOUT;
325 1 : sp->max_loops = 30;
326 1 : }
327 :
328 2 : static void test_tstream_server_spin_client_both_timer(struct tevent_context *ev,
329 : struct tevent_timer *te,
330 : struct timeval current_time,
331 : void *private_data)
332 : {
333 2 : struct socket_pair *sp =
334 2 : talloc_get_type_abort(private_data,
335 : struct socket_pair);
336 :
337 2 : test_tstream_server_spin_client_shutdown(sp);
338 2 : }
339 :
340 2 : static void test_tstream_server_spin_client_both(struct socket_pair *sp)
341 : {
342 2 : struct tevent_timer *te = NULL;
343 2 : struct timeval endtime;
344 :
345 2 : test_tstream_server_spin_client_write(sp);
346 :
347 2 : endtime = timeval_current_ofs_msec(2500);
348 :
349 2 : te = tevent_add_timer(sp->ev,
350 : sp,
351 : endtime,
352 : test_tstream_server_spin_client_both_timer,
353 : sp);
354 2 : assert_non_null(te);
355 2 : sp->expected_errno = ENXIO;
356 2 : }
357 :
358 7 : static void test_tstream_server_spin(struct socket_pair *sp,
359 : void (*client_fn)(struct socket_pair *sp))
360 : {
361 7 : struct tstream_context *stream = NULL;
362 7 : struct tevent_req *req = NULL;
363 7 : struct iovec iov;
364 7 : int rc;
365 :
366 7 : rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
367 7 : assert_return_code(rc, errno);
368 7 : assert_int_equal(rc, sizeof(TEST_STRING));
369 :
370 7 : rc = set_blocking(sp->socket_server, 0);
371 7 : assert_return_code(rc, errno);
372 :
373 7 : test_fill_socket(sp->socket_server);
374 :
375 : /*
376 : * by default we don't expect more then 2 loop iterations
377 : * for a timeout of 5 seconds.
378 : */
379 7 : sp->max_loops = 10;
380 :
381 7 : client_fn(sp);
382 :
383 7 : rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING));
384 7 : assert_int_equal(rc, -1);
385 7 : assert_int_equal(errno, EAGAIN);
386 :
387 : /* OK, so we now know the socket is in CLOSE_WAIT */
388 :
389 7 : rc = tstream_bsd_existing_socket(sp->ev, sp->socket_server, &stream);
390 7 : assert_return_code(rc, errno);
391 7 : sp->socket_server = -1;
392 :
393 7 : iov.iov_base = discard_const_p(char, TEST_STRING);
394 7 : iov.iov_len = sizeof(TEST_STRING);
395 :
396 7 : req = tstream_writev_send(stream, sp->ev, stream, &iov, 1);
397 7 : assert_non_null(req);
398 7 : if (!timeval_is_zero(&sp->endtime)) {
399 4 : assert_true(tevent_req_set_endtime(req, sp->ev, sp->endtime));
400 : }
401 7 : tevent_req_set_callback(req, test_call_writev_done, sp);
402 :
403 16 : while (tevent_req_is_in_progress(req)) {
404 9 : if (sp->num_loops >= sp->max_loops) {
405 0 : assert_int_not_equal(sp->num_loops, sp->max_loops);
406 0 : assert_int_equal(sp->num_loops, sp->max_loops);
407 : }
408 9 : sp->num_loops += 1;
409 :
410 9 : rc = tevent_loop_once(sp->ev);
411 9 : assert_int_equal(rc, 0);
412 : }
413 :
414 7 : assert_int_equal(sp->rc, -1);
415 7 : assert_int_equal(sp->sys_errno, sp->expected_errno);
416 7 : return;
417 : }
418 :
419 : /*
420 : * We need two names to run this with the two different setup
421 : * routines
422 : */
423 1 : static void test_tstream_disconnected_tcp_client_spin(void **state)
424 : {
425 1 : struct socket_pair *sp = *state;
426 1 : test_tstream_server_spin(sp, test_tstream_server_spin_client_shutdown);
427 1 : }
428 :
429 1 : static void test_tstream_disconnected_unix_client_spin(void **state)
430 : {
431 1 : struct socket_pair *sp = *state;
432 1 : test_tstream_server_spin(sp, test_tstream_server_spin_client_shutdown);
433 1 : }
434 :
435 1 : static void test_tstream_more_tcp_client_spin(void **state)
436 : {
437 1 : struct socket_pair *sp = *state;
438 1 : test_tstream_server_spin(sp, test_tstream_server_spin_client_write);
439 1 : }
440 :
441 1 : static void test_tstream_more_unix_client_spin(void **state)
442 : {
443 1 : struct socket_pair *sp = *state;
444 1 : test_tstream_server_spin(sp, test_tstream_server_spin_client_write);
445 1 : }
446 :
447 1 : static void test_tstream_more_disconnect_tcp_client_spin(void **state)
448 : {
449 1 : struct socket_pair *sp = *state;
450 1 : test_tstream_server_spin(sp, test_tstream_server_spin_client_both);
451 1 : }
452 :
453 1 : static void test_tstream_more_disconnect_unix_client_spin(void **state)
454 : {
455 1 : struct socket_pair *sp = *state;
456 1 : test_tstream_server_spin(sp, test_tstream_server_spin_client_both);
457 1 : }
458 :
459 1 : static void test_tstream_more_tcp_user_timeout_spin(void **state)
460 : {
461 1 : struct socket_pair *sp = *state;
462 1 : if (socket_wrapper_enabled()) {
463 0 : skip();
464 : }
465 1 : test_tstream_server_spin(sp, test_tstream_server_spin_client_tcp_user_timeout);
466 1 : }
467 :
468 1 : int main(void) {
469 1 : const struct CMUnitTest tests[] = {
470 : cmocka_unit_test_setup_teardown(test_simple_socketpair,
471 : setup_socketpair_tcp_context,
472 : teardown_socketpair_context),
473 : cmocka_unit_test_setup_teardown(test_read_client_after_close_server_socket,
474 : setup_socketpair_tcp_context,
475 : teardown_socketpair_context),
476 : cmocka_unit_test_setup_teardown(test_write_server_after_close_client_socket,
477 : setup_socketpair_tcp_context,
478 : teardown_socketpair_context),
479 : cmocka_unit_test_setup_teardown(test_big_write_server,
480 : setup_socketpair_tcp_context,
481 : teardown_socketpair_context),
482 : cmocka_unit_test_setup_teardown(test_big_write_server_close_write,
483 : setup_socketpair_tcp_context,
484 : teardown_socketpair_context),
485 : cmocka_unit_test_setup_teardown(test_big_write_server_shutdown_wr_write,
486 : setup_socketpair_tcp_context,
487 : teardown_socketpair_context),
488 : cmocka_unit_test_setup_teardown(test_big_write_server_shutdown_rd_write,
489 : setup_socketpair_tcp_context,
490 : teardown_socketpair_context),
491 : cmocka_unit_test_setup_teardown(test_tstream_disconnected_tcp_client_spin,
492 : setup_socketpair_tcp_context,
493 : teardown_socketpair_context),
494 : cmocka_unit_test_setup_teardown(test_tstream_disconnected_unix_client_spin,
495 : setup_socketpair_context,
496 : teardown_socketpair_context),
497 : cmocka_unit_test_setup_teardown(test_tstream_more_tcp_client_spin,
498 : setup_socketpair_tcp_context,
499 : teardown_socketpair_context),
500 : cmocka_unit_test_setup_teardown(test_tstream_more_unix_client_spin,
501 : setup_socketpair_context,
502 : teardown_socketpair_context),
503 : cmocka_unit_test_setup_teardown(test_tstream_more_disconnect_tcp_client_spin,
504 : setup_socketpair_tcp_context,
505 : teardown_socketpair_context),
506 : cmocka_unit_test_setup_teardown(test_tstream_more_disconnect_unix_client_spin,
507 : setup_socketpair_context,
508 : teardown_socketpair_context),
509 : cmocka_unit_test_setup_teardown(test_tstream_more_tcp_user_timeout_spin,
510 : setup_socketpair_tcp_context,
511 : teardown_socketpair_context),
512 : };
513 :
514 1 : cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
515 :
516 1 : return cmocka_run_group_tests(tests, sigpipe_setup, NULL);
517 : }
|