Coverage Report

Created: 2025-07-18 06:43

/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
}