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)
32static int
33lws_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)
133static const char * const dns_nxdomain = "DNS NXDOMAIN";
134#endif
135
136struct lws *
137lws_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
237solo:
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)
380next_step:
381#endif
382	return lws_client_connect_3_connect(wsi, adsin, result, n, NULL);
383
384//#if defined(LWS_WITH_SYS_ASYNC_DNS)
385failed1:
386	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2");
387
388	return NULL;
389//#endif
390}
391