Coverage Report

Created: 2026-07-24 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/roles/http/client/client-http.c
Line
Count
Source
1
/*
2
 * libwebsockets - small server side websockets and web server implementation
3
 *
4
 * Copyright (C) 2010 - 2025 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
void
28
lws_client_http_body_pending(struct lws *wsi, int something_left_to_send)
29
0
{
30
0
  wsi->client_http_body_pending = !!something_left_to_send;
31
0
}
32
33
/*
34
 * Returns 0 for wsi survived OK, or LWS_HPI_RET_WSI_ALREADY_DIED
35
 * meaning the wsi was destroyed by us before return.
36
 */
37
  
38
int
39
lws_http_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
40
0
{
41
0
  struct lws_context *context = wsi->a.context;
42
0
  struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
43
0
  char *p = (char *)&pt->serv_buf[0], *end = p + wsi->a.context->pt_serv_buf_size;
44
0
#if defined(LWS_WITH_TLS)
45
0
  char ebuf[128];
46
0
#endif
47
0
  const char *cce = NULL;
48
0
  char *sb = p;
49
0
  int n = 0;
50
51
0
  switch (lwsi_state(wsi)) {
52
53
0
  case LRS_WAITING_DNS:
54
    /*
55
     * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
56
     * timeout protection set in client-handshake.c
57
     */
58
0
    lwsl_err("%s: %s: WAITING_DNS\n", __func__, lws_wsi_tag(wsi));
59
0
    if (!lws_client_connect_2_dnsreq_MAY_CLOSE_WSI(wsi)) {
60
      /* closed */
61
0
      lwsl_client("closed\n");
62
0
      return LWS_HPI_RET_WSI_ALREADY_DIED;
63
0
    }
64
65
    /* either still pending connection, or changed mode */
66
0
    return 0;
67
68
0
  case LRS_WAITING_CONNECT:
69
70
    /*
71
     * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
72
     * timeout protection set in client-handshake.c
73
     */
74
0
    if (pollfd->revents & LWS_POLLOUT)
75
0
      if (lws_client_connect_3_connect(wsi, NULL, NULL, 0, pollfd) == NULL) {
76
0
        lwsl_client("closed\\n");
77
0
        return LWS_HPI_RET_WSI_ALREADY_DIED;
78
0
      }
79
0
    break;
80
81
#if defined(LWS_WITH_SOCKS5)
82
  /* SOCKS Greeting Reply */
83
  case LRS_WAITING_SOCKS_GREETING_REPLY:
84
  case LRS_WAITING_SOCKS_AUTH_REPLY:
85
  case LRS_WAITING_SOCKS_CONNECT_REPLY:
86
87
    switch (lws_socks5c_handle_state(wsi, pollfd, &cce)) {
88
    case LW5CHS_RET_RET0:
89
      return 0;
90
    case LW5CHS_RET_BAIL3:
91
      goto bail3_l;
92
    case LW5CHS_RET_STARTHS:
93
      goto start_ws_handshake_l;
94
    default:
95
      break;
96
    }
97
    break;
98
#endif
99
100
0
#if defined(LWS_CLIENT_HTTP_PROXYING) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3))
101
102
0
  case LRS_WAITING_PROXY_REPLY:
103
104
    /* handle proxy hung up on us */
105
106
0
    if (pollfd->revents & LWS_POLLHUP) {
107
108
0
      lwsl_warn("Proxy conn %s (fd=%d) dead\n",
109
0
          lws_wsi_tag(wsi), pollfd->fd);
110
111
0
      cce = "proxy conn dead";
112
0
      goto bail3_l;
113
0
    }
114
115
0
    n = (int)recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
116
0
    if (n < 0) {
117
0
      if (LWS_ERRNO == LWS_EAGAIN) {
118
0
        lwsl_debug("Proxy read EAGAIN... retrying\n");
119
0
        return 0;
120
0
      }
121
0
      lwsl_err("ERROR reading from proxy socket\n");
122
0
      cce = "proxy read err";
123
0
      goto bail3_l;
124
0
    }
125
126
    /* sanity check what we were sent... */
127
128
0
    pt->serv_buf[13] = '\0';
129
0
    if (n < 13 || strncmp(sb, "HTTP/1.", 7) ||
130
0
            (sb[7] != '0' && sb[7] != '1') || sb[8] != ' ') {
131
      /* lwsl_hexdump_notice(sb, n); */
132
0
      cce = "http_proxy fail";
133
0
      goto bail3_l;
134
0
    }
135
136
    /* it's h1 alright... what's his logical response code? */
137
0
    n = atoi(&sb[9]);
138
0
    if (n != 200) {
139
0
      lws_snprintf(sb, 20, "http_proxy -> %u",
140
0
             (unsigned int)n);
141
0
      cce = sb;
142
0
      goto bail3_l;
143
0
    }
144
145
0
    lwsl_info("%s: proxy connection established\n", __func__);
146
147
    /* clear his proxy connection timeout */
148
149
0
    lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
150
151
               /* fallthru */
152
153
0
#endif
154
155
               /* dummy fallthru to satisfy compiler */
156
               /* fallthru */
157
0
  case LRS_H1C_ISSUE_HANDSHAKE:
158
159
    // lwsl_debug("%s: LRS_H1C_ISSUE_HANDSHAKE\n", __func__);
160
161
    /*
162
     * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
163
     * timeout protection set in client-handshake.c
164
     *
165
     * take care of our lws_callback_on_writable
166
     * happening at a time when there's no real connection yet
167
     */
168
#if defined(LWS_WITH_SOCKS5)
169
start_ws_handshake_l:
170
#endif
171
0
    if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
172
0
      cce = "unable to clear POLLOUT";
173
      /* turn whatever went wrong into a clean close */
174
0
      goto bail3_l;
175
0
    }
176
177
0
#if defined(LWS_ROLE_H2) || defined(LWS_WITH_TLS)
178
0
    if (
179
0
#if defined(LWS_WITH_TLS)
180
0
        !(wsi->tls.use_ssl & LCCSCF_USE_SSL)
181
0
#endif
182
0
#if defined(LWS_ROLE_H2) && defined(LWS_WITH_TLS)
183
0
        &&
184
0
#endif
185
0
#if defined(LWS_ROLE_H2)
186
0
        !(wsi->flags & LCCSCF_H2_PRIOR_KNOWLEDGE)
187
0
#endif
188
0
        )
189
0
      goto hs2;
190
0
#endif
191
192
0
#if defined(LWS_WITH_TLS)
193
0
    n = lws_client_create_tls(wsi, &cce, 1);
194
0
    if (n == CCTLS_RETURN_ERROR)
195
0
      goto bail3_l;
196
0
    if (n == CCTLS_RETURN_RETRY)
197
0
      return 0;
198
199
    /*
200
     * lws_client_create_tls() can already have done the
201
     * whole tls setup and preface send... if so he set our state
202
     * to LRS_H1C_ISSUE_HANDSHAKE2... let's proceed but be prepared
203
     * to notice our state and not resend the preface...
204
     */
205
206
    // lwsl_debug("%s: LRS_H1C_ISSUE_HANDSHAKE fallthru\n", __func__);
207
208
    /* fallthru */
209
210
0
  case LRS_WAITING_SSL:
211
212
0
    if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
213
0
      n = lws_ssl_client_connect2(wsi, ebuf, sizeof(ebuf));
214
0
      if (!n)
215
0
        return 0;
216
0
      if (n < 0) {
217
0
        cce = ebuf;
218
0
        goto bail3_l;
219
0
      }
220
0
    } else {
221
0
      wsi->tls.ssl = NULL;
222
0
      if (wsi->flags & LCCSCF_H2_PRIOR_KNOWLEDGE) {
223
0
        lwsl_info("h2 prior knowledge\n");
224
0
        lws_role_call_alpn_negotiated(wsi, "h2");
225
0
      }
226
0
    }
227
0
#endif
228
229
0
#if defined (LWS_WITH_HTTP2)
230
0
    if (wsi->client_h2_alpn //&&
231
        //lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE2
232
0
        ) {
233
      /*
234
       * We connected to the server and set up tls and
235
       * negotiated "h2" or connected as clear text
236
       * with http/2 prior knowledge.
237
       *
238
       * So this is it, we are an h2 nwsi client connection
239
       * now, not an h1 client connection.
240
       */
241
242
0
      lwsl_info("%s: doing h2 hello path\n", __func__);
243
244
      /*
245
       * send the H2 preface to legitimize the connection
246
       *
247
       * transitions us to LRS_H2_WAITING_TO_SEND_HEADERS
248
       */
249
0
      if (wsi->client_h2_alpn)
250
0
        if (lws_h2_issue_preface(wsi)) {
251
0
          cce = "error sending h2 preface";
252
0
          goto bail3_l;
253
0
        }
254
255
    //  lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
256
0
      lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
257
0
          (int)context->timeout_secs);
258
259
0
      break;
260
0
    }
261
0
#endif
262
263
    /* fallthru */
264
265
0
  case LRS_H1C_ISSUE_HANDSHAKE2:
266
267
0
#if defined(LWS_ROLE_H2) || defined(LWS_WITH_TLS)
268
0
hs2:
269
0
#endif
270
271
0
    p = lws_generate_client_handshake(wsi, p,
272
0
              lws_ptr_diff_size_t(end, p));
273
0
    if (p == NULL) {
274
0
      if (wsi->role_ops == &role_ops_raw_skt
275
0
#if defined(LWS_ROLE_RAW_FILE)
276
0
        || wsi->role_ops == &role_ops_raw_file
277
0
#endif
278
0
          )
279
0
        return 0;
280
281
0
      lwsl_err("Failed to generate handshake for client\n");
282
0
      lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
283
0
             "chs");
284
0
      return -1;
285
0
    }
286
287
    /* send our request to the server */
288
289
0
    lwsl_info("%s: HANDSHAKE2: %s: sending headers "
290
0
        "(wsistate 0x%lx), w sock %d\n",
291
0
        __func__, lws_wsi_tag(wsi),
292
0
        (unsigned long)wsi->wsistate, wsi->desc.sockfd);
293
294
0
    n = lws_ssl_capable_write(wsi, (unsigned char *)sb, lws_ptr_diff_size_t(p, sb));
295
0
    switch (n) {
296
0
    case LWS_SSL_CAPABLE_ERROR:
297
0
      lwsl_debug("ERROR writing to client socket\n");
298
0
      lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
299
0
             "cws");
300
0
      return LWS_HPI_RET_WSI_ALREADY_DIED;
301
0
    case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
302
0
    case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
303
0
      lws_callback_on_writable(wsi);
304
0
      break;
305
0
    }
306
307
0
    if (wsi->client_http_body_pending || lws_has_buffered_out(wsi)) {
308
0
      lwsl_debug("body pending\n");
309
0
      lwsi_set_state(wsi, LRS_ISSUE_HTTP_BODY);
310
0
      lws_set_timeout(wsi,
311
0
          PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
312
0
          (int)context->timeout_secs);
313
314
0
      if (wsi->flags & LCCSCF_HTTP_X_WWW_FORM_URLENCODED)
315
0
        lws_callback_on_writable(wsi);
316
#if defined(LWS_WITH_HTTP_PROXY)
317
      if (wsi->http.proxy_clientside && wsi->parent &&
318
          wsi->parent->http.buflist_post_body)
319
        lws_callback_on_writable(wsi);
320
#endif
321
      /* user code must ask for writable callback */
322
0
      break;
323
0
    }
324
325
0
    lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
326
0
    wsi->hdr_parsing_completed = 0;
327
328
0
    if (lwsi_state(wsi) == LRS_IDLING) {
329
0
      lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
330
0
      wsi->hdr_parsing_completed = 0;
331
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3)
332
0
      wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
333
0
      wsi->http.ah->lextable_pos = 0;
334
0
      wsi->http.ah->unk_pos = 0;
335
      /* If we're (re)starting on hdr, need other implied init */
336
0
      wsi->http.ah->ues = URIES_IDLE;
337
0
#endif
338
0
    }
339
340
0
    lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
341
0
        (int)wsi->a.context->timeout_secs);
342
343
0
    lws_callback_on_writable(wsi);
344
345
0
    goto client_http_body_sent;
346
347
0
  case LRS_ISSUE_HTTP_BODY:
348
#if defined(LWS_WITH_HTTP_PROXY)
349
      if (wsi->http.proxy_clientside && wsi->parent &&
350
          wsi->parent->http.buflist_post_body)
351
        lws_callback_on_writable(wsi);
352
#endif
353
0
    if (wsi->client_http_body_pending || lws_has_buffered_out(wsi)) {
354
      //lws_set_timeout(wsi,
355
      //    PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
356
      //    context->timeout_secs);
357
      /* user code must ask for writable callback */
358
0
      break;
359
0
    }
360
0
client_http_body_sent:
361
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3)
362
    /* prepare ourselves to do the parsing */
363
0
    wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
364
0
    wsi->http.ah->lextable_pos = 0;
365
0
    wsi->http.ah->unk_pos = 0;
366
0
#endif
367
0
    lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
368
0
    lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
369
0
        (int)context->timeout_secs);
