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 #if !defined(WIN32)
28 #include <netdb.h>
29 #endif
30
31 #if !defined(LWS_WITH_SYS_ASYNC_DNS)
32 static int
lws_getaddrinfo46(struct lws * wsi,const char * ads,struct addrinfo ** result)33 lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
34 {
35 lws_metrics_caliper_declare(cal, wsi->a.context->mt_conn_dns);
36 struct addrinfo hints;
37 #if defined(LWS_WITH_SYS_METRICS)
38 char buckname[32];
39 #endif
40 int n;
41
42 memset(&hints, 0, sizeof(hints));
43 *result = NULL;
44
45 hints.ai_socktype = SOCK_STREAM;
46
47 #ifdef LWS_WITH_IPV6
48 if (wsi->ipv6) {
49
50 #if !defined(__ANDROID__)
51 hints.ai_family = AF_UNSPEC;
52 #if !defined(__OpenBSD__) && !defined(__OPENBSD)
53 hints.ai_flags = AI_V4MAPPED;
54 #endif
55 #endif
56 } else
57 #endif
58 {
59 hints.ai_family = PF_UNSPEC;
60 }
61
62 #if defined(LWS_WITH_CONMON)
63 wsi->conmon_datum = lws_now_usecs();
64 #endif
65
66 wsi->dns_reachability = 0;
67 if (lws_fi(&wsi->fic, "dnsfail"))
68 n = EAI_FAIL;
69 else
70 n = getaddrinfo(ads, NULL, &hints, result);
71
72 #if defined(LWS_WITH_CONMON)
73 wsi->conmon.ciu_dns = (lws_conmon_interval_us_t)
74 (lws_now_usecs() - wsi->conmon_datum);
75 #endif
76
77 /*
78 * Which EAI_* are available and the meanings are highly platform-
79 * dependent, even different linux distros differ.
80 */
81
82 if (0
83 #if defined(EAI_SYSTEM)
84 || n == EAI_SYSTEM
85 #endif
86 #if defined(EAI_NODATA)
87 || n == EAI_NODATA
88 #endif
89 #if defined(EAI_FAIL)
90 || n == EAI_FAIL
91 #endif
92 #if defined(EAI_AGAIN)
93 || n == EAI_AGAIN
94 #endif
95 ) {
96 wsi->dns_reachability = 1;
97 lws_metrics_caliper_report(cal, METRES_NOGO);
98 #if defined(LWS_WITH_SYS_METRICS)
99 lws_snprintf(buckname, sizeof(buckname), "dns=\"unreachable %d\"", n);
100 lws_metrics_hist_bump_priv_wsi(wsi, mth_conn_failures, buckname);
101 #endif
102
103 #if defined(LWS_WITH_CONMON)
104 wsi->conmon.dns_disposition = LWSCONMON_DNS_SERVER_UNREACHABLE;
105 #endif
106
107 #if 0
108 lwsl_wsi_debug(wsi, "asking to recheck CPD in 1s");
109 lws_system_cpd_start_defer(wsi->a.context, LWS_US_PER_SEC);
110 #endif
111 }
112
113 lwsl_wsi_info(wsi, "getaddrinfo '%s' says %d", ads, n);
114
115 #if defined(LWS_WITH_SYS_METRICS)
116 if (n < 0) {
117 lws_snprintf(buckname, sizeof(buckname), "dns=\"nores %d\"", n);
118 lws_metrics_hist_bump_priv_wsi(wsi, mth_conn_failures, buckname);
119 }
120 #endif
121 #if defined(LWS_WITH_CONMON)
122 wsi->conmon.dns_disposition = n < 0 ? LWSCONMON_DNS_NO_RESULT :
123 LWSCONMON_DNS_OK;
124 #endif
125
126 lws_metrics_caliper_report(cal, n >= 0 ? METRES_GO : METRES_NOGO);
127
128 return n;
129 }
130 #endif
131
132 #if !defined(LWS_WITH_SYS_ASYNC_DNS) && defined(EAI_NONAME)
133 static const char * const dns_nxdomain = "DNS NXDOMAIN";
134 #endif
135
136 struct lws *
lws_client_connect_2_dnsreq(struct lws * wsi)137 lws_client_connect_2_dnsreq(struct lws *wsi)
138 {
139 struct addrinfo *result = NULL;
140 const char *meth = NULL;
141 #if defined(LWS_WITH_IPV6)
142 struct sockaddr_in addr;
143 const char *iface;
144 #endif
145 const char *adsin;
146 int n, port = 0;
147 struct lws *w;
148
149 if (lwsi_state(wsi) == LRS_WAITING_DNS ||
150 lwsi_state(wsi) == LRS_WAITING_CONNECT) {
151 lwsl_wsi_info(wsi, "LRS_WAITING_DNS / CONNECT");
152
153 return wsi;
154 }
155
156 /*
157 * clients who will create their own fresh connection keep a copy of
158 * the hostname they originally connected to, in case other connections
159 * want to use it too
160 */
161
162 if (!wsi->cli_hostname_copy) {
163 const char *pa = lws_wsi_client_stash_item(wsi, CIS_HOST,
164 _WSI_TOKEN_CLIENT_PEER_ADDRESS);
165
166 if (pa)
167 wsi->cli_hostname_copy = lws_strdup(pa);
168 }
169
170 /*
171 * The first job is figure out if we want to pipeline on or just join
172 * an existing "active connection" to the same place
173 */
174
175 meth = lws_wsi_client_stash_item(wsi, CIS_METHOD,
176 _WSI_TOKEN_CLIENT_METHOD);
177 /* consult active connections to find out disposition */
178
179 adsin = lws_wsi_client_stash_item(wsi, CIS_ADDRESS,
180 _WSI_TOKEN_CLIENT_PEER_ADDRESS);
181
182 /* we only pipeline connections that said it was okay */
183
184 if (!wsi->client_pipeline) {
185 lwsl_wsi_debug(wsi, "new conn on no pipeline flag");
186
187 goto solo;
188 }
189
190 /* only pipeline things we associate with being a stream */
191
192 if (meth && strcmp(meth, "RAW") && strcmp(meth, "GET") &&
193 strcmp(meth, "POST") && strcmp(meth, "PUT") &&
194 strcmp(meth, "UDP") && strcmp(meth, "MQTT"))
195 goto solo;
196
197 if (!adsin)
198 /*
199 * This cannot happen since user code must provide the client
200 * address to get this far, it's here to satisfy Coverity
201 */
202 return NULL;
203
204 switch (lws_vhost_active_conns(wsi, &w, adsin)) {
205 case ACTIVE_CONNS_SOLO:
206 break;
207 case ACTIVE_CONNS_MUXED:
208 lwsl_wsi_notice(wsi, "ACTIVE_CONNS_MUXED");
209 if (lwsi_role_h2(wsi)) {
210
211 if (wsi->a.protocol->callback(wsi,
212 LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
213 wsi->user_space, NULL, 0))
214 goto failed1;
215
216 //lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
217 //lwsi_set_state(w, LRS_ESTABLISHED);
218 lws_callback_on_writable(wsi);
219 }
220
221 return wsi;
222 case ACTIVE_CONNS_QUEUED:
223 lwsl_wsi_debug(wsi, "ACTIVE_CONNS_QUEUED st 0x%x: ",
224 lwsi_state(wsi));
225
226 if (lwsi_state(wsi) == LRS_UNCONNECTED) {
227 if (lwsi_role_h2(w))
228 lwsi_set_state(wsi,
229 LRS_H2_WAITING_TO_SEND_HEADERS);
230 else
231 lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
232 }
233
234 return lws_client_connect_4_established(wsi, w, 0);
235 }
236
237 solo:
238
239 /*
240 * If we made our own connection, and we're doing a method that can
241 * take a pipeline, we are an "active client connection".
242 *
243 * Add ourselves to the vhost list of those so that others can
244 * piggyback on our transaction queue
245 */
246
247 if (meth && (!strcmp(meth, "RAW") || !strcmp(meth, "GET") ||
248 !strcmp(meth, "POST") || !strcmp(meth, "PUT") ||
249 !strcmp(meth, "MQTT")) &&
250 lws_dll2_is_detached(&wsi->dll2_cli_txn_queue) &&
251 lws_dll2_is_detached(&wsi->dll_cli_active_conns)) {
252 lws_context_lock(wsi->a.context, __func__);
253 lws_vhost_lock(wsi->a.vhost);
254 lwsl_wsi_info(wsi, "adding as active conn");
255 /* caution... we will have to unpick this on oom4 path */
256 lws_dll2_add_head(&wsi->dll_cli_active_conns,
257 &wsi->a.vhost->dll_cli_active_conns_owner);
258 lws_vhost_unlock(wsi->a.vhost);
259 lws_context_unlock(wsi->a.context);
260 }
261
262 /*
263 * Since address must be given at client creation, should not be
264 * possible, but necessary to satisfy coverity
265 */
266 if (!adsin)
267 return NULL;
268
269 #if defined(LWS_WITH_UNIX_SOCK)
270 /*
271 * unix socket destination?
272 */
273
274 if (*adsin == '+') {
275 wsi->unix_skt = 1;
276 n = 0;
277 goto next_step;
278 }
279 #endif
280
281 /*
282 * start off allowing ipv6 on connection if vhost allows it
283 */
284 wsi->ipv6 = LWS_IPV6_ENABLED(wsi->a.vhost);
285 #ifdef LWS_WITH_IPV6
286 if (wsi->stash)
287 iface = wsi->stash->cis[CIS_IFACE];
288 else
289 iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
290
291 if (wsi->ipv6 && iface &&
292 inet_pton(AF_INET, iface, &addr.sin_addr) == 1) {
293 lwsl_wsi_notice(wsi, "client connection forced to IPv4");
294 wsi->ipv6 = 0;
295 }
296 #endif
297
298 #if defined(LWS_CLIENT_HTTP_PROXYING) && \
299 (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
300
301 /* Decide what it is we need to connect to:
302 *
303 * Priority 1: connect to http proxy */
304
305 if (wsi->a.vhost->http.http_proxy_port) {
306 adsin = wsi->a.vhost->http.http_proxy_address;
307 port = (int)wsi->a.vhost->http.http_proxy_port;
308 #else
309 if (0) {
310 #endif
311
312 #if defined(LWS_WITH_SOCKS5)
313
314 /* Priority 2: Connect to SOCK5 Proxy */
315
316 } else if (wsi->a.vhost->socks_proxy_port) {
317 lwsl_wsi_client(wsi, "Sending SOCKS Greeting");
318 adsin = wsi->a.vhost->socks_proxy_address;
319 port = (int)wsi->a.vhost->socks_proxy_port;
320 #endif
321 } else {
322
323 /* Priority 3: Connect directly */
324
325 /* ads already set */
326 port = wsi->c_port;
327 }
328
329 /*
330 * prepare the actual connection
331 * to whatever we decided to connect to
332 */
333 lwsi_set_state(wsi, LRS_WAITING_DNS);
334
335 lwsl_wsi_info(wsi, "lookup %s:%u", adsin, port);
336 wsi->conn_port = (uint16_t)port;
337
338 #if !defined(LWS_WITH_SYS_ASYNC_DNS)
339 n = 0;
340 if (!wsi->dns_sorted_list.count) {
341 /*
342 * blocking dns resolution
343 */
344 n = lws_getaddrinfo46(wsi, adsin, &result);
345 #if defined(EAI_NONAME)
346 if (n == EAI_NONAME) {
347 /*
348 * The DNS server responded with NXDOMAIN... even
349 * though this is still in the client creation call,
350 * we need to make a CCE, otherwise there won't be
351 * any user indication of what went wrong
352 */
353 wsi->client_suppress_CONNECTION_ERROR = 0;
354 lws_inform_client_conn_fail(wsi, (void *)dns_nxdomain,
355 strlen(dns_nxdomain));
356 goto failed1;
357 }
358 #endif
359 }
360 #else
361 /* this is either FAILED, CONTINUING, or already called connect_4 */
362
363 if (lws_fi(&wsi->fic, "dnsfail"))
364 return lws_client_connect_3_connect(wsi, NULL, NULL, -4, NULL);
365 else
366 n = lws_async_dns_query(wsi->a.context, wsi->tsi, adsin,
367 LWS_ADNS_RECORD_A, lws_client_connect_3_connect,
368 wsi, NULL);
369
370 if (n == LADNS_RET_FAILED_WSI_CLOSED)
371 return NULL;
372
373 if (n == LADNS_RET_FAILED)
374 goto failed1;
375
376 return wsi;
377 #endif
378
379 #if defined(LWS_WITH_UNIX_SOCK)
380 next_step:
381 #endif
382 return lws_client_connect_3_connect(wsi, adsin, result, n, NULL);
383
384 //#if defined(LWS_WITH_SYS_ASYNC_DNS)
385 failed1:
386 lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2");
387
388 return NULL;
389 //#endif
390 }
391