• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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