370
0
    break;
371
372
0
  case LRS_WAITING_SERVER_REPLY:
373
    /*
374
     * handle server hanging up on us...
375
     * but if there is POLLIN waiting, handle that first
376
     */
377
0
    if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) ==
378
0
                LWS_POLLHUP) {
379
380
0
      if (lws_buflist_total_len(&wsi->buflist))
381
0
        lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 3);
382
0
      else {
383
0
        lwsl_debug("Server conn %s (fd=%d) dead\n",
384
0
            lws_wsi_tag(wsi), pollfd->fd);
385
0
        cce = "Peer hung up";
386
0
        goto bail3_l;
387
0
      }
388
0
    }
389
390
0
    if (pollfd->revents & LWS_POLLOUT)
391
0
      if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
392
0
        cce = "Unable to clear POLLOUT";
393
0
        goto bail3_l;
394
0
      }
395
396
0
    if (!(pollfd->revents & LWS_POLLIN))
397
0
      break;
398
399
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3)
400
    /* interpret the server response
401
     *
402
     *  HTTP/1.1 101 Switching Protocols
403
     *  Upgrade: websocket
404
     *  Connection: Upgrade
405
     *  Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
406
     *  Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
407
     *  Sec-WebSocket-Protocol: chat
408
     *
409
     * we have to take some care here to only take from the
410
     * socket bytewise.  The browser may (and has been seen to
411
     * in the case that onopen() performs websocket traffic)
412
     * coalesce both handshake response and websocket traffic
413
     * in one packet, since at that point the connection is
414
     * definitively ready from browser pov.
415
     */
416
0
    while (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE) {
417
0
      struct lws_tokens eb;
418
0
      int n, m, buffered;
419
420
0
      eb.token = NULL;
421
0
      eb.len = 0;
422
0
      buffered = lws_buflist_aware_read(pt, wsi, &eb, 0, __func__);
423
0
      lwsl_debug("%s: buflist-aware-read %d %d\n", __func__,
424
0
          buffered, eb.len);
425
0
      if (buffered < 0) {
426
0
        cce = "read failed";
427
0
        goto bail3_l;
428
0
      }
429
0
      if (eb.len <= 0)
430
0
        return 0;
431
0
      if (!eb.len)
432
0
        return 0;
433
434
0
      n = eb.len;
435
0
      if (lws_parse(wsi, eb.token, &n)) {
436
0
        lwsl_warn("problems parsing header\n");
437
0
        cce = "problems parsing header";
438
0
        goto bail3_l;
439
0
      }
440
441
0
      m = eb.len - n;
442
#if defined(LWS_WITH_SECURE_STREAMS_BUFFER_DUMP)
443
      do {
444
        lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi);
445
        if (!h)
446
          break;
447
448
        if (h->info.dump) {
449
          h->info.dump(ss_to_userobj(h),
450
            (const uint8_t *)eb.token,
451
            (size_t)m,
452
            (wsi->http.ah->parser_state ==
453
             WSI_PARSING_COMPLETE) ? 1 : 0);
454
        }
455
      } while (0);
456
#endif
457
0
      if (lws_buflist_aware_finished_consuming(wsi, &eb, m,
458
0
                 buffered,
459
0
                 __func__))
460
0
              goto bail3_l;
461
462
      /*
463
       * coverity: uncomment if extended
464
       *
465
       * eb.token += m;
466
       * eb.len -= m;
467
       */
468
469
0
      if (n) {
470
0
        assert(wsi->http.ah->parser_state ==
471
0
            WSI_PARSING_COMPLETE);
472
473
0
        break;
474
0
      }
475
0
    }
476
477
    /*
478
     * hs may also be coming in multiple packets, there is a 5-sec
479
     * libwebsocket timeout still active here too, so if parsing did
480
     * not complete just wait for next packet coming in this state
481
     */
482
0
    if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
483
0
      break;
484
0
#endif
485
486
    /*
487
     * otherwise deal with the handshake.  If there's any
488
     * packet traffic already arrived we'll trigger poll() again
489
     * right away and deal with it that way
490
     */
491
0
    return lws_client_interpret_server_handshake(wsi);
492
493
0
bail3_l:
494
0
    lwsl_info("%s: closing conn at LWS_CONNMODE...SERVER_REPLY, %s, state 0x%x\n",
495
0
        __func__, lws_wsi_tag(wsi), lwsi_state(wsi));
496
0
    if (cce)
497
0
      lwsl_info("reason: %s\n", cce);
498
0
    else
499
0
      cce = "unknown";
500
0
    lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
501
502
0
    lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cbail3");
503
0
    return LWS_HPI_RET_WSI_ALREADY_DIED;
504
505
0
  default:
506
0
    break;
507
0
  }
508
509
0
  return 0;
510
0
}
511
512
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3)
513
514
int LWS_WARN_UNUSED_RESULT
515
lws_http_transaction_completed_client(struct lws *wsi)
516
0
{
517
0
  struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
518
0
  int n;
519
520
0
  lwsl_info("%s: %s (%s)\n", __func__, lws_wsi_tag(wsi),
521
0
      wsi->a.protocol->name);
522
523
  // if (wsi->http.ah && wsi->http.ah->http_response)
524
  /* we're only judging if any (200, or 500 etc) http txn completed */
525
0
  lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
526
527
0
  if (user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
528
0
          LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
529
0
          wsi->user_space, NULL, 0)) {
530
0
    lwsl_debug("%s: Completed call returned nonzero (role 0x%lx)\n",
531
0
         __func__, (unsigned long)lwsi_role(wsi));
532
0
    return -1;
533
0
  }
534
535
0
  wsi->http.rx_content_length = 0;
536
537
  /*
538
   * For h1, wsi may pass some assets on to a queued child and be
539
   * destroyed during this.
540
   */
541
0
  lws_pt_lock(pt, __func__);
542
0
  n = _lws_generic_transaction_completed_active_conn(&wsi, 1);
543
0
  lws_pt_unlock(pt);
544
545
0
  if (wsi->http.ah) {
546
0
    if (wsi->client_mux_substream)
547
      /*
548
       * As an h2 client, once we did our transaction, that is
549
       * it for us.  Further transactions will happen as new
550
       * SIDs on the connection.
551
       */
552
0
      __lws_header_table_detach(wsi, 0);
553
0
    else
554
0
      if (!n)
555
0
        _lws_header_table_reset(wsi->http.ah);
556
0
  }
557
558
0
  if (!n || !wsi->http.ah)
559
0
    return 0;
560
561
  /*
562
   * H1: we can serialize the queued guys into the same ah
563
   * H2: everybody needs their own ah until their own STREAM_END
564
   */
565
566
  /* otherwise set ourselves up ready to go again */
567
0
  lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
568
569
0
  wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
570
0
  wsi->http.ah->lextable_pos = 0;
571
0
  wsi->http.ah->unk_pos = 0;
572
573
0
  lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
574
0
      (int)wsi->a.context->timeout_secs);
575
576
  /* If we're (re)starting on headers, need other implied init */
577
0
  wsi->http.ah->ues = URIES_IDLE;
578
0
  lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
579
580
0
  lwsl_info("%s: %s: new queued transaction\n", __func__, lws_wsi_tag(wsi));
581
0
  lws_callback_on_writable(wsi);
582
583
0
  return 0;
584
0
}
585
586
unsigned int
587
lws_http_client_http_response(struct lws *wsi)
588
0
{
589
0
  if (wsi->http.ah && wsi->http.ah->http_response)
590
0
    return wsi->http.ah->http_response;
591
592
0
  return 0;
593
0
}
594
#endif
595
596
597
#if defined(LWS_WITH_HTTP_DIGEST_AUTH) && defined(LWS_WITH_TLS)
598
599
static const char *digest_toks[] = {
600
  "Digest", // 1 <<  0
601
  "username", // 1 <<  1
602
  "realm",  // 1 <<  2
603
  "nonce",  // 1 <<  3
604
  "uri",    // 1 <<  4 optional
605
  "response", // 1 <<  5
606
  "opaque", // 1 <<  6
607
  "qop",    // 1 <<  7
608
  "algorithm",  // 1 <<  8
609
  "nc",   // 1 <<  9
610
  "cnonce", // 1 << 10
611
  "domain", // 1 << 11
612
};
613
614
0
#define PEND_NAME_EQ -1
615
0
#define PEND_DELIM -2
616
617
enum lws_check_basic_auth_results
618
lws_http_digest_auth(struct lws* wsi)
619
0
{
620
0
  uint8_t nonce[256], response[LWS_GENHASH_LARGEST], qop[32];
621
0
  int seen = 0, n, pend = -1;
622
0
  char *tmp_digest = NULL;
623
0
  struct lws_tokenize ts;
624
0
  char resp_username[32];
625
0
  lws_tokenize_elem e;
626
0
  char realm[64];
627
0
  char b64[512];
628
0
  int m, ml, fi;
629
0
  char algo[16];
630
0
  enum lws_genhash_types hash_type = LWS_GENHASH_TYPE_MD5;
631
632
0
  qop[0] = '\0';
633
0
  lws_strncpy(algo, "MD5", sizeof(algo));
634
635
  /* Did he send auth? */
636
0
  ml = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_WWW_AUTHENTICATE);
637
0
  if (!ml)
638
0
    return LCBA_FAILED_AUTH;
639
640
  /* Disallow fragmentation monkey business */
641
642
0
  fi = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_WWW_AUTHENTICATE];
643
0
  if (wsi->http.ah->frags[fi].nfrag) {
644
0
    lwsl_wsi_err(wsi, "fragmented http auth header not allowed\n");
645
0
    return LCBA_FAILED_AUTH;
646
0
  }
647
648
0
  m = lws_hdr_copy(wsi, b64, sizeof(b64), WSI_TOKEN_HTTP_WWW_AUTHENTICATE);
649
0
  if (m < 7) {
650
0
    lwsl_wsi_err(wsi, "HTTP auth length bad\n");
651
0
    return LCBA_END_TRANSACTION;
652
0
  }
653
654
  /*
655
   * We are expecting AUTHORIZATION to have something like this
656
   *
657
   * Authorization: Digest
658
   *   username="Mufasa",
659
   *   realm="testrealm@host.com",
660
   *   nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
661
   *   uri="/dir/index.html",
662
   *   response="e966c932a9242554e42c8ee200cec7f6",
663
   *   opaque="5ccc069c403ebaf9f0171e9517f40e41"
664
   *
665
   * but the order, whitespace etc is quite open.  uri is optional
666
   */
667
0
  lws_tokenize_init(&ts,b64, LWS_TOKENIZE_F_MINUS_NONTERM |
668
0
           LWS_TOKENIZE_F_NO_INTEGERS |
669
0
           LWS_TOKENIZE_F_RFC7230_DELIMS);
