1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2020 Andy Green <[email protected]>
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
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell 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 shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include "private-lib-core.h"
26
27 void
lws_client_conn_wait_timeout(lws_sorted_usec_list_t * sul)28 lws_client_conn_wait_timeout(lws_sorted_usec_list_t *sul)
29 {
30 struct lws *wsi = lws_container_of(sul, struct lws,
31 sul_connect_timeout);
32
33 /*
34 * This is used to constrain the time we're willing to wait for a
35 * connection before giving up on it and retrying.
36 */
37
38 lwsl_wsi_info(wsi, "connect wait timeout has fired");
39 lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL);
40 }
41
42 void
lws_client_dns_retry_timeout(lws_sorted_usec_list_t * sul)43 lws_client_dns_retry_timeout(lws_sorted_usec_list_t *sul)
44 {
45 struct lws *wsi = lws_container_of(sul, struct lws,
46 sul_connect_timeout);
47
48 /*
49 * This limits the amount of dns lookups we will try before
50 * giving up and failing... it reuses sul_connect_timeout, which
51 * isn't officially used until we connected somewhere.
52 */
53
54 lwsl_wsi_info(wsi, "dns retry");
55 if (!lws_client_connect_2_dnsreq(wsi))
56 lwsl_wsi_notice(wsi, "DNS lookup failed");
57 }
58
59 /*
60 * Figure out if an ongoing connect() has arrived at a final disposition or not
61 *
62 * We can check using getsockopt if our connect actually completed.
63 * Posix connect() allows nonblocking to redo the connect to
64 * find out if it succeeded.
65 */
66
67 typedef enum {
68 LCCCR_CONNECTED = 1,
69 LCCCR_CONTINUE = 0,
70 LCCCR_FAILED = -1,
71 } lcccr_t;
72
73 static lcccr_t
lws_client_connect_check(struct lws * wsi)74 lws_client_connect_check(struct lws *wsi)
75 {
76 int en = 0;
77 #if !defined(WIN32)
78 int e;
79 socklen_t sl = sizeof(e);
80 #endif
81
82 (void)en;
83
84 /*
85 * This resets SO_ERROR after reading it. If there's an error
86 * condition, the connect definitively failed.
87 */
88
89 #if !defined(WIN32)
90 if (!getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR, &e, &sl)) {
91 en = LWS_ERRNO;
92 if (!e) {
93 lwsl_wsi_debug(wsi, "getsockopt: conn OK errno %d", en);
94
95 return LCCCR_CONNECTED;
96 }
97
98 lwsl_wsi_notice(wsi, "getsockopt fd %d says e %d",
99 wsi->desc.sockfd, e);
100
101 return LCCCR_FAILED;
102 }
103
104 #else
105
106 if (!connect(wsi->desc.sockfd, NULL, 0))
107 return LCCCR_CONNECTED;
108
109 en = LWS_ERRNO;
110
111 if (en == WSAEISCONN) /* already connected */
112 return LCCCR_CONNECTED;
113
114 if (en == WSAEALREADY) {
115 /* reset the POLLOUT wait */
116 if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
117 lwsl_wsi_notice(wsi, "pollfd failed");
118 }
119
120 if (!en || en == WSAEINVAL ||
121 en == WSAEWOULDBLOCK ||
122 en == WSAEALREADY) {
123 lwsl_wsi_debug(wsi, "errno %d", en);
124 return LCCCR_CONTINUE;
125 }
126 #endif
127
128 lwsl_wsi_notice(wsi, "connect check take as FAILED: errno %d", en);
129
130 return LCCCR_FAILED;
131 }
132
133 /*
134 * We come here to fire off a connect, and to check its disposition later.
135 *
136 * If it did not complete before the individual attempt timeout, we will try to
137 * connect again with the next dns result.
138 */
139
140 struct lws *
lws_client_connect_3_connect(struct lws * wsi,const char * ads,const struct addrinfo * result,int n,void * opaque)141 lws_client_connect_3_connect(struct lws *wsi, const char *ads,
142 const struct addrinfo *result, int n, void *opaque)
143 {
144 #if defined(LWS_WITH_UNIX_SOCK)
145 struct sockaddr_un sau;
146 #endif
147 struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
148 const char *cce = "Unable to connect", *iface;
149 const struct sockaddr *psa = NULL;
150 uint16_t port = wsi->conn_port;
151 lws_dns_sort_t *curr;
152 ssize_t plen = 0;
153 lws_dll2_t *d;
154 char dcce[48];
155 #if defined(LWS_WITH_SYS_FAULT_INJECTION)
156 int cfail;
157 #endif
158 int m, af = 0;
159
160 /*
161 * If we come here with result set, we need to convert getaddrinfo
162 * results to a lws_dns_sort_t list one time and free the results.
163 *
164 * We use this pattern because ASYNC_DNS will callback here with the
165 * results when it gets them (and may come here more than once, eg, for
166 * AAAA then A or vice-versa)
167 */
168
169 if (result) {
170 lws_sul_cancel(&wsi->sul_connect_timeout);
171
172 #if defined(LWS_WITH_CONMON)
173 /* append a copy from before the sorting */
174 lws_conmon_append_copy_new_dns_results(wsi, result);
175 #endif
176
177 lws_sort_dns(wsi, result);
178 #if defined(LWS_WITH_SYS_ASYNC_DNS)
179 lws_async_dns_freeaddrinfo(&result);
180 #else
181 freeaddrinfo((struct addrinfo *)result);
182 #endif
183 result = NULL;
184 }
185
186 /*
187 * async dns calls back here for everybody who cares when it gets a
188 * result... but if we are piggybacking, we do not want to connect
189 * ourselves
190 */
191
192 if (!lws_dll2_is_detached(&wsi->dll2_cli_txn_queue))
193 return wsi;
194
195 if (n && /* calling back with a problem */
196 !wsi->dns_sorted_list.count && /* there's no results */
197 !lws_socket_is_valid(wsi->desc.sockfd) && /* no attempt ongoing */
198 !wsi->speculative_connect_owner.count /* no spec attempt */ ) {
199 lwsl_wsi_notice(wsi, "dns lookup failed %d", n);
200
201 /*
202 * DNS lookup itself failed... let's try again until we
203 * timeout
204 */
205
206 lwsi_set_state(wsi, LRS_UNCONNECTED);
207 lws_sul_schedule(wsi->a.context, 0, &wsi->sul_connect_timeout,
208 lws_client_dns_retry_timeout,
209 LWS_USEC_PER_SEC);
210 return wsi;
211
212 // cce = "dns lookup failed";
213 // goto oom4;
214 }
215
216 /*
217 * We come back here again when we think the connect() may have
218 * completed one way or the other, we can't proceed until we know we
219 * actually connected.
220 */
221
222 if (lwsi_state(wsi) == LRS_WAITING_CONNECT &&
223 lws_socket_is_valid(wsi->desc.sockfd)) {
224
225 if (!wsi->dns_sorted_list.count &&
226 !wsi->sul_connect_timeout.list.owner)
227 /* no dns results and no ongoing timeout for one */
228 goto connect_to;
229
230 switch (lws_client_connect_check(wsi)) {
231 case LCCCR_CONNECTED:
232 /*
233 * Oh, it has happened...
234 */
235 goto conn_good;
236 case LCCCR_CONTINUE:
237 return NULL;
238 default:
239 lws_snprintf(dcce, sizeof(dcce), "conn fail: errno %d",
240 LWS_ERRNO);
241 cce = dcce;
242 lwsl_wsi_debug(wsi, "%s", dcce);
243 lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
244 goto try_next_dns_result_fds;
245 }
246 }
247
248 #if defined(LWS_WITH_UNIX_SOCK)
249 if (ads && *ads == '+') {
250 ads++;
251 memset(&wsi->sa46_peer, 0, sizeof(wsi->sa46_peer));
252 memset(&sau, 0, sizeof(sau));
253 af = sau.sun_family = AF_UNIX;
254 strncpy(sau.sun_path, ads, sizeof(sau.sun_path));
255 sau.sun_path[sizeof(sau.sun_path) - 1] = '\0';
256
257 lwsl_wsi_info(wsi, "Unix skt: %s", ads);
258
259 if (sau.sun_path[0] == '@')
260 sau.sun_path[0] = '\0';
261
262 goto ads_known;
263 }
264 #endif
265
266 #if defined(LWS_WITH_SYS_ASYNC_DNS)
267 if (n == LADNS_RET_FAILED) {
268 lwsl_wsi_notice(wsi, "adns failed %s", ads);
269 /*
270 * Caller that is giving us LADNS_RET_FAILED will deal
271 * with cleanup
272 */
273 return NULL;
274 }
275 #endif
276
277 /*
278 * Let's try directly connecting to each of the results in turn until
279 * one works, or we run out of results...
280 *
281 * We have a sorted dll2 list with the head one most preferable
282 */
283
284 next_dns_result:
285
286 cce = "Unable to connect";
287
288 if (!wsi->dns_sorted_list.count)
289 goto failed1;
290
291 /*
292 * Copy the wsi head sorted dns result into the wsi->sa46_peer, and
293 * remove and free the original from the sorted list
294 */
295
296 d = lws_dll2_get_head(&wsi->dns_sorted_list);
297 curr = lws_container_of(d, lws_dns_sort_t, list);
298
299 lws_dll2_remove(&curr->list);
300 wsi->sa46_peer = curr->dest;
301 #if defined(LWS_WITH_NETLINK)
302 wsi->peer_route_uidx = curr->uidx;
303 lwsl_wsi_info(wsi, "peer_route_uidx %d", wsi->peer_route_uidx);
304 #endif
305
306 lws_free(curr);
307
308 sa46_sockport(&wsi->sa46_peer, htons(port));
309
310 psa = sa46_sockaddr(&wsi->sa46_peer);
311 n = (int)sa46_socklen(&wsi->sa46_peer);
312
313 #if defined(LWS_WITH_UNIX_SOCK)
314 ads_known:
315 #endif
316
317 /*
318 * Now we prepared psa, if not already connecting, create the related
319 * socket and add to the fds
320 */
321
322 if (!lws_socket_is_valid(wsi->desc.sockfd)) {
323
324 if (wsi->a.context->event_loop_ops->check_client_connect_ok &&
325 wsi->a.context->event_loop_ops->check_client_connect_ok(wsi)
326 ) {
327 cce = "waiting for event loop watcher to close";
328 goto oom4;
329 }
330
331 #if defined(LWS_WITH_UNIX_SOCK)
332 af = 0;
333 if (wsi->unix_skt) {
334 af = AF_UNIX;
335 wsi->desc.sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
336 }
337 else
338 #endif
339 {
340 af = wsi->sa46_peer.sa4.sin_family;
341 wsi->desc.sockfd = socket(wsi->sa46_peer.sa4.sin_family,
342 SOCK_STREAM, 0);
343 }
344
345 if (!lws_socket_is_valid(wsi->desc.sockfd)) {
346 lws_snprintf(dcce, sizeof(dcce),
347 "conn fail: skt creation: errno %d",
348 LWS_ERRNO);
349 cce = dcce;
350 lwsl_wsi_warn(wsi, "%s", dcce);
351 goto try_next_dns_result;
352 }
353
354 if (lws_plat_set_socket_options(wsi->a.vhost, wsi->desc.sockfd,
355 #if defined(LWS_WITH_UNIX_SOCK)
356 wsi->unix_skt)) {
357 #else
358 0)) {
359 #endif
360 lws_snprintf(dcce, sizeof(dcce),
361 "conn fail: skt options: errno %d",
362 LWS_ERRNO);
363 cce = dcce;
364 lwsl_wsi_warn(wsi, "%s", dcce);
365 goto try_next_dns_result_closesock;
366 }
367
368 /* apply requested socket options */
369 if (lws_plat_set_socket_options_ip(wsi->desc.sockfd,
370 wsi->c_pri, wsi->flags))
371 lwsl_wsi_warn(wsi, "unable to set ip options");
372
373 lwsl_wsi_debug(wsi, "WAITING_CONNECT");
374 lwsi_set_state(wsi, LRS_WAITING_CONNECT);
375
376 if (wsi->a.context->event_loop_ops->sock_accept)
377 if (wsi->a.context->event_loop_ops->sock_accept(wsi)) {
378 lws_snprintf(dcce, sizeof(dcce),
379 "conn fail: sock accept");
380 cce = dcce;
381 lwsl_wsi_warn(wsi, "%s", dcce);
382 goto try_next_dns_result_closesock;
383 }
384
385 lws_pt_lock(pt, __func__);
386 if (__insert_wsi_socket_into_fds(wsi->a.context, wsi)) {
387 lws_snprintf(dcce, sizeof(dcce),
388 "conn fail: insert fd");
389 cce = dcce;
390 lws_pt_unlock(pt);
391 goto try_next_dns_result_closesock;
392 }
393 lws_pt_unlock(pt);
394
395 /*
396 * The fd + wsi combination is entered into the wsi tables
397 * at this point, with a pollfd
398 *
399 * Past here, we can't simply free the structs as error
400 * handling as oom4 does.
401 *
402 * We can run the whole close flow, or unpick the fds inclusion
403 * and anything else we have done.
404 */
405
406 if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
407 lws_snprintf(dcce, sizeof(dcce),
408 "conn fail: change pollfd");
409 cce = dcce;
410 goto try_next_dns_result_fds;
411 }
412
413 if (!wsi->a.protocol)
414 wsi->a.protocol = &wsi->a.vhost->protocols[0];
415
416 lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
417 wsi->a.vhost->connect_timeout_secs);
418
419 iface = lws_wsi_client_stash_item(wsi, CIS_IFACE,
420 _WSI_TOKEN_CLIENT_IFACE);
421
422 if (iface && *iface) {
423 m = lws_socket_bind(wsi->a.vhost, wsi, wsi->desc.sockfd,
424 0, iface, af);
425 if (m < 0) {
426 lws_snprintf(dcce, sizeof(dcce),
427 "conn fail: socket bind");
428 cce = dcce;
429 goto try_next_dns_result_fds;
430 }
431 }
432 }
433
434 #if defined(LWS_WITH_UNIX_SOCK)
435 if (wsi->unix_skt) {
436 psa = (const struct sockaddr *)&sau;
437 if (sau.sun_path[0])
438 n = (int)(sizeof(uint16_t) + strlen(sau.sun_path));
439 else
440 n = (int)(sizeof(uint16_t) +
441 strlen(&sau.sun_path[1]) + 1);
442 } else
443 #endif
444
445 if (!psa) /* coverity */
446 goto try_next_dns_result_fds;
447
448 /*
449 * The actual connection attempt
450 */
451
452 #if defined(LWS_ESP_PLATFORM)
453 errno = 0;
454 #endif
455
456 /* grab a copy for peer tracking */
457 #if defined(LWS_WITH_UNIX_SOCK)
458 if (!wsi->unix_skt)
459 #endif
460 memmove(&wsi->sa46_peer, psa, (unsigned int)n);
461
462 /*
463 * Finally, make the actual connection attempt
464 */
465
466 #if defined(LWS_WITH_SYS_METRICS)
467 if (wsi->cal_conn.mt) {
468 lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
469 }
470 lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_conn_tcp);
471 #endif
472
473 wsi->socket_is_permanently_unusable = 0;
474
475 if (lws_fi(&wsi->fic, "conn_cb_rej") ||
476 user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
477 LWS_CALLBACK_CONNECTING, wsi->user_space,
478 (void *)(intptr_t)wsi->desc.sockfd, 0)) {
479 lwsl_wsi_info(wsi, "CONNECTION CB closed");
480 goto failed1;
481 }
482
483 #if defined(LWS_WITH_SYS_FAULT_INJECTION)
484 cfail = lws_fi(&wsi->fic, "connfail");
485 if (cfail)
486 m = -1;
487 else
488 #endif
489 m = connect(wsi->desc.sockfd, (const struct sockaddr *)psa,
490 (socklen_t)n);
491
492 #if defined(LWS_WITH_CONMON)
493 wsi->conmon_datum = lws_now_usecs();
494 wsi->conmon.ciu_sockconn = 0;
495 #endif
496
497 if (m == -1) {
498 /*
499 * Since we're nonblocking, connect not having completed is not
500 * necessarily indicating any problem... we have to look at
501 * either errno or the socket to understand if we actually
502 * failed already...
503 */
504
505 int errno_copy = LWS_ERRNO;
506
507 #if defined(LWS_WITH_SYS_FAULT_INJECTION)
508 if (cfail)
509 /* fake an abnormal, fatal situation */
510 errno_copy = 999;
511 #endif
512
513 lwsl_wsi_debug(wsi, "connect: fd %d errno: %d",
514 wsi->desc.sockfd, errno_copy);
515
516 if (errno_copy &&
517 errno_copy != LWS_EALREADY &&
518 errno_copy != LWS_EINPROGRESS &&
519 errno_copy != LWS_EWOULDBLOCK
520 #ifdef _WIN32
521 && errno_copy != WSAEINVAL
522 && errno_copy != WSAEISCONN
523 #endif
524 ) {
525 /*
526 * The connect() failed immediately...
527 */
528
529 #if defined(LWS_WITH_CONMON)
530 wsi->conmon.ciu_sockconn = (lws_conmon_interval_us_t)
531 (lws_now_usecs() - wsi->conmon_datum);
532 #endif
533
534 lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
535
536 #if defined(_DEBUG)
537 #if defined(LWS_WITH_UNIX_SOCK)
538 if (!wsi->unix_skt) {
539 #endif
540
541 char nads[48];
542
543 lws_sa46_write_numeric_address(&wsi->sa46_peer, nads,
544 sizeof(nads));
545
546 lws_snprintf(dcce, sizeof(dcce),
547 "conn fail: errno %d: %s:%d",
548 errno_copy, nads, port);
549 cce = dcce;
550
551 wsi->sa46_peer.sa4.sin_family = 0;
552 lwsl_wsi_info(wsi, "%s", cce);
553 #if defined(LWS_WITH_UNIX_SOCK)
554 } else {
555 lws_snprintf(dcce, sizeof(dcce),
556 "conn fail: errno %d: UDS %s",
557 errno_copy, ads);
558 cce = dcce;
559 }
560 #endif
561 #endif
562
563 goto try_next_dns_result_fds;
564 }
565
566 #if defined(WIN32)
567 if (lws_plat_check_connection_error(wsi))
568 goto try_next_dns_result_fds;
569
570 if (errno_copy == WSAEISCONN)
571 goto conn_good;
572 #endif
573
574 /*
575 * The connection attempt is ongoing asynchronously... let's set
576 * a specialized timeout for this connect attempt completion, it
577 * uses wsi->sul_connect_timeout just for this purpose
578 */
579
580 lws_sul_schedule(wsi->a.context, 0, &wsi->sul_connect_timeout,
581 lws_client_conn_wait_timeout,
582 wsi->a.context->timeout_secs *
583 LWS_USEC_PER_SEC);
584
585 /*
586 * must do specifically a POLLOUT poll to hear
587 * about the connect completion
588 */
589 if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
590 goto try_next_dns_result_fds;
591
592 return wsi;
593 }
594
595 conn_good:
596
597 /*
598 * The connection has happened
599 */
600
601 #if defined(LWS_WITH_CONMON)
602 wsi->conmon.ciu_sockconn = (lws_conmon_interval_us_t)
603 (lws_now_usecs() - wsi->conmon_datum);
604 #endif
605
606 #if !defined(LWS_PLAT_OPTEE)
607 {
608 socklen_t salen = sizeof(wsi->sa46_local);
609 #if defined(_DEBUG)
610 char buf[64];
611 #endif
612 if (getsockname((int)wsi->desc.sockfd,
613 (struct sockaddr *)&wsi->sa46_local,
614 &salen) == -1)
615 lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO));
616 #if defined(_DEBUG)
617 #if defined(LWS_WITH_UNIX_SOCK)
618 if (wsi->unix_skt)
619 buf[0] = '\0';
620 else
621 #endif
622 lws_sa46_write_numeric_address(&wsi->sa46_local, buf, sizeof(buf));
623
624 lwsl_wsi_info(wsi, "source ads %s", buf);
625 #endif
626 }
627 #endif
628
629 lws_sul_cancel(&wsi->sul_connect_timeout);
630 lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
631
632 lws_addrinfo_clean(wsi);
633
634 if (wsi->a.protocol)
635 wsi->a.protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE,
636 wsi->user_space, NULL, 0);
637
638 lwsl_wsi_debug(wsi, "going into connect_4");
639
640 return lws_client_connect_4_established(wsi, NULL, plen);
641
642 oom4:
643 /*
644 * We get here if we're trying to clean up a connection attempt that
645 * didn't make it as far as getting inserted into the wsi / fd tables
646 */
647
648 if (lwsi_role_client(wsi) && wsi->a.protocol
649 /* && lwsi_state_est(wsi) */)
650 lws_inform_client_conn_fail(wsi,(void *)cce, strlen(cce));
651
652 /* take care that we might be inserted in fds already */
653 if (wsi->position_in_fds_table != LWS_NO_FDS_POS)
654 /* do the full wsi close flow */
655 goto failed1;
656
657 lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
658
659 /*
660 * We can't be an active client connection any more, if we thought
661 * that was what we were going to be doing. It should be if we are
662 * failing by oom4 path, we are still called by
663 * lws_client_connect_via_info() and will be returning NULL to that,
664 * so nobody else should have had a chance to queue on us.
665 */
666 {
667 struct lws_vhost *vhost = wsi->a.vhost;
668 lws_sockfd_type sfd = wsi->desc.sockfd;
669
670 //lws_vhost_lock(vhost);
671 __lws_free_wsi(wsi); /* acquires vhost lock in wsi reset */
672 //lws_vhost_unlock(vhost);
673
674 sanity_assert_no_wsi_traces(vhost->context, wsi);
675 sanity_assert_no_sockfd_traces(vhost->context, sfd);
676 }
677
678 return NULL;
679
680 connect_to:
681 /*
682 * It looks like the sul_connect_timeout fired
683 */
684 lwsl_wsi_info(wsi, "abandoning connect due to timeout");
685
686 try_next_dns_result_fds:
687 lws_pt_lock(pt, __func__);
688 __remove_wsi_socket_from_fds(wsi);
689 lws_pt_unlock(pt);
690
691 try_next_dns_result_closesock:
692 /*
693 * We are killing the socket but leaving
694 */
695 compatible_close(wsi->desc.sockfd);
696 wsi->desc.sockfd = LWS_SOCK_INVALID;
697
698 try_next_dns_result:
699 lws_sul_cancel(&wsi->sul_connect_timeout);
700 if (lws_dll2_get_head(&wsi->dns_sorted_list))
701 goto next_dns_result;
702
703 lws_addrinfo_clean(wsi);
704 lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
705
706 failed1:
707 lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect3");
708
709 return NULL;
710 }
711