quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

ares_conn.c (16260B)


      1 /* MIT License
      2  *
      3  * Copyright (c) Massachusetts Institute of Technology
      4  * Copyright (c) The c-ares project and its contributors
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a copy
      7  * of this software and associated documentation files (the "Software"), to deal
      8  * in the Software without restriction, including without limitation the rights
      9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10  * copies of the Software, and to permit persons to whom the Software is
     11  * furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice (including the next
     14  * paragraph) shall be included in all copies or substantial portions of the
     15  * Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     23  * SOFTWARE.
     24  *
     25  * SPDX-License-Identifier: MIT
     26  */
     27 #include "ares_private.h"
     28 
     29 void ares_conn_sock_state_cb_update(ares_conn_t            *conn,
     30                                     ares_conn_state_flags_t flags)
     31 {
     32   ares_channel_t *channel = conn->server->channel;
     33 
     34   if ((conn->state_flags & ARES_CONN_STATE_CBFLAGS) != flags &&
     35       channel->sock_state_cb) {
     36     channel->sock_state_cb(channel->sock_state_cb_data, conn->fd,
     37                            flags & ARES_CONN_STATE_READ ? 1 : 0,
     38                            flags & ARES_CONN_STATE_WRITE ? 1 : 0);
     39   }
     40 
     41   conn->state_flags &= ~((unsigned int)ARES_CONN_STATE_CBFLAGS);
     42   conn->state_flags |= flags;
     43 }
     44 
     45 ares_conn_err_t ares_conn_read(ares_conn_t *conn, void *data, size_t len,
     46                                size_t *read_bytes)
     47 {
     48   ares_channel_t *channel = conn->server->channel;
     49   ares_conn_err_t err;
     50 
     51   if (!(conn->flags & ARES_CONN_FLAG_TCP)) {
     52     struct sockaddr_storage sa_storage;
     53     ares_socklen_t          salen = sizeof(sa_storage);
     54 
     55     memset(&sa_storage, 0, sizeof(sa_storage));
     56 
     57     err =
     58       ares_socket_recvfrom(channel, conn->fd, ARES_FALSE, data, len, 0,
     59                            (struct sockaddr *)&sa_storage, &salen, read_bytes);
     60 
     61 #ifdef HAVE_RECVFROM
     62     if (err == ARES_CONN_ERR_SUCCESS &&
     63         !ares_sockaddr_addr_eq((struct sockaddr *)&sa_storage,
     64                                &conn->server->addr)) {
     65       err = ARES_CONN_ERR_WOULDBLOCK;
     66     }
     67 #endif
     68   } else {
     69     err = ares_socket_recv(channel, conn->fd, ARES_TRUE, data, len, read_bytes);
     70   }
     71 
     72   /* Toggle connected state if needed */
     73   if (err == ARES_CONN_ERR_SUCCESS) {
     74     conn->state_flags |= ARES_CONN_STATE_CONNECTED;
     75   }
     76 
     77   return err;
     78 }
     79 
     80 /* Use like:
     81  *   struct sockaddr_storage sa_storage;
     82  *   ares_socklen_t          salen     = sizeof(sa_storage);
     83  *   struct sockaddr        *sa        = (struct sockaddr *)&sa_storage;
     84  *   ares_conn_set_sockaddr(conn, sa, &salen);
     85  */
     86 static ares_status_t ares_conn_set_sockaddr(const ares_conn_t *conn,
     87                                             struct sockaddr   *sa,
     88                                             ares_socklen_t    *salen)
     89 {
     90   const ares_server_t *server = conn->server;
     91   unsigned short       port =
     92     conn->flags & ARES_CONN_FLAG_TCP ? server->tcp_port : server->udp_port;
     93   struct sockaddr_in  *sin;
     94   struct sockaddr_in6 *sin6;
     95 
     96   switch (server->addr.family) {
     97     case AF_INET:
     98       sin = (struct sockaddr_in *)(void *)sa;
     99       if (*salen < (ares_socklen_t)sizeof(*sin)) {
    100         return ARES_EFORMERR;
    101       }
    102       *salen = sizeof(*sin);
    103       memset(sin, 0, sizeof(*sin));
    104       sin->sin_family = AF_INET;
    105       sin->sin_port   = htons(port);
    106       memcpy(&sin->sin_addr, &server->addr.addr.addr4, sizeof(sin->sin_addr));
    107       return ARES_SUCCESS;
    108     case AF_INET6:
    109       sin6 = (struct sockaddr_in6 *)(void *)sa;
    110       if (*salen < (ares_socklen_t)sizeof(*sin6)) {
    111         return ARES_EFORMERR;
    112       }
    113       *salen = sizeof(*sin6);
    114       memset(sin6, 0, sizeof(*sin6));
    115       sin6->sin6_family = AF_INET6;
    116       sin6->sin6_port   = htons(port);
    117       memcpy(&sin6->sin6_addr, &server->addr.addr.addr6,
    118              sizeof(sin6->sin6_addr));
    119 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
    120       sin6->sin6_scope_id = server->ll_scope;
    121 #endif
    122       return ARES_SUCCESS;
    123     default:
    124       break;
    125   }
    126 
    127   return ARES_EBADFAMILY;
    128 }
    129 
    130 static ares_status_t ares_conn_set_self_ip(ares_conn_t *conn, ares_bool_t early)
    131 {
    132   ares_channel_t         *channel = conn->server->channel;
    133   struct sockaddr_storage sa_storage;
    134   int                     rv;
    135   ares_socklen_t          len = sizeof(sa_storage);
    136 
    137   /* We call this twice on TFO, if we already have the IP we can go ahead and
    138    * skip processing */
    139   if (!early && conn->self_ip.family != AF_UNSPEC) {
    140     return ARES_SUCCESS;
    141   }
    142 
    143   memset(&sa_storage, 0, sizeof(sa_storage));
    144 
    145   if (channel->sock_funcs.agetsockname == NULL) {
    146     /* Not specified, we can still use cookies cooked with an empty self_ip */
    147     memset(&conn->self_ip, 0, sizeof(conn->self_ip));
    148     return ARES_SUCCESS;
    149   }
    150   rv = channel->sock_funcs.agetsockname(conn->fd,
    151                                         (struct sockaddr *)(void *)&sa_storage,
    152                                         &len, channel->sock_func_cb_data);
    153   if (rv != 0) {
    154     /* During TCP FastOpen, we can't get the IP this early since connect()
    155      * may not be called.  That's ok, we'll try again later */
    156     if (early && conn->flags & ARES_CONN_FLAG_TCP &&
    157         conn->flags & ARES_CONN_FLAG_TFO) {
    158       memset(&conn->self_ip, 0, sizeof(conn->self_ip));
    159       return ARES_SUCCESS;
    160     }
    161     return ARES_ECONNREFUSED;
    162   }
    163 
    164   if (!ares_sockaddr_to_ares_addr(&conn->self_ip, NULL,
    165                                   (struct sockaddr *)(void *)&sa_storage)) {
    166     return ARES_ECONNREFUSED;
    167   }
    168 
    169   return ARES_SUCCESS;
    170 }
    171 
    172 ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len,
    173                                 size_t *written)
    174 {
    175   ares_channel_t         *channel = conn->server->channel;
    176   ares_bool_t             is_tfo  = ARES_FALSE;
    177   ares_conn_err_t         err     = ARES_CONN_ERR_SUCCESS;
    178   struct sockaddr_storage sa_storage;
    179   ares_socklen_t          salen = 0;
    180   struct sockaddr        *sa    = NULL;
    181 
    182   *written = 0;
    183 
    184   /* Don't try to write if not doing initial TFO and not connected */
    185   if (conn->flags & ARES_CONN_FLAG_TCP &&
    186       !(conn->state_flags & ARES_CONN_STATE_CONNECTED) &&
    187       !(conn->flags & ARES_CONN_FLAG_TFO_INITIAL)) {
    188     return ARES_CONN_ERR_WOULDBLOCK;
    189   }
    190 
    191   /* On initial write during TFO we need to send an address */
    192   if (conn->flags & ARES_CONN_FLAG_TFO_INITIAL) {
    193     salen = sizeof(sa_storage);
    194     sa    = (struct sockaddr *)&sa_storage;
    195 
    196     conn->flags &= ~((unsigned int)ARES_CONN_FLAG_TFO_INITIAL);
    197     is_tfo       = ARES_TRUE;
    198 
    199     if (ares_conn_set_sockaddr(conn, sa, &salen) != ARES_SUCCESS) {
    200       return ARES_CONN_ERR_FAILURE;
    201     }
    202   }
    203 
    204   err = ares_socket_write(channel, conn->fd, data, len, written, sa, salen);
    205   if (err != ARES_CONN_ERR_SUCCESS) {
    206     goto done;
    207   }
    208 
    209   if (is_tfo) {
    210     /* If using TFO, we might not have been able to get an IP earlier, since
    211      * we hadn't informed the OS of the destination.  When using sendto()
    212      * now we have so we should be able to fetch it */
    213     ares_conn_set_self_ip(conn, ARES_FALSE);
    214     goto done;
    215   }
    216 
    217 done:
    218   if (err == ARES_CONN_ERR_SUCCESS && len == *written) {
    219     /* Wrote all data, make sure we're not listening for write events unless
    220      * using TFO, in which case we'll need a write event to know when
    221      * we're connected. */
    222     ares_conn_sock_state_cb_update(
    223       conn, ARES_CONN_STATE_READ |
    224               (is_tfo ? ARES_CONN_STATE_WRITE : ARES_CONN_STATE_NONE));
    225   } else if (err == ARES_CONN_ERR_WOULDBLOCK) {
    226     /* Need to wait on more buffer space to write */
    227     ares_conn_sock_state_cb_update(conn, ARES_CONN_STATE_READ |
    228                                            ARES_CONN_STATE_WRITE);
    229   }
    230 
    231   return err;
    232 }
    233 
    234 ares_status_t ares_conn_flush(ares_conn_t *conn)
    235 {
    236   const unsigned char *data;
    237   size_t               data_len;
    238   size_t               count;
    239   ares_conn_err_t      err;
    240   ares_status_t        status;
    241   ares_bool_t          tfo = ARES_FALSE;
    242 
    243   if (conn == NULL) {
    244     return ARES_EFORMERR;
    245   }
    246 
    247   if (conn->flags & ARES_CONN_FLAG_TFO_INITIAL) {
    248     tfo = ARES_TRUE;
    249   }
    250 
    251   do {
    252     if (ares_buf_len(conn->out_buf) == 0) {
    253       status = ARES_SUCCESS;
    254       goto done;
    255     }
    256 
    257     if (conn->flags & ARES_CONN_FLAG_TCP) {
    258       data = ares_buf_peek(conn->out_buf, &data_len);
    259     } else {
    260       unsigned short msg_len;
    261 
    262       /* Read length, then provide buffer without length */
    263       ares_buf_tag(conn->out_buf);
    264       status = ares_buf_fetch_be16(conn->out_buf, &msg_len);
    265       if (status != ARES_SUCCESS) {
    266         return status;
    267       }
    268       ares_buf_tag_rollback(conn->out_buf);
    269 
    270       data = ares_buf_peek(conn->out_buf, &data_len);
    271       if (data_len < (size_t)(msg_len + 2)) {
    272         status = ARES_EFORMERR;
    273         goto done;
    274       }
    275       data     += 2;
    276       data_len  = msg_len;
    277     }
    278 
    279     err = ares_conn_write(conn, data, data_len, &count);
    280     if (err != ARES_CONN_ERR_SUCCESS) {
    281       if (err != ARES_CONN_ERR_WOULDBLOCK) {
    282         status = ARES_ECONNREFUSED;
    283         goto done;
    284       }
    285       status = ARES_SUCCESS;
    286       goto done;
    287     }
    288 
    289     /* UDP didn't send the length prefix so augment that here */
    290     if (!(conn->flags & ARES_CONN_FLAG_TCP)) {
    291       count += 2;
    292     }
    293 
    294     /* Strip data written from the buffer */
    295     ares_buf_consume(conn->out_buf, count);
    296     status = ARES_SUCCESS;
    297 
    298     /* Loop only for UDP since we have to send per-packet.  We already
    299      * sent everything we could if using tcp */
    300   } while (!(conn->flags & ARES_CONN_FLAG_TCP));
    301 
    302 done:
    303   if (status == ARES_SUCCESS) {
    304     ares_conn_state_flags_t flags = ARES_CONN_STATE_READ;
    305 
    306     /* When using TFO, the we need to enabling waiting on a write event to
    307      * be notified of when a connection is actually established */
    308     if (tfo) {
    309       flags |= ARES_CONN_STATE_WRITE;
    310     }
    311 
    312     /* If using TCP and not all data was written (partial write), that means
    313      * we need to also wait on a write event */
    314     if (conn->flags & ARES_CONN_FLAG_TCP && ares_buf_len(conn->out_buf)) {
    315       flags |= ARES_CONN_STATE_WRITE;
    316     }
    317 
    318     ares_conn_sock_state_cb_update(conn, flags);
    319   }
    320 
    321   return status;
    322 }
    323 
    324 static ares_status_t ares_conn_connect(ares_conn_t           *conn,
    325                                        const struct sockaddr *sa,
    326                                        ares_socklen_t         salen)
    327 {
    328   ares_conn_err_t err;
    329 
    330   err = ares_socket_connect(
    331     conn->server->channel, conn->fd,
    332     (conn->flags & ARES_CONN_FLAG_TFO) ? ARES_TRUE : ARES_FALSE, sa, salen);
    333 
    334   if (err != ARES_CONN_ERR_WOULDBLOCK && err != ARES_CONN_ERR_SUCCESS) {
    335     return ARES_ECONNREFUSED;
    336   }
    337   return ARES_SUCCESS;
    338 }
    339 
    340 ares_status_t ares_open_connection(ares_conn_t   **conn_out,
    341                                    ares_channel_t *channel,
    342                                    ares_server_t *server, ares_bool_t is_tcp)
    343 {
    344   ares_status_t           status;
    345   struct sockaddr_storage sa_storage;
    346   ares_socklen_t          salen = sizeof(sa_storage);
    347   struct sockaddr        *sa    = (struct sockaddr *)&sa_storage;
    348   ares_conn_t            *conn;
    349   ares_llist_node_t      *node  = NULL;
    350   int                     stype = is_tcp ? SOCK_STREAM : SOCK_DGRAM;
    351   ares_conn_state_flags_t state_flags;
    352 
    353   *conn_out = NULL;
    354 
    355   conn = ares_malloc(sizeof(*conn));
    356   if (conn == NULL) {
    357     return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
    358   }
    359 
    360   memset(conn, 0, sizeof(*conn));
    361   conn->fd              = ARES_SOCKET_BAD;
    362   conn->server          = server;
    363   conn->queries_to_conn = ares_llist_create(NULL);
    364   conn->flags           = is_tcp ? ARES_CONN_FLAG_TCP : ARES_CONN_FLAG_NONE;
    365   conn->out_buf         = ares_buf_create();
    366   conn->in_buf          = ares_buf_create();
    367 
    368   if (conn->queries_to_conn == NULL || conn->out_buf == NULL ||
    369       conn->in_buf == NULL) {
    370     /* LCOV_EXCL_START: OutOfMemory */
    371     status = ARES_ENOMEM;
    372     goto done;
    373     /* LCOV_EXCL_STOP */
    374   }
    375 
    376   /* Try to enable TFO always if using TCP. it will fail later on if its
    377    * really not supported when we try to enable it on the socket. */
    378   if (conn->flags & ARES_CONN_FLAG_TCP) {
    379     conn->flags |= ARES_CONN_FLAG_TFO;
    380   }
    381 
    382   /* Convert into the struct sockaddr structure needed by the OS */
    383   status = ares_conn_set_sockaddr(conn, sa, &salen);
    384   if (status != ARES_SUCCESS) {
    385     goto done;
    386   }
    387 
    388   /* Acquire a socket. */
    389   if (ares_socket_open(&conn->fd, channel, server->addr.family, stype, 0) !=
    390       ARES_CONN_ERR_SUCCESS) {
    391     status = ARES_ECONNREFUSED;
    392     goto done;
    393   }
    394 
    395   /* Configure channel configured options */
    396   status = ares_socket_configure(
    397     channel, server->addr.family,
    398     (conn->flags & ARES_CONN_FLAG_TCP) ? ARES_TRUE : ARES_FALSE, conn->fd);
    399   if (status != ARES_SUCCESS) {
    400     goto done;
    401   }
    402 
    403   /* Enable TFO if possible */
    404   if (conn->flags & ARES_CONN_FLAG_TFO &&
    405       ares_socket_enable_tfo(channel, conn->fd) != ARES_CONN_ERR_SUCCESS) {
    406     conn->flags &= ~((unsigned int)ARES_CONN_FLAG_TFO);
    407   }
    408 
    409   if (channel->sock_config_cb) {
    410     int err =
    411       channel->sock_config_cb(conn->fd, stype, channel->sock_config_cb_data);
    412     if (err < 0) {
    413       status = ARES_ECONNREFUSED;
    414       goto done;
    415     }
    416   }
    417 
    418   /* Connect */
    419   status = ares_conn_connect(conn, sa, salen);
    420   if (status != ARES_SUCCESS) {
    421     goto done;
    422   }
    423 
    424   if (channel->sock_create_cb) {
    425     int err =
    426       channel->sock_create_cb(conn->fd, stype, channel->sock_create_cb_data);
    427     if (err < 0) {
    428       status = ARES_ECONNREFUSED;
    429       goto done;
    430     }
    431   }
    432 
    433   /* Let the connection know we haven't written our first packet yet for TFO */
    434   if (conn->flags & ARES_CONN_FLAG_TFO) {
    435     conn->flags |= ARES_CONN_FLAG_TFO_INITIAL;
    436   }
    437 
    438   /* Need to store our own ip for DNS cookie support */
    439   status = ares_conn_set_self_ip(conn, ARES_TRUE);
    440   if (status != ARES_SUCCESS) {
    441     goto done; /* LCOV_EXCL_LINE: UntestablePath */
    442   }
    443 
    444   /* TCP connections are thrown to the end as we don't spawn multiple TCP
    445    * connections. UDP connections are put on front where the newest connection
    446    * can be quickly pulled */
    447   if (is_tcp) {
    448     node = ares_llist_insert_last(server->connections, conn);
    449   } else {
    450     node = ares_llist_insert_first(server->connections, conn);
    451   }
    452   if (node == NULL) {
    453     /* LCOV_EXCL_START: OutOfMemory */
    454     status = ARES_ENOMEM;
    455     goto done;
    456     /* LCOV_EXCL_STOP */
    457   }
    458 
    459   /* Register globally to quickly map event on file descriptor to connection
    460    * node object */
    461   if (!ares_htable_asvp_insert(channel->connnode_by_socket, conn->fd, node)) {
    462     /* LCOV_EXCL_START: OutOfMemory */
    463     status = ARES_ENOMEM;
    464     goto done;
    465     /* LCOV_EXCL_STOP */
    466   }
    467 
    468   state_flags = ARES_CONN_STATE_READ;
    469 
    470   /* Get notified on connect if using TCP */
    471   if (conn->flags & ARES_CONN_FLAG_TCP) {
    472     state_flags |= ARES_CONN_STATE_WRITE;
    473   }
    474 
    475   /* Dot no attempt to update sock state callbacks on TFO until *after* the
    476    * initial write is performed.  Due to the notification event, its possible
    477    * an erroneous read can come in before the attempt to write the data which
    478    * might be used to set the ip address */
    479   if (!(conn->flags & ARES_CONN_FLAG_TFO_INITIAL)) {
    480     ares_conn_sock_state_cb_update(conn, state_flags);
    481   }
    482 
    483   if (is_tcp) {
    484     server->tcp_conn = conn;
    485   }
    486 
    487 done:
    488   if (status != ARES_SUCCESS) {
    489     ares_llist_node_claim(node);
    490     ares_llist_destroy(conn->queries_to_conn);
    491     ares_socket_close(channel, conn->fd);
    492     ares_buf_destroy(conn->out_buf);
    493     ares_buf_destroy(conn->in_buf);
    494     ares_free(conn);
    495   } else {
    496     *conn_out = conn;
    497   }
    498   return status;
    499 }
    500 
    501 ares_conn_t *ares_conn_from_fd(const ares_channel_t *channel, ares_socket_t fd)
    502 {
    503   ares_llist_node_t *node;
    504 
    505   node = ares_htable_asvp_get_direct(channel->connnode_by_socket, fd);
    506   if (node == NULL) {
    507     return NULL;
    508   }
    509 
    510   return ares_llist_node_val(node);
    511 }