670
671
0
  do {
672
0
    e = lws_tokenize(&ts);
673
0
    switch (e) {
674
0
    case LWS_TOKZE_TOKEN:
675
0
      if (pend >= 0)
676
0
        goto str_val;
677
678
0
      if (!strncasecmp(ts.token, "Digest", ts.token_len)) {
679
0
        seen |= 1 << 0;
680
0
        break;
681
0
      }
682
0
      if (seen) /* we must be first and one time */
683
0
        return LCBA_END_TRANSACTION;
684
685
0
      seen |= 1 << 15;
686
0
      pend = PEND_NAME_EQ;
687
0
      break;
688
689
0
    case LWS_TOKZE_TOKEN_NAME_EQUALS:
690
0
      if ((seen & (1 << 15)) == (1 << 15) || pend != -1)
691
        /* no auth type token or disordered */
692
0
        return LCBA_END_TRANSACTION;
693
694
0
      for (n = 0; n < (int)LWS_ARRAY_SIZE(digest_toks); n++)
695
0
        if (!strncmp(ts.token, digest_toks[n], ts.token_len))
696
0
          break;
697
698
0
      if (n == LWS_ARRAY_SIZE(digest_toks)) {
699
0
        lwsl_wsi_notice(wsi, "c: '%.*s'\n",
700
0
            (int)ts.token_len,
701
0
            ts.token);
702
703
0
        return LCBA_END_TRANSACTION;
704
0
      }
705
706
0
      if (seen & (1 << n) || (seen & (1 << 15)) == (1 << 15))
707
        /* dup or no auth type token */
708
0
        return LCBA_END_TRANSACTION;
709
710
0
      seen |= 1 << n;
711
0
      pend = n;
712
0
      break;
713
714
0
    case LWS_TOKZE_QUOTED_STRING:
715
0
str_val:
716
0
      if (pend < 0)
717
0
        return LCBA_END_TRANSACTION;
718
719
0
      switch (pend) {
720
0
      case 1: /* username */
721
0
        if (ts.token_len >= (int)sizeof(resp_username))
722
0
          return LCBA_END_TRANSACTION;
723
724
0
        strncpy(resp_username, ts.token, ts.token_len);
725
0
        break;
726
0
      case 2: /* realm */
727
0
        if (ts.token_len >= (int)sizeof(realm))
728
0
          return LCBA_END_TRANSACTION;
729
730
0
        strncpy(realm, ts.token, ts.token_len);
731
0
        realm[ts.token_len] = 0;
732
0
        break;
733
0
      case 3: /* nonce */
734
0
        if (ts.token_len >= (int)sizeof(nonce))
735
0
          return LCBA_END_TRANSACTION;
736
737
0
        strncpy((char *)nonce, ts.token, ts.token_len);
738
0
        nonce[ts.token_len] = 0;
739
0
        break;
740
0
      case 4: /* uri */
741
0
        break;
742
0
      case 5: /* response */
743
0
        if (ts.token_len !=
744
0
          lws_genhash_size(hash_type) * 2)
745
0
          return LCBA_END_TRANSACTION;
746
747
0
        if (lws_hex_len_to_byte_array(ts.token, ts.token_len,
748
0
                response,
749
0
                sizeof(response)) < 0)
750
0
          return LCBA_END_TRANSACTION;
751
0
        break;
752
0
      case 6: /* opaque */
753
0
        break;
754
0
      case 7: /* qop */
755
0
        if (ts.token_len >= (int)sizeof(qop))
756
0
          return LCBA_END_TRANSACTION;
757
758
0
        strncpy((char *)qop, ts.token, ts.token_len);
759
0
        qop[ts.token_len] = '\0';
760
0
        {
761
0
          char *p = (char *)qop;
762
0
          while (p) {
763
0
            if (!strncmp(p, "auth", 4) && (p[4] == '\0' || p[4] == ',' || p[4] == ' ')) {
764
0
              strcpy((char *)qop, "auth");
765
0
              break;
766
0
            }
767
0
            p = (char *)strchr(p, ',');
768
0
            if (p) {
769
0
              p++;
770
0
              while (*p == ' ') p++;
771
0
            }
772
0
          }
773
0
          if (!p)
774
0
            return LCBA_END_TRANSACTION;
775
0
        }
776
0
        break;
777
0
      case 8: /* algorithm */
778
0
        if (ts.token_len == 3 && !strncasecmp(ts.token, "MD5", 3)) {
779
0
          hash_type = LWS_GENHASH_TYPE_MD5;
780
0
          lws_strncpy(algo, "MD5", sizeof(algo));
781
0
        } else if (ts.token_len == 7 && !strncasecmp(ts.token, "SHA-256", 7)) {
782
0
          hash_type = LWS_GENHASH_TYPE_SHA256;
783
0
          lws_strncpy(algo, "SHA-256", sizeof(algo));
784
0
        } else if (ts.token_len == 7 && !strncasecmp(ts.token, "SHA-384", 7)) {
785
0
          hash_type = LWS_GENHASH_TYPE_SHA384;
786
0
          lws_strncpy(algo, "SHA-384", sizeof(algo));
787
0
        } else if (ts.token_len == 7 && !strncasecmp(ts.token, "SHA-512", 7)) {
788
0
          hash_type = LWS_GENHASH_TYPE_SHA512;
789
0
          lws_strncpy(algo, "SHA-512", sizeof(algo));
790
0
        } else {
791
0
          lwsl_wsi_err(wsi, "wrong alg %.*s\n", (int)ts.token_len, ts.token);
792
0
          return LCBA_END_TRANSACTION;
793
0
        }
794
0
        break;
795
0
      }
796
0
      pend = PEND_DELIM;
797
0
      break;
798
799
0
      case LWS_TOKZE_DELIMITER:
800
0
        if (*ts.token == ',') {
801
0
          if (pend != PEND_DELIM)
802
0
            return LCBA_END_TRANSACTION;
803
804
0
          pend = PEND_NAME_EQ;
805
0
          break;
806
0
        }
807
0
        if (*ts.token == ';') {
808
          /* it's the end */
809
0
          e = LWS_TOKZE_ENDED;
810
0
          break;
811
0
        }
812
0
        break;
813
814
0
      case LWS_TOKZE_ENDED:
815
0
        break;
816
817
0
      default:
818
0
        lwsl_wsi_notice(wsi, "unexpected token %d\n", e);
819
0
        return LCBA_END_TRANSACTION;
820
0
    }
821
822
0
  } while (e > 0);
823
824
  /* we got all the parts we care about? Realm + Nonce... */
825
826
0
  if ((seen & 0xc) != 0xc) {
827
0
    lwsl_wsi_err(wsi,
828
0
        "%s: Not all digest auth tokens found! "
829
0
        "m: 0x%x\nServer sent: %s",
830
0
        __func__, seen & 0x81ef, b64);
831
832
0
    return LCBA_END_TRANSACTION;
833
0
  }
834
835
0
  lwsl_wsi_info(wsi, "HTTP digest auth realm %s nonce %s\n", realm, nonce);
836
837
0
  if (wsi->stash &&
838
0
      wsi->stash->cis[CIS_PATH]) {
839
0
    char *username =  wsi->stash->cis[CIS_USERNAME];
840
0
    char *password = wsi->stash->cis[CIS_PASSWORD];
841
0
    uint8_t digest[LWS_GENHASH_LARGEST * 2 + 1];
842
0
    char *uri = wsi->stash->cis[CIS_PATH];
843
0
    char a1[LWS_GENHASH_LARGEST * 2 + 1];
844
0
    char a2[LWS_GENHASH_LARGEST * 2 + 1];
845
0
    char nc[sizeof(int) * 2 + 1];
846
0
    struct lws_genhash_ctx hc;
847
0
    int ncount = 1, ssl;
848
0
    const char *a, *p;
849
0
    struct lws *nwsi;
850
0
    char cnonce[256];
851
0
    size_t l;
852
853
0
    l = sizeof(a1) + sizeof(a2) + sizeof(nonce) +
854
0
      (sizeof(ncount) *2) + sizeof(response) +
855
0
      sizeof(cnonce) + sizeof(qop) + strlen(uri) +
856
0
      strlen(username) + strlen(password) +
857
0
      strlen(realm) + 111;
858
859
0
    tmp_digest = lws_malloc(l, __func__);
860
0
    if (!tmp_digest)
861
0
      return LCBA_FAILED_AUTH;
862
863
0
    n = lws_snprintf(tmp_digest, l, "%s:%s:%s",
864
0
         username, realm, password);
865
866
0
    if (lws_genhash_init(&hc, hash_type) ||
867
0
        lws_genhash_update(&hc,
868
0
               tmp_digest,
869
0
              (size_t)n) ||
870
0
        lws_genhash_destroy(&hc, digest)) {
871
0
      lws_genhash_destroy(&hc, NULL);
872
873
0
      goto bail;
874
0
    }
875
876
0
    lws_hex_from_byte_array(digest,
877
0
          lws_genhash_size(hash_type),
878
0
          a1, sizeof(a1));
879
0
    lwsl_debug("A1: %s:%s:%s = %s\n", username, realm, password, a1);
880
881
    /*
882
     * In case of Websocket upgrade, method is NULL
883
     * we assume it is a GET
884
    */
885
886
0
    n = lws_snprintf(tmp_digest, l, "%s:%s",
887
0
           wsi->stash->cis[CIS_METHOD] ?
888
0
           wsi->stash->cis[CIS_METHOD] : "GET", uri);
889
890
0
    if (lws_genhash_init(&hc, hash_type) ||
891
0
             lws_genhash_update(&hc,
892
0
                tmp_digest,
893
0
                (size_t)n) ||
894
0
             lws_genhash_destroy(&hc, digest)) {
895
0
      lws_genhash_destroy(&hc, NULL);
896
0
      lwsl_err("%s: hash failed\n", __func__);
897
898
0
      goto bail;
899
0
    }
900
0
    lws_hex_from_byte_array(digest,
901
0
          lws_genhash_size(hash_type),
902
0
          a2, sizeof(a2));
903
0
    lwsl_debug("A2: %s:%s = %s\n", wsi->stash->cis[CIS_METHOD],
904
0
        uri, a2);
905
906
0
    lws_hex_random(lws_get_context(wsi), cnonce, sizeof(cnonce));
907
0
    lws_hex_from_byte_array((const uint8_t *)&ncount,
908
0
          sizeof(ncount), nc, sizeof(nc));
909
910
0
    if (qop[0])
911
0
      n = lws_snprintf(tmp_digest, l, "%s:%s:%08x:%s:%s:%s", a1,
912
0
          nonce, ncount, cnonce, qop, a2);
913
0
    else
914
0
      n = lws_snprintf(tmp_digest, l, "%s:%s:%s", a1,
915
0
          nonce, a2);
916
917
0
    lwsl_wsi_debug(wsi, "digest response: %s\n", tmp_digest);
918
919
920
0
    if (lws_genhash_init(&hc, hash_type) ||
921
0
        lws_genhash_update(&hc, tmp_digest, (size_t)n) ||
922
0
        lws_genhash_destroy(&hc, digest)) {
923
0
      lws_genhash_destroy(&hc, NULL);
924
0
      lwsl_wsi_err(wsi, "hash failed\n");
925
926
0
      goto bail;
927
0
    }
928
0
    lws_hex_from_byte_array(digest,
929
0
          lws_genhash_size(hash_type),
930
0
          (char *)response,
931
0
          lws_genhash_size(hash_type) * 2 + 1);
932
933
0
    if (qop[0]) {
934
0
      n = lws_snprintf(tmp_digest, l,
935
0
           "Digest username=\"%s\", realm=\"%s\", "
936
0
           "nonce=\"%s\", uri=\"%s\", qop=%s, nc=%08x, "
937
0
           "cnonce=\"%s\", response=\"%s\", "
938
0
           "algorithm=\"%s\"",
939
0
           username, realm, nonce, uri, qop, ncount,
940
0
           cnonce, response, algo);
941
0
    } else {
942
0
      n = lws_snprintf(tmp_digest, l,
943
0
           "Digest username=\"%s\", realm=\"%s\", "
944
0
           "nonce=\"%s\", uri=\"%s\", "
945
0
           "response=\"%s\", "
946
0
           "algorithm=\"%s\"",
947
0
           username, realm, nonce, uri,
948
0
           response, algo);
949
0
    }
950
0
    (void)n;
951
952
0
    lwsl_hexdump(tmp_digest, l);
953
954
0
    if (lws_hdr_simple_create(wsi, WSI_TOKEN_HTTP_AUTHORIZATION,
955
0
                tmp_digest)) {
956
0
      lwsl_wsi_err(wsi, "Failed to add Digest auth header");
957
0
      goto bail;
958
0
    }
959
960
0
    nwsi = lws_get_network_wsi(wsi);
961
0
    ssl = nwsi->tls.use_ssl & LCCSCF_USE_SSL;
962
963
0
    a = wsi->stash->cis[CIS_ADDRESS];
964
0
    p = &wsi->stash->cis[CIS_PATH][1];
965
966
0
    wsi->client_pipeline = 0;
967
968
    /*
969
     * If the server kept the TCP/TLS connection alive on the 401
970
     * and sent an explicitly empty body, reuse the existing
971
     * session instead of tearing down and recreating it.
972
     * Fall back to the close/reconnect path if any doubt exists.
973
     */
974
0
    {
975
0
      const char *cl401 = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH);
976
0
      const char *te401 = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING);
977
0
      const char *conn = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION);
978
0
      int keep_alive = 1;
979
980
0
      if (conn) {
981
0
        struct lws_tokenize ts;
982
0
        lws_tokenize_init(&ts, conn, LWS_TOKENIZE_F_COMMA_SEP_LIST | LWS_TOKENIZE_F_MINUS_NONTERM);
983
0
        do {
984
0
          ts.e = (int8_t)lws_tokenize(&ts);
985
0
          if (ts.e == LWS_TOKZE_TOKEN &&
986
0
              ts.token_len == 5 &&
987
0
              !strncasecmp(ts.token, "close", 5)) {
988
0
            keep_alive = 0;
989
0
            break;
990
0
          }
991
0
        } while (ts.e > 0);
992
0
      }
993
994
0
      if (wsi->http.conn_type == HTTP_CONNECTION_KEEP_ALIVE &&
995
0
          keep_alive &&
996
0
          (!te401 || strncasecmp(te401, "chunked", 7)) &&
997
0
          cl401 && atoi(cl401) == 0) {
998
0
        wsi->http.digest_auth_hdr = tmp_digest;
999
0
        return LCBA_AUTH_RETRY_KEEPALIVE;
1000
0
      }
1001
0
    }
1002
1003
    /*
1004
     * This prevents connection pipelining when two
1005
     * HTTP connection use the same tcp socket.
1006
     */
1007
0
    wsi->keepalive_rejected = 1;
1008
1009
0
    if (!lws_client_reset(&wsi, ssl, a, wsi->c_port, p, a, 1)) {
1010
0
      lwsl_wsi_err(wsi, "Failed to reset WSI for Digest auth");
1011
1012
0
      goto bail;
1013
0
    }
1014
1015
    /*
1016
     * Keep track of digest auth to send it at next attempt, lws_client_reset will free it
1017
    */
1018
1019
0
    wsi->http.digest_auth_hdr = tmp_digest;
1020
0
  }
1021
1022
0
  return 0;
1023
1024
0
bail:
1025
0
  lws_free(tmp_digest);
