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