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