1026
1027
0
  return LCBA_FAILED_AUTH;
1028
0
}
1029
#endif
1030
1031
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3)
1032
1033
int
1034
lws_http_is_redirected_to_get(struct lws *wsi)
1035
0
{
1036
0
  return wsi->redirected_to_get;
1037
0
}
1038
1039
int
1040
lws_client_interpret_server_handshake(struct lws *wsi)
1041
0
{
1042
0
  int n, port = 0, ssl = 0;
1043
0
  int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;
1044
0
  const char *prot, *ads = NULL, *path, *cce = NULL;
1045
0
  struct allocated_headers *ah, *ah1;
1046
0
  struct lws *nwsi = lws_get_network_wsi(wsi);
1047
0
  char *p = NULL, *q, *simp;
1048
0
  char new_path[300];
1049
0
  void *opaque;
1050
0
  lws_parse_uri_t *puri = NULL;
1051
1052
  // lws_free_set_NULL(wsi->stash);
1053
1054
0
#if defined(LWS_WITH_CONMON)
1055
0
  wsi->conmon.ciu_txn_resp = (lws_conmon_interval_us_t)
1056
0
          (lws_now_usecs() - wsi->conmon_datum);
1057
0
#endif
1058
  // lws_free_set_NULL(wsi->stash);
1059
1060
0
  ah = wsi->http.ah;
1061
0
  if (!wsi->do_ws) {
1062
    /* we are being an http client...
1063
     */
1064
#if defined(LWS_ROLE_WT)
1065
    if (wsi->a.protocol && !strcmp(wsi->a.protocol->name, "webtransport")) {
1066
      extern const struct lws_role_ops role_ops_wt;
1067
      lwsl_debug("%s: %s: transitioning to WebTransport client\n",
1068
           __func__, lws_wsi_tag(wsi));
1069
      lws_role_transition(wsi, LWSIFR_CLIENT,
1070
              LRS_ESTABLISHED, &role_ops_wt);
1071
      wsi->wt.is_session = 1;
1072
    } else
1073
#endif
1074
0
#if defined(LWS_ROLE_H2)
1075
0
    if (wsi->client_h2_alpn || wsi->client_mux_substream) {
1076
0
      lwsl_debug("%s: %s: transitioning to mux client\n",
1077
0
           __func__, lws_wsi_tag(wsi));
1078
0
      lws_role_transition(wsi, LWSIFR_CLIENT,
1079
0
              LRS_ESTABLISHED, wsi->role_ops);
1080
0
    } else
1081
0
#endif
1082
0
    {
1083
0
#if defined(LWS_ROLE_H1)
1084
0
      {
1085
0
      lwsl_debug("%s: %s: transitioning to h1 client\n",
1086
0
           __func__, lws_wsi_tag(wsi));
1087
0
      lws_role_transition(wsi, LWSIFR_CLIENT,
1088
0
              LRS_ESTABLISHED, &role_ops_h1);
1089
0
      }
1090
#else
1091
      cce = "h1 not built";
1092
      goto bail3_l;
1093
#endif
1094
0
    }
1095
1096
0
    wsi->http.ah = ah;
1097
0
    ah->http_response = 0;
1098
0
  }
1099
1100
0
#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
1101
1102
0
  if ((wsi->flags & LCCSCF_CACHE_COOKIES) &&
1103
0
      lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_SET_COOKIE))
1104
0
    lws_parse_set_cookie(wsi);
1105
1106
0
#endif
1107
  /*
1108
   * well, what the server sent looked reasonable for syntax.
1109
   * Now let's confirm it sent all the necessary headers
1110
   *
1111
   * http (non-ws) client will expect something like this
1112
   *
1113
   * HTTP/1.0.200
1114
   * server:.libwebsockets
1115
   * content-type:.text/html
1116
   * content-length:.17703
1117
   * set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000
1118
   */
1119
1120
0
  wsi->http.conn_type = HTTP_CONNECTION_KEEP_ALIVE;
1121
0
  if (!wsi->client_mux_substream) {
1122
0
    p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP);
1123
    /*
1124
    if (wsi->do_ws && !p) {
1125
      lwsl_info("no URI\n");
1126
      cce = "HS: URI missing";
1127
      goto bail3_l;
1128
    }
1129
    */
1130
0
    if (!p) {
1131
0
      p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0);
1132
0
      wsi->http.conn_type = HTTP_CONNECTION_CLOSE;
1133
0
    }
1134
0
    if (!p) {
1135
0
      cce = "HS: URI missing";
1136
0
      lwsl_info("no URI\n");
1137
0
      goto bail3_l;
1138
0
    }
1139
0
#if defined(LWS_ROLE_H2)
1140
0
  } else {
1141
0
    p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_STATUS);
1142
0
    if (!p) {
1143
0
      cce = "HS: :status missing";
1144
0
      lwsl_info("no status\n");
1145
0
      goto bail3_l;
1146
0
    }
1147
0
#endif
1148
0
  }
1149
#if !defined(LWS_ROLE_H2)
1150
  if (!p) {
1151
    cce = "HS: :status missing";
1152
    lwsl_info("no status\n");
1153
    goto bail3_l;
1154
  }
1155
#endif
1156
0
  n = atoi(p);
1157
1158
0
#if defined(LWS_WITH_HTTP_DIGEST_AUTH) && defined(LWS_WITH_TLS)
1159
0
  if (n == 401 && lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_WWW_AUTHENTICATE)) {
1160
0
    if (!(wsi->stash && wsi->stash->cis[CIS_USERNAME] &&
1161
0
                        wsi->stash->cis[CIS_PASSWORD])) {
1162
0
      lwsl_err("Digest auth requested by server but no credentials provided by user\n");
1163
      
1164
0
      return LCBA_FAILED_AUTH;
1165
0
    }
1166
1167
0
    enum lws_check_basic_auth_results auth_res = lws_http_digest_auth(wsi);
1168
1169
0
    if (auth_res == LCBA_AUTH_RETRY_KEEPALIVE) {
1170
      /*
1171
       * Server kept the TCP/TLS connection alive:
1172
       * reuse it for the authenticated retry without
1173
       * any close/reconnect.  Reset the AH parser and
1174
       * re-populate client request headers from stash,
1175
       * then schedule a new HTTP handshake send.
1176
       */
1177
0
      static const uint8_t hnames_cis[] = {
1178
0
        _WSI_TOKEN_CLIENT_PEER_ADDRESS,
1179
0
        _WSI_TOKEN_CLIENT_URI,
1180
0
        _WSI_TOKEN_CLIENT_HOST,
1181
0
        _WSI_TOKEN_CLIENT_ORIGIN,
1182
0
        _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
1183
0
        _WSI_TOKEN_CLIENT_METHOD,
1184
0
        _WSI_TOKEN_CLIENT_IFACE,
1185
0
        _WSI_TOKEN_CLIENT_ALPN
1186
0
      };
1187
0
      int m;
1188
1189
0
      lwsl_wsi_info(wsi, "digest auth: reusing TCP/TLS connection\n");
1190
1191
0
      _lws_header_table_reset(wsi->http.ah);
1192
1193
0
      if (wsi->stash)
1194
0
        for (m = 0; m < (int)LWS_ARRAY_SIZE(hnames_cis); m++)
1195
0
          if (hnames_cis[m] &&
1196
0
              wsi->stash->cis[m] &&
1197
0
              lws_hdr_simple_create(wsi, hnames_cis[m], wsi->stash->cis[m]))
1198
0
            goto bail3_l;
1199
1200
0
      wsi->hdr_parsing_completed = 0;
1201
0
      wsi->http.ah->ues = URIES_IDLE;
1202
0
      lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
1203
0
      lws_callback_on_writable(wsi);
1204
0
      return 0;
1205
0
    }
1206
1207
0
    if (auth_res)
1208
0
      goto bail3_l;
1209
1210
0
    opaque = wsi->a.opaque_user_data;
1211
0
    lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "digest_auth_step2");
1212
0
    wsi->a.opaque_user_data = opaque;
1213
1214
0
    return -1;
1215
0
  }
1216
1217
0
    ah = wsi->http.ah;
1218
0
#endif
1219
0
  if (ah)
1220
0
    ah->http_response = (unsigned int)n;
1221
1222
0
  if (!wsi->client_no_follow_redirect &&
1223
#if defined(LWS_WITH_HTTP_PROXY)
1224
      !wsi->http.proxy_clientside &&
1225
#endif
1226
0
      (n == 301 || n == 302 || n == 303 || n == 307 || n == 308)) {
1227
0
    p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION);
1228
0
    if (!p) {
1229
0
      cce = "HS: Redirect code but no Location";
1230
0
      goto bail3_l;
1231
0
    }
1232
1233
0
#if defined(LWS_WITH_CONMON)
1234
0
    if (wsi->conmon.pcol == LWSCONMON_PCOL_NONE) {
1235
0
      wsi->conmon.pcol = LWSCONMON_PCOL_HTTP;
1236
0
      wsi->conmon.protocol_specific.http.response = n;
1237
0
    }
1238
1239
0
#if defined(LWS_WITH_SECURE_STREAMS)
1240
0
    if (wsi->for_ss
1241
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
1242
        && !wsi->client_bound_sspc
1243
#endif
1244
0
       ) {
1245
  
1246
0
      lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi);
1247
1248
0
      if (h)
1249
0
        lws_conmon_ss_json(h);
1250
0
    }
1251
0
#endif
1252
0
#endif
1253
1254
    /* let's let the user code know, if he cares */
1255
1256
0
    if (wsi->a.protocol->callback(wsi,
1257
0
          LWS_CALLBACK_CLIENT_HTTP_REDIRECT,
1258
0
          wsi->user_space, p, (unsigned int)n)) {
1259
0
      cce = "HS: user code rejected redirect";
1260
0
      goto bail3_l;
1261
0
    }
1262
1263
    /* Relative reference absolute path */
1264
0
    if (p[0] == '/' || !(char *)strchr(p, ':')) {
1265
0
#if defined(LWS_WITH_TLS)
1266
0
      ssl = nwsi->tls.use_ssl & LCCSCF_USE_SSL;
1267
0
#endif
1268
0
      ads = lws_hdr_simple_ptr(wsi,
1269
0
             _WSI_TOKEN_CLIENT_PEER_ADDRESS);
1270
0
      port = nwsi->c_port;
1271
0
      path = p;
1272
      /* lws_client_reset expects leading / omitted */
1273
0
      if (*path == '/')
1274
0
        path++;
1275
0
    }
1276
    /* Absolute (Full) URI */
1277
0
    else if ((char *)strchr(p, ':')) {
1278
0
      puri = lws_parse_uri_create(p);
1279
0
      if (!puri) {
1280
0
        cce = "HS: URI did not parse";
1281
0
        goto bail3_l;
1282
0
      }
1283
0
      prot = puri->scheme;
1284
0
      ads = puri->host;
1285
0
      port = puri->port;
1286
0
      path = puri->path;
1287
1288
0
      if (!strcmp(prot, "wss") || !strcmp(prot, "https"))
1289
0
        ssl = LCCSCF_USE_SSL;
1290
0
    }
1291
    /* Relative reference relative path */
1292
0
    else {
1293
      /* This doesn't try to calculate an absolute path,
1294
       * that will be left to the server */
1295
0
#if defined(LWS_WITH_TLS)
1296
0
      ssl = nwsi->tls.use_ssl & LCCSCF_USE_SSL;
1297
0
#endif
1298
0
      ads = lws_hdr_simple_ptr(wsi,
1299
0
             _WSI_TOKEN_CLIENT_PEER_ADDRESS);
1300
0
      port = wsi->c_port;
1301
      /* +1 as lws_client_reset expects leading / omitted */
1302
0
      path = new_path + 1;
1303
0
      if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI))
1304
0
        lws_strncpy(new_path, lws_hdr_simple_ptr(wsi,
1305
0
           _WSI_TOKEN_CLIENT_URI), sizeof(new_path));
1306
0
      else {
1307
0
        new_path[0] = '/';
1308
0
        new_path[1] = '\0';
1309
0
      }
1310
0
      q = (char *)strrchr(new_path, '/');
1311
0
      if (q)
1312
0
        lws_strncpy(q + 1, p, sizeof(new_path) -
1313
0
              (unsigned int)(q - new_path) - 1);
1314
0
      else
1315
0
        path = p;
1316
0
    }
1317
1318
    /*
1319
     * Some redirect codes imply we have to change the method
1320
     * used for the subsequent transaction.
1321
     *
1322
     * ugh... https://peterdaugaardrasmussen.com/2020/05/09/how-to-redirect-http-put-or-post-requests/
1323
     * says only 307 or 308 mean keep POST or other method
1324
     */
1325
1326
0
    if (n != 307 && n != 308) {
1327
0
      char *mp = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
1328
0
      int ml = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_METHOD);
1329
0
      uint16_t pl = (uint16_t)strlen(path);
