/src/libwebsockets/lib/core-net/adopt.c
Line | Count | Source |
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 "private-lib-core.h" |
26 | | #include "private-lib-async-dns.h" |
27 | | |
28 | | static int |
29 | | lws_get_idlest_tsi(struct lws_context *context) |
30 | 0 | { |
31 | 0 | unsigned int lowest = ~0u; |
32 | 0 | int n = 0, hit = -1; |
33 | |
|
34 | 0 | for (; n < context->count_threads; n++) { |
35 | 0 | lwsl_cx_debug(context, "%d %d\n", context->pt[n].fds_count, |
36 | 0 | context->fd_limit_per_thread - 1); |
37 | 0 | if ((unsigned int)context->pt[n].fds_count != |
38 | 0 | context->fd_limit_per_thread - 1 && |
39 | 0 | (unsigned int)context->pt[n].fds_count < lowest) { |
40 | 0 | lowest = context->pt[n].fds_count; |
41 | 0 | hit = n; |
42 | 0 | } |
43 | 0 | } |
44 | |
|
45 | 0 | return hit; |
46 | 0 | } |
47 | | |
48 | | struct lws * |
49 | | lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi, int group, |
50 | | const char *desc) |
51 | 0 | { |
52 | 0 | struct lws *new_wsi; |
53 | 0 | int n = fixed_tsi; |
54 | |
|
55 | 0 | if (n < 0) |
56 | 0 | n = lws_get_idlest_tsi(vhost->context); |
57 | |
|
58 | 0 | if (n < 0) { |
59 | 0 | lwsl_vhost_err(vhost, "no space for new conn"); |
60 | 0 | return NULL; |
61 | 0 | } |
62 | | |
63 | 0 | lws_context_lock(vhost->context, __func__); |
64 | 0 | new_wsi = __lws_wsi_create_with_role(vhost->context, n, NULL, |
65 | 0 | vhost->lc.log_cx); |
66 | 0 | lws_context_unlock(vhost->context); |
67 | 0 | if (new_wsi == NULL) { |
68 | 0 | lwsl_vhost_err(vhost, "OOM"); |
69 | 0 | return NULL; |
70 | 0 | } |
71 | | |
72 | 0 | lws_wsi_fault_timedclose(new_wsi); |
73 | |
|
74 | 0 | __lws_lc_tag(vhost->context, &vhost->context->lcg[group], |
75 | 0 | &new_wsi->lc, "%s|%s", vhost->name, desc); |
76 | |
|
77 | 0 | new_wsi->wsistate |= LWSIFR_SERVER; |
78 | 0 | new_wsi->tsi = (char)n; |
79 | 0 | lwsl_wsi_debug(new_wsi, "joining vh %s, tsi %d", |
80 | 0 | vhost->name, new_wsi->tsi); |
81 | |
|
82 | 0 | lws_vhost_bind_wsi(vhost, new_wsi); |
83 | 0 | new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; |
84 | 0 | new_wsi->retry_policy = vhost->retry_policy; |
85 | | |
86 | | /* initialize the instance struct */ |
87 | |
|
88 | 0 | lwsi_set_state(new_wsi, LRS_UNCONNECTED); |
89 | 0 | new_wsi->hdr_parsing_completed = 0; |
90 | |
|
91 | 0 | #ifdef LWS_WITH_TLS |
92 | 0 | new_wsi->tls.use_ssl = LWS_SSL_ENABLED(vhost); |
93 | 0 | #endif |
94 | | |
95 | | /* |
96 | | * these can only be set once the protocol is known |
97 | | * we set an un-established connection's protocol pointer |
98 | | * to the start of the supported list, so it can look |
99 | | * for matching ones during the handshake |
100 | | */ |
101 | 0 | new_wsi->a.protocol = vhost->protocols; |
102 | 0 | new_wsi->user_space = NULL; |
103 | | |
104 | | /* |
105 | | * outermost create notification for wsi |
106 | | * no user_space because no protocol selection |
107 | | */ |
108 | 0 | vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE, NULL, |
109 | 0 | NULL, 0); |
110 | |
|
111 | 0 | return new_wsi; |
112 | 0 | } |
113 | | |
114 | | |
115 | | /* if not a socket, it's a raw, non-ssl file descriptor |
116 | | * req cx lock, acq pt lock, acq vh lock |
117 | | */ |
118 | | |
119 | | static struct lws * |
120 | | __lws_adopt_descriptor_vhost1(struct lws_vhost *vh, lws_adoption_type type, |
121 | | const char *vh_prot_name, struct lws *parent, |
122 | | void *opaque, const char *fi_wsi_name) |
123 | 0 | { |
124 | 0 | struct lws_context *context; |
125 | 0 | struct lws_context_per_thread *pt; |
126 | 0 | struct lws *new_wsi; |
127 | 0 | int n; |
128 | | |
129 | | /* |
130 | | * Notice that in SMP case, the wsi may be being created on an |
131 | | * entirely different pt / tsi for load balancing. In that case as |
132 | | * we initialize it, it may become "live" concurrently unexpectedly... |
133 | | */ |
134 | |
|
135 | 0 | if (!vh) |
136 | 0 | return NULL; |
137 | | |
138 | 0 | context = vh->context; |
139 | |
|
140 | 0 | lws_context_assert_lock_held(vh->context); |
141 | |
|
142 | 0 | n = -1; |
143 | 0 | if (parent) |
144 | 0 | n = parent->tsi; |
145 | 0 | new_wsi = lws_create_new_server_wsi(vh, n, LWSLCG_WSI_SERVER, fi_wsi_name); |
146 | 0 | if (!new_wsi) |
147 | 0 | return NULL; |
148 | | |
149 | | /* bring in specific fault injection rules early */ |
150 | 0 | lws_fi_inherit_copy(&new_wsi->fic, &context->fic, "wsi", fi_wsi_name); |
151 | |
|
152 | 0 | if (lws_fi(&new_wsi->fic, "createfail")) { |
153 | 0 | lws_fi_destroy(&new_wsi->fic); |
154 | |
|
155 | 0 | return NULL; |
156 | 0 | } |
157 | | |
158 | 0 | new_wsi->a.opaque_user_data = opaque; |
159 | |
|
160 | 0 | pt = &context->pt[(int)new_wsi->tsi]; |
161 | 0 | lws_pt_lock(pt, __func__); |
162 | |
|
163 | 0 | if (parent) { |
164 | 0 | new_wsi->parent = parent; |
165 | 0 | new_wsi->sibling_list = parent->child_list; |
166 | 0 | parent->child_list = new_wsi; |
167 | 0 | } |
168 | |
|
169 | 0 | if (vh_prot_name) { |
170 | 0 | new_wsi->a.protocol = lws_vhost_name_to_protocol(new_wsi->a.vhost, |
171 | 0 | vh_prot_name); |
172 | 0 | if (!new_wsi->a.protocol) { |
173 | 0 | lwsl_vhost_err(new_wsi->a.vhost, "Protocol %s not enabled", |
174 | 0 | vh_prot_name); |
175 | 0 | goto bail; |
176 | 0 | } |
177 | 0 | if (lws_ensure_user_space(new_wsi)) { |
178 | 0 | lwsl_wsi_notice(new_wsi, "OOM"); |
179 | 0 | goto bail; |
180 | 0 | } |
181 | 0 | } |
182 | | |
183 | 0 | if (!LWS_SSL_ENABLED(new_wsi->a.vhost) || |
184 | 0 | !(type & LWS_ADOPT_SOCKET)) |
185 | 0 | type &= (unsigned int)~LWS_ADOPT_ALLOW_SSL; |
186 | |
|
187 | 0 | if (lws_role_call_adoption_bind(new_wsi, (int)type, vh_prot_name)) { |
188 | 0 | lwsl_wsi_err(new_wsi, "no role for desc type 0x%x", type); |
189 | 0 | goto bail; |
190 | 0 | } |
191 | | |
192 | 0 | #if defined(LWS_WITH_SERVER) |
193 | 0 | if (new_wsi->role_ops) { |
194 | 0 | lws_metrics_tag_wsi_add(new_wsi, "role", new_wsi->role_ops->name); |
195 | 0 | } |
196 | 0 | #endif |
197 | |
|
198 | 0 | lws_pt_unlock(pt); |
199 | | |
200 | | /* |
201 | | * We can lose him from the context pre_natal "last resort" bind now, |
202 | | * because we will list him on a specific vhost |
203 | | */ |
204 | |
|
205 | 0 | lws_dll2_remove(&new_wsi->pre_natal); |
206 | | |
207 | | /* |
208 | | * he's an allocated wsi, but he's not on any fds list or child list, |
209 | | * join him to the vhost's list of these kinds of incomplete wsi until |
210 | | * he gets another identity (he may do async dns now...) |
211 | | */ |
212 | 0 | lws_vhost_lock(new_wsi->a.vhost); |
213 | |
|
214 | 0 | lws_dll2_add_head(&new_wsi->vh_awaiting_socket, |
215 | 0 | &new_wsi->a.vhost->vh_awaiting_socket_owner); |
216 | 0 | lws_vhost_unlock(new_wsi->a.vhost); |
217 | |
|
218 | 0 | return new_wsi; |
219 | | |
220 | 0 | bail: |
221 | 0 | lws_pt_lock(pt, __func__); /* -------------- pt { */ |
222 | 0 | lws_dll2_remove(&new_wsi->pre_natal); |
223 | 0 | lws_pt_unlock(pt); /* } pt --------------- */ |
224 | |
|
225 | 0 | lwsl_wsi_notice(new_wsi, "exiting on bail"); |
226 | 0 | if (parent) |
227 | 0 | parent->child_list = new_wsi->sibling_list; |
228 | 0 | if (new_wsi->user_space) |
229 | 0 | lws_free(new_wsi->user_space); |
230 | |
|
231 | 0 | lws_fi_destroy(&new_wsi->fic); |
232 | |
|
233 | 0 | lws_pt_unlock(pt); |
234 | 0 | __lws_vhost_unbind_wsi(new_wsi); /* req cx, acq vh lock */ |
235 | |
|
236 | 0 | lws_free(new_wsi); |
237 | |
|
238 | 0 | return NULL; |
239 | 0 | } |
240 | | |
241 | | #if defined(LWS_WITH_SERVER) && defined(LWS_WITH_SECURE_STREAMS) |
242 | | |
243 | | /* |
244 | | * If the incoming wsi is bound to a vhost that is a ss server, this creates |
245 | | * an accepted ss bound to the wsi. |
246 | | * |
247 | | * For h1 or raw, we can do the binding here, but for muxed protocols like h2 |
248 | | * or mqtt we have to do it not on the nwsi but on the stream. And for h2 we |
249 | | * start off bound to h1 role, since we don't know if we will upgrade to h2 |
250 | | * until we meet the server. |
251 | | * |
252 | | * 1) No tls is assumed to mean no muxed protocol so can do it at adopt. |
253 | | * |
254 | | * 2) After alpn if not muxed we can do it. |
255 | | * |
256 | | * 3) For muxed, do it at the nwsi migration and on new stream |
257 | | */ |
258 | | |
259 | | int |
260 | | lws_adopt_ss_server_accept(struct lws *new_wsi) |
261 | 0 | { |
262 | 0 | struct lws_context_per_thread *pt = |
263 | 0 | &new_wsi->a.context->pt[(int)new_wsi->tsi]; |
264 | 0 | lws_ss_handle_t *h; |
265 | 0 | void *pv, **ppv; |
266 | |
|
267 | 0 | if (!new_wsi->a.vhost->ss_handle) |
268 | 0 | return 0; |
269 | | |
270 | 0 | pv = (char *)&new_wsi->a.vhost->ss_handle[1]; |
271 | | |
272 | | /* |
273 | | * Yes... the vhost is pointing to its secure stream representing the |
274 | | * server... we want to create an accepted SS and bind it to new_wsi, |
275 | | * the info/ssi from the server SS (so the SS callbacks defined there), |
276 | | * the opaque_user_data of the server object and the policy of it. |
277 | | */ |
278 | |
|
279 | 0 | ppv = (void **)((char *)pv + |
280 | 0 | new_wsi->a.vhost->ss_handle->info.opaque_user_data_offset); |
281 | | |
282 | | /* |
283 | | * indicate we are an accepted connection referencing the |
284 | | * server object |
285 | | */ |
286 | |
|
287 | 0 | new_wsi->a.vhost->ss_handle->info.flags |= LWSSSINFLAGS_SERVER; |
288 | |
|
289 | 0 | if (lws_ss_create(new_wsi->a.context, new_wsi->tsi, |
290 | 0 | &new_wsi->a.vhost->ss_handle->info, |
291 | 0 | *ppv, &h, NULL, NULL)) { |
292 | 0 | lwsl_wsi_err(new_wsi, "accept ss creation failed"); |
293 | 0 | goto fail1; |
294 | 0 | } |
295 | | |
296 | | /* |
297 | | * We made a fresh accepted SS conn from the server pieces, |
298 | | * now bind the wsi... the problem is, this is the nwsi if it's |
299 | | * h2. |
300 | | */ |
301 | | |
302 | 0 | h->wsi = new_wsi; |
303 | 0 | new_wsi->a.opaque_user_data = h; |
304 | 0 | h->info.flags |= LWSSSINFLAGS_ACCEPTED; |
305 | | /* indicate wsi should invalidate any ss link to it on close */ |
306 | 0 | new_wsi->for_ss = 1; |
307 | | |
308 | | // lwsl_wsi_notice(new_wsi, "%s: opaq %p, role %s", |
309 | | // new_wsi->a.opaque_user_data, |
310 | | // new_wsi->role_ops->name); |
311 | |
|
312 | 0 | h->policy = new_wsi->a.vhost->ss_handle->policy; |
313 | | |
314 | | /* apply requested socket options */ |
315 | 0 | if (lws_plat_set_socket_options_ip(new_wsi->desc.sockfd, |
316 | 0 | h->policy->priority, |
317 | 0 | (LCCSCF_IP_LOW_LATENCY * |
318 | 0 | !!(h->policy->flags & LWSSSPOLF_ATTR_LOW_LATENCY)) | |
319 | 0 | (LCCSCF_IP_HIGH_THROUGHPUT * |
320 | 0 | !!(h->policy->flags & LWSSSPOLF_ATTR_HIGH_THROUGHPUT)) | |
321 | 0 | (LCCSCF_IP_HIGH_RELIABILITY * |
322 | 0 | !!(h->policy->flags & LWSSSPOLF_ATTR_HIGH_RELIABILITY)) | |
323 | 0 | (LCCSCF_IP_LOW_COST * |
324 | 0 | !!(h->policy->flags & LWSSSPOLF_ATTR_LOW_COST)))) |
325 | 0 | lwsl_wsi_warn(new_wsi, "unable to set ip options"); |
326 | | |
327 | | /* |
328 | | * add us to the list of clients that came in from the server |
329 | | */ |
330 | |
|
331 | 0 | lws_pt_lock(pt, __func__); |
332 | 0 | lws_dll2_add_tail(&h->cli_list, &new_wsi->a.vhost->ss_handle->src_list); |
333 | 0 | lws_pt_unlock(pt); |
334 | | |
335 | | /* |
336 | | * Let's give it appropriate state notifications |
337 | | */ |
338 | |
|
339 | 0 | if (lws_ss_event_helper(h, LWSSSCS_CREATING)) |
340 | 0 | goto fail; |
341 | 0 | if (lws_ss_event_helper(h, LWSSSCS_CONNECTING)) |
342 | 0 | goto fail; |
343 | | |
344 | | /* defer CONNECTED until we see if he is upgrading */ |
345 | | |
346 | | // if (lws_ss_event_helper(h, LWSSSCS_CONNECTED)) |
347 | | // goto fail; |
348 | | |
349 | | // lwsl_notice("%s: accepted ss complete, pcol %s\n", __func__, |
350 | | // new_wsi->a.protocol->name); |
351 | | |
352 | 0 | return 0; |
353 | | |
354 | 0 | fail: |
355 | 0 | lws_ss_destroy(&h); |
356 | 0 | fail1: |
357 | 0 | return 1; |
358 | 0 | } |
359 | | |
360 | | #endif |
361 | | |
362 | | |
363 | | static struct lws * |
364 | | lws_adopt_descriptor_vhost2(struct lws *new_wsi, lws_adoption_type type, |
365 | | lws_sock_file_fd_type fd) |
366 | 0 | { |
367 | 0 | struct lws_context_per_thread *pt = |
368 | 0 | &new_wsi->a.context->pt[(int)new_wsi->tsi]; |
369 | 0 | int n; |
370 | | |
371 | | /* enforce that every fd is nonblocking */ |
372 | |
|
373 | 0 | if (type & LWS_ADOPT_SOCKET) { |
374 | 0 | if (lws_plat_set_nonblocking(fd.sockfd)) { |
375 | 0 | lwsl_wsi_err(new_wsi, "unable to set sockfd %d nonblocking", |
376 | 0 | fd.sockfd); |
377 | 0 | goto fail; |
378 | 0 | } |
379 | 0 | } |
380 | 0 | #if !defined(WIN32) |
381 | 0 | else |
382 | 0 | if (lws_plat_set_nonblocking(fd.filefd)) { |
383 | 0 | lwsl_wsi_err(new_wsi, "unable to set filefd nonblocking"); |
384 | 0 | goto fail; |
385 | 0 | } |
386 | 0 | #endif |
387 | | |
388 | 0 | new_wsi->desc = fd; |
389 | |
|
390 | 0 | if (!LWS_SSL_ENABLED(new_wsi->a.vhost) || |
391 | 0 | !(type & LWS_ADOPT_SOCKET)) |
392 | 0 | type &= (unsigned int)~LWS_ADOPT_ALLOW_SSL; |
393 | | |
394 | | /* |
395 | | * A new connection was accepted. Give the user a chance to |
396 | | * set properties of the newly created wsi. There's no protocol |
397 | | * selected yet so we issue this to the vhosts's default protocol, |
398 | | * itself by default protocols[0] |
399 | | */ |
400 | 0 | new_wsi->wsistate |= LWSIFR_SERVER; |
401 | 0 | n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED; |
402 | 0 | if (new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)]) |
403 | 0 | n = new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)]; |
404 | |
|
405 | 0 | if (new_wsi->a.context->event_loop_ops->sock_accept) |
406 | 0 | if (new_wsi->a.context->event_loop_ops->sock_accept(new_wsi)) |
407 | 0 | goto fail; |
408 | | |
409 | 0 | { |
410 | 0 | struct lws *nwsi = lws_get_network_wsi(new_wsi); |
411 | 0 | char ta[64]; |
412 | |
|
413 | 0 | if (nwsi->sa46_peer.sa4.sin_family) |
414 | 0 | lws_sa46_write_numeric_address(&nwsi->sa46_peer, ta, sizeof(ta)); |
415 | 0 | else |
416 | 0 | strncpy(ta, "unknown", sizeof(ta)); |
417 | 0 | __lws_lc_tag_append(&new_wsi->lc, ta); |
418 | 0 | } |
419 | |
|
420 | | #if LWS_MAX_SMP > 1 |
421 | | /* |
422 | | * Caution: after this point the wsi is live on its service thread |
423 | | * which may be concurrent to this. We mark the wsi as still undergoing |
424 | | * init in another pt so the assigned pt leaves it alone. |
425 | | */ |
426 | | new_wsi->undergoing_init_from_other_pt = 1; |
427 | | #endif |
428 | |
|
429 | 0 | if (!(type & LWS_ADOPT_ALLOW_SSL)) { |
430 | 0 | lws_pt_lock(pt, __func__); |
431 | 0 | if (__insert_wsi_socket_into_fds(new_wsi->a.context, new_wsi)) { |
432 | 0 | lws_pt_unlock(pt); |
433 | 0 | lwsl_wsi_err(new_wsi, "fail inserting socket"); |
434 | 0 | goto fail; |
435 | 0 | } |
436 | 0 | lws_pt_unlock(pt); |
437 | 0 | } |
438 | 0 | #if defined(LWS_WITH_SERVER) |
439 | 0 | else |
440 | 0 | if (lws_server_socket_service_ssl(new_wsi, fd.sockfd, 0)) { |
441 | 0 | lwsl_wsi_info(new_wsi, "fail ssl negotiation"); |
442 | |
|
443 | 0 | goto fail; |
444 | 0 | } |
445 | 0 | #endif |
446 | | |
447 | 0 | lws_vhost_lock(new_wsi->a.vhost); |
448 | | /* he has fds visibility now, remove from vhost orphan list */ |
449 | 0 | lws_dll2_remove(&new_wsi->vh_awaiting_socket); |
450 | 0 | lws_vhost_unlock(new_wsi->a.vhost); |
451 | | |
452 | | /* |
453 | | * by deferring callback to this point, after insertion to fds, |
454 | | * lws_callback_on_writable() can work from the callback |
455 | | */ |
456 | 0 | if ((new_wsi->a.protocol->callback)(new_wsi, (enum lws_callback_reasons)n, new_wsi->user_space, |
457 | 0 | NULL, 0)) |
458 | 0 | goto fail; |
459 | | |
460 | | /* role may need to do something after all adoption completed */ |
461 | | |
462 | 0 | lws_role_call_adoption_bind(new_wsi, (int)type | _LWS_ADOPT_FINISH, |
463 | 0 | new_wsi->a.protocol->name); |
464 | |
|
465 | 0 | #if defined(LWS_WITH_SERVER) && defined(LWS_WITH_SECURE_STREAMS) |
466 | | /* |
467 | | * Did we come from an accepted client connection to a ss server? |
468 | | * |
469 | | * !!! For mux protocols, this will cause an additional inactive ss |
470 | | * representing the nwsi. Doing that allows us to support both h1 |
471 | | * (here) and h2 (at __lws_wsi_server_new()) |
472 | | */ |
473 | |
|
474 | 0 | lwsl_wsi_info(new_wsi, "vhost %s", new_wsi->a.vhost->lc.gutag); |
475 | |
|
476 | 0 | if (lws_adopt_ss_server_accept(new_wsi)) |
477 | 0 | goto fail; |
478 | 0 | #endif |
479 | | |
480 | | #if LWS_MAX_SMP > 1 |
481 | | /* its actual pt can service it now */ |
482 | | |
483 | | new_wsi->undergoing_init_from_other_pt = 0; |
484 | | #endif |
485 | | |
486 | 0 | lws_cancel_service_pt(new_wsi); |
487 | |
|
488 | 0 | return new_wsi; |
489 | | |
490 | 0 | fail: |
491 | 0 | if (type & LWS_ADOPT_SOCKET) |
492 | 0 | lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS, |
493 | 0 | "adopt skt fail"); |
494 | |
|
495 | 0 | return NULL; |
496 | 0 | } |
497 | | |
498 | | |
499 | | /* if not a socket, it's a raw, non-ssl file descriptor */ |
500 | | |
501 | | struct lws * |
502 | | lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, |
503 | | lws_sock_file_fd_type fd, const char *vh_prot_name, |
504 | | struct lws *parent) |
505 | 0 | { |
506 | 0 | lws_adopt_desc_t info; |
507 | |
|
508 | 0 | memset(&info, 0, sizeof(info)); |
509 | |
|
510 | 0 | info.vh = vh; |
511 | 0 | info.type = type; |
512 | 0 | info.fd = fd; |
513 | 0 | info.vh_prot_name = vh_prot_name; |
514 | 0 | info.parent = parent; |
515 | |
|
516 | 0 | return lws_adopt_descriptor_vhost_via_info(&info); |
517 | 0 | } |
518 | | |
519 | | struct lws * |
520 | | lws_adopt_descriptor_vhost_via_info(const lws_adopt_desc_t *info) |
521 | 0 | { |
522 | 0 | socklen_t slen = sizeof(lws_sockaddr46); |
523 | 0 | struct lws *new_wsi; |
524 | |
|
525 | | #if defined(LWS_WITH_PEER_LIMITS) |
526 | | struct lws_peer *peer = NULL; |
527 | | |
528 | | if (info->type & LWS_ADOPT_SOCKET) { |
529 | | peer = lws_get_or_create_peer(info->vh, info->fd.sockfd); |
530 | | |
531 | | if (peer && info->vh->context->ip_limit_wsi && |
532 | | peer->count_wsi >= info->vh->context->ip_limit_wsi) { |
533 | | lwsl_info("Peer reached wsi limit %d\n", |
534 | | info->vh->context->ip_limit_wsi); |
535 | | if (info->vh->context->pl_notify_cb) |
536 | | info->vh->context->pl_notify_cb( |
537 | | info->vh->context, |
538 | | info->fd.sockfd, |
539 | | &peer->sa46); |
540 | | compatible_close(info->fd.sockfd); |
541 | | return NULL; |
542 | | } |
543 | | } |
544 | | #endif |
545 | |
|
546 | 0 | lws_context_lock(info->vh->context, __func__); |
547 | |
|
548 | 0 | new_wsi = __lws_adopt_descriptor_vhost1(info->vh, info->type, |
549 | 0 | info->vh_prot_name, info->parent, |
550 | 0 | info->opaque, info->fi_wsi_name); |
551 | 0 | if (!new_wsi) { |
552 | 0 | if (info->type & LWS_ADOPT_SOCKET) |
553 | 0 | compatible_close(info->fd.sockfd); |
554 | 0 | goto bail; |
555 | 0 | } |
556 | | |
557 | 0 | if (info->type & LWS_ADOPT_SOCKET && |
558 | 0 | getpeername(info->fd.sockfd, (struct sockaddr *)&new_wsi->sa46_peer, |
559 | 0 | &slen) < 0) |
560 | 0 | lwsl_info("%s: getpeername failed\n", __func__); |
561 | |
|
562 | | #if defined(LWS_WITH_PEER_LIMITS) |
563 | | if (peer) |
564 | | lws_peer_add_wsi(info->vh->context, peer, new_wsi); |
565 | | #endif |
566 | |
|
567 | 0 | new_wsi = lws_adopt_descriptor_vhost2(new_wsi, info->type, info->fd); |
568 | |
|
569 | 0 | bail: |
570 | 0 | lws_context_unlock(info->vh->context); |
571 | |
|
572 | 0 | return new_wsi; |
573 | 0 | } |
574 | | |
575 | | struct lws * |
576 | | lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd) |
577 | 0 | { |
578 | 0 | lws_sock_file_fd_type fd; |
579 | |
|
580 | 0 | fd.sockfd = accept_fd; |
581 | 0 | return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET | |
582 | 0 | LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL); |
583 | 0 | } |
584 | | |
585 | | struct lws * |
586 | | lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) |
587 | 0 | { |
588 | 0 | return lws_adopt_socket_vhost(context->vhost_list, accept_fd); |
589 | 0 | } |
590 | | |
591 | | /* Common read-buffer adoption for lws_adopt_*_readbuf */ |
592 | | static struct lws* |
593 | | adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len) |
594 | 0 | { |
595 | 0 | struct lws_context_per_thread *pt; |
596 | 0 | struct lws_pollfd *pfd; |
597 | 0 | int n; |
598 | |
|
599 | 0 | if (!wsi) |
600 | 0 | return NULL; |
601 | | |
602 | 0 | if (!readbuf || len == 0) |
603 | 0 | return wsi; |
604 | | |
605 | 0 | if (wsi->position_in_fds_table == LWS_NO_FDS_POS) |
606 | 0 | return wsi; |
607 | | |
608 | 0 | pt = &wsi->a.context->pt[(int)wsi->tsi]; |
609 | |
|
610 | 0 | n = lws_buflist_append_segment(&wsi->buflist, (const uint8_t *)readbuf, |
611 | 0 | len); |
612 | 0 | if (n < 0) |
613 | 0 | goto bail; |
614 | 0 | if (n) |
615 | 0 | lws_dll2_add_head(&wsi->dll_buflist, &pt->dll_buflist_owner); |
616 | | |
617 | | /* |
618 | | * we can't process the initial read data until we can attach an ah. |
619 | | * |
620 | | * if one is available, get it and place the data in his ah rxbuf... |
621 | | * wsi with ah that have pending rxbuf get auto-POLLIN service. |
622 | | * |
623 | | * no autoservice because we didn't get a chance to attach the |
624 | | * readbuf data to wsi or ah yet, and we will do it next if we get |
625 | | * the ah. |
626 | | */ |
627 | 0 | if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) { |
628 | |
|
629 | 0 | lwsl_notice("%s: calling service on readbuf ah\n", __func__); |
630 | | |
631 | | /* |
632 | | * unlike a normal connect, we have the headers already |
633 | | * (or the first part of them anyway). |
634 | | * libuv won't come back and service us without a network |
635 | | * event, so we need to do the header service right here. |
636 | | */ |
637 | 0 | pfd = &pt->fds[wsi->position_in_fds_table]; |
638 | 0 | pfd->revents |= LWS_POLLIN; |
639 | 0 | lwsl_err("%s: calling service\n", __func__); |
640 | 0 | if (lws_service_fd_tsi(wsi->a.context, pfd, wsi->tsi)) |
641 | | /* service closed us */ |
642 | 0 | return NULL; |
643 | | |
644 | 0 | return wsi; |
645 | 0 | } |
646 | 0 | lwsl_err("%s: deferring handling ah\n", __func__); |
647 | |
|
648 | 0 | return wsi; |
649 | | |
650 | 0 | bail: |
651 | 0 | lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, |
652 | 0 | "adopt skt readbuf fail"); |
653 | |
|
654 | 0 | return NULL; |
655 | 0 | } |
656 | | |
657 | | #if defined(LWS_WITH_UDP) |
658 | | #if defined(LWS_WITH_CLIENT) |
659 | | |
660 | | /* |
661 | | * This is the ASYNC_DNS callback target for udp client, it's analogous to |
662 | | * connect3() |
663 | | */ |
664 | | |
665 | | static struct lws * |
666 | | lws_create_adopt_udp2(struct lws *wsi, const char *ads, |
667 | | const struct addrinfo *r, int n, void *opaque) |
668 | 0 | { |
669 | 0 | lws_sock_file_fd_type sock; |
670 | 0 | int bc = 1, m; |
671 | |
|
672 | 0 | assert(wsi); |
673 | |
|
674 | 0 | if (ads && (n < 0 || !r)) { |
675 | | /* |
676 | | * DNS lookup failed: there are no usable results. Fail the |
677 | | * overall connection request. |
678 | | */ |
679 | 0 | lwsl_notice("%s: bad: n %d, r %p\n", __func__, n, r); |
680 | |
|
681 | 0 | goto bail; |
682 | 0 | } |
683 | | |
684 | 0 | m = lws_sort_dns(wsi, r); |
685 | | #if defined(LWS_WITH_SYS_ASYNC_DNS) |
686 | | lws_async_dns_freeaddrinfo(&r); |
687 | | #else |
688 | 0 | freeaddrinfo((struct addrinfo *)r); |
689 | 0 | #endif |
690 | 0 | if (m) |
691 | 0 | goto bail; |
692 | | |
693 | 0 | while (lws_dll2_get_head(&wsi->dns_sorted_list)) { |
694 | 0 | lws_dns_sort_t *s = lws_container_of( |
695 | 0 | lws_dll2_get_head(&wsi->dns_sorted_list), |
696 | 0 | lws_dns_sort_t, list); |
697 | | |
698 | | /* |
699 | | * Remove it from the head, but don't free it yet... we are |
700 | | * taking responsibility to free it |
701 | | */ |
702 | 0 | lws_dll2_remove(&s->list); |
703 | | |
704 | | /* |
705 | | * We have done the dns lookup, identify the result we want |
706 | | * if any, and then complete the adoption by binding wsi to |
707 | | * socket opened on it. |
708 | | * |
709 | | * Ignore the weak assumptions about protocol driven by port |
710 | | * number and force to DGRAM / UDP since that's what this |
711 | | * function is for. |
712 | | */ |
713 | |
|
714 | | #if !defined(__linux__) |
715 | | sock.sockfd = socket(s->dest.sa4.sin_family, |
716 | | SOCK_DGRAM, IPPROTO_UDP); |
717 | | #else |
718 | | /* PF_PACKET is linux-only */ |
719 | 0 | sock.sockfd = socket(wsi->pf_packet ? PF_PACKET : |
720 | 0 | s->dest.sa4.sin_family, |
721 | 0 | SOCK_DGRAM, wsi->pf_packet ? |
722 | 0 | htons(0x800) : IPPROTO_UDP); |
723 | 0 | #endif |
724 | 0 | if (sock.sockfd == LWS_SOCK_INVALID) |
725 | 0 | goto resume; |
726 | | |
727 | | /* ipv6 udp!!! */ |
728 | | |
729 | 0 | if (s->af == AF_INET) |
730 | 0 | s->dest.sa4.sin_port = htons(wsi->c_port); |
731 | 0 | #if defined(LWS_WITH_IPV6) |
732 | 0 | else |
733 | 0 | s->dest.sa6.sin6_port = htons(wsi->c_port); |
734 | 0 | #endif |
735 | |
|
736 | 0 | if (setsockopt(sock.sockfd, SOL_SOCKET, SO_REUSEADDR, |
737 | 0 | (const char *)&bc, sizeof(bc)) < 0) |
738 | 0 | lwsl_err("%s: failed to set reuse\n", __func__); |
739 | |
|
740 | 0 | if (wsi->do_broadcast && |
741 | 0 | setsockopt(sock.sockfd, SOL_SOCKET, SO_BROADCAST, |
742 | 0 | (const char *)&bc, sizeof(bc)) < 0) |
743 | 0 | lwsl_err("%s: failed to set broadcast\n", __func__); |
744 | | |
745 | | /* Bind the udp socket to a particular network interface */ |
746 | |
|
747 | 0 | if (opaque && |
748 | 0 | lws_plat_BINDTODEVICE(sock.sockfd, (const char *)opaque)) |
749 | 0 | goto resume; |
750 | | |
751 | 0 | if (wsi->do_bind && |
752 | 0 | bind(sock.sockfd, sa46_sockaddr(&s->dest), |
753 | | #if defined(_WIN32) |
754 | | (int)sa46_socklen(&s->dest) |
755 | | #else |
756 | 0 | sizeof(struct sockaddr) |
757 | 0 | #endif |
758 | 0 | ) == -1) { |
759 | 0 | lwsl_err("%s: bind failed\n", __func__); |
760 | 0 | goto resume; |
761 | 0 | } |
762 | | |
763 | 0 | if (!wsi->do_bind && !wsi->pf_packet) { |
764 | 0 | #if !defined(__APPLE__) |
765 | 0 | if (connect(sock.sockfd, sa46_sockaddr(&s->dest), |
766 | 0 | sa46_socklen(&s->dest)) == -1 && |
767 | 0 | errno != EADDRNOTAVAIL /* openbsd */ ) { |
768 | 0 | lwsl_err("%s: conn fd %d fam %d %s:%u failed " |
769 | 0 | "errno %d\n", __func__, sock.sockfd, |
770 | 0 | s->dest.sa4.sin_family, |
771 | 0 | ads ? ads : "null", wsi->c_port, |
772 | 0 | LWS_ERRNO); |
773 | 0 | compatible_close(sock.sockfd); |
774 | 0 | goto resume; |
775 | 0 | } |
776 | 0 | #endif |
777 | 0 | } |
778 | | |
779 | 0 | if (wsi->udp) |
780 | 0 | wsi->udp->sa46 = s->dest; |
781 | 0 | wsi->sa46_peer = s->dest; |
782 | | |
783 | | /* we connected: complete the udp socket adoption flow */ |
784 | |
|
785 | | #if defined(LWS_WITH_SYS_ASYNC_DNS) |
786 | | { |
787 | | lws_async_dns_server_t *asds = |
788 | | __lws_async_dns_server_find_wsi( |
789 | | &wsi->a.context->async_dns, wsi); |
790 | | if (asds) |
791 | | asds->dns_server_connected = 1; |
792 | | } |
793 | | #endif |
794 | |
|
795 | 0 | lws_free(s); |
796 | 0 | lws_addrinfo_clean(wsi); |
797 | 0 | return lws_adopt_descriptor_vhost2(wsi, |
798 | 0 | LWS_ADOPT_RAW_SOCKET_UDP, sock); |
799 | | |
800 | 0 | resume: |
801 | 0 | lws_free(s); |
802 | 0 | } |
803 | | |
804 | 0 | lwsl_err("%s: unable to create INET socket %d\n", __func__, LWS_ERRNO); |
805 | 0 | lws_addrinfo_clean(wsi); |
806 | |
|
807 | | #if defined(LWS_WITH_SYS_ASYNC_DNS) |
808 | | { |
809 | | lws_async_dns_server_t *asds = __lws_async_dns_server_find_wsi( |
810 | | &wsi->a.context->async_dns, wsi); |
811 | | if (asds) |
812 | | lws_async_dns_drop_server(asds); |
813 | | } |
814 | | #endif |
815 | |
|
816 | 0 | bail: |
817 | | |
818 | | /* caller must close */ |
819 | |
|
820 | 0 | return NULL; |
821 | 0 | } |
822 | | |
823 | | struct lws * |
824 | | lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port, |
825 | | int flags, const char *protocol_name, const char *ifname, |
826 | | struct lws *parent_wsi, void *opaque, |
827 | | const lws_retry_bo_t *retry_policy, const char *fi_wsi_name) |
828 | 0 | { |
829 | 0 | #if !defined(LWS_PLAT_OPTEE) |
830 | 0 | struct lws *wsi; |
831 | 0 | int n; |
832 | |
|
833 | 0 | lwsl_info("%s: %s:%u\n", __func__, ads ? ads : "null", port); |
834 | | |
835 | | /* create the logical wsi without any valid fd */ |
836 | |
|
837 | 0 | lws_context_lock(vhost->context, __func__); |
838 | |
|
839 | 0 | wsi = __lws_adopt_descriptor_vhost1(vhost, LWS_ADOPT_SOCKET | |
840 | 0 | LWS_ADOPT_RAW_SOCKET_UDP, |
841 | 0 | protocol_name, parent_wsi, opaque, |
842 | 0 | fi_wsi_name); |
843 | |
|
844 | 0 | lws_context_unlock(vhost->context); |
845 | 0 | if (!wsi) { |
846 | 0 | lwsl_err("%s: udp wsi creation failed\n", __func__); |
847 | 0 | goto bail; |
848 | 0 | } |
849 | | |
850 | | // lwsl_notice("%s: role %s\n", __func__, wsi->role_ops->name); |
851 | | |
852 | 0 | wsi->do_bind = !!(flags & LWS_CAUDP_BIND); |
853 | 0 | wsi->do_broadcast = !!(flags & LWS_CAUDP_BROADCAST); |
854 | 0 | wsi->pf_packet = !!(flags & LWS_CAUDP_PF_PACKET); |
855 | 0 | wsi->c_port = (uint16_t)(unsigned int)port; |
856 | 0 | if (retry_policy) |
857 | 0 | wsi->retry_policy = retry_policy; |
858 | 0 | else |
859 | 0 | wsi->retry_policy = vhost->retry_policy; |
860 | |
|
861 | 0 | #if !defined(LWS_WITH_SYS_ASYNC_DNS) |
862 | 0 | { |
863 | 0 | struct addrinfo *r, h; |
864 | 0 | char buf[16]; |
865 | |
|
866 | 0 | memset(&h, 0, sizeof(h)); |
867 | 0 | h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ |
868 | 0 | h.ai_socktype = SOCK_DGRAM; |
869 | 0 | h.ai_protocol = IPPROTO_UDP; |
870 | 0 | #if defined(AI_PASSIVE) |
871 | 0 | h.ai_flags = AI_PASSIVE; |
872 | 0 | #endif |
873 | 0 | #ifdef AI_ADDRCONFIG |
874 | 0 | h.ai_flags |= AI_ADDRCONFIG; |
875 | 0 | #endif |
876 | | |
877 | | /* if the dns lookup is synchronous, do the whole thing now */ |
878 | 0 | lws_snprintf(buf, sizeof(buf), "%u", port); |
879 | 0 | n = getaddrinfo(ads, buf, &h, &r); |
880 | 0 | if (n) { |
881 | 0 | #if !defined(LWS_PLAT_FREERTOS) |
882 | 0 | lwsl_cx_info(vhost->context, "getaddrinfo error: %d", n); |
883 | | #else |
884 | | #if (_LWS_ENABLED_LOGS & LLL_INFO) |
885 | | char t16[16]; |
886 | | lwsl_cx_info(vhost->context, "getaddrinfo error: %s", |
887 | | lws_errno_describe(LWS_ERRNO, t16, sizeof(t16))); |
888 | | #endif |
889 | | #endif |
890 | | //freeaddrinfo(r); |
891 | 0 | goto bail1; |
892 | 0 | } |
893 | | /* |
894 | | * With synchronous dns, complete it immediately after the |
895 | | * blocking dns lookup finished... free r when connect either |
896 | | * completed or failed |
897 | | */ |
898 | 0 | wsi = lws_create_adopt_udp2(wsi, ads, r, 0, NULL); |
899 | |
|
900 | 0 | return wsi; |
901 | 0 | } |
902 | | #else |
903 | | if (ads) { |
904 | | /* |
905 | | * with async dns, use the wsi as the point about which to do |
906 | | * the dns lookup and have it call the second part when it's |
907 | | * done. |
908 | | * |
909 | | * Keep a refcount on the results and free it when we connected |
910 | | * or definitively failed. |
911 | | * |
912 | | * Notice wsi has no socket at this point (we don't know what |
913 | | * kind to ask for until we get the dns back). But it is bound |
914 | | * to a vhost and can be cleaned up from that at vhost destroy. |
915 | | */ |
916 | | n = lws_async_dns_query(vhost->context, 0, ads, |
917 | | LWS_ADNS_RECORD_A, |
918 | | lws_create_adopt_udp2, wsi, |
919 | | (void *)ifname, NULL); |
920 | | // lwsl_notice("%s: dns query returned %d\n", __func__, n); |
921 | | if (n == LADNS_RET_FAILED) { |
922 | | lwsl_err("%s: async dns failed\n", __func__); |
923 | | wsi = NULL; |
924 | | /* |
925 | | * It was already closed by calling callback with error |
926 | | * from lws_async_dns_query() |
927 | | */ |
928 | | goto bail; |
929 | | } |
930 | | } else { |
931 | | lwsl_debug("%s: udp adopt has no ads\n", __func__); |
932 | | wsi = lws_create_adopt_udp2(wsi, ads, NULL, 0, (void *)ifname); |
933 | | } |
934 | | |
935 | | /* dns lookup is happening asynchronously */ |
936 | | |
937 | | // lwsl_notice("%s: returning wsi %p\n", __func__, wsi); |
938 | | |
939 | | return wsi; |
940 | | #endif |
941 | 0 | #if !defined(LWS_WITH_SYS_ASYNC_DNS) |
942 | 0 | bail1: |
943 | 0 | lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt udp2 fail"); |
944 | 0 | wsi = NULL; |
945 | 0 | #endif |
946 | 0 | bail: |
947 | 0 | return wsi; |
948 | | #else |
949 | | return NULL; |
950 | | #endif |
951 | 0 | } |
952 | | #endif |
953 | | #endif |
954 | | |
955 | | struct lws * |
956 | | lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, |
957 | | const char *readbuf, size_t len) |
958 | 0 | { |
959 | 0 | return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd), |
960 | 0 | readbuf, len); |
961 | 0 | } |
962 | | |
963 | | struct lws * |
964 | | lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, |
965 | | lws_sockfd_type accept_fd, |
966 | | const char *readbuf, size_t len) |
967 | 0 | { |
968 | 0 | return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd), |
969 | 0 | readbuf, len); |
970 | 0 | } |