• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ares_conn_sock_state_cb_update(ares_conn_t * conn,ares_conn_state_flags_t flags)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 
ares_conn_read(ares_conn_t * conn,void * data,size_t len,size_t * read_bytes)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  */
ares_conn_set_sockaddr(const ares_conn_t * conn,struct sockaddr * sa,ares_socklen_t * salen)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 
ares_conn_set_self_ip(ares_conn_t * conn,ares_bool_t early)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 
ares_conn_write(ares_conn_t * conn,const void * data,size_t len,size_t * written)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 
ares_conn_flush(ares_conn_t * conn)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 
ares_conn_connect(ares_conn_t * conn,const struct sockaddr * sa,ares_socklen_t salen)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 
ares_open_connection(ares_conn_t ** conn_out,ares_channel_t * channel,ares_server_t * server,ares_bool_t is_tcp)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 
ares_conn_from_fd(const ares_channel_t * channel,ares_socket_t fd)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 }
512