1330
1331
0
      if (ml >= 3 && mp) {
1332
0
        lwsl_info("%s: 303 switching to GET\n", __func__);
1333
0
        memcpy(mp, "GET", 4);
1334
0
        wsi->redirected_to_get = 1;
1335
0
        wsi->http.ah->frags[wsi->http.ah->frag_index[
1336
0
          _WSI_TOKEN_CLIENT_METHOD]].len = 3;
1337
0
      }
1338
0
            if (wsi->stash)
1339
0
                    wsi->stash->cis[CIS_METHOD] = "GET";
1340
1341
0
      mp = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI);
1342
0
      ml = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI);
1343
0
      (void)mp;
1344
0
      (void)ml;
1345
1346
0
      if (wsi->http.ah->pos + pl + 1 >= wsi->http.ah->data_length) {
1347
0
        lwsl_warn("%s: redirect path exceeds ah size\n", __func__);
1348
0
        goto bail3_l;
1349
0
      }
1350
0
      memcpy(wsi->http.ah->data + wsi->http.ah->pos + 1, path, pl + 1u);
1351
0
      wsi->http.ah->data[wsi->http.ah->pos] = '/';
1352
0
      wsi->http.ah->frags[wsi->http.ah->frag_index[_WSI_TOKEN_CLIENT_URI]].offset = wsi->http.ah->pos;
1353
0
      wsi->http.ah->frags[wsi->http.ah->frag_index[_WSI_TOKEN_CLIENT_URI]].len = (uint16_t)(pl + 1u);
1354
1355
0
      if (wsi->stash) {
1356
0
        struct client_info_stash *ostash = wsi->stash;
1357
0
        const char *cisin[CIS_COUNT];
1358
0
        int m;
1359
1360
0
        wsi->stash = NULL;
1361
0
        for (m = 0; m < CIS_COUNT; m++)
1362
0
          cisin[m] = ostash->cis[m];
1363
0
        cisin[CIS_PATH] = wsi->http.ah->data + wsi->http.ah->pos;
1364
1365
0
        if (lws_client_stash_create(wsi, cisin)) {
1366
0
          lwsl_err("%s: failed to realloc stash for redirect\n", __func__);
1367
0
          lws_free(ostash);
1368
0
          cce = "HS: stash realloc failed";
1369
0
          goto bail3_l;
1370
0
        }
1371
0
        lws_free(ostash);
1372
0
      }
1373
1374
0
      wsi->http.ah->pos += pl + 1u;
1375
0
    }
1376
1377
1378
0
#if defined(LWS_WITH_TLS)
1379
0
    if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) && !ssl &&
1380
0
         !(wsi->flags & LCCSCF_ACCEPT_TLS_DOWNGRADE_REDIRECTS)) {
1381
0
      cce = "HS: Redirect attempted SSL downgrade";
1382
0
      goto bail3_l;
1383
0
    }
1384
0
#endif
1385
1386
0
    if (!ads) /* make coverity happy */ {
1387
0
      cce = "no ads";
1388
0
      goto bail3_l;
1389
0
    }
1390
1391
0
    if (!lws_client_reset(&wsi, ssl, ads, port, path, ads, 1)) {
1392
0
      lwsl_err("Redirect failed\n");
1393
0
      cce = "HS: Redirect failed";
1394
0
      goto bail3_l;
1395
0
    }
1396
1397
    /*
1398
     * We are redirecting, let's close in order to extricate
1399
     * ourselves from the current wsi usage, eg, h2 mux cleanly.
1400
     *
1401
     * We will notice close_is_redirect and switch to redirect
1402
     * flow late in the close action.
1403
     */
1404
1405
0
    opaque = wsi->a.opaque_user_data;
1406
0
    lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "redir");
1407
0
    wsi->a.opaque_user_data = opaque;
1408
1409
0
    if (puri)
1410
0
      lws_parse_uri_destroy(&puri);
1411
1412
0
    return LWS_HPI_RET_WSI_ALREADY_DIED;
1413
0
  }
1414
1415
  /* if h1 KA is allowed, enable the queued pipeline guys */
1416
1417
0
  if (!wsi->client_h2_alpn && !wsi->client_mux_substream) {
1418
    /* ie, coming to this for the first time */
1419
0
    if (wsi->http.conn_type == HTTP_CONNECTION_KEEP_ALIVE)
1420
0
      wsi->keepalive_active = 1;
1421
0
    else {
1422
      /*
1423
       * Ugh... now the main http connection has seen
1424
       * both sides, we learn the server doesn't
1425
       * support keepalive.
1426
       *
1427
       * That means any guys queued on us are going
1428
       * to have to be restarted from connect2 with
1429
       * their own connections.
1430
       */
1431
1432
      /*
1433
       * stick around telling any new guys they can't
1434
       * pipeline to this server
1435
       */
1436
0
      wsi->keepalive_rejected = 1;
1437
1438
0
      lws_vhost_lock(wsi->a.vhost);
1439
0
      lws_start_foreach_dll_safe(struct lws_dll2 *,
1440
0
               d, d1,
1441
0
        wsi->dll2_cli_txn_queue_owner.head) {
1442
0
        struct lws *ww = lws_container_of(d,
1443
0
          struct lws,
1444
0
          dll2_cli_txn_queue);
1445
1446
        /* remove him from our queue */
1447
0
        lws_dll2_remove(&ww->dll2_cli_txn_queue);
1448
        /* give up on pipelining */
1449
0
        ww->client_pipeline = 0;
1450
1451
        /* go back to "trying to connect" state */
1452
0
        lws_role_transition(ww, LWSIFR_CLIENT,
1453
0
                LRS_UNCONNECTED,
1454
0
#if defined(LWS_ROLE_H1)
1455
0
                &role_ops_h1);
1456
#else
1457
#if defined (LWS_ROLE_H2)
1458
                &role_ops_h2);
1459
#else
1460
#if defined (LWS_ROLE_H3)
1461
                &role_ops_h3);
1462
#else
1463
                NULL);
1464
#endif
1465
#endif
1466
#endif
1467
0
        ww->user_space = NULL;
1468
0
      } lws_end_foreach_dll_safe(d, d1);
1469
0
      lws_vhost_unlock(wsi->a.vhost);
1470
0
    }
1471
0
  }
1472
1473
#ifdef LWS_WITH_HTTP_PROXY
1474
  wsi->http.perform_rewrite = 0;
1475
  if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
1476
    if (!strncmp(lws_hdr_simple_ptr(wsi,
1477
          WSI_TOKEN_HTTP_CONTENT_TYPE),
1478
          "text/html", 9))
1479
      wsi->http.perform_rewrite = 0;
1480
  }
1481
#endif
1482
1483
  /* he may choose to send us stuff in chunked transfer-coding */
1484
0
  wsi->chunked = 0;
1485
0
  wsi->chunk_remaining = 0; /* ie, next thing is chunk size */
1486
0
  if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING)) {
1487
0
    simp = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING);
1488
1489
    /* cannot be NULL, since it has nonzero length... coverity */
1490
0
    if (!simp)
1491
0
      goto bail2;
1492
0
    if (!strcasecmp(simp, "chunked")) {
1493
0
      wsi->chunked = 1;
1494
0
    } else {
1495
0
      lwsl_err("%s: unsupported TE %s\n", __func__, simp);
1496
0
      cce = "HS: unsupported TE";
1497
0
      goto bail2;
1498
0
    }
1499
    /* first thing is hex, after payload there is crlf */
1500
0
    wsi->chunk_parser = ELCP_HEX;
1501
0
  }
1502
1503
0
  wsi->http.content_length_given = 0;
1504
0
  if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
1505
0
    simp = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH);
1506
1507
    /* cannot be NULL, since it has nonzero length... coverity */
1508
0
    if (!simp)
1509
0
      goto bail2;
1510
1511
0
    {
1512
0
      long long cl_val = atoll(simp);
1513
0
      if (cl_val < 0)
1514
0
        goto bail2;
1515
0
      wsi->http.rx_content_length = (lws_filepos_t)cl_val;
1516
0
    }
1517
0
    lwsl_info("%s: incoming content length %llu\n",
1518
0
          __func__, (unsigned long long)
1519
0
            wsi->http.rx_content_length);
1520
0
    wsi->http.rx_content_remain =
1521
0
        wsi->http.rx_content_length;
1522
0
    wsi->http.content_length_given = 1;
1523
0
  } else { /* can't do 1.1 without a content length or chunked */
1524
0
    if (!wsi->chunked)
1525
0
      wsi->http.conn_type = HTTP_CONNECTION_CLOSE;
1526
0
    lwsl_debug("%s: no content length\n", __func__);
1527
0
  }
1528
1529
0
  if (wsi->do_ws) {
1530
    /*
1531
     * Give one last opportunity to ws protocols to inspect server reply
1532
     * before the ws upgrade code discard it. ie: download reply body in case
1533
     * of any other response code than 101.
1534
     */
1535
0
    if (wsi->a.protocol->callback(wsi,
1536
0
            LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
1537
0
            wsi->user_space, NULL, 0)) {
1538
1539
0
      cce = "HS: disallowed by client filter";
1540
0
      goto bail2;
1541
0
    }
1542
0
  } else {
1543
    /* allocate the per-connection user memory (if any) */
1544
0
    if (lws_ensure_user_space(wsi)) {
1545
0
      lwsl_err("Problem allocating wsi user mem\n");
1546
0
      cce = "HS: OOM";
1547
0
      goto bail2;
1548
0
    }
1549
1550
1551
    /*
1552
     * we seem to be good to go, give client last chance to check
1553
     * headers and OK it
1554
     */
1555
0
    ah1 = wsi->http.ah;
1556
0
    wsi->http.ah = ah;
1557
0
    if (wsi->a.protocol->callback(wsi,
1558
0
        LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
1559
0
              wsi->user_space, NULL, 0)) {
1560
0
      wsi->http.ah = ah1;
1561
0
      cce = "HS: disallowed by client filter";
1562
0
      goto bail2;
1563
0
    }
1564
1565
    /* clear his proxy connection timeout */
1566
0
    lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
1567
1568
0
    wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
1569
1570
    /* call him back to inform him he is up */
1571
0
    if (wsi->a.protocol->callback(wsi,
1572
0
              LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
1573
0
              wsi->user_space, NULL, 0)) {
1574
0
      wsi->http.ah = ah1;
1575
0
      cce = "HS: disallowed at ESTABLISHED";
1576
0
      goto bail3_l;
1577
0
    }
1578
1579
0
    wsi->http.ah = ah1;
1580
1581
0
    lwsl_info("%s: %s: client conn up\n", __func__, lws_wsi_tag(wsi));
1582
1583
    /*
1584
     * Did we get a response from the server with an explicit
1585
     * content-length of zero?  If so, and it's not H2 which will
1586
     * notice it via END_STREAM, this transaction is already
1587
     * completed at the end of the header processing...
1588
     * We also completed it if the request method is HEAD which as
1589
     * no content leftover.
1590
     * Or if the response status code is 204 : No Content
1591
     */
1592
0
    simp = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
1593
0
    if (!wsi->mux_substream &&
1594
0
        !wsi->client_mux_substream &&
1595
0
      (204 == lws_http_client_http_response(wsi) ||
1596
0
       (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) &&
1597
0
        (!wsi->http.rx_content_length ||
1598
0
        (simp && !strcmp(simp,"HEAD"))))))
1599
0
        return !!lws_http_transaction_completed_client(wsi);
1600
1601
    /*
1602
     * We can also get a case where it's http/1 and there's no
1603
     * content-length at all, so anything that comes is the body
1604
     * until it hangs up on us.  With that situation, hanging up
1605
     * on us past this point should generate a valid
1606
     * LWS_CALLBACK_COMPLETED_CLIENT_HTTP.
1607
     *
1608
     * In that situation, he can't pipeline because in h1 there's
1609
     * no post-header in-band way to signal the end of the
1610
     * transaction except hangup.
1611
     *
1612
     * lws_http_transaction_completed_client() is the right guy to
1613
     * issue it when we see the peer has hung up on us.
1614
     */
1615
1616
0
    return 0;
1617
0
  }
1618
1619
0
#if defined(LWS_ROLE_WS)
1620
0
  switch (lws_client_ws_upgrade(wsi, &cce)) {
1621
0
  case 2:
1622
0
    goto bail2;
1623
0
  case 3:
1624
0
    goto bail3_l;
1625
0
  }
1626
1627
0
  return 0;
1628
0
#endif
1629
1630
0
bail3_l:
1631
0
  close_reason = LWS_CLOSE_STATUS_NOSTATUS;
1632
1633
0
bail2:
1634
0
  if (wsi->a.protocol) {
1635
0
    n = 0;
1636
0
    if (cce)
1637
0
      n = (int)strlen(cce);
1638
1639
0
    lws_inform_client_conn_fail(wsi, (void *)cce, (unsigned int)n);
1640
0
  }
1641
1642
0
  lwsl_info("closing connection (prot %s) "
1643
0
      "due to bail2 connection error: %s\n", wsi->a.protocol ?
1644
0
          wsi->a.protocol->name : "unknown", cce);
1645
1646
  /* closing will free up his parsing allocations */
1647
0
  lws_close_free_wsi(wsi, (enum lws_close_status)close_reason, "c hs interp");
1648
1649
0
  if (puri)
