(root)/
glibc-2.38/
sunrpc/
tst-udp-nonblocking.c
       1  /* Test non-blocking use of the UDP client.
       2     Copyright (C) 2017-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <netinet/in.h>
      20  #include <rpc/clnt.h>
      21  #include <rpc/svc.h>
      22  #include <stdbool.h>
      23  #include <string.h>
      24  #include <support/check.h>
      25  #include <support/namespace.h>
      26  #include <support/test-driver.h>
      27  #include <support/xsocket.h>
      28  #include <support/xunistd.h>
      29  #include <sys/socket.h>
      30  #include <time.h>
      31  #include <unistd.h>
      32  
      33  /* Test data serialization and deserialization.   */
      34  
      35  struct test_query
      36  {
      37    uint32_t a;
      38    uint32_t b;
      39    uint32_t timeout_ms;
      40  };
      41  
      42  static bool_t
      43  xdr_test_query (XDR *xdrs, void *data, ...)
      44  {
      45    struct test_query *p = data;
      46    return xdr_uint32_t (xdrs, &p->a)
      47      && xdr_uint32_t (xdrs, &p->b)
      48      && xdr_uint32_t (xdrs, &p->timeout_ms);
      49  }
      50  
      51  struct test_response
      52  {
      53    uint32_t server_id;
      54    uint32_t seq;
      55    uint32_t sum;
      56  };
      57  
      58  static bool_t
      59  xdr_test_response (XDR *xdrs, void *data, ...)
      60  {
      61    struct test_response *p = data;
      62    return xdr_uint32_t (xdrs, &p->server_id)
      63      && xdr_uint32_t (xdrs, &p->seq)
      64      && xdr_uint32_t (xdrs, &p->sum);
      65  }
      66  
      67  /* Implementation of the test server.  */
      68  
      69  enum
      70    {
      71      /* Number of test servers to run. */
      72      SERVER_COUNT = 3,
      73  
      74      /* RPC parameters, chosen at random.  */
      75      PROGNUM = 8242,
      76      VERSNUM = 19654,
      77  
      78      /* Main RPC operation.  */
      79      PROC_ADD = 1,
      80  
      81      /* Request process termination.  */
      82      PROC_EXIT,
      83  
      84      /* Special exit status to mark successful processing.  */
      85      EXIT_MARKER = 55,
      86    };
      87  
      88  /* Set by the parent process to tell test servers apart.  */
      89  static int server_id;
      90  
      91  /* Implementation of the test server.  */
      92  static void
      93  server_dispatch (struct svc_req *request, SVCXPRT *transport)
      94  {
      95    /* Query sequence number.  */
      96    static uint32_t seq = 0;
      97    ++seq;
      98    static bool proc_add_seen;
      99  
     100    if (test_verbose)
     101      printf ("info: server_dispatch server_id=%d seq=%u rq_proc=%lu\n",
     102              server_id, seq, request->rq_proc);
     103  
     104    switch (request->rq_proc)
     105      {
     106      case PROC_ADD:
     107        {
     108          struct test_query query;
     109          memset (&query, 0xc0, sizeof (query));
     110          TEST_VERIFY_EXIT
     111            (svc_getargs (transport, xdr_test_query,
     112                          (void *) &query));
     113  
     114          if (test_verbose)
     115            printf ("  a=%u b=%u timeout_ms=%u\n",
     116                    query.a, query.b, query.timeout_ms);
     117  
     118          usleep (query.timeout_ms * 1000);
     119  
     120          struct test_response response =
     121            {
     122              .server_id = server_id,
     123              .seq = seq,
     124              .sum = query.a + query.b,
     125            };
     126          TEST_VERIFY (svc_sendreply (transport, xdr_test_response,
     127                                      (void *) &response));
     128          if (test_verbose)
     129            printf ("  server id %d response seq=%u sent\n", server_id, seq);
     130          proc_add_seen = true;
     131        }
     132        break;
     133  
     134      case PROC_EXIT:
     135        TEST_VERIFY (proc_add_seen);
     136        TEST_VERIFY (svc_sendreply (transport, (xdrproc_t) xdr_void, NULL));
     137        _exit (EXIT_MARKER);
     138        break;
     139  
     140      default:
     141        FAIL_EXIT1 ("invalid rq_proc value: %lu", request->rq_proc);
     142        break;
     143      }
     144  }
     145  
     146  /* Return the number seconds since an arbitrary point in time.  */
     147  static double
     148  get_ticks (void)
     149  {
     150    {
     151      struct timespec ts;
     152      if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0)
     153        return ts.tv_sec + ts.tv_nsec * 1e-9;
     154    }
     155    {
     156      struct timeval tv;
     157      TEST_VERIFY_EXIT (gettimeofday (&tv, NULL) == 0);
     158      return tv.tv_sec + tv.tv_usec * 1e-6;
     159    }
     160  }
     161  
     162  static int
     163  do_test (void)
     164  {
     165    support_become_root ();
     166    support_enter_network_namespace ();
     167  
     168    /* Information about the test servers.  */
     169    struct
     170    {
     171      SVCXPRT *transport;
     172      struct sockaddr_in address;
     173      pid_t pid;
     174      uint32_t xid;
     175    } servers[SERVER_COUNT];
     176  
     177    /* Spawn the test servers.  */
     178    for (int i = 0; i < SERVER_COUNT; ++i)
     179      {
     180        servers[i].transport = svcudp_create (RPC_ANYSOCK);
     181        TEST_VERIFY_EXIT (servers[i].transport != NULL);
     182        servers[i].address = (struct sockaddr_in)
     183          {
     184            .sin_family = AF_INET,
     185            .sin_addr.s_addr = htonl (INADDR_LOOPBACK),
     186            .sin_port = htons (servers[i].transport->xp_port),
     187          };
     188        servers[i].xid = 0xabcd0101 + i;
     189        if (test_verbose)
     190          printf ("info: setting up server %d xid=%x on port %d\n",
     191                  i, servers[i].xid, servers[i].transport->xp_port);
     192  
     193        server_id = i;
     194        servers[i].pid = xfork ();
     195        if (servers[i].pid == 0)
     196          {
     197            TEST_VERIFY (svc_register (servers[i].transport,
     198                                       PROGNUM, VERSNUM, server_dispatch, 0));
     199            svc_run ();
     200            FAIL_EXIT1 ("supposed to be unreachable");
     201          }
     202        /* We need to close the socket so that we do not accidentally
     203           consume the request.  */
     204        TEST_VERIFY (close (servers[i].transport->xp_sock) == 0);
     205      }
     206  
     207  
     208    /* The following code mirrors what ypbind does.  */
     209  
     210    /* Copied from clnt_udp.c (like ypbind).  */
     211    struct cu_data
     212    {
     213      int cu_sock;
     214      bool_t cu_closeit;
     215      struct sockaddr_in cu_raddr;
     216      int cu_rlen;
     217      struct timeval cu_wait;
     218      struct timeval cu_total;
     219      struct rpc_err cu_error;
     220      XDR cu_outxdrs;
     221      u_int cu_xdrpos;
     222      u_int cu_sendsz;
     223      char *cu_outbuf;
     224      u_int cu_recvsz;
     225      char cu_inbuf[1];
     226    };
     227  
     228    int client_socket = xsocket (AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
     229    CLIENT *clnt = clntudp_create (&servers[0].address, PROGNUM, VERSNUM,
     230                                   /* 5 seconds per-response timeout.  */
     231                                   ((struct timeval) { 5, 0 }),
     232                                   &client_socket);
     233    TEST_VERIFY (clnt != NULL);
     234    clnt->cl_auth = authunix_create_default ();
     235    {
     236      struct timeval zero = { 0, 0 };
     237      TEST_VERIFY (clnt_control (clnt, CLSET_TIMEOUT, (void *) &zero));
     238    }
     239  
     240    /* Poke at internal data structures (like ypbind).  */
     241    struct cu_data *cu = (struct cu_data *) clnt->cl_private;
     242  
     243    /* Send a ping to each server.  */
     244    double before_pings = get_ticks ();
     245    for (int i = 0; i < SERVER_COUNT; ++i)
     246      {
     247        if (test_verbose)
     248          printf ("info: sending server %d ping\n", i);
     249        /* Reset the xid because it is changed by each invocation of
     250           clnt_call.  Subtract one to compensate for the xid update
     251           during the call.  */
     252        *((uint32_t *) (cu->cu_outbuf)) = servers[i].xid - 1;
     253        cu->cu_raddr = servers[i].address;
     254  
     255        struct test_query query = { .a = 100, .b = i + 1 };
     256        if (i == 1)
     257          /* Shorter timeout to prefer this server.  These timeouts must
     258             be much shorter than the 5-second per-response timeout
     259             configured with clntudp_create.  */
     260          query.timeout_ms = 750;
     261        else
     262          query.timeout_ms = 1500;
     263        struct test_response response = { 0 };
     264        /* NB: Do not check the return value.  The server reply will
     265           prove that the call worked.  */
     266        double before_one_ping = get_ticks ();
     267        clnt_call (clnt, PROC_ADD,
     268                   xdr_test_query, (void *) &query,
     269                   xdr_test_response, (void *) &response,
     270                   ((struct timeval) { 0, 0 }));
     271        double after_one_ping = get_ticks ();
     272        if (test_verbose)
     273          printf ("info: non-blocking send took %f seconds\n",
     274                  after_one_ping - before_one_ping);
     275        /* clnt_call should return immediately.  Accept some delay in
     276           case the process is descheduled.  */
     277        TEST_VERIFY (after_one_ping - before_one_ping < 0.3);
     278      }
     279  
     280    /* Collect the non-blocking response.  */
     281    if (test_verbose)
     282      printf ("info: collecting response\n");
     283    struct test_response response = { 0 };
     284    TEST_VERIFY
     285      (clnt_call (clnt, PROC_ADD, NULL, NULL,
     286                  xdr_test_response, (void *) &response,
     287                  ((struct timeval) { 0, 0 })) == RPC_SUCCESS);
     288    double after_pings = get_ticks ();
     289    if (test_verbose)
     290      printf ("info: send/receive took %f seconds\n",
     291              after_pings - before_pings);
     292    /* Expected timeout is 0.75 seconds.  */
     293    TEST_VERIFY (0.70 <= after_pings - before_pings);
     294    TEST_VERIFY (after_pings - before_pings < 1.2);
     295  
     296    uint32_t xid;
     297    memcpy (&xid, &cu->cu_inbuf, sizeof (xid));
     298    if (test_verbose)
     299      printf ("info: non-blocking response: xid=%x server_id=%u seq=%u sum=%u\n",
     300              xid, response.server_id, response.seq, response.sum);
     301    /* Check that the reply from the preferred server was used.  */
     302    TEST_VERIFY (servers[1].xid == xid);
     303    TEST_VERIFY (response.server_id == 1);
     304    TEST_VERIFY (response.seq == 1);
     305    TEST_VERIFY (response.sum == 102);
     306  
     307    auth_destroy (clnt->cl_auth);
     308    clnt_destroy (clnt);
     309  
     310    for (int i = 0; i < SERVER_COUNT; ++i)
     311      {
     312        if (test_verbose)
     313          printf ("info: requesting server %d termination\n", i);
     314        client_socket = RPC_ANYSOCK;
     315        clnt = clntudp_create (&servers[i].address, PROGNUM, VERSNUM,
     316                               ((struct timeval) { 5, 0 }),
     317                               &client_socket);
     318        TEST_VERIFY_EXIT (clnt != NULL);
     319        TEST_VERIFY (clnt_call (clnt, PROC_EXIT,
     320                                (xdrproc_t) xdr_void, NULL,
     321                                (xdrproc_t) xdr_void, NULL,
     322                                ((struct timeval) { 3, 0 })) == RPC_SUCCESS);
     323        clnt_destroy (clnt);
     324  
     325        int status;
     326        xwaitpid (servers[i].pid, &status, 0);
     327        TEST_VERIFY (WIFEXITED (status) && WEXITSTATUS (status) == EXIT_MARKER);
     328      }
     329  
     330    return 0;
     331  }
     332  
     333  #include <support/test-driver.c>