• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2019 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 <libwebsockets.h>
26 #include "private-lib-core.h"
27 
28 struct lws *
lws_client_connect_via_info(const struct lws_client_connect_info * i)29 lws_client_connect_via_info(const struct lws_client_connect_info *i)
30 {
31 	const char *local = i->protocol;
32 	struct lws *wsi, *safe = NULL;
33 	const struct lws_protocols *p;
34 	const char *cisin[CIS_COUNT];
35 	int tid = 0, n, m;
36 	size_t size;
37 	char *pc;
38 
39 	if (i->context->requested_kill)
40 		return NULL;
41 
42 	if (!i->context->protocol_init_done)
43 		if (lws_protocol_init(i->context))
44 			return NULL;
45 
46 	/*
47 	 * If we have .local_protocol_name, use it to select the local protocol
48 	 * handler to bind to.  Otherwise use .protocol if http[s].
49 	 */
50 	if (i->local_protocol_name)
51 		local = i->local_protocol_name;
52 
53 	lws_stats_bump(&i->context->pt[tid], LWSSTATS_C_CONNS_CLIENT, 1);
54 
55 	/* PHASE 1: create a bare wsi */
56 
57 	wsi = lws_zalloc(sizeof(struct lws), "client wsi");
58 	if (wsi == NULL)
59 		goto bail;
60 
61 	/*
62 	 * Until we exit, we can report connection failure directly to the
63 	 * caller without needing to call through to protocol CONNECTION_ERROR.
64 	 */
65 	wsi->client_suppress_CONNECTION_ERROR = 1;
66 
67 	wsi->context = i->context;
68 	wsi->desc.sockfd = LWS_SOCK_INVALID;
69 	wsi->seq = i->seq;
70 	wsi->flags = i->ssl_connection;
71 	if (i->retry_and_idle_policy)
72 		wsi->retry_policy = i->retry_and_idle_policy;
73 	else
74 		wsi->retry_policy = &i->context->default_retry;
75 
76 #if defined(LWS_WITH_DETAILED_LATENCY)
77 	if (i->context->detailed_latency_cb)
78 		wsi->detlat.earliest_write_req_pre_write = lws_now_usecs();
79 #endif
80 
81 	wsi->vhost = NULL;
82 	if (!i->vhost) {
83 		struct lws_vhost *v = i->context->vhost_list;
84 		if (v && !strcmp(v->name, "system"))
85 			v = v->vhost_next;
86 		lws_vhost_bind_wsi(v, wsi);
87 	} else
88 		lws_vhost_bind_wsi(i->vhost, wsi);
89 
90 	if (!wsi->vhost) {
91 		lwsl_err("%s: No vhost in the context\n", __func__);
92 
93 		goto bail;
94 	}
95 
96 #if LWS_MAX_SMP > 1
97 	tid = wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_GET_THREAD_ID,
98 						NULL, NULL, 0);
99 #endif
100 
101 	/*
102 	 * PHASE 2: if SMP, bind the client to whatever tsi the current thread
103 	 * represents
104 	 */
105 
106 #if LWS_MAX_SMP > 1
107 	lws_context_lock(i->context, "client find tsi");
108 
109 	for (n = 0; n < i->context->count_threads; n++)
110 		if (i->context->pt[n].service_tid == tid) {
111 			lwsl_info("%s: client binds to caller tsi %d\n",
112 				  __func__, n);
113 			wsi->tsi = n;
114 #if defined(LWS_WITH_DETAILED_LATENCY)
115 			wsi->detlat.tsi = n;
116 #endif
117 			break;
118 		}
119 
120 	/*
121 	 * this binding is sort of provisional, since when we try to insert
122 	 * into the pt fds, there may be no space and it will fail
123 	 */
124 
125 	lws_context_unlock(i->context);
126 #endif
127 
128 	/*
129 	 * PHASE 3: Choose an initial role for the wsi and do role-specific init
130 	 *
131 	 * Note the initial role may not reflect the final role, eg,
132 	 * we may want ws, but first we have to go through h1 to get that
133 	 */
134 
135 	if (lws_role_call_client_bind(wsi, i) < 0) {
136 		lwsl_err("%s: unable to bind to role\n", __func__);
137 
138 		goto bail;
139 	}
140 	lwsl_info("%s: role binding to %s\n", __func__, wsi->role_ops->name);
141 
142 	/*
143 	 * PHASE 4: fill up the wsi with stuff from the connect_info as far as
144 	 * it can go.  It's uncertain because not only is our connection
145 	 * going to complete asynchronously, we might have bound to h1 and not
146 	 * even be able to get ahold of an ah immediately.
147 	 */
148 
149 	wsi->user_space = NULL;
150 	wsi->pending_timeout = NO_PENDING_TIMEOUT;
151 	wsi->position_in_fds_table = LWS_NO_FDS_POS;
152 	wsi->ocport = wsi->c_port = i->port;
153 	wsi->sys_tls_client_cert = i->sys_tls_client_cert;
154 
155 #if defined(LWS_ROLE_H2)
156 	wsi->txc.manual_initial_tx_credit = (int32_t)i->manual_initial_tx_credit;
157 #endif
158 
159 	wsi->protocol = &wsi->vhost->protocols[0];
160 	wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE);
161 	wsi->client_no_follow_redirect = !!(i->ssl_connection &
162 					    LCCSCF_HTTP_NO_FOLLOW_REDIRECT);
163 
164 	/*
165 	 * PHASE 5: handle external user_space now, generic alloc is done in
166 	 * role finalization
167 	 */
168 
169 	if (i->userdata) {
170 		wsi->user_space_externally_allocated = 1;
171 		wsi->user_space = i->userdata;
172 	}
173 
174 	if (local) {
175 		lwsl_info("%s: protocol binding to %s\n", __func__, local);
176 		p = lws_vhost_name_to_protocol(wsi->vhost, local);
177 		if (p)
178 			lws_bind_protocol(wsi, p, __func__);
179 		else
180 			lwsl_err("%s: unknown protocol %s\n", __func__, local);
181 
182 		lwsl_info("%s: wsi %p: %s %s entry\n",
183 			    __func__, wsi, wsi->role_ops->name,
184 			    wsi->protocol ? wsi->protocol->name : "none");
185 	}
186 
187 	/*
188 	 * PHASE 5: handle external user_space now, generic alloc is done in
189 	 * role finalization
190 	 */
191 
192 	if (!wsi->user_space && i->userdata) {
193 		wsi->user_space_externally_allocated = 1;
194 		wsi->user_space = i->userdata;
195 	}
196 
197 #if defined(LWS_WITH_TLS)
198 	wsi->tls.use_ssl = i->ssl_connection;
199 #else
200 	if (i->ssl_connection & LCCSCF_USE_SSL) {
201 		lwsl_err("%s: lws not configured for tls\n", __func__);
202 		goto bail;
203 	}
204 #endif
205 
206 	/*
207 	 * PHASE 6: stash the things from connect_info that we can't process
208 	 * right now, eg, if http binding, without an ah.  If h1 and no ah, we
209 	 * will go on the ah waiting list and process those things later (after
210 	 * the connect_info and maybe the things pointed to have gone out of
211 	 * scope)
212 	 *
213 	 * However these things are stashed in a generic way at this point,
214 	 * with no relationship to http or ah
215 	 */
216 
217 	cisin[CIS_ADDRESS]	= i->address;
218 	cisin[CIS_PATH]		= i->path;
219 	cisin[CIS_HOST]		= i->host;
220 	cisin[CIS_ORIGIN]	= i->origin;
221 	cisin[CIS_PROTOCOL]	= i->protocol;
222 	cisin[CIS_METHOD]	= i->method;
223 	cisin[CIS_IFACE]	= i->iface;
224 	cisin[CIS_ALPN]		= i->alpn;
225 
226 	size = sizeof(*wsi->stash);
227 
228 	/*
229 	 * Let's overallocate the stash object with space for all the args
230 	 * in one hit.
231 	 */
232 	for (n = 0; n < CIS_COUNT; n++)
233 		if (cisin[n])
234 			size += strlen(cisin[n]) + 1;
235 
236 	wsi->stash = lws_malloc(size, "client stash");
237 	if (!wsi->stash) {
238 		lwsl_err("%s: OOM\n", __func__);
239 		goto bail1;
240 	}
241 	/* all the pointers default to NULL, but no need to zero the args */
242 	memset(wsi->stash, 0, sizeof(*wsi->stash));
243 
244 	wsi->opaque_user_data = wsi->stash->opaque_user_data =
245 		i->opaque_user_data;
246 	pc = (char *)&wsi->stash[1];
247 
248 	for (n = 0; n < CIS_COUNT; n++)
249 		if (cisin[n]) {
250 			wsi->stash->cis[n] = pc;
251 			m = (int)strlen(cisin[n]) + 1;
252 			memcpy(pc, cisin[n], m);
253 			pc += m;
254 		}
255 
256 	/*
257 	 * at this point user callbacks like
258 	 * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER will be interested to
259 	 * know the parent... eg for proxying we can grab extra headers from
260 	 * the parent's incoming ah and add them to the child client handshake
261 	 */
262 
263 	if (i->parent_wsi) {
264 		lwsl_info("%s: created child %p of parent %p\n", __func__,
265 			  wsi, i->parent_wsi);
266 		wsi->parent = i->parent_wsi;
267 		safe = wsi->sibling_list = i->parent_wsi->child_list;
268 		i->parent_wsi->child_list = wsi;
269 	}
270 
271 	/*
272 	 * PHASE 7: Do any role-specific finalization processing.  We can still
273 	 * see important info things via wsi->stash
274 	 */
275 
276 	if (wsi->role_ops->client_bind) {
277 		int n = wsi->role_ops->client_bind(wsi, NULL);
278 
279 		if (n && i->parent_wsi) {
280 			/* unpick from parent */
281 
282 			i->parent_wsi->child_list = safe;
283 		}
284 
285 		if (n < 0)
286 			/* we didn't survive, wsi is freed */
287 			goto bail2;
288 
289 		if (n)
290 			/* something else failed, wsi needs freeing */
291 			goto bail;
292 	}
293 
294 	/* let the caller's optional wsi storage have the wsi we created */
295 
296 	if (i->pwsi)
297 		*i->pwsi = wsi;
298 
299 	/* PHASE 8: notify protocol with role-specific connected callback */
300 
301 	/* raw socket per se doesn't want this... raw socket proxy wants it... */
302 
303 	if (wsi->role_ops != &role_ops_raw_skt ||
304 	    (i->local_protocol_name &&
305 	     !strcmp(i->local_protocol_name, "raw-proxy"))) {
306 		lwsl_debug("%s: wsi %p: adoption cb %d to %s %s\n", __func__,
307 			   wsi, wsi->role_ops->adoption_cb[0],
308 			   wsi->role_ops->name, wsi->protocol->name);
309 
310 		wsi->protocol->callback(wsi, wsi->role_ops->adoption_cb[0],
311 				wsi->user_space, NULL, 0);
312 	}
313 
314 #if defined(LWS_WITH_HUBBUB)
315 	if (i->uri_replace_to)
316 		wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb,
317 					     i->uri_replace_from,
318 					     i->uri_replace_to);
319 #endif
320 
321 	if (i->method && (!strcmp(i->method, "RAW") // ||
322 //			  !strcmp(i->method, "MQTT")
323 	)) {
324 
325 		/*
326 		 * Not for MQTT here, since we don't know if we will
327 		 * pipeline it or not...
328 		 */
329 
330 #if defined(LWS_WITH_TLS)
331 
332 		wsi->tls.ssl = NULL;
333 
334 		if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
335 			const char *cce = NULL;
336 
337 			switch (
338 #if !defined(LWS_WITH_SYS_ASYNC_DNS)
339 			lws_client_create_tls(wsi, &cce, 1)
340 #else
341 			lws_client_create_tls(wsi, &cce, 0)
342 #endif
343 			) {
344 			case 1:
345 				return wsi;
346 			case 0:
347 				break;
348 			default:
349 				goto bail3;
350 			}
351 		}
352 #endif
353 
354 		/* fallthru */
355 
356 		wsi = lws_http_client_connect_via_info2(wsi);
357 	}
358 
359 	if (wsi)
360 		/*
361 		 * If it subsequently fails, report CONNECTION_ERROR,
362 		 * because we're going to return a non-error return now.
363 		 */
364 		wsi->client_suppress_CONNECTION_ERROR = 0;
365 
366 	return wsi;
367 
368 #if defined(LWS_WITH_TLS)
369 bail3:
370 	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "tls start fail");
371 
372 	return NULL;
373 #endif
374 
375 bail1:
376 	lws_free_set_NULL(wsi->stash);
377 
378 bail:
379 	lws_free(wsi);
380 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
381 bail2:
382 #endif
383 
384 	if (i->ssl_connection & LCCSCF_USE_SSL)
385 		lws_tls_restrict_return(i->context);
386 
387 	if (i->pwsi)
388 		*i->pwsi = NULL;
389 
390 	lws_stats_bump(&i->context->pt[tid], LWSSTATS_C_CONNS_CLIENT_FAILED, 1);
391 
392 	return NULL;
393 }
394