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