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