1650
0
    lws_parse_uri_destroy(&puri);
1651
1652
0
  return LWS_HPI_RET_WSI_ALREADY_DIED;
1653
0
}
1654
#endif
1655
1656
/*
1657
 * set the boundary string and the content-type for client multipart mime
1658
 */
1659
1660
uint8_t *
1661
lws_http_multipart_headers(struct lws *wsi, uint8_t *p)
1662
0
{
1663
0
  char buf[10], arg[48];
1664
0
  int n;
1665
1666
0
  if (lws_get_random(wsi->a.context, (uint8_t *)buf, sizeof(buf)) !=
1667
0
      sizeof(buf))
1668
0
    return NULL;
1669
1670
0
  lws_b64_encode_string(buf, sizeof(buf),
1671
0
             wsi->http.multipart_boundary,
1672
0
             sizeof(wsi->http.multipart_boundary));
1673
1674
0
  n = lws_snprintf(arg, sizeof(arg), "multipart/form-data; boundary=\"%s\"",
1675
0
       wsi->http.multipart_boundary);
1676
1677
0
  if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
1678
0
           (uint8_t *)arg, n, &p, p + 100))
1679
0
    return NULL;
1680
1681
0
  wsi->http.multipart = wsi->http.multipart_issue_boundary = 1;
1682
0
  lws_client_http_body_pending(wsi, 1);
1683
1684
0
  return p;
1685
0
}
1686
1687
int
1688
lws_client_http_multipart(struct lws *wsi, const char *name,
1689
        const char *filename, const char *content_type,
1690
        char **p, char *end)
1691
0
{
1692
  /*
1693
   * Client conn must have been created with LCCSCF_HTTP_MULTIPART_MIME
1694
   * flag to use this api
1695
   */
1696
0
  assert(wsi->http.multipart);
1697
1698
0
  if (!name) {
1699
0
    *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p),
1700
0
          "\xd\xa--%s--\xd\xa",
1701
0
          wsi->http.multipart_boundary);
1702
1703
0
    return 0;
1704
0
  }
1705
1706
0
  if (wsi->client_subsequent_mime_part)
1707
0
    *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "\xd\xa");
1708
0
  wsi->client_subsequent_mime_part = 1;
1709
1710
0
  *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "--%s\xd\xa"
1711
0
            "Content-Disposition: form-data; "
1712
0
              "name=\"%s\"",
1713
0
              wsi->http.multipart_boundary, name);
1714
0
  if (filename)
1715
0
    *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p),
1716
0
           "; filename=\"%s\"", filename);
1717
1718
0
  if (content_type)
1719
0
    *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "\xd\xa"
1720
0
        "Content-Type: %s", content_type);
1721
1722
0
  *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "\xd\xa\xd\xa");
1723
1724
0
  return *p == end;
1725
0
}
1726
1727
/*
1728
 * replacement multipart state machine
1729
 *
1730
 * We want it to emit this kind of thing:
1731
 *
1732
 * POST /builds?project=warmcat%2Flibwebsockets HTTP/1.1
1733
 * Host: 127.0.0.1
1734
 * User-Agent: lws
1735
 * Accept: * / *
1736
 * Content-Length: 698
1737
 * Content-Type: multipart/form-data; boundary=------------------------dbe229171d826cc3
1738
 *
1739
 * --------------------------dbe229171d826cc3
1740
 * Content-Disposition: form-data; name="file"; filename="xxx.bin"
1741
 * Content-Type: application/octet-stream
1742
 *
1743
 * #!/bin/bash -x
1744
 * xxx
1745
 * exit $?
1746
 *
1747
 * --------------------------dbe229171d826cc3
1748
 * Content-Disposition: form-data; name="version"
1749
 *
1750
 * f2dcc4ea
1751
 * --------------------------dbe229171d826cc3
1752
 * Content-Disposition: form-data; name="description"
1753
 * 
1754
 * lws qa
1755
 * --------------------------dbe229171d826cc3
1756
 * Content-Disposition: form-data; name="token"
1757
 *
1758
 * mytoken
1759
 * --------------------------dbe229171d826cc3
1760
 * Content-Disposition: form-data; name="email"
1761
 *
1762
 * my@email.com
1763
 * --------------------------dbe229171d826cc3--
1764
 *
1765
 */
1766
1767
typedef enum {
1768
  LWS_POST_STATE__NEXT,
1769
  LWS_POST_STATE__FILE,
1770
  LWS_POST_STATE__DATA,
1771
} post_state;
1772
1773
typedef struct lws_http_mp_sm {
1774
  struct lws_context  *cx;
1775
  lws_http_mp_sm_cb_t cb;
1776
  char      boundary[24 + 16 + 1];
1777
  char      ft[4096];
1778
  char      *eq;
1779
  int     fd;
1780
  lws_filepos_t   pos;
1781
  lws_filepos_t   total;
1782
  const char    *a; /* last hit */
1783
  post_state    ps;
1784
} lws_http_mp_sm_t;
1785
1786
struct lws_http_mp_sm *
1787
lws_http_mp_sm_init(struct lws *wsi, lws_http_mp_sm_cb_t cb, uint8_t **p, uint8_t *end)
1788
0
{
1789
0
  struct lws_http_mp_sm *phms;
1790
0
  char cla[512 + sizeof(phms->boundary)], ft[256], *eq;
1791
0
  uint64_t cl = 0;
1792
0
  struct stat s;
1793
0
  int n;
1794
1795
0
  phms = lws_malloc(sizeof(*phms), __func__);
1796
0
  if (!phms)
1797
0
    return NULL;
1798
0
  phms->cb = cb;
1799
0
  phms->cx = lws_get_context(wsi);
1800
1801
0
  for (n = 0; n < 24; n++)
1802
0
    phms->boundary[n] = '-';
1803
0
  lws_hex_random(phms->cx, phms->boundary + 24, 16);
1804
0
  phms->boundary[24 + 16] = '\0';
1805
1806
0
  n = lws_snprintf(cla, sizeof(cla), "multipart/form-data; boundary=%s", phms->boundary);
1807
0
  if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
1808
0
         (const uint8_t *)cla, n, p, end)) {
1809
0
    lwsl_warn("%s: failed to set content_type\n", __func__);
1810
0
    goto bail;
1811
0
  }
1812
1813
  /*
1814
   * We have to now add together the length of everything we will put in
1815
   * the body, in order to know the content-length now at header-time.
1816
   *
1817
   * That includes the multipart boundaries, headers, and CRLF delimiters.
1818
   */
1819
1820
0
  phms->a = NULL;
1821
0
  do {
1822
    /* The cb will a) use cla / len as a scratchpad and
1823
     * b) provide a string formelem=@name or formelem=name */
1824
1825
0
    n = phms->cb(lws_get_context(wsi), ft, sizeof(ft), &phms->a);
1826
0
    if (n < 0)
1827
0
      goto bail;
1828
0
    if (n)
1829
0
      break;
1830
0
    eq = (char *)strchr(ft, '=');
1831
0
    if (eq) {
1832
0
      *eq = '\0';
1833
0
      eq++;
1834
0
    } /* ft contains the lhs of the = (now NUL) and eq the rhs sz */
1835
1836
0
    cl += 2 /* -- */ + strlen(phms->boundary) + 2 /* CRLF */;
1837
0
    if (eq && *eq == '@') { /* ie, form file contents */
1838
0
      cl += (unsigned int)lws_snprintf(cla, sizeof(cla),
1839
0
             "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\x0d\x0a"
1840
0
             "Content-Type: application/octet-stream\x0d\x0a\x0d\x0a",
1841
0
             ft, eq + 1);
1842
0
      if (stat(eq + 1, &s)) {
1843
0
        lwsl_warn("%s: failed to stat %s\n", __func__, eq + 1);
1844
0
        goto bail;
1845
0
      }
1846
1847
0
      cl += (uint64_t)s.st_size + 2 /* ending CRLF */;
1848
0
      continue;
1849
0
    }
1850
1851
    /* form data */
1852
1853
0
    cl += (unsigned int)lws_snprintf(cla, sizeof(cla),
1854
0
           "Content-Disposition: form-data; name=\"%s\"\x0d\x0a\x0d\x0a", ft);
1855
0
    if (eq)
1856
0
      cl += strlen(eq) + 2 /* CRLF */;
1857
1858
0
  } while (1);
1859
1860
0
  cl += 2 /* -- */ + strlen(phms->boundary) + 2 /* -- */ + 2 /* CRLF */;
1861
1862
  // lwsl_warn("%s: going with content length 0x%x\n", __func__, (unsigned int)cl);
1863
1864
0
  if (lws_add_http_header_content_length(wsi, cl, p, end))
1865
0
    goto bail;
1866
1867
0
  phms->a   = NULL;
1868
0
  phms->pos = 0;
1869
0
  phms->total = 0;
1870
0
  phms->ps  = LWS_POST_STATE__NEXT;
1871
1872
  /*
1873
   * Tell lws we are going to send the body next...
1874
   */
1875
1876
0
  return phms;
1877
1878
0
bail:
1879
0
  free(phms);
1880
1881
0
  return NULL;
1882
0
}
1883
1884
void
1885
lws_http_mp_sm_destroy(struct lws_http_mp_sm **pphms)
1886
0
{
1887
0
  if (*pphms) {
1888
0
    lws_free(*pphms);
1889
0
    *pphms = NULL;
1890
0
  }
1891
0
}
1892
1893
int
1894
lws_http_mp_sm_fill(struct lws_http_mp_sm *phms, uint8_t **p, uint8_t *end)
1895
0
{
1896
0
  int n;
1897
1898
0
  assert(phms);
1899
1900
0
  do {
1901
0
    switch (phms->ps) {
1902
0
    case LWS_POST_STATE__NEXT:
1903
1904
0
      if (lws_ptr_diff(end, *p) < 300)
1905
0
        return 1;
1906
1907
0
      n = phms->cb(phms->cx, phms->ft, sizeof(phms->ft), &phms->a);
1908
0
      if (n < 0) { /* error */
1909
0
        return -1;
1910
0
      }
1911
0
      if (n) { /* no more form elements */
1912
0
        *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "--%s--\x0d\x0a", phms->boundary);
1913
1914
0
        return 0; /* finished then */
1915
0
      }
1916
1917
0
      phms->eq = (char *)strchr(phms->ft, '=');
1918
0
      if (phms->eq) {
1919
0
        *phms->eq = '\0';
1920
0
        phms->eq++;
1921
0
      } /* phms->ft contains the lhs of the = (now NUL) and eq the rhs sz */
1922
1923
0
      *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "--%s\x0d\x0a", phms->boundary);
1924
1925
0
      if (phms->eq && *phms->eq == '@') { /* ie, form file contents */
1926
0
        struct stat s;
1927
1928
0
        *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p),
1929
0
               "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\x0d\x0a"
1930
0
               "Content-Type: application/octet-stream\x0d\x0a\x0d\x0a",
1931
0
               phms->ft, phms->eq + 1);
1932
0
        phms->fd = open(phms->eq + 1, O_RDONLY);
1933
0
        if (phms->fd == -1) {
1934
0
          lwsl_warn("%s: unable to open '%s'\n", __func__, phms->eq + 1);
1935
0
          return -1; /* failed */
1936
0
        }
1937
0
        if (fstat(phms->fd, &s)) {
1938
0
          lwsl_warn("%s: failed to stat %s\n", __func__, phms->eq + 1);
1939
0
          return -1; /* failed */
1940
0
        }
1941
0
        phms->pos = 0;
1942
0
        phms->total = (lws_filepos_t)s.st_size;
1943
0
        phms->ps = LWS_POST_STATE__FILE;
1944
0
        continue;
1945
0
      }
1946
1947
      /* form data */
1948
1949
0
      *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p),
1950
0
           "Content-Disposition: form-data; name=\"%s\"\x0d\x0a\x0d\x0a", phms->ft);
1951
0
      phms->ps = LWS_POST_STATE__DATA;
1952
0
      break;
1953
1954
0
    case LWS_POST_STATE__FILE: {
1955
0
      size_t chunk = lws_ptr_diff_size_t(end, *p) - 2;
1956
0
      ssize_t r;
1957
1958
0
      if (lws_ptr_diff(end, *p) < 100)
1959
0
                                return 1;
1960
1961
0
      r = read(phms->fd, *p, LWS_POSIX_LENGTH_CAST(chunk));
1962
0
      if (r < 0) {
1963
0
        close(phms->fd);
1964
0
        lwsl_warn("%s: unable to read\n", __func__);
1965
0
        return -1; /* failed */
1966
0
      }
1967
1968
0
      *p += r;
1969
0
      phms->pos += (uint64_t)r;
1970
0
      if (phms->pos == phms->total) {
1971
0
        **p = '\x0d';
1972
0
        *p += 1;
1973
0
        **p = '\x0a';
1974
0
        *p += 1;
1975
0
        close(phms->fd);
1976
0
        phms->ps = LWS_POST_STATE__NEXT;
1977
0
      }
1978
0
      break;
1979
0
    }
1980
0
    case LWS_POST_STATE__DATA:
