1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
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 #if defined(LWS_WITH_UNIX_SOCK)
187 memset(&sau, 0, sizeof(sau));
188 #endif
189
190 /*
191 * async dns calls back here for everybody who cares when it gets a
192 * result... but if we are piggybacking, we do not want to connect
193 * ourselves
194 */
195
196 if (!lws_dll2_is_detached(&wsi->dll2_cli_txn_queue))
197 return wsi;
198
199 if (n && /* calling back with a problem */
200 !wsi->dns_sorted_list.count && /* there's no results */
201 !lws_socket_is_valid(wsi->desc.sockfd) && /* no attempt ongoing */
202 !wsi->speculative_connect_owner.count /* no spec attempt */ ) {
203 lwsl_wsi_notice(wsi, "dns lookup failed %d", n);
204
205 /*
206 * DNS lookup itself failed... let's try again until we
207 * timeout
208 */
209
210 lwsi_set_state(wsi, LRS_UNCONNECTED);
211 lws_sul_schedule(wsi->a.context, 0, &wsi->sul_connect_timeout,
212 lws_client_dns_retry_timeout,
213 LWS_USEC_PER_SEC);
214 return wsi;
215
216 // cce = "dns lookup failed";
217 // goto oom4;
218 }
219
220 /*
221 * We come back here again when we think the connect() may have
222 * completed one way or the other, we can't proceed until we know we
223 * actually connected.
224 */
225
226 if (lwsi_state(wsi) == LRS_WAITING_CONNECT &&
227 lws_socket_is_valid(wsi->desc.sockfd)) {
228
229 if (!wsi->dns_sorted_list.count &&
230 !wsi->sul_connect_timeout.list.owner)
231 /* no dns results and no ongoing timeout for one */
232 goto connect_to;
233
234 switch (lws_client_connect_check(wsi)) {
235 case LCCCR_CONNECTED:
236 /*
237 * Oh, it has happened...
238 */
239 goto conn_good;
240 case LCCCR_CONTINUE:
241 return NULL;
242 default:
243 lws_snprintf(dcce, sizeof(dcce), "conn fail: errno %d",
244 LWS_ERRNO);
245 cce = dcce;
246 lwsl_wsi_debug(wsi, "%s", dcce);
247 lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
248 goto try_next_dns_result_fds;
249 }
250 }
251
252 #if defined(LWS_WITH_UNIX_SOCK)
253
254 if (ads && *ads == '+') {
255 ads++;
256 memset(&wsi->sa46_peer, 0, sizeof(wsi->sa46_peer));
257 af = sau.sun_family = AF_UNIX;
258 strncpy(sau.sun_path, ads, sizeof(sau.sun_path));
259 sau.sun_path[sizeof(sau.sun_path) - 1] = '\0';
260
261 lwsl_wsi_info(wsi, "Unix skt: %s", ads);
262
263 if (sau.sun_path[0] == '@')
264 sau.sun_path[0] = '\0';
265
266 goto ads_known;
267 }
268 #endif
269
270 #if defined(LWS_WITH_SYS_ASYNC_DNS)
271 if (n == LADNS_RET_FAILED) {
272 lwsl_wsi_notice(wsi, "adns failed %s", ads);
273 /*
274 * Caller that is giving us LADNS_RET_FAILED will deal
275 * with cleanup
276 */
277 return NULL;
278 }
279 #endif
280
281 /*
282 * Let's try directly connecting to each of the results in turn until
283 * one works, or we run out of results...
284 *
285 * We have a sorted dll2 list with the head one most preferable
286 */
287
288 next_dns_result:
289
290 cce = "Unable to connect";
291
292 if (!wsi->dns_sorted_list.count)
293 goto failed1;
294
295 /*
296 * Copy the wsi head sorted dns result into the wsi->sa46_peer, and
297 * remove and free the original from the sorted list
298 */
299
300 d = lws_dll2_get_head(&wsi->dns_sorted_list);
301 curr = lws_container_of(d, lws_dns_sort_t, list);
302
303 lws_dll2_remove(&curr->list);
304 wsi->sa46_peer = curr->dest;
305 #if defined(LWS_WITH_NETLINK)
306 wsi->peer_route_uidx = curr->uidx;
307 lwsl_wsi_info(wsi, "peer_route_uidx %d", wsi->peer_route_uidx);
308 #endif
309
310 lws_free(curr);
311
312 sa46_sockport(&wsi->sa46_peer, htons(port));
313
314 psa = sa46_sockaddr(&wsi->sa46_peer);
315 n = (int)sa46_socklen(&wsi->sa46_peer);
316
317 #if defined(LWS_WITH_UNIX_SOCK)
318 ads_known:
319 #endif
320
321 /*
322 * Now we prepared psa, if not already connecting, create the related
323 * socket and add to the fds
324 */
325
326 if (!lws_socket_is_valid(wsi->desc.sockfd)) {
327
328 if (wsi->a.context->event_loop_ops->check_client_connect_ok &&
329 wsi->a.context->event_loop_ops->check_client_connect_ok(wsi)
330 ) {
331 cce = "waiting for event loop watcher to close";
332 goto oom4;
333 }
334
335 #if defined(LWS_WITH_UNIX_SOCK)
336 af = 0;
337 if (wsi->unix_skt) {
338 af = AF_UNIX;
339 wsi->desc.sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
340 }
341 else
342 #endif
343 {
344 af = wsi->sa46_peer.sa4.sin_family;
345 wsi->desc.sockfd = socket(wsi->sa46_peer.sa4.sin_family,
346 SOCK_STREAM, 0);
347 }
348
349 if (!lws_socket_is_valid(wsi->desc.sockfd)) {
350 lws_snprintf(dcce, sizeof(dcce),
351 "conn fail: skt creation: errno %d",
352 LWS_ERRNO);
353 cce = dcce;
354 lwsl_wsi_warn(wsi, "%s", dcce);
355 goto try_next_dns_result;
356 }
357
358 if (lws_plat_set_socket_options(wsi->a.vhost, wsi->desc.sockfd,
359 #if defined(LWS_WITH_UNIX_SOCK)
360 wsi->unix_skt)) {
361 #else
362 0)) {
363 #endif
364 lws_snprintf(dcce, sizeof(dcce),
365 "conn fail: skt options: errno %d",
366 LWS_ERRNO);
367 cce = dcce;
368 lwsl_wsi_warn(wsi, "%s", dcce);
369 goto try_next_dns_result_closesock;
370 }
371
372 /* apply requested socket options */
373 if (lws_plat_set_socket_options_ip(wsi->desc.sockfd,
374 wsi->c_pri, wsi->flags))
375 lwsl_wsi_warn(wsi, "unable to set ip options");
376
377 lwsl_wsi_debug(wsi, "WAITING_CONNECT");
378 lwsi_set_state(wsi, LRS_WAITING_CONNECT);
379
380 if (wsi->a.context->event_loop_ops->sock_accept)
381 if (wsi->a.context->event_loop_ops->sock_accept(wsi)) {
382 lws_snprintf(dcce, sizeof(dcce),
383 "conn fail: sock accept");
384 cce = dcce;
385 lwsl_wsi_warn(wsi, "%s", dcce);
386 goto try_next_dns_result_closesock;
387 }
388
389 lws_pt_lock(pt, __func__);
390 if (__insert_wsi_socket_into_fds(wsi->a.context, wsi)) {
391 lws_snprintf(dcce, sizeof(dcce),
392 "conn fail: insert fd");
393 cce = dcce;
394 lws_pt_unlock(pt);
395 goto try_next_dns_result_closesock;
396 }
397 lws_pt_unlock(pt);
398
399 /*
400 * The fd + wsi combination is entered into the wsi tables
401 * at this point, with a pollfd
402 *
403 * Past here, we can't simply free the structs as error
404 * handling as oom4 does.
405 *
406 * We can run the whole close flow, or unpick the fds inclusion
407 * and anything else we have done.
408 */
409
410 if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
411 lws_snprintf(dcce, sizeof(dcce),
412 "conn fail: change pollfd");
413 cce = dcce;
414 goto try_next_dns_result_fds;
415 }
416
417 if (!wsi->a.protocol)
418 wsi->a.protocol = &wsi->a.vhost->protocols[0];
419
420 lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
421 wsi->a.vhost->connect_timeout_secs);
422
423 iface = lws_wsi_client_stash_item(wsi, CIS_IFACE,
424 _WSI_TOKEN_CLIENT_IFACE);
425
426 if (iface && *iface) {
427 m = lws_socket_bind(wsi->a.vhost, wsi, wsi->desc.sockfd,
428 0, iface, af);
429 if (m < 0) {
430 lws_snprintf(dcce, sizeof(dcce),
431 "conn fail: socket bind");
432 cce = dcce;
433 goto try_next_dns_result_fds;
434 }
435 }
436 }
437
438 #if defined(LWS_WITH_UNIX_SOCK)
439 if (wsi->unix_skt) {
440 psa = (const struct sockaddr *)&sau;
441 if (sau.sun_path[0])
442 n = (int)(sizeof(uint16_t) + strlen(sau.sun_path));
443 else
444 n = (int)(sizeof(uint16_t) +
445 strlen(&sau.sun_path[1]) + 1);
446 } else
447 #endif
448
449 if (!psa) /* coverity */
450 goto try_next_dns_result_fds;
451
452 /*
453 * The actual connection attempt
454 */
455
456 #if defined(LWS_ESP_PLATFORM)
457 errno = 0;
458 #endif
459
460 /* grab a copy for peer tracking */
461 #if defined(LWS_WITH_UNIX_SOCK)
462 if (!wsi->unix_skt)
463 #endif
464 memmove(&wsi->sa46_peer, psa, (unsigned int)n);
465
466 /*
467 * Finally, make the actual connection attempt
468 */
469
470 #if defined(LWS_WITH_SYS_METRICS)
471 if (wsi->cal_conn.mt) {
472 lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
473 }
474 lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_conn_tcp);
475 #endif
476
477 wsi->socket_is_permanently_unusable = 0;
478
479 if (lws_fi(&wsi->fic, "conn_cb_rej") ||
480 user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
481 LWS_CALLBACK_CONNECTING, wsi->user_space,
482 (void *)(intptr_t)wsi->desc.sockfd, 0)) {
483 lwsl_wsi_info(wsi, "CONNECTION CB closed");
484 goto failed1;
485 }
486
487 #if defined(LWS_WITH_SYS_FAULT_INJECTION)
488 cfail = lws_fi(&wsi->fic, "connfail");
489 if (cfail)
490 m = -1;
491 else
492 #endif
493 m = connect(wsi->desc.sockfd, (const struct sockaddr *)psa,
494 (socklen_t)n);
495
496 #if defined(LWS_WITH_CONMON)
497 wsi->conmon_datum = lws_now_usecs();
498 wsi->conmon.ciu_sockconn = 0;
499 #endif
500
501 if (m == -1) {
502 /*
503 * Since we're nonblocking, connect not having completed is not
504 * necessarily indicating any problem... we have to look at
505 * either errno or the socket to understand if we actually
506 * failed already...
507 */
508
509 int errno_copy = LWS_ERRNO;
510
511 #if defined(LWS_WITH_SYS_FAULT_INJECTION)
512 if (cfail)
513 /* fake an abnormal, fatal situation */
514 errno_copy = 999;
515 #endif
516
517 lwsl_wsi_debug(wsi, "connect: fd %d errno: %d",
518 wsi->desc.sockfd, errno_copy);
519
520 if (errno_copy &&
521 errno_copy != LWS_EALREADY &&
522 errno_copy != LWS_EINPROGRESS &&
523 errno_copy != LWS_EWOULDBLOCK
524 #ifdef _WIN32
525 && errno_copy != WSAEINVAL
526 && errno_copy != WSAEISCONN
527 #endif
528 ) {
529 /*
530 * The connect() failed immediately...
531 */
532
533 #if defined(LWS_WITH_CONMON)
534 wsi->conmon.ciu_sockconn = (lws_conmon_interval_us_t)
535 (lws_now_usecs() - wsi->conmon_datum);
536 #endif
537
538 lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
539
540 #if defined(_DEBUG)
541 #if defined(LWS_WITH_UNIX_SOCK)
542 if (!wsi->unix_skt) {
543 #endif
544
545 char nads[48];
546
547 lws_sa46_write_numeric_address(&wsi->sa46_peer, nads,
548 sizeof(nads));
549
550 lws_snprintf(dcce, sizeof(dcce),
551 "conn fail: errno %d: %s:%d",
552 errno_copy, nads, port);
553 cce = dcce;
554
555 wsi->sa46_peer.sa4.sin_family = 0;
556 lwsl_wsi_info(wsi, "%s", cce);
557 #if defined(LWS_WITH_UNIX_SOCK)
558 } else {
559 lws_snprintf(dcce, sizeof(dcce),
560 "conn fail: errno %d: UDS %s",
561 errno_copy, ads);
562 cce = dcce;
563 }
564 #endif
565 #endif
566
567 goto try_next_dns_result_fds;
568 }
569
570 #if defined(WIN32)
571 if (lws_plat_check_connection_error(wsi))
572 goto try_next_dns_result_fds;
573
574 if (errno_copy == WSAEISCONN)
575 goto conn_good;
576 #endif
577
578 /*
579 * The connection attempt is ongoing asynchronously... let's set
580 * a specialized timeout for this connect attempt completion, it
581 * uses wsi->sul_connect_timeout just for this purpose
582 */
583
584 lws_sul_schedule(wsi->a.context, 0, &wsi->sul_connect_timeout,
585 lws_client_conn_wait_timeout,
586 wsi->a.context->timeout_secs *
587 LWS_USEC_PER_SEC);
588
589 /*
590 * must do specifically a POLLOUT poll to hear
591 * about the connect completion
592 */
593 if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
594 goto try_next_dns_result_fds;
595
596 return wsi;
597 }
598
599 conn_good:
600
601 /*
602 * The connection has happened
603 */
604
605 #if defined(LWS_WITH_CONMON)
606 wsi->conmon.ciu_sockconn = (lws_conmon_interval_us_t)
607 (lws_now_usecs() - wsi->conmon_datum);
608 #endif
609
610 #if !defined(LWS_PLAT_OPTEE)
611 {
612 socklen_t salen = sizeof(wsi->sa46_local);
613 #if defined(_DEBUG)
614 char buf[64];
615 #endif
616 if (getsockname((int)wsi->desc.sockfd,
617 (struct sockaddr *)&wsi->sa46_local,
618 &salen) == -1)
619 lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO));
620 #if defined(_DEBUG)
621 #if defined(LWS_WITH_UNIX_SOCK)
622 if (wsi->unix_skt)
623 buf[0] = '\0';
624 else
625 #endif
626 lws_sa46_write_numeric_address(&wsi->sa46_local, buf, sizeof(buf));
627
628 lwsl_wsi_info(wsi, "source ads %s", buf);
629 #endif
630 }
631 #endif
632
633 lws_sul_cancel(&wsi->sul_connect_timeout);
634 lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
635
636 lws_addrinfo_clean(wsi);
637
638 if (wsi->a.protocol)
639 wsi->a.protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE,
640 wsi->user_space, NULL, 0);
641
642 lwsl_wsi_debug(wsi, "going into connect_4");
643
644 return lws_client_connect_4_established(wsi, NULL, plen);
645
646 oom4:
647 /*
648 * We get here if we're trying to clean up a connection attempt that
649 * didn't make it as far as getting inserted into the wsi / fd tables
650 */
651
652 if (lwsi_role_client(wsi) && wsi->a.protocol
653 /* && lwsi_state_est(wsi) */)
654 lws_inform_client_conn_fail(wsi,(void *)cce, strlen(cce));
655
656 /* take care that we might be inserted in fds already */
657 if (wsi->position_in_fds_table != LWS_NO_FDS_POS)
658 /* do the full wsi close flow */
659 goto failed1;
660
661 lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
662
663 /*
664 * We can't be an active client connection any more, if we thought
665 * that was what we were going to be doing. It should be if we are
666 * failing by oom4 path, we are still called by
667 * lws_client_connect_via_info() and will be returning NULL to that,
668 * so nobody else should have had a chance to queue on us.
669 */
670 {
671 struct lws_vhost *vhost = wsi->a.vhost;
672 lws_sockfd_type sfd = wsi->desc.sockfd;
673
674 //lws_vhost_lock(vhost);
675 __lws_free_wsi(wsi); /* acquires vhost lock in wsi reset */
676 //lws_vhost_unlock(vhost);
677
678 sanity_assert_no_wsi_traces(vhost->context, wsi);
679 sanity_assert_no_sockfd_traces(vhost->context, sfd);
680 }
681
682 return NULL;
683
684 connect_to:
685 /*
686 * It looks like the sul_connect_timeout fired
687 */
688 lwsl_wsi_info(wsi, "abandoning connect due to timeout");
689
690 try_next_dns_result_fds:
691 lws_pt_lock(pt, __func__);
692 __remove_wsi_socket_from_fds(wsi);
693 lws_pt_unlock(pt);
694
695 try_next_dns_result_closesock:
696 /*
697 * We are killing the socket but leaving
698 */
699 compatible_close(wsi->desc.sockfd);
700 wsi->desc.sockfd = LWS_SOCK_INVALID;
701
702 try_next_dns_result:
703 lws_sul_cancel(&wsi->sul_connect_timeout);
704 if (lws_dll2_get_head(&wsi->dns_sorted_list))
705 goto next_dns_result;
706
707 lws_addrinfo_clean(wsi);
708 lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
709
710 failed1:
711 lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect3");
712
713 return NULL;
714 }
715