/src/libwebsockets/lib/roles/ws/client-ws.c
Line | Count | Source |
1 | | /* |
2 | | * libwebsockets - small server side websockets and web server implementation |
3 | | * |
4 | | * Copyright (C) 2010 - 2021 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 | | /* |
28 | | * In-place str to lower case |
29 | | */ |
30 | | |
31 | | static void |
32 | | strtolower(char *s) |
33 | 0 | { |
34 | 0 | while (*s) { |
35 | | #ifdef LWS_PLAT_OPTEE |
36 | | int tolower_optee(int c); |
37 | | *s = tolower_optee((int)*s); |
38 | | #else |
39 | 0 | *s = (char)tolower((int)*s); |
40 | 0 | #endif |
41 | 0 | s++; |
42 | 0 | } |
43 | 0 | } |
44 | | |
45 | | int |
46 | | lws_create_client_ws_object(const struct lws_client_connect_info *i, |
47 | | struct lws *wsi) |
48 | 0 | { |
49 | 0 | int v = SPEC_LATEST_SUPPORTED; |
50 | | |
51 | | /* allocate the ws struct for the wsi */ |
52 | 0 | wsi->ws = lws_zalloc(sizeof(*wsi->ws), "client ws struct"); |
53 | 0 | if (!wsi->ws) { |
54 | 0 | lwsl_wsi_notice(wsi, "OOM"); |
55 | 0 | return 1; |
56 | 0 | } |
57 | | |
58 | | /* -1 means just use latest supported */ |
59 | 0 | if (i->ietf_version_or_minus_one != -1 && |
60 | 0 | i->ietf_version_or_minus_one) |
61 | 0 | v = i->ietf_version_or_minus_one; |
62 | |
|
63 | 0 | wsi->ws->ietf_spec_revision = (uint8_t)v; |
64 | |
|
65 | 0 | if (i->allow_reserved_bits) |
66 | 0 | wsi->ws->allow_reserved_bits = 1; |
67 | |
|
68 | 0 | if (i->allow_unknown_opcode) |
69 | 0 | wsi->ws->allow_unknown_opcode = 1; |
70 | |
|
71 | 0 | return 0; |
72 | 0 | } |
73 | | |
74 | | #if defined(LWS_WITH_CLIENT) |
75 | | |
76 | | /* |
77 | | * Returns either LWS_HPI_RET_PLEASE_CLOSE_ME or LWS_HPI_RET_HANDLED |
78 | | */ |
79 | | |
80 | | lws_handling_result_t |
81 | | lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len) |
82 | 0 | { |
83 | 0 | const uint8_t **cbuf = (const uint8_t **)buf; |
84 | 0 | unsigned char *start_buf = *buf; |
85 | |
|
86 | 0 | if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) && |
87 | 0 | (lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) && |
88 | 0 | (lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) && |
89 | 0 | !lwsi_role_client(wsi)) |
90 | 0 | return LWS_HPI_RET_HANDLED; |
91 | | |
92 | 0 | lwsl_wsi_debug(wsi, "hs client has %d in", (int)len); |
93 | |
|
94 | 0 | if (lws_is_flowcontrolled(wsi)) { |
95 | 0 | lwsl_wsi_debug(wsi, "caching %ld", (long)len); |
96 | 0 | if (lws_rxflow_cache(wsi, *buf, 0, len) == LWSRXFC_TRIMMED) { |
97 | 0 | lwsl_wsi_info(wsi, "trimming inside rxflow cache"); |
98 | | /* we indicate we used nothing, caller will not advance */ |
99 | 0 | } else { |
100 | 0 | *buf += len; /* we indicate we used it all */ |
101 | 0 | } |
102 | 0 | return LWS_HPI_RET_HANDLED; |
103 | 0 | } |
104 | | |
105 | | #if !defined(LWS_WITHOUT_EXTENSIONS) |
106 | | if (wsi->ws->rx_draining_ext) { |
107 | | lwsl_wsi_info(wsi, "draining ext"); |
108 | | if (lws_ws_client_rx_sm(wsi, 0) == LWS_HPI_RET_PLEASE_CLOSE_ME) |
109 | | return LWS_HPI_RET_PLEASE_CLOSE_ME; |
110 | | } |
111 | | #endif |
112 | | |
113 | 0 | if (len) { |
114 | 0 | if (lws_ws_client_rx_parser_block(wsi, cbuf, &len) != |
115 | 0 | LWS_HPI_RET_HANDLED) { |
116 | 0 | lwsl_wsi_info(wsi, "client_rx_parser exited, closing"); |
117 | 0 | *buf = start_buf + len; /* Update how much we used */ |
118 | 0 | return LWS_HPI_RET_PLEASE_CLOSE_ME; |
119 | 0 | } |
120 | 0 | } |
121 | | |
122 | 0 | return LWS_HPI_RET_HANDLED; |
123 | 0 | } |
124 | | #endif |
125 | | |
126 | | char * |
127 | | lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1, size_t p_len) |
128 | 0 | { |
129 | 0 | char buf[128], hash[20], key_b64[40], *end = p + p_len; |
130 | 0 | size_t s; |
131 | | #if !defined(LWS_WITHOUT_EXTENSIONS) |
132 | | const struct lws_extension *ext; |
133 | | int ext_count = 0; |
134 | | #endif |
135 | | |
136 | | /* |
137 | | * create the random key |
138 | | */ |
139 | 0 | if (lws_get_random(wsi->a.context, hash, 16) != 16) { |
140 | 0 | lwsl_wsi_err(wsi, "Unable to read from random dev %s", |
141 | 0 | SYSTEM_RANDOM_FILEPATH); |
142 | 0 | return NULL; |
143 | 0 | } |
144 | | |
145 | | /* coverity[tainted_scalar] */ |
146 | 0 | lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64)); |
147 | |
|
148 | 0 | p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), |
149 | 0 | "Upgrade: websocket\x0d\x0a" |
150 | 0 | "Connection: %sUpgrade\x0d\x0a" |
151 | 0 | "Sec-WebSocket-Key: ", conn1); |
152 | |
|
153 | 0 | if (lws_ptr_diff_size_t(end, p) < strlen(key_b64) + 2 + 128) |
154 | 0 | return NULL; |
155 | | |
156 | 0 | lws_strncpy(p, key_b64, lws_ptr_diff_size_t(end, p)); |
157 | 0 | p += strlen(key_b64); |
158 | 0 | p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\x0d\x0a"); |
159 | 0 | if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)) |
160 | 0 | p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), |
161 | 0 | "Sec-WebSocket-Protocol: %s\x0d\x0a", |
162 | 0 | lws_hdr_simple_ptr(wsi, |
163 | 0 | _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)); |
164 | | |
165 | | /* tell the server what extensions we could support */ |
166 | |
|
167 | | #if !defined(LWS_WITHOUT_EXTENSIONS) |
168 | | ext = wsi->a.vhost->ws.extensions; |
169 | | while (ext && ext->callback) { |
170 | | |
171 | | int n = wsi->a.vhost->protocols[0].callback(wsi, |
172 | | LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED, |
173 | | wsi->user_space, (char *)ext->name, 0); |
174 | | |
175 | | /* |
176 | | * zero return from callback means go ahead and allow |
177 | | * the extension, it's what we get if the callback is |
178 | | * unhandled |
179 | | */ |
180 | | |
181 | | if (n) { |
182 | | ext++; |
183 | | continue; |
184 | | } |
185 | | |
186 | | /* apply it */ |
187 | | |
188 | | if (ext_count) |
189 | | *p++ = ','; |
190 | | else |
191 | | p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), |
192 | | "Sec-WebSocket-Extensions: "); |
193 | | p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), |
194 | | "%s", ext->client_offer); |
195 | | ext_count++; |
196 | | |
197 | | ext++; |
198 | | } |
199 | | if (ext_count) |
200 | | p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\x0d\x0a"); |
201 | | #endif |
202 | |
|
203 | 0 | if (wsi->ws->ietf_spec_revision) |
204 | 0 | p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), |
205 | 0 | "Sec-WebSocket-Version: %d\x0d\x0a", |
206 | 0 | wsi->ws->ietf_spec_revision); |
207 | | |
208 | | /* prepare the expected server accept response */ |
209 | |
|
210 | 0 | key_b64[39] = '\0'; /* enforce composed length below buf sizeof */ |
211 | |
|
212 | 0 | s = strlen(key_b64); |
213 | 0 | memcpy(buf, key_b64, s); |
214 | 0 | memcpy(buf + s, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", 36); |
215 | 0 | s += 36; |
216 | |
|
217 | 0 | lws_SHA1((unsigned char *)buf, (unsigned int)s, (unsigned char *)hash); |
218 | |
|
219 | 0 | lws_b64_encode_string(hash, 20, |
220 | 0 | wsi->http.ah->initial_handshake_hash_base64, |
221 | 0 | sizeof(wsi->http.ah->initial_handshake_hash_base64)); |
222 | |
|
223 | 0 | return p; |
224 | 0 | } |
225 | | |
226 | | int |
227 | | lws_client_ws_upgrade(struct lws *wsi, const char **cce) |
228 | 0 | { |
229 | 0 | struct lws_context *context = wsi->a.context; |
230 | 0 | struct lws_tokenize ts; |
231 | 0 | int n, len, okay = 0; |
232 | 0 | lws_tokenize_elem e; |
233 | 0 | char *p, buf[64]; |
234 | 0 | const char *pc; |
235 | | #if !defined(LWS_WITHOUT_EXTENSIONS) |
236 | | struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; |
237 | | char *sb = (char *)&pt->serv_buf[0]; |
238 | | const struct lws_ext_options *opts; |
239 | | const struct lws_extension *ext; |
240 | | char ext_name[128]; |
241 | | const char *c, *a; |
242 | | int more = 1; |
243 | | char ignore; |
244 | | #endif |
245 | |
|
246 | 0 | if (wsi->client_mux_substream) {/* !!! client ws-over-h2 not there yet */ |
247 | 0 | lwsl_wsi_warn(wsi, "client ws-over-h2 upgrade not supported yet"); |
248 | 0 | *cce = "HS: h2 / ws upgrade unsupported"; |
249 | 0 | goto bail3; |
250 | 0 | } |
251 | | |
252 | 0 | if (wsi->http.ah->http_response == 401) { |
253 | 0 | lwsl_wsi_warn(wsi, "got bad HTTP response '%ld'", |
254 | 0 | (long)wsi->http.ah->http_response); |
255 | 0 | *cce = "HS: ws upgrade unauthorized"; |
256 | 0 | goto bail3; |
257 | 0 | } |
258 | | |
259 | 0 | if (wsi->http.ah->http_response != 101) { |
260 | 0 | lwsl_wsi_warn(wsi, "got bad HTTP response '%ld'", |
261 | 0 | (long)wsi->http.ah->http_response); |
262 | 0 | *cce = "HS: ws upgrade response not 101"; |
263 | 0 | goto bail3; |
264 | 0 | } |
265 | | |
266 | 0 | if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) { |
267 | 0 | lwsl_wsi_info(wsi, "no ACCEPT"); |
268 | 0 | *cce = "HS: ACCEPT missing"; |
269 | 0 | goto bail3; |
270 | 0 | } |
271 | | |
272 | 0 | p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE); |
273 | 0 | if (!p) { |
274 | 0 | lwsl_wsi_info(wsi, "no UPGRADE"); |
275 | 0 | *cce = "HS: UPGRADE missing"; |
276 | 0 | goto bail3; |
277 | 0 | } |
278 | 0 | strtolower(p); |
279 | 0 | if (strcmp(p, "websocket")) { |
280 | 0 | lwsl_wsi_warn(wsi, "got bad Upgrade header '%s'", p); |
281 | 0 | *cce = "HS: Upgrade to something other than websocket"; |
282 | 0 | goto bail3; |
283 | 0 | } |
284 | | |
285 | | /* connection: must have "upgrade" */ |
286 | | |
287 | 0 | lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST | |
288 | 0 | LWS_TOKENIZE_F_MINUS_NONTERM); |
289 | 0 | n = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_CONNECTION); |
290 | 0 | if (n <= 0) /* won't fit, or absent */ |
291 | 0 | goto bad_conn_format; |
292 | 0 | ts.len = (unsigned int)n; |
293 | |
|
294 | 0 | do { |
295 | 0 | e = lws_tokenize(&ts); |
296 | 0 | switch (e) { |
297 | 0 | case LWS_TOKZE_TOKEN: |
298 | 0 | if (!strncasecmp(ts.token, "upgrade", ts.token_len)) |
299 | 0 | e = LWS_TOKZE_ENDED; |
300 | 0 | break; |
301 | | |
302 | 0 | case LWS_TOKZE_DELIMITER: |
303 | 0 | break; |
304 | | |
305 | 0 | default: /* includes ENDED found by the tokenizer itself */ |
306 | 0 | bad_conn_format: |
307 | 0 | lwsl_wsi_info(wsi, "malformed connection '%s'", buf); |
308 | 0 | *cce = "HS: UPGRADE malformed"; |
309 | 0 | goto bail3; |
310 | 0 | } |
311 | 0 | } while (e > 0); |
312 | | |
313 | 0 | pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); |
314 | 0 | #if defined(_DEBUG) |
315 | 0 | if (!pc) |
316 | 0 | lwsl_wsi_parser(wsi, "lws_client_int_s_hs: no protocol list"); |
317 | 0 | else |
318 | 0 | lwsl_wsi_parser(wsi, "lws_client_int_s_hs: protocol list '%s'", pc); |
319 | 0 | #endif |
320 | | |
321 | | /* |
322 | | * confirm the protocol the server wants to talk was in the list |
323 | | * of protocols we offered |
324 | | */ |
325 | |
|
326 | 0 | len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL); |
327 | 0 | if (!len) { |
328 | 0 | lwsl_wsi_info(wsi, "WSI_TOKEN_PROTOCOL is null"); |
329 | | /* |
330 | | * no protocol name to work from, if we don't already have one |
331 | | * default to first protocol |
332 | | */ |
333 | |
|
334 | 0 | if (wsi->a.protocol) { |
335 | 0 | p = (char *)wsi->a.protocol->name; |
336 | 0 | goto identify_protocol; |
337 | 0 | } |
338 | | |
339 | | /* no choice but to use the default protocol */ |
340 | | |
341 | 0 | n = 0; |
342 | 0 | wsi->a.protocol = &wsi->a.vhost->protocols[0]; |
343 | 0 | goto check_extensions; |
344 | 0 | } |
345 | | |
346 | 0 | p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL); |
347 | 0 | len = (int)strlen(p); |
348 | |
|
349 | 0 | while (pc && *pc && !okay) { |
350 | 0 | if (!strncmp(pc, p, (unsigned int)len) && |
351 | 0 | (pc[len] == ',' || pc[len] == '\0')) { |
352 | 0 | okay = 1; |
353 | 0 | continue; |
354 | 0 | } |
355 | 0 | while (*pc && *pc++ != ',') |
356 | 0 | ; |
357 | 0 | while (*pc == ' ') |
358 | 0 | pc++; |
359 | 0 | } |
360 | |
|
361 | 0 | if (!okay) { |
362 | 0 | lwsl_wsi_info(wsi, "got bad protocol %s", p); |
363 | 0 | *cce = "HS: PROTOCOL malformed"; |
364 | 0 | goto bail2; |
365 | 0 | } |
366 | | |
367 | 0 | identify_protocol: |
368 | |
|
369 | | #if defined(LWS_WITH_HTTP_PROXY) |
370 | | lws_strncpy(wsi->ws->actual_protocol, p, |
371 | | sizeof(wsi->ws->actual_protocol)); |
372 | | #endif |
373 | | |
374 | | /* |
375 | | * identify the selected protocol struct and set it |
376 | | */ |
377 | 0 | n = 0; |
378 | | /* keep client connection pre-bound protocol */ |
379 | 0 | if (!lwsi_role_client(wsi)) |
380 | 0 | wsi->a.protocol = NULL; |
381 | |
|
382 | 0 | while (n < wsi->a.vhost->count_protocols) { |
383 | 0 | if (!wsi->a.protocol && |
384 | 0 | strcmp(p, wsi->a.vhost->protocols[n].name) == 0) { |
385 | 0 | wsi->a.protocol = &wsi->a.vhost->protocols[n]; |
386 | 0 | break; |
387 | 0 | } |
388 | 0 | n++; |
389 | 0 | } |
390 | |
|
391 | 0 | if (n == wsi->a.vhost->count_protocols) { /* no match */ |
392 | | /* if server, that's already fatal */ |
393 | 0 | if (!lwsi_role_client(wsi)) { |
394 | 0 | lwsl_wsi_info(wsi, "fail protocol %s", p); |
395 | 0 | *cce = "HS: Cannot match protocol"; |
396 | 0 | goto bail2; |
397 | 0 | } |
398 | | |
399 | | /* for client, find the index of our pre-bound protocol */ |
400 | | |
401 | 0 | n = 0; |
402 | 0 | while (wsi->a.vhost->protocols[n].callback) { |
403 | 0 | if (wsi->a.protocol && strcmp(wsi->a.protocol->name, |
404 | 0 | wsi->a.vhost->protocols[n].name) == 0) { |
405 | 0 | wsi->a.protocol = &wsi->a.vhost->protocols[n]; |
406 | 0 | break; |
407 | 0 | } |
408 | 0 | n++; |
409 | 0 | } |
410 | |
|
411 | 0 | if (!wsi->a.vhost->protocols[n].callback) { |
412 | 0 | if (wsi->a.protocol) |
413 | 0 | lwsl_wsi_err(wsi, "Failed to match protocol %s", |
414 | 0 | wsi->a.protocol->name); |
415 | 0 | else |
416 | 0 | lwsl_wsi_err(wsi, "No protocol on client"); |
417 | 0 | *cce = "ws protocol no match"; |
418 | 0 | goto bail2; |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | 0 | lwsl_wsi_debug(wsi, "Selected protocol %s", wsi->a.protocol ? |
423 | 0 | wsi->a.protocol->name : "no pcol"); |
424 | |
|
425 | 0 | check_extensions: |
426 | | /* |
427 | | * stitch protocol choice into the vh protocol linked list |
428 | | * We always insert ourselves at the start of the list |
429 | | * |
430 | | * X <-> B |
431 | | * X <-> pAn <-> pB |
432 | | */ |
433 | |
|
434 | 0 | lws_same_vh_protocol_insert(wsi, n); |
435 | |
|
436 | | #if !defined(LWS_WITHOUT_EXTENSIONS) |
437 | | /* instantiate the accepted extensions */ |
438 | | |
439 | | if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) { |
440 | | lwsl_wsi_ext(wsi, "no client extensions allowed by server"); |
441 | | goto check_accept; |
442 | | } |
443 | | |
444 | | /* |
445 | | * break down the list of server accepted extensions |
446 | | * and go through matching them or identifying bogons |
447 | | */ |
448 | | |
449 | | if (lws_hdr_copy(wsi, sb, (int)context->pt_serv_buf_size, |
450 | | WSI_TOKEN_EXTENSIONS) < 0) { |
451 | | lwsl_wsi_warn(wsi, "ext list from server failed to copy"); |
452 | | *cce = "HS: EXT: list too big"; |
453 | | goto bail2; |
454 | | } |
455 | | |
456 | | c = sb; |
457 | | n = 0; |
458 | | ignore = 0; |
459 | | a = NULL; |
460 | | while (more) { |
461 | | |
462 | | if (*c && (*c != ',' && *c != '\t')) { |
463 | | if (*c == ';') { |
464 | | ignore = 1; |
465 | | if (!a) |
466 | | a = c + 1; |
467 | | } |
468 | | if (ignore || *c == ' ') { |
469 | | c++; |
470 | | continue; |
471 | | } |
472 | | |
473 | | ext_name[n] = *c++; |
474 | | if (n < (int)sizeof(ext_name) - 1) |
475 | | n++; |
476 | | continue; |
477 | | } |
478 | | ext_name[n] = '\0'; |
479 | | ignore = 0; |
480 | | if (!*c) |
481 | | more = 0; |
482 | | else { |
483 | | c++; |
484 | | if (!n) |
485 | | continue; |
486 | | } |
487 | | |
488 | | /* check we actually support it */ |
489 | | |
490 | | lwsl_wsi_notice(wsi, "checking client ext %s", ext_name); |
491 | | |
492 | | n = 0; |
493 | | ext = wsi->a.vhost->ws.extensions; |
494 | | while (ext && ext->callback) { |
495 | | if (strcmp(ext_name, ext->name)) { |
496 | | ext++; |
497 | | continue; |
498 | | } |
499 | | |
500 | | n = 1; |
501 | | lwsl_wsi_notice(wsi, "instantiating client ext %s", ext_name); |
502 | | |
503 | | /* instantiate the extension on this conn */ |
504 | | |
505 | | wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext; |
506 | | |
507 | | /* allow him to construct his ext instance */ |
508 | | |
509 | | if (ext->callback(lws_get_context(wsi), ext, wsi, |
510 | | LWS_EXT_CB_CLIENT_CONSTRUCT, |
511 | | (void *)&wsi->ws->act_ext_user[ |
512 | | wsi->ws->count_act_ext], |
513 | | (void *)&opts, 0)) { |
514 | | lwsl_wsi_info(wsi, " ext %s failed construction", |
515 | | ext_name); |
516 | | ext++; |
517 | | continue; |
518 | | } |
519 | | |
520 | | /* |
521 | | * allow the user code to override ext defaults if it |
522 | | * wants to |
523 | | */ |
524 | | ext_name[0] = '\0'; |
525 | | if (user_callback_handle_rxflow(wsi->a.protocol->callback, |
526 | | wsi, LWS_CALLBACK_WS_EXT_DEFAULTS, |
527 | | (char *)ext->name, ext_name, |
528 | | sizeof(ext_name))) { |
529 | | *cce = "HS: EXT: failed setting defaults"; |
530 | | goto bail2; |
531 | | } |
532 | | |
533 | | if (ext_name[0] && |
534 | | lws_ext_parse_options(ext, wsi, |
535 | | wsi->ws->act_ext_user[ |
536 | | wsi->ws->count_act_ext], |
537 | | opts, ext_name, |
538 | | (int)strlen(ext_name))) { |
539 | | lwsl_wsi_err(wsi, "unable to parse user defaults '%s'", |
540 | | ext_name); |
541 | | *cce = "HS: EXT: failed parsing defaults"; |
542 | | goto bail2; |
543 | | } |
544 | | |
545 | | /* |
546 | | * give the extension the server options |
547 | | */ |
548 | | if (a && lws_ext_parse_options(ext, wsi, |
549 | | wsi->ws->act_ext_user[ |
550 | | wsi->ws->count_act_ext], |
551 | | opts, a, lws_ptr_diff(c, a))) { |
552 | | lwsl_wsi_err(wsi, "unable to parse remote def '%s'", a); |
553 | | *cce = "HS: EXT: failed parsing options"; |
554 | | goto bail2; |
555 | | } |
556 | | |
557 | | if (ext->callback(lws_get_context(wsi), ext, wsi, |
558 | | LWS_EXT_CB_OPTION_CONFIRM, |
559 | | wsi->ws->act_ext_user[wsi->ws->count_act_ext], |
560 | | NULL, 0)) { |
561 | | lwsl_wsi_err(wsi, "ext %s rejects server options %s", |
562 | | ext->name, a); |
563 | | *cce = "HS: EXT: Rejects server options"; |
564 | | goto bail2; |
565 | | } |
566 | | |
567 | | wsi->ws->count_act_ext++; |
568 | | |
569 | | ext++; |
570 | | } |
571 | | |
572 | | if (n == 0) { |
573 | | lwsl_wsi_warn(wsi, "Unknown ext '%s'!", ext_name); |
574 | | *cce = "HS: EXT: unknown ext"; |
575 | | goto bail2; |
576 | | } |
577 | | |
578 | | a = NULL; |
579 | | n = 0; |
580 | | } |
581 | | |
582 | | check_accept: |
583 | | #endif |
584 | | |
585 | | /* |
586 | | * Confirm his accept token is the one we precomputed |
587 | | */ |
588 | |
|
589 | 0 | p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT); |
590 | 0 | if (strcmp(p, wsi->http.ah->initial_handshake_hash_base64)) { |
591 | 0 | lwsl_wsi_warn(wsi, "lws_client_int_s_hs: accept '%s' wrong vs '%s'", p, |
592 | 0 | wsi->http.ah->initial_handshake_hash_base64); |
593 | 0 | *cce = "HS: Accept hash wrong"; |
594 | 0 | goto bail2; |
595 | 0 | } |
596 | | |
597 | | /* allocate the per-connection user memory (if any) */ |
598 | 0 | if (lws_ensure_user_space(wsi)) { |
599 | 0 | lwsl_wsi_err(wsi, "Problem allocating wsi user mem"); |
600 | 0 | *cce = "HS: OOM"; |
601 | 0 | goto bail2; |
602 | 0 | } |
603 | | |
604 | | /* |
605 | | * we seem to be good to go, give client last chance to check |
606 | | * headers and OK it |
607 | | */ |
608 | 0 | if (wsi->a.protocol->callback(wsi, |
609 | 0 | LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, |
610 | 0 | wsi->user_space, NULL, 0)) { |
611 | 0 | *cce = "HS: Rejected by filter cb"; |
612 | 0 | goto bail2; |
613 | 0 | } |
614 | | |
615 | | /* clear his proxy connection timeout */ |
616 | 0 | lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); |
617 | | |
618 | | /* free up his parsing allocations */ |
619 | 0 | lws_header_table_detach(wsi, 0); |
620 | |
|
621 | 0 | lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED, &role_ops_ws); |
622 | 0 | lws_validity_confirmed(wsi); |
623 | |
|
624 | 0 | wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; |
625 | | |
626 | | /* |
627 | | * create the frame buffer for this connection according to the |
628 | | * size mentioned in the protocol definition. If 0 there, then |
629 | | * use a big default for compatibility |
630 | | */ |
631 | 0 | n = (int)wsi->a.protocol->rx_buffer_size; |
632 | 0 | if (!n) |
633 | 0 | n = (int)context->pt_serv_buf_size; |
634 | 0 | n += LWS_PRE; |
635 | 0 | wsi->ws->rx_ubuf = lws_malloc((unsigned int)n + 4 /* 0x0000ffff zlib */, |
636 | 0 | "client frame buffer"); |
637 | 0 | if (!wsi->ws->rx_ubuf) { |
638 | 0 | lwsl_wsi_err(wsi, "OOM allocating rx buffer %d", n); |
639 | 0 | *cce = "HS: OOM"; |
640 | 0 | goto bail2; |
641 | 0 | } |
642 | 0 | wsi->ws->rx_ubuf_alloc = (unsigned int)n; |
643 | |
|
644 | 0 | lwsl_wsi_debug(wsi, "handshake OK for protocol %s", wsi->a.protocol->name); |
645 | | |
646 | | /* call him back to inform him he is up */ |
647 | |
|
648 | 0 | if (wsi->a.protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED, |
649 | 0 | wsi->user_space, NULL, 0)) { |
650 | 0 | *cce = "HS: Rejected at CLIENT_ESTABLISHED"; |
651 | 0 | goto bail3; |
652 | 0 | } |
653 | | |
654 | 0 | return 0; |
655 | | |
656 | 0 | bail3: |
657 | 0 | return 3; |
658 | | |
659 | 0 | bail2: |
660 | 0 | return 2; |
661 | 0 | } |