1981
0
      if (lws_ptr_diff(end, *p) < 300)
1982
0
        return 1;
1983
1984
0
      *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "%s\x0d\x0a", phms->eq);
1985
0
      phms->ps = LWS_POST_STATE__NEXT;
1986
0
      break;
1987
1988
0
    } /* switch */
1989
0
  } while (lws_ptr_diff(end, *p) > 100);
1990
1991
0
  return 1; /* more to do */
1992
0
}
1993
1994
1995
char *
1996
lws_generate_client_handshake(struct lws *wsi, char *pkt, size_t pkt_len)
1997
0
{
1998
0
  const char *meth, *pp = lws_hdr_simple_ptr(wsi,
1999
0
        _WSI_TOKEN_CLIENT_SENT_PROTOCOLS), *path;
2000
0
  char *p = pkt, *p1, *end = p + pkt_len;
2001
2002
0
  meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
2003
0
  if (!meth) {
2004
0
    meth = "GET";
2005
0
#if defined(LWS_ROLE_WS)
2006
0
    wsi->do_ws = wsi->ws ? 1 : 0;
2007
#else
2008
    wsi->do_ws = 0;
2009
#endif
2010
0
  } else {
2011
0
    wsi->do_ws = 0;
2012
0
  }
2013
2014
0
  if (!strcmp(meth, "RAW")) {
2015
0
    lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
2016
0
    lwsl_notice("client transition to raw\n");
2017
2018
0
    if (pp) {
2019
0
      const struct lws_protocols *pr;
2020
2021
0
      pr = lws_vhost_name_to_protocol(wsi->a.vhost, pp);
2022
2023
0
      if (!pr) {
2024
0
        lwsl_err("protocol %s not enabled on vhost\n",
2025
0
           pp);
2026
0
        return NULL;
2027
0
      }
2028
2029
0
      lws_bind_protocol(wsi, pr, __func__);
2030
0
    }
2031
2032
0
    if ((wsi->a.protocol->callback)(wsi, LWS_CALLBACK_RAW_ADOPT,
2033
0
                wsi->user_space, NULL, 0))
2034
0
      return NULL;
2035
2036
0
    lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED,
2037
0
            &role_ops_raw_skt);
2038
0
    lws_header_table_detach(wsi, 1);
2039
2040
0
    return NULL;
2041
0
  }
2042
2043
  /*
2044
   * 04 example client handshake
2045
   *
2046
   * GET /chat HTTP/1.1
2047
   * Host: server.example.com
2048
   * Upgrade: websocket
2049
   * Connection: Upgrade
2050
   * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
2051
   * Sec-WebSocket-Origin: http://example.com
2052
   * Sec-WebSocket-Protocol: chat, superchat
2053
   * Sec-WebSocket-Version: 4
2054
   */
2055
2056
0
  path = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI);
2057
0
  if (!path) {
2058
0
    if (wsi->stash && wsi->stash->cis[CIS_PATH] &&
2059
0
      wsi->stash->cis[CIS_PATH][0])
2060
0
      path = wsi->stash->cis[CIS_PATH];
2061
0
    else
2062
0
      path = "/";
2063
0
  }
2064
2065
0
  p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
2066
0
        "%s %s HTTP/1.1\x0d\x0a", meth, path);
2067
2068
0
  if (!(wsi->flags & LCCSCF_HTTP_NO_CACHE_CONTROL))
2069
0
    p += lws_snprintf(p,  lws_ptr_diff_size_t(end, p),
2070
0
          "Pragma: no-cache\x0d\x0a"
2071
0
          "Cache-Control: no-cache\x0d\x0a");
2072
2073
0
  const char *host = lws_wsi_client_stash_item(wsi, CIS_HOST, _WSI_TOKEN_CLIENT_HOST);
2074
0
  if (host)
2075
0
    p += lws_snprintf(p,  lws_ptr_diff_size_t(end, p),
2076
0
          "Host: %s\x0d\x0a", host);
2077
2078
0
  const char *origin = lws_wsi_client_stash_item(wsi, CIS_ORIGIN, _WSI_TOKEN_CLIENT_ORIGIN);
2079
0
  if (origin) {
2080
0
    if (lws_check_opt(wsi->a.context->options,
2081
0
          LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN))
2082
0
      p += lws_snprintf(p,  lws_ptr_diff_size_t(end, p),
2083
0
            "Origin: %s\x0d\x0a", origin);
2084
0
    else
2085
0
      p += lws_snprintf(p,  lws_ptr_diff_size_t(end, p),
2086
0
            "Origin: %s://%s\x0d\x0a",
2087
0
            wsi->flags & LCCSCF_USE_SSL ?
2088
0
               "https" : "http",
2089
0
            origin);
2090
0
  }
2091
2092
0
  if (wsi->flags & LCCSCF_HTTP_MULTIPART_MIME) {
2093
0
    p1 = (char *)lws_http_multipart_headers(wsi, (uint8_t *)p);
2094
0
    if (!p1)
2095
0
      return NULL;
2096
0
    p = p1;
2097
0
  }
2098
2099
#if defined(LWS_WITH_HTTP_PROXY)
2100
  if (wsi->parent &&
2101
      lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
2102
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
2103
          "Content-Length: %s\x0d\x0a",
2104
      lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH));
2105
    if (atoi(lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH)))
2106
      wsi->client_http_body_pending = 1;
2107
  }
2108
  if (wsi->parent &&
2109
      lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_AUTHORIZATION)) {
2110
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
2111
          "Authorization: %s\x0d\x0a",
2112
      lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_AUTHORIZATION));
2113
  }
2114
  if (wsi->parent &&
2115
      lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
2116
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
2117
          "Content-Type: %s\x0d\x0a",
2118
      lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_TYPE));
2119
  }
2120
2121
  if (wsi->parent && wsi->parent->http.extra_onward_headers) {
2122
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "%s",
2123
          wsi->parent->http.extra_onward_headers);
2124
  }
2125
#endif
2126
2127
0
#if defined(LWS_WITH_HTTP_DIGEST_AUTH)
2128
0
    if (wsi->http.digest_auth_hdr) {
2129
0
        p += lws_snprintf(p, 1024, "Authorization: %s\x0d\x0a",
2130
0
                          wsi->http.digest_auth_hdr);
2131
0
        lws_free(wsi->http.digest_auth_hdr);
2132
0
        wsi->http.digest_auth_hdr = NULL;
2133
0
    }
2134
0
#endif
2135
2136
0
#if defined(LWS_ROLE_WS)
2137
0
  if (wsi->do_ws) {
2138
0
    const char *conn1 = "";
2139
  //  if (!wsi->client_pipeline)
2140
  //    conn1 = "close, ";
2141
0
    p = lws_generate_client_ws_handshake(wsi, p, conn1,
2142
0
                 lws_ptr_diff_size_t(end, p));
2143
0
                if (!p)
2144
0
                    return NULL;
2145
0
  } else
2146
0
#endif
2147
0
  {
2148
0
    if (!wsi->client_pipeline)
2149
0
      p += lws_snprintf(p, 64, "connection: close\x0d\x0a");
2150
0
  }
2151
2152
  /* give userland a chance to append, eg, cookies */
2153
2154
0
#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
2155
0
  if (wsi->flags & LCCSCF_CACHE_COOKIES)
2156
0
    lws_cookie_send_cookies(wsi, &p, end);
2157
0
#endif
2158
2159
0
  if (wsi->a.protocol->callback(wsi,
2160
0
      LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
2161
0
      wsi->user_space, &p,
2162
0
      (unsigned int)((pkt + wsi->a.context->pt_serv_buf_size) - p - 12)))
2163
0
    return NULL;
2164
2165
0
  if (wsi->flags & LCCSCF_HTTP_X_WWW_FORM_URLENCODED) {
2166
0
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "Content-Type: application/x-www-form-urlencoded\x0d\x0a");
2167
0
    p += lws_snprintf(p,  lws_ptr_diff_size_t(end, p), "Content-Length: %lu\x0d\x0a", wsi->http.writeable_len);
2168
0
    lws_client_http_body_pending(wsi, 1);
2169
0
  }
2170
2171
0
  p += lws_snprintf(p,  lws_ptr_diff_size_t(end, p), "\x0d\x0a");
2172
2173
0
  if (wsi->client_http_body_pending || lws_has_buffered_out(wsi))
2174
0
    lws_callback_on_writable(wsi);
2175
2176
0
  lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_http_txn);
2177
0
#if defined(LWS_WITH_CONMON)
2178
0
  wsi->conmon_datum = lws_now_usecs();
2179
0
#endif
2180
2181
  // puts(pkt);
2182
2183
0
  return p;
2184
0
}
2185
2186
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3)
2187
#if defined(LWS_WITH_HTTP_BASIC_AUTH)
2188
2189
int
2190
lws_http_basic_auth_gen2(const char *user, const void *pw, size_t pwd_len,
2191
       char *buf, size_t len)
2192
0
{
2193
0
  size_t n = strlen(user), m = pwd_len;
2194
0
  char b[128];
2195
2196
0
  if (len < 6 + ((4 * (n + m + 1)) / 3) + 1)
2197
0
    return 1;
2198
2199
0
  memcpy(buf, "Basic ", 6);
2200
2201
0
  n = (unsigned int)lws_snprintf(b, sizeof(b), "%s:", user);
2202
0
  if ((n + pwd_len) >= sizeof(b) - 2)
2203
0
    return 2;
2204
2205
0
  memcpy(&b[n], pw, pwd_len);
2206
0
  n += pwd_len;
2207
2208
0
  lws_b64_encode_string(b, (int)n, buf + 6, (int)len - 6);
2209
0
  buf[len - 1] = '\0';
2210
2211
0
  return 0;
2212
0
}
2213
2214
int lws_http_basic_auth_gen(const char *user, const char *pw, char *buf, size_t len)
2215
0
{
2216
0
  return lws_http_basic_auth_gen2(user, pw, strlen(pw), buf, len);
2217
0
}
2218
2219
#endif
2220
2221
int
2222
lws_http_client_read(struct lws *wsi, char **buf, int *len)
2223
0
{
2224
0
  struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
2225
0
  struct lws_tokens eb;
2226
0
  int buffered, n, consumed = 0;
2227
2228
  /*
2229
   * If the caller provided a non-NULL *buf and nonzero *len, we should
2230
   * use that as the buffer for the read action, limititing it to *len
2231
   * (actual payload will be less if chunked headers inside).
2232
   *
2233
   * If it's NULL / 0 length, buflist_aware_read will use the pt_serv_buf
2234
   */
2235
2236
0
  eb.token = (unsigned char *)*buf;
2237
0
  eb.len = *len;
2238
2239
0
  buffered = lws_buflist_aware_read(pt, wsi, &eb, 0, __func__);
2240
0
  *buf = (char *)eb.token; /* may be pointing to buflist or pt_serv_buf */
2241
0
  *len = 0;
2242
2243
  /*
2244
   * we're taking on responsibility for handling used / unused eb
2245
   * when we leave, via lws_buflist_aware_finished_consuming()
2246
   */
2247
2248
//  lwsl_notice("%s: eb.len %d ENTRY chunk remaining %d\n", __func__, eb.len,
2249
//      wsi->chunk_remaining);
2250
2251
  /* allow the source to signal he has data again next time */
2252
0
  if (lws_change_pollfd(wsi, 0, LWS_POLLIN))
2253
0
    return -1;
2254
2255
0
  if (buffered < 0) {
2256
0
    lwsl_debug("%s: SSL capable error\n", __func__);
2257
0
    lwsl_notice("%s: SSL capable error, hdr_parsing_completed=%d, content_length_given=%d, chunked=%d, ah_ptr=%p\n",
2258
0
      __func__, wsi->hdr_parsing_completed, wsi->http.content_length_given, wsi->chunked, wsi->http.ah);
2259
2260
0
    if (wsi->hdr_parsing_completed &&
2261
0
        !wsi->http.content_length_given &&
2262
0
        !wsi->chunked) {
2263
0
      lwsl_notice("%s: generating lws_http_transaction_completed_client\n", __func__);
2264
      /*
2265
       * We had the headers from this stream, but as there
2266
       * was no content-length: we had to wait until the
2267
       * stream ended to inform the user code the transaction
2268
       * has completed to the best of our knowledge
2269
       */
2270
0
      if (lws_http_transaction_completed_client(wsi))
2271
        /*
2272
         * We're going to close anyway, but that api has
2273
         * warn_unused_result
2274
         */
2275
0
        return -1;
2276
0
    }
2277
2278
0
    return -1;
2279
0
  }
2280
2281
0
  if (eb.len <= 0)
2282
0
    return 0;
2283
2284
0
  *len = eb.len;
2285
0
  wsi->client_rx_avail = 0;
2286
2287
  /*
2288
   * server may insist on transfer-encoding: chunked,
2289
   * so http client must deal with it
2290
   */
2291
0
spin_chunks:
2292
  //lwsl_notice("%s: len %d SPIN chunk remaining %d\n", __func__, *len,
2293
  //    wsi->chunk_remaining);
2294
0
  while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) {
2295
0
    switch (wsi->chunk_parser) {
2296
0
    case ELCP_HEX:
2297
0
      if ((*buf)[0] == '\x0d') {
2298
0
        wsi->chunk_parser = ELCP_CR;
2299
0
        break;
2300
0
      }
2301
0
      n = char_to_hex((*buf)[0]);
2302
0
      if (n < 0) {
2303
0
        lwsl_err("%s: chunking failure A\n", __func__);
2304
0
        return -1;
2305
0
      }
2306
0
      if (wsi->chunk_remaining > (INT_MAX - 15) / 16) {
2307
0
        lwsl_err("%s: chunk size overflow\n", __func__);
2308
0
        return -1;
2309
0
      }
2310
0
      wsi->chunk_remaining <<= 4;
2311
0
      wsi->chunk_remaining |= n;
2312
0
      break;
2313
0
    case ELCP_CR:
2314
0
      if ((*buf)[0] != '\x0a') {
2315
0
        lwsl_err("%s: chunking failure B\n", __func__);
2316
0
        return -1;
2317
0
      }
2318
0
      if (wsi->chunk_remaining) {
2319
0
        wsi->chunk_parser = ELCP_CONTENT;
2320
        //lwsl_notice("starting chunk size %d (block rem %d)\n",
2321
        //    wsi->chunk_remaining, *len);
2322
0
        break;
2323
0
      }
2324
2325
0
      wsi->chunk_parser = ELCP_TRAILER_CR;
2326
0
      break;
2327
2328
0
    case ELCP_CONTENT:
2329
0
      break;
2330
2331
0
    case ELCP_POST_CR:
2332
0
      if ((*buf)[0] != '\x0d') {
2333
0
        lwsl_err("%s: chunking failure C\n", __func__);
2334
0
        lwsl_hexdump_err(*buf, (unsigned int)*len);
2335
2336
0
        return -1;
2337
0
      }
2338
2339
0
      wsi->chunk_parser = ELCP_POST_LF;
2340
0
      break;
2341
2342
0
    case ELCP_POST_LF:
2343
0
      if ((*buf)[0] != '\x0a') {
2344
0
        lwsl_err("%s: chunking failure D\n", __func__);
2345
2346
0
        return -1;
2347
0
      }
2348
2349
0
      wsi->chunk_parser = ELCP_HEX;
2350
0
      wsi->chunk_remaining = 0;
2351
0
      break;
2352
2353
0
    case ELCP_TRAILER_CR:
2354
0
      if ((*buf)[0] != '\x0d') {
2355
0
        lwsl_err("%s: chunking failure F\n", __func__);
2356
0
        lwsl_hexdump_err(*buf, (unsigned int)*len);
2357
2358
0
        return -1;
2359
0
      }
2360
2361
0
      wsi->chunk_parser = ELCP_TRAILER_LF;
2362
0
      break;
2363
2364
0
    case ELCP_TRAILER_LF:
2365
0
      if ((*buf)[0] != '\x0a') {
2366
0
        lwsl_err("%s: chunking failure F\n", __func__);
2367
0
        lwsl_hexdump_err(*buf, (unsigned int)*len);
2368
2369
0
        return -1;
2370
0
      }
2371
2372
0
      (*buf)++;
2373
0
      (*len)--;
2374
0
      consumed++;
2375
2376
0
      lwsl_info("final chunk\n");
2377
0
      goto completed;
2378
0
    }
2379
0
    (*buf)++;
2380
0
    (*len)--;
2381
0
    consumed++;
2382
0
  }
