/src/c-ares/src/lib/ares_conn.c
Line | Count | Source |
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 | 0 | { |
32 | 0 | ares_channel_t *channel = conn->server->channel; |
33 | |
|
34 | 0 | if ((conn->state_flags & ARES_CONN_STATE_CBFLAGS) != flags && |
35 | 0 | channel->sock_state_cb) { |
36 | 0 | channel->sock_state_cb(channel->sock_state_cb_data, conn->fd, |
37 | 0 | flags & ARES_CONN_STATE_READ ? 1 : 0, |
38 | 0 | flags & ARES_CONN_STATE_WRITE ? 1 : 0); |
39 | 0 | } |
40 | |
|
41 | 0 | conn->state_flags &= ~((unsigned int)ARES_CONN_STATE_CBFLAGS); |
42 | 0 | conn->state_flags |= flags; |
43 | 0 | } |
44 | | |
45 | | ares_conn_err_t ares_conn_read(ares_conn_t *conn, void *data, size_t len, |
46 | | size_t *read_bytes) |
47 | 0 | { |
48 | 0 | ares_channel_t *channel = conn->server->channel; |
49 | 0 | ares_conn_err_t err; |
50 | |
|
51 | 0 | if (!(conn->flags & ARES_CONN_FLAG_TCP)) { |
52 | 0 | struct sockaddr_storage sa_storage; |
53 | 0 | ares_socklen_t salen = sizeof(sa_storage); |
54 | |
|
55 | 0 | memset(&sa_storage, 0, sizeof(sa_storage)); |
56 | |
|
57 | 0 | err = |
58 | 0 | ares_socket_recvfrom(channel, conn->fd, ARES_FALSE, data, len, 0, |
59 | 0 | (struct sockaddr *)&sa_storage, &salen, read_bytes); |
60 | |
|
61 | 0 | #ifdef HAVE_RECVFROM |
62 | 0 | if (err == ARES_CONN_ERR_SUCCESS && |
63 | 0 | !ares_sockaddr_addr_eq((struct sockaddr *)&sa_storage, |
64 | 0 | &conn->server->addr)) { |
65 | 0 | err = ARES_CONN_ERR_WOULDBLOCK; |
66 | 0 | } |
67 | 0 | #endif |
68 | 0 | } else { |
69 | 0 | err = ares_socket_recv(channel, conn->fd, ARES_TRUE, data, len, read_bytes); |
70 | 0 | } |
71 | | |
72 | | /* Toggle connected state if needed */ |
73 | 0 | if (err == ARES_CONN_ERR_SUCCESS) { |
74 | 0 | conn->state_flags |= ARES_CONN_STATE_CONNECTED; |
75 | 0 | } |
76 | |
|
77 | 0 | return err; |
78 | 0 | } |
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 | 0 | { |
90 | 0 | const ares_server_t *server = conn->server; |
91 | 0 | unsigned short port = |
92 | 0 | conn->flags & ARES_CONN_FLAG_TCP ? server->tcp_port : server->udp_port; |
93 | 0 | struct sockaddr_in *sin; |
94 | 0 | struct sockaddr_in6 *sin6; |
95 | |
|
96 | 0 | switch (server->addr.family) { |
97 | 0 | case AF_INET: |
98 | 0 | sin = (struct sockaddr_in *)(void *)sa; |
99 | 0 | if (*salen < (ares_socklen_t)sizeof(*sin)) { |
100 | 0 | return ARES_EFORMERR; |
101 | 0 | } |
102 | 0 | *salen = sizeof(*sin); |
103 | 0 | memset(sin, 0, sizeof(*sin)); |
104 | 0 | sin->sin_family = AF_INET; |
105 | 0 | sin->sin_port = htons(port); |
106 | 0 | memcpy(&sin->sin_addr, &server->addr.addr.addr4, sizeof(sin->sin_addr)); |
107 | 0 | return ARES_SUCCESS; |
108 | 0 | case AF_INET6: |
109 | 0 | sin6 = (struct sockaddr_in6 *)(void *)sa; |
110 | 0 | if (*salen < (ares_socklen_t)sizeof(*sin6)) { |
111 | 0 | return ARES_EFORMERR; |
112 | 0 | } |
113 | 0 | *salen = sizeof(*sin6); |
114 | 0 | memset(sin6, 0, sizeof(*sin6)); |
115 | 0 | sin6->sin6_family = AF_INET6; |
116 | 0 | sin6->sin6_port = htons(port); |
117 | 0 | memcpy(&sin6->sin6_addr, &server->addr.addr.addr6, |
118 | 0 | sizeof(sin6->sin6_addr)); |
119 | 0 | #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID |
120 | 0 | sin6->sin6_scope_id = server->ll_scope; |
121 | 0 | #endif |
122 | 0 | return ARES_SUCCESS; |
123 | 0 | default: |
124 | 0 | break; |
125 | 0 | } |
126 | | |
127 | 0 | return ARES_EBADFAMILY; |
128 | 0 | } |
129 | | |
130 | | static ares_status_t ares_conn_set_self_ip(ares_conn_t *conn, ares_bool_t early) |
131 | 0 | { |
132 | 0 | ares_channel_t *channel = conn->server->channel; |
133 | 0 | struct sockaddr_storage sa_storage; |
134 | 0 | int rv; |
135 | 0 | 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 | 0 | if (!early && conn->self_ip.family != AF_UNSPEC) { |
140 | 0 | return ARES_SUCCESS; |
141 | 0 | } |
142 | | |
143 | 0 | memset(&sa_storage, 0, sizeof(sa_storage)); |
144 | |
|
145 | 0 | if (channel->sock_funcs.agetsockname == NULL) { |
146 | | /* Not specified, we can still use cookies cooked with an empty self_ip */ |
147 | 0 | memset(&conn->self_ip, 0, sizeof(conn->self_ip)); |
148 | 0 | return ARES_SUCCESS; |
149 | 0 | } |
150 | 0 | rv = channel->sock_funcs.agetsockname(conn->fd, |
151 | 0 | (struct sockaddr *)(void *)&sa_storage, |
152 | 0 | &len, channel->sock_func_cb_data); |
153 | 0 | 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 | 0 | if (early && conn->flags & ARES_CONN_FLAG_TCP && |
157 | 0 | conn->flags & ARES_CONN_FLAG_TFO) { |
158 | 0 | memset(&conn->self_ip, 0, sizeof(conn->self_ip)); |
159 | 0 | return ARES_SUCCESS; |
160 | 0 | } |
161 | 0 | return ARES_ECONNREFUSED; |
162 | 0 | } |
163 | | |
164 | 0 | if (!ares_sockaddr_to_ares_addr(&conn->self_ip, NULL, |
165 | 0 | (struct sockaddr *)(void *)&sa_storage)) { |
166 | 0 | return ARES_ECONNREFUSED; |
167 | 0 | } |
168 | | |
169 | 0 | return ARES_SUCCESS; |
170 | 0 | } |
171 | | |
172 | | ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len, |
173 | | size_t *written) |
174 | 0 | { |
175 | 0 | ares_channel_t *channel = conn->server->channel; |
176 | 0 | ares_bool_t is_tfo = ARES_FALSE; |
177 | 0 | ares_conn_err_t err = ARES_CONN_ERR_SUCCESS; |
178 | 0 | struct sockaddr_storage sa_storage; |
179 | 0 | ares_socklen_t salen = 0; |
180 | 0 | struct sockaddr *sa = NULL; |
181 | |
|
182 | 0 | *written = 0; |
183 | | |
184 | | /* Don't try to write if not doing initial TFO and not connected */ |
185 | 0 | if (conn->flags & ARES_CONN_FLAG_TCP && |
186 | 0 | !(conn->state_flags & ARES_CONN_STATE_CONNECTED) && |
187 | 0 | !(conn->flags & ARES_CONN_FLAG_TFO_INITIAL)) { |
188 | 0 | return ARES_CONN_ERR_WOULDBLOCK; |
189 | 0 | } |
190 | | |
191 | | /* On initial write during TFO we need to send an address */ |
192 | 0 | if (conn->flags & ARES_CONN_FLAG_TFO_INITIAL) { |
193 | 0 | salen = sizeof(sa_storage); |
194 | 0 | sa = (struct sockaddr *)&sa_storage; |
195 | |
|
196 | 0 | conn->flags &= ~((unsigned int)ARES_CONN_FLAG_TFO_INITIAL); |
197 | 0 | is_tfo = ARES_TRUE; |
198 | |
|
199 | 0 | if (ares_conn_set_sockaddr(conn, sa, &salen) != ARES_SUCCESS) { |
200 | 0 | return ARES_CONN_ERR_FAILURE; |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | 0 | err = ares_socket_write(channel, conn->fd, data, len, written, sa, salen); |
205 | 0 | if (err != ARES_CONN_ERR_SUCCESS) { |
206 | 0 | goto done; |
207 | 0 | } |
208 | | |
209 | 0 | 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 | 0 | ares_conn_set_self_ip(conn, ARES_FALSE); |
214 | 0 | goto done; |
215 | 0 | } |
216 | | |
217 | 0 | done: |
218 | 0 | 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 | 0 | ares_conn_sock_state_cb_update( |
223 | 0 | conn, ARES_CONN_STATE_READ | |
224 | 0 | (is_tfo ? ARES_CONN_STATE_WRITE : ARES_CONN_STATE_NONE)); |
225 | 0 | } else if (err == ARES_CONN_ERR_WOULDBLOCK) { |
226 | | /* Need to wait on more buffer space to write */ |
227 | 0 | ares_conn_sock_state_cb_update(conn, ARES_CONN_STATE_READ | |
228 | 0 | ARES_CONN_STATE_WRITE); |
229 | 0 | } |
230 | |
|
231 | 0 | return err; |
232 | 0 | } |
233 | | |
234 | | ares_status_t ares_conn_flush(ares_conn_t *conn) |
235 | 0 | { |
236 | 0 | const unsigned char *data; |
237 | 0 | size_t data_len; |
238 | 0 | size_t count; |
239 | 0 | ares_conn_err_t err; |
240 | 0 | ares_status_t status; |
241 | 0 | ares_bool_t tfo = ARES_FALSE; |
242 | |
|
243 | 0 | if (conn == NULL) { |
244 | 0 | return ARES_EFORMERR; |
245 | 0 | } |
246 | | |
247 | 0 | if (conn->flags & ARES_CONN_FLAG_TFO_INITIAL) { |
248 | 0 | tfo = ARES_TRUE; |
249 | 0 | } |
250 | |
|
251 | 0 | do { |
252 | 0 | if (ares_buf_len(conn->out_buf) == 0) { |
253 | 0 | status = ARES_SUCCESS; |
254 | 0 | goto done; |
255 | 0 | } |
256 | | |
257 | 0 | if (conn->flags & ARES_CONN_FLAG_TCP) { |
258 | 0 | data = ares_buf_peek(conn->out_buf, &data_len); |
259 | 0 | } else { |
260 | 0 | unsigned short msg_len; |
261 | | |
262 | | /* Read length, then provide buffer without length */ |
263 | 0 | ares_buf_tag(conn->out_buf); |
264 | 0 | status = ares_buf_fetch_be16(conn->out_buf, &msg_len); |
265 | 0 | if (status != ARES_SUCCESS) { |
266 | 0 | return status; |
267 | 0 | } |
268 | 0 | ares_buf_tag_rollback(conn->out_buf); |
269 | |
|
270 | 0 | data = ares_buf_peek(conn->out_buf, &data_len); |
271 | 0 | if (data_len < (size_t)(msg_len + 2)) { |
272 | 0 | status = ARES_EFORMERR; |
273 | 0 | goto done; |
274 | 0 | } |
275 | 0 | data += 2; |
276 | 0 | data_len = msg_len; |
277 | 0 | } |
278 | | |
279 | 0 | err = ares_conn_write(conn, data, data_len, &count); |
280 | 0 | if (err != ARES_CONN_ERR_SUCCESS) { |
281 | 0 | if (err != ARES_CONN_ERR_WOULDBLOCK) { |
282 | 0 | status = ARES_ECONNREFUSED; |
283 | 0 | goto done; |
284 | 0 | } |
285 | 0 | status = ARES_SUCCESS; |
286 | 0 | goto done; |
287 | 0 | } |
288 | | |
289 | | /* UDP didn't send the length prefix so augment that here */ |
290 | 0 | if (!(conn->flags & ARES_CONN_FLAG_TCP)) { |
291 | 0 | count += 2; |
292 | 0 | } |
293 | | |
294 | | /* Strip data written from the buffer */ |
295 | 0 | ares_buf_consume(conn->out_buf, count); |
296 | 0 | 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 | 0 | } while (!(conn->flags & ARES_CONN_FLAG_TCP)); |
301 | | |
302 | 0 | done: |
303 | 0 | if (status == ARES_SUCCESS) { |
304 | 0 | 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 | 0 | if (tfo) { |
309 | 0 | flags |= ARES_CONN_STATE_WRITE; |
310 | 0 | } |
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 | 0 | if (conn->flags & ARES_CONN_FLAG_TCP && ares_buf_len(conn->out_buf)) { |
315 | 0 | flags |= ARES_CONN_STATE_WRITE; |
316 | 0 | } |
317 | |
|
318 | 0 | ares_conn_sock_state_cb_update(conn, flags); |
319 | 0 | } |
320 | |
|
321 | 0 | return status; |
322 | 0 | } |
323 | | |
324 | | static ares_status_t ares_conn_connect(ares_conn_t *conn, |
325 | | const struct sockaddr *sa, |
326 | | ares_socklen_t salen) |
327 | 0 | { |
328 | 0 | ares_conn_err_t err; |
329 | |
|
330 | 0 | err = ares_socket_connect( |
331 | 0 | conn->server->channel, conn->fd, |
332 | 0 | (conn->flags & ARES_CONN_FLAG_TFO) ? ARES_TRUE : ARES_FALSE, sa, salen); |
333 | |
|
334 | 0 | if (err != ARES_CONN_ERR_WOULDBLOCK && err != ARES_CONN_ERR_SUCCESS) { |
335 | 0 | return ARES_ECONNREFUSED; |
336 | 0 | } |
337 | 0 | return ARES_SUCCESS; |
338 | 0 | } |
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 | 0 | { |
344 | 0 | ares_status_t status; |
345 | 0 | struct sockaddr_storage sa_storage; |
346 | 0 | ares_socklen_t salen = sizeof(sa_storage); |
347 | 0 | struct sockaddr *sa = (struct sockaddr *)&sa_storage; |
348 | 0 | ares_conn_t *conn; |
349 | 0 | ares_llist_node_t *node = NULL; |
350 | 0 | int stype = is_tcp ? SOCK_STREAM : SOCK_DGRAM; |
351 | 0 | ares_conn_state_flags_t state_flags; |
352 | |
|
353 | 0 | *conn_out = NULL; |
354 | |
|
355 | 0 | conn = ares_malloc(sizeof(*conn)); |
356 | 0 | if (conn == NULL) { |
357 | 0 | return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ |
358 | 0 | } |
359 | | |
360 | 0 | memset(conn, 0, sizeof(*conn)); |
361 | 0 | conn->fd = ARES_SOCKET_BAD; |
362 | 0 | conn->server = server; |
363 | 0 | conn->queries_to_conn = ares_llist_create(NULL); |
364 | 0 | conn->flags = is_tcp ? ARES_CONN_FLAG_TCP : ARES_CONN_FLAG_NONE; |
365 | 0 | conn->out_buf = ares_buf_create(); |
366 | 0 | conn->in_buf = ares_buf_create(); |
367 | |
|
368 | 0 | if (conn->queries_to_conn == NULL || conn->out_buf == NULL || |
369 | 0 | conn->in_buf == NULL) { |
370 | | /* LCOV_EXCL_START: OutOfMemory */ |
371 | 0 | status = ARES_ENOMEM; |
372 | 0 | goto done; |
373 | | /* LCOV_EXCL_STOP */ |
374 | 0 | } |
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 | 0 | if (conn->flags & ARES_CONN_FLAG_TCP) { |
379 | 0 | conn->flags |= ARES_CONN_FLAG_TFO; |
380 | 0 | } |
381 | | |
382 | | /* Convert into the struct sockaddr structure needed by the OS */ |
383 | 0 | status = ares_conn_set_sockaddr(conn, sa, &salen); |
384 | 0 | if (status != ARES_SUCCESS) { |
385 | 0 | goto done; |
386 | 0 | } |
387 | | |
388 | | /* Acquire a socket. */ |
389 | 0 | if (ares_socket_open(&conn->fd, channel, server->addr.family, stype, 0) != |
390 | 0 | ARES_CONN_ERR_SUCCESS) { |
391 | 0 | status = ARES_ECONNREFUSED; |
392 | 0 | goto done; |
393 | 0 | } |
394 | | |
395 | | /* Configure channel configured options */ |
396 | 0 | status = ares_socket_configure( |
397 | 0 | channel, server->addr.family, |
398 | 0 | (conn->flags & ARES_CONN_FLAG_TCP) ? ARES_TRUE : ARES_FALSE, conn->fd); |
399 | 0 | if (status != ARES_SUCCESS) { |
400 | 0 | goto done; |
401 | 0 | } |
402 | | |
403 | | /* Enable TFO if possible */ |
404 | 0 | if (conn->flags & ARES_CONN_FLAG_TFO && |
405 | 0 | ares_socket_enable_tfo(channel, conn->fd) != ARES_CONN_ERR_SUCCESS) { |
406 | 0 | conn->flags &= ~((unsigned int)ARES_CONN_FLAG_TFO); |
407 | 0 | } |
408 | |
|
409 | 0 | if (channel->sock_config_cb) { |
410 | 0 | int err = |
411 | 0 | channel->sock_config_cb(conn->fd, stype, channel->sock_config_cb_data); |
412 | 0 | if (err < 0) { |
413 | 0 | status = ARES_ECONNREFUSED; |
414 | 0 | goto done; |
415 | 0 | } |
416 | 0 | } |
417 | | |
418 | | /* Connect */ |
419 | 0 | status = ares_conn_connect(conn, sa, salen); |
420 | 0 | if (status != ARES_SUCCESS) { |
421 | 0 | goto done; |
422 | 0 | } |
423 | | |
424 | 0 | if (channel->sock_create_cb) { |
425 | 0 | int err = |
426 | 0 | channel->sock_create_cb(conn->fd, stype, channel->sock_create_cb_data); |
427 | 0 | if (err < 0) { |
428 | 0 | status = ARES_ECONNREFUSED; |
429 | 0 | goto done; |
430 | 0 | } |
431 | 0 | } |
432 | | |
433 | | /* Let the connection know we haven't written our first packet yet for TFO */ |
434 | 0 | if (conn->flags & ARES_CONN_FLAG_TFO) { |
435 | 0 | conn->flags |= ARES_CONN_FLAG_TFO_INITIAL; |
436 | 0 | } |
437 | | |
438 | | /* Need to store our own ip for DNS cookie support */ |
439 | 0 | status = ares_conn_set_self_ip(conn, ARES_TRUE); |
440 | 0 | if (status != ARES_SUCCESS) { |
441 | 0 | goto done; /* LCOV_EXCL_LINE: UntestablePath */ |
442 | 0 | } |
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 | 0 | if (is_tcp) { |
448 | 0 | node = ares_llist_insert_last(server->connections, conn); |
449 | 0 | } else { |
450 | 0 | node = ares_llist_insert_first(server->connections, conn); |
451 | 0 | } |
452 | 0 | if (node == NULL) { |
453 | | /* LCOV_EXCL_START: OutOfMemory */ |
454 | 0 | status = ARES_ENOMEM; |
455 | 0 | goto done; |
456 | | /* LCOV_EXCL_STOP */ |
457 | 0 | } |
458 | | |
459 | | /* Register globally to quickly map event on file descriptor to connection |
460 | | * node object */ |
461 | 0 | if (!ares_htable_asvp_insert(channel->connnode_by_socket, conn->fd, node)) { |
462 | | /* LCOV_EXCL_START: OutOfMemory */ |
463 | 0 | status = ARES_ENOMEM; |
464 | 0 | goto done; |
465 | | /* LCOV_EXCL_STOP */ |
466 | 0 | } |
467 | | |
468 | 0 | state_flags = ARES_CONN_STATE_READ; |
469 | | |
470 | | /* Get notified on connect if using TCP */ |
471 | 0 | if (conn->flags & ARES_CONN_FLAG_TCP) { |
472 | 0 | state_flags |= ARES_CONN_STATE_WRITE; |
473 | 0 | } |
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 | 0 | if (!(conn->flags & ARES_CONN_FLAG_TFO_INITIAL)) { |
480 | 0 | ares_conn_sock_state_cb_update(conn, state_flags); |
481 | 0 | } |
482 | |
|
483 | 0 | if (is_tcp) { |
484 | 0 | server->tcp_conn = conn; |
485 | 0 | } |
486 | |
|
487 | 0 | done: |
488 | 0 | if (status != ARES_SUCCESS) { |
489 | 0 | ares_llist_node_claim(node); |
490 | 0 | ares_llist_destroy(conn->queries_to_conn); |
491 | 0 | ares_socket_close(channel, conn->fd); |
492 | 0 | ares_buf_destroy(conn->out_buf); |
493 | 0 | ares_buf_destroy(conn->in_buf); |
494 | 0 | ares_free(conn); |
495 | 0 | } else { |
496 | 0 | *conn_out = conn; |
497 | 0 | } |
498 | 0 | return status; |
499 | 0 | } |
500 | | |
501 | | ares_conn_t *ares_conn_from_fd(const ares_channel_t *channel, ares_socket_t fd) |
502 | 0 | { |
503 | 0 | ares_llist_node_t *node; |
504 | |
|
505 | 0 | node = ares_htable_asvp_get_direct(channel->connnode_by_socket, fd); |
506 | 0 | if (node == NULL) { |
507 | 0 | return NULL; |
508 | 0 | } |
509 | | |
510 | 0 | return ares_llist_node_val(node); |
511 | 0 | } |