LCOV - code coverage report
Current view: top level - lib/tsocket/tests - test_tstream.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 242 245 98.8 %
Date: 2024-05-31 13:13:24 Functions: 27 27 100.0 %

          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      161398 :         int rc;
     169             : 
     170      161398 :         while (true) {
     171      161398 :                 rc = write(sock, TEST_STRING, sizeof(TEST_STRING));
     172      161398 :                 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         239 :                         num_busy++;
     180         239 :                         if (num_busy > 20) {
     181             :                                 break;
     182             :                         }
     183         228 :                         smb_msleep(50);
     184         228 :                         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             : }

Generated by: LCOV version 1.14