2383
2384
0
  if (wsi->chunked && !wsi->chunk_remaining)
2385
0
    goto account_and_ret;
2386
2387
0
  if (wsi->http.rx_content_remain &&
2388
0
      wsi->http.rx_content_remain < (unsigned int)*len)
2389
0
    n = (int)wsi->http.rx_content_remain;
2390
0
  else
2391
0
    n = *len;
2392
2393
0
  if (wsi->chunked && wsi->chunk_remaining &&
2394
0
      wsi->chunk_remaining < n)
2395
0
    n = wsi->chunk_remaining;
2396
2397
#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_WITH_HUBBUB)
2398
  /* hubbub */
2399
  if (wsi->http.perform_rewrite)
2400
    lws_rewrite_parse(wsi->http.rw, (unsigned char *)*buf, n);
2401
  else
2402
#endif
2403
0
  {
2404
0
    if (
2405
#if defined(LWS_WITH_HTTP_PROXY)
2406
        !wsi->protocol_bind_balance ==
2407
        !!wsi->http.proxy_clientside
2408
#else
2409
0
        !!wsi->protocol_bind_balance
2410
0
#endif
2411
0
      ) {
2412
0
      int q;
2413
2414
0
      q = user_callback_handle_rxflow(wsi->a.protocol->callback,
2415
0
        wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
2416
0
        wsi->user_space, *buf, (unsigned int)n);
2417
0
      if (q) {
2418
0
        lwsl_info("%s: RECEIVE_CLIENT_HTTP_READ returned %d\n",
2419
0
            __func__, q);
2420
2421
0
        return q;
2422
0
      }
2423
0
    } else
2424
0
      lwsl_notice("%s: swallowed read (%d)\n", __func__, n);
2425
0
  }
2426
2427
0
  (*buf) += n;
2428
0
  *len -= n;
2429
0
  if (wsi->chunked && wsi->chunk_remaining)
2430
0
    wsi->chunk_remaining -= n;
2431
2432
  //lwsl_notice("chunk_remaining <- %d, block remaining %d\n",
2433
  //    wsi->chunk_remaining, *len);
2434
2435
0
  consumed += n;
2436
  //eb.token += n;
2437
  //eb.len -= n;
2438
2439
0
  if (wsi->chunked && !wsi->chunk_remaining)
2440
0
    wsi->chunk_parser = ELCP_POST_CR;
2441
2442
0
  if (wsi->chunked && *len)
2443
0
    goto spin_chunks;
2444
2445
0
  if (wsi->chunked)
2446
0
    goto account_and_ret;
2447
2448
  /* if we know the content length, decrement the content remaining */
2449
0
  if (wsi->http.rx_content_length > 0)
2450
0
    wsi->http.rx_content_remain -= (unsigned int)n;
2451
2452
  // lwsl_notice("rx_content_remain %lld, rx_content_length %lld, giv %d\n",
2453
  //      wsi->http.rx_content_remain, wsi->http.rx_content_length,
2454
  //      wsi->http.content_length_given);
2455
2456
0
  if (wsi->http.rx_content_remain || !wsi->http.content_length_given)
2457
0
    goto account_and_ret;
2458
2459
0
completed:
2460
2461
0
  if (lws_http_transaction_completed_client(wsi)) {
2462
0
    lwsl_info("%s: transaction completed says -1\n", __func__);
2463
0
    return -1;
2464
0
  }
2465
2466
0
account_and_ret:
2467
//  lwsl_warn("%s: on way out, consuming %d / %d\n", __func__, consumed, eb.len);
2468
0
  if (lws_buflist_aware_finished_consuming(wsi, &eb, consumed, buffered,
2469
0
              __func__))
2470
0
    return -1;
2471
2472
0
  return 0;
2473
0
}
2474
2475
#endif
2476
2477
static uint8_t hnames2[] = {
2478
  _WSI_TOKEN_CLIENT_ORIGIN,
2479
  _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
2480
  _WSI_TOKEN_CLIENT_METHOD,
2481
  _WSI_TOKEN_CLIENT_IFACE
2482
};
2483
2484
/**
2485
 * lws_client_reset() - retarget a connected wsi to start over with a new
2486
 *      connection (ie, redirect)
2487
 *      this only works if still in HTTP, ie, not upgraded yet
2488
 * wsi:   connection to reset
2489
 * address: network address of the new server
2490
 * port:  port to connect to
2491
 * path:  uri path to connect to on the new server
2492
 * host:  host header to send to the new server
2493
 */
2494
struct lws *
2495
lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
2496
    const char *path, const char *host, char weak)
2497
0
{
2498
0
  struct lws_context_per_thread *pt;
2499
0
#if defined(LWS_ROLE_WS)
2500
0
  struct _lws_websocket_related *ws;
2501
0
#endif
2502
0
  const char *cisin[CIS_COUNT];
2503
0
  struct lws *wsi;
2504
0
  size_t o;
2505
0
  int n, r;
2506
2507
0
  if (!pwsi)
2508
0
    return NULL;
2509
2510
0
  wsi = *pwsi;
2511
0
  pt = &wsi->a.context->pt[(int)wsi->tsi];
2512
2513
0
  lwsl_debug("%s: %s: redir %d: %s\n", __func__, lws_wsi_tag(wsi),
2514
0
      wsi->redirects, address);
2515
2516
0
  if (wsi->redirects == 4) {
2517
0
    lwsl_err("%s: Too many redirects\n", __func__);
2518
0
    return NULL;
2519
0
  }
2520
0
  wsi->redirects++;
2521
2522
  /*
2523
   * goal is to close our role part, close the sockfd, detach the ah
2524
   * but leave our wsi extant and still bound to whatever vhost it was
2525
   */
2526
2527
0
  o = path[0] == '/' && path[1] == '/';
2528
2529
0
  memset((char *)cisin, 0, sizeof(cisin));
2530
2531
0
  cisin[CIS_ADDRESS]  = address;
2532
0
  cisin[CIS_PATH]   = path + o;
2533
0
  cisin[CIS_HOST]   = host;
2534
2535
0
  for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++) {
2536
0
    cisin[n + 3] = lws_hdr_simple_ptr(wsi, hnames2[n]);
2537
0
#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3)
2538
0
    if (!cisin[n + 3] && hnames2[n] == _WSI_TOKEN_CLIENT_METHOD)
2539
0
      cisin[n + 3] = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
2540
0
#endif
2541
0
  }
2542
2543
0
  r = wsi->http.ah ? (int)wsi->http.ah->http_response : 0;
2544
2545
0
#if defined(LWS_WITH_TLS)
2546
0
  cisin[CIS_ALPN]   = wsi->alpn;
2547
0
#endif
2548
2549
0
  if (!port) {
2550
0
    lwsl_info("%s: forcing port 443\n", __func__);
2551
2552
0
    port = 443;
2553
0
    ssl = 1;
2554
0
  }
2555
2556
0
  wsi->c_port = (uint16_t)port;
2557
2558
0
  wsi->flags = (wsi->flags & (~LCCSCF_USE_SSL)) |
2559
0
    (ssl ? LCCSCF_USE_SSL : 0);
2560
2561
0
  if (!cisin[CIS_ALPN] || !cisin[CIS_ALPN][0])
2562
0
#if defined(LWS_ROLE_H2)
2563
0
    cisin[CIS_ALPN] = "h2,http/1.1";
2564
#else
2565
  cisin[CIS_ALPN] = "http/1.1";
2566
#endif
2567
2568
0
  lwsl_notice("%s: REDIRECT %d: %s %s:%d, path='%s', ssl = %d, alpn='%s'\n",
2569
0
        __func__, r, cisin[CIS_METHOD], address,
2570
0
        port, path, ssl, cisin[CIS_ALPN]);
2571
2572
0
  {
2573
0
    void *opaque = wsi->stash ? wsi->stash->opaque_user_data : NULL;
2574
2575
0
    if (lws_client_stash_create(wsi, cisin))
2576
0
      return NULL;
2577
2578
0
    wsi->stash->opaque_user_data = opaque;
2579
0
  }
2580
2581
0
  lws_pt_lock(pt, __func__);
2582
#if defined(LWS_ROLE_H3) || defined(LWS_ROLE_QUIC)
2583
  lws_sul_cancel(&wsi->sul_h3_grace);
2584
#endif
2585
0
  __remove_wsi_socket_from_fds(wsi);
2586
0
  lws_pt_unlock(pt);
2587
2588
0
#if defined(LWS_ROLE_WS)
2589
0
  if (weak) {
2590
0
    ws = wsi->ws;
2591
0
    wsi->ws = NULL;
2592
0
  }
2593
0
#endif
2594
2595
  /*
2596
   * After this point we can't trust the incoming strings like address,
2597
   * path any more, since they may have been pointing into the old ah.
2598
   *
2599
   * We must use the copies in the wsi->stash instead if we want them.
2600
   */
2601
2602
0
  __lws_reset_wsi(wsi); /* detaches ah here */
2603
0
#if defined(LWS_ROLE_WS)
2604
0
  if (weak)
2605
0
    wsi->ws = ws;
2606
0
#endif
2607
0
  wsi->client_pipeline = 1;
2608
2609
  /*
2610
   * We could be a redirect before, or after the POST was done.
2611
   * Http's hack around this is 307 / 308 keep the method, ie,
2612
   * it's pre and they have to repeat the body.  Other 3xx
2613
   * turn it into a GET.
2614
   */
2615
2616
0
  if ((r / 100) == 3 && r != 307 && r != 308)
2617
0
    wsi->redirected_to_get = 1;
2618
2619
  /*
2620
   * Will complete at close flow
2621
   */
2622
2623
0
  wsi->close_is_redirect = 1;
2624
2625
0
  return *pwsi;
2626
0
}