Coverage Report

Created: 2024-05-04 12:45

/proc/self/cwd/external/curl/lib/sendf.c
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
25
#include "curl_setup.h"
26
27
#ifdef HAVE_NETINET_IN_H
28
#include <netinet/in.h>
29
#endif
30
31
#ifdef HAVE_LINUX_TCP_H
32
#include <linux/tcp.h>
33
#elif defined(HAVE_NETINET_TCP_H)
34
#include <netinet/tcp.h>
35
#endif
36
37
#include <curl/curl.h>
38
39
#include "urldata.h"
40
#include "sendf.h"
41
#include "cfilters.h"
42
#include "connect.h"
43
#include "content_encoding.h"
44
#include "vtls/vtls.h"
45
#include "vssh/ssh.h"
46
#include "easyif.h"
47
#include "multiif.h"
48
#include "strerror.h"
49
#include "select.h"
50
#include "strdup.h"
51
#include "http2.h"
52
#include "headers.h"
53
#include "ws.h"
54
55
/* The last 3 #include files should be in this order */
56
#include "curl_printf.h"
57
#include "curl_memory.h"
58
#include "memdebug.h"
59
60
#if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP)
61
/*
62
 * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
63
 * (\n), with special processing for CRLF sequences that are split between two
64
 * blocks of data.  Remaining, bare CRs are changed to LFs.  The possibly new
65
 * size of the data is returned.
66
 */
67
static size_t convert_lineends(struct Curl_easy *data,
68
                               char *startPtr, size_t size)
69
{
70
  char *inPtr, *outPtr;
71
72
  /* sanity check */
73
  if(!startPtr || (size < 1)) {
74
    return size;
75
  }
76
77
  if(data->state.prev_block_had_trailing_cr) {
78
    /* The previous block of incoming data
79
       had a trailing CR, which was turned into a LF. */
80
    if(*startPtr == '\n') {
81
      /* This block of incoming data starts with the
82
         previous block's LF so get rid of it */
83
      memmove(startPtr, startPtr + 1, size-1);
84
      size--;
85
      /* and it wasn't a bare CR but a CRLF conversion instead */
86
      data->state.crlf_conversions++;
87
    }
88
    data->state.prev_block_had_trailing_cr = FALSE; /* reset the flag */
89
  }
90
91
  /* find 1st CR, if any */
92
  inPtr = outPtr = memchr(startPtr, '\r', size);
93
  if(inPtr) {
94
    /* at least one CR, now look for CRLF */
95
    while(inPtr < (startPtr + size-1)) {
96
      /* note that it's size-1, so we'll never look past the last byte */
97
      if(memcmp(inPtr, "\r\n", 2) == 0) {
98
        /* CRLF found, bump past the CR and copy the NL */
99
        inPtr++;
100
        *outPtr = *inPtr;
101
        /* keep track of how many CRLFs we converted */
102
        data->state.crlf_conversions++;
103
      }
104
      else {
105
        if(*inPtr == '\r') {
106
          /* lone CR, move LF instead */
107
          *outPtr = '\n';
108
        }
109
        else {
110
          /* not a CRLF nor a CR, just copy whatever it is */
111
          *outPtr = *inPtr;
112
        }
113
      }
114
      outPtr++;
115
      inPtr++;
116
    } /* end of while loop */
117
118
    if(inPtr < startPtr + size) {
119
      /* handle last byte */
120
      if(*inPtr == '\r') {
121
        /* deal with a CR at the end of the buffer */
122
        *outPtr = '\n'; /* copy a NL instead */
123
        /* note that a CRLF might be split across two blocks */
124
        data->state.prev_block_had_trailing_cr = TRUE;
125
      }
126
      else {
127
        /* copy last byte */
128
        *outPtr = *inPtr;
129
      }
130
      outPtr++;
131
    }
132
    if(outPtr < startPtr + size)
133
      /* tidy up by null terminating the now shorter data */
134
      *outPtr = '\0';
135
136
    return (outPtr - startPtr);
137
  }
138
  return size;
139
}
140
#endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */
141
142
/*
143
 * Curl_nwrite() is an internal write function that sends data to the
144
 * server. Works with a socket index for the connection.
145
 *
146
 * If the write would block (CURLE_AGAIN), it returns CURLE_OK and
147
 * (*nwritten == 0). Otherwise we return regular CURLcode value.
148
 */
149
CURLcode Curl_nwrite(struct Curl_easy *data,
150
                     int sockindex,
151
                     const void *buf,
152
                     size_t blen,
153
                     ssize_t *pnwritten)
154
0
{
155
0
  ssize_t nwritten;
156
0
  CURLcode result = CURLE_OK;
157
0
  struct connectdata *conn;
158
159
0
  DEBUGASSERT(sockindex >= 0 && sockindex < 2);
160
0
  DEBUGASSERT(pnwritten);
161
0
  DEBUGASSERT(data);
162
0
  DEBUGASSERT(data->conn);
163
0
  conn = data->conn;
164
#ifdef CURLDEBUG
165
  {
166
    /* Allow debug builds to override this logic to force short sends
167
    */
168
    char *p = getenv("CURL_SMALLSENDS");
169
    if(p) {
170
      size_t altsize = (size_t)strtoul(p, NULL, 10);
171
      if(altsize)
172
        blen = CURLMIN(blen, altsize);
173
    }
174
  }
175
#endif
176
0
  nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result);
177
0
  if(result == CURLE_AGAIN) {
178
0
    nwritten = 0;
179
0
    result = CURLE_OK;
180
0
  }
181
0
  else if(result) {
182
0
    nwritten = -1; /* make sure */
183
0
  }
184
0
  else {
185
0
    DEBUGASSERT(nwritten >= 0);
186
0
  }
187
188
0
  *pnwritten = nwritten;
189
0
  return result;
190
0
}
191
192
/*
193
 * Curl_write() is an internal write function that sends data to the
194
 * server. Works with plain sockets, SCP, SSL or kerberos.
195
 *
196
 * If the write would block (CURLE_AGAIN), we return CURLE_OK and
197
 * (*written == 0). Otherwise we return regular CURLcode value.
198
 */
199
CURLcode Curl_write(struct Curl_easy *data,
200
                    curl_socket_t sockfd,
201
                    const void *mem,
202
                    size_t len,
203
                    ssize_t *written)
204
0
{
205
0
  struct connectdata *conn;
206
0
  int num;
207
208
0
  DEBUGASSERT(data);
209
0
  DEBUGASSERT(data->conn);
210
0
  conn = data->conn;
211
0
  num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]);
212
0
  return Curl_nwrite(data, num, mem, len, written);
213
0
}
214
215
static CURLcode pausewrite(struct Curl_easy *data,
216
                           int type, /* what type of data */
217
                           bool paused_body,
218
                           const char *ptr,
219
                           size_t len)
220
0
{
221
  /* signalled to pause sending on this connection, but since we have data
222
     we want to send we need to dup it to save a copy for when the sending
223
     is again enabled */
224
0
  struct SingleRequest *k = &data->req;
225
0
  struct UrlState *s = &data->state;
226
0
  unsigned int i;
227
0
  bool newtype = TRUE;
228
229
0
  Curl_conn_ev_data_pause(data, TRUE);
230
231
0
  if(s->tempcount) {
232
0
    for(i = 0; i< s->tempcount; i++) {
233
0
      if(s->tempwrite[i].type == type &&
234
0
         !!s->tempwrite[i].paused_body == !!paused_body) {
235
        /* data for this type exists */
236
0
        newtype = FALSE;
237
0
        break;
238
0
      }
239
0
    }
240
0
    DEBUGASSERT(i < 3);
241
0
    if(i >= 3)
242
      /* There are more types to store than what fits: very bad */
243
0
      return CURLE_OUT_OF_MEMORY;
244
0
  }
245
0
  else
246
0
    i = 0;
247
248
0
  if(newtype) {
249
    /* store this information in the state struct for later use */
250
0
    Curl_dyn_init(&s->tempwrite[i].b, DYN_PAUSE_BUFFER);
251
0
    s->tempwrite[i].type = type;
252
0
    s->tempwrite[i].paused_body = paused_body;
253
0
    s->tempcount++;
254
0
  }
255
256
0
  if(Curl_dyn_addn(&s->tempwrite[i].b, (unsigned char *)ptr, len))
257
0
    return CURLE_OUT_OF_MEMORY;
258
259
  /* mark the connection as RECV paused */
260
0
  k->keepon |= KEEP_RECV_PAUSE;
261
262
0
  return CURLE_OK;
263
0
}
264
265
266
/* chop_write() writes chunks of data not larger than CURL_MAX_WRITE_SIZE via
267
 * client write callback(s) and takes care of pause requests from the
268
 * callbacks.
269
 */
270
static CURLcode chop_write(struct Curl_easy *data,
271
                           int type,
272
                           bool skip_body_write,
273
                           char *optr,
274
                           size_t olen)
275
0
{
276
0
  struct connectdata *conn = data->conn;
277
0
  curl_write_callback writeheader = NULL;
278
0
  curl_write_callback writebody = NULL;
279
0
  char *ptr = optr;
280
0
  size_t len = olen;
281
0
  void *writebody_ptr = data->set.out;
282
283
0
  if(!len)
284
0
    return CURLE_OK;
285
286
  /* If reading is paused, append this data to the already held data for this
287
     type. */
288
0
  if(data->req.keepon & KEEP_RECV_PAUSE)
289
0
    return pausewrite(data, type, !skip_body_write, ptr, len);
290
291
  /* Determine the callback(s) to use. */
292
0
  if(!skip_body_write &&
293
0
     ((type & CLIENTWRITE_BODY) ||
294
0
      ((type & CLIENTWRITE_HEADER) && data->set.include_header))) {
295
#ifdef USE_WEBSOCKETS
296
    if(conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) {
297
      writebody = Curl_ws_writecb;
298
      writebody_ptr = data;
299
    }
300
    else
301
#endif
302
0
    writebody = data->set.fwrite_func;
303
0
  }
304
0
  if((type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) &&
305
0
     (data->set.fwrite_header || data->set.writeheader)) {
306
    /*
307
     * Write headers to the same callback or to the especially setup
308
     * header callback function (added after version 7.7.1).
309
     */
310
0
    writeheader =
311
0
      data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func;
312
0
  }
313
314
  /* Chop data, write chunks. */
315
0
  while(len) {
316
0
    size_t chunklen = len <= CURL_MAX_WRITE_SIZE? len: CURL_MAX_WRITE_SIZE;
317
318
0
    if(writebody) {
319
0
      size_t wrote;
320
0
      Curl_set_in_callback(data, true);
321
0
      wrote = writebody(ptr, 1, chunklen, writebody_ptr);
322
0
      Curl_set_in_callback(data, false);
323
324
0
      if(CURL_WRITEFUNC_PAUSE == wrote) {
325
0
        if(conn->handler->flags & PROTOPT_NONETWORK) {
326
          /* Protocols that work without network cannot be paused. This is
327
             actually only FILE:// just now, and it can't pause since the
328
             transfer isn't done using the "normal" procedure. */
329
0
          failf(data, "Write callback asked for PAUSE when not supported");
330
0
          return CURLE_WRITE_ERROR;
331
0
        }
332
0
        return pausewrite(data, type, TRUE, ptr, len);
333
0
      }
334
0
      if(wrote != chunklen) {
335
0
        failf(data, "Failure writing output to destination");
336
0
        return CURLE_WRITE_ERROR;
337
0
      }
338
0
    }
339
340
0
    ptr += chunklen;
341
0
    len -= chunklen;
342
0
  }
343
344
0
#ifndef CURL_DISABLE_HTTP
345
  /* HTTP header, but not status-line */
346
0
  if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
347
0
     (type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS) ) {
348
0
    unsigned char htype = (unsigned char)
349
0
      (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
350
0
       (type & CLIENTWRITE_1XX ? CURLH_1XX :
351
0
        (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
352
0
         CURLH_HEADER)));
353
0
    CURLcode result = Curl_headers_push(data, optr, htype);
354
0
    if(result)
355
0
      return result;
356
0
  }
357
0
#endif
358
359
0
  if(writeheader) {
360
0
    size_t wrote;
361
362
0
    Curl_set_in_callback(data, true);
363
0
    wrote = writeheader(optr, 1, olen, data->set.writeheader);
364
0
    Curl_set_in_callback(data, false);
365
366
0
    if(CURL_WRITEFUNC_PAUSE == wrote)
367
0
      return pausewrite(data, type, FALSE, optr, olen);
368
0
    if(wrote != olen) {
369
0
      failf(data, "Failed writing header");
370
0
      return CURLE_WRITE_ERROR;
371
0
    }
372
0
  }
373
374
0
  return CURLE_OK;
375
0
}
376
377
378
/* Curl_client_write() sends data to the write callback(s)
379
380
   The bit pattern defines to what "streams" to write to. Body and/or header.
381
   The defines are in sendf.h of course.
382
383
   If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
384
   local character encoding.  This is a problem and should be changed in
385
   the future to leave the original data alone.
386
 */
387
CURLcode Curl_client_write(struct Curl_easy *data,
388
                           int type,
389
                           char *ptr,
390
                           size_t len)
391
0
{
392
#if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV)
393
  /* FTP data may need conversion. */
394
  if((type & CLIENTWRITE_BODY) &&
395
     (data->conn->handler->protocol & PROTO_FAMILY_FTP) &&
396
     data->conn->proto.ftpc.transfertype == 'A') {
397
    /* convert end-of-line markers */
398
    len = convert_lineends(data, ptr, len);
399
  }
400
#endif
401
  /* it is one of those, at least */
402
0
  DEBUGASSERT(type & (CLIENTWRITE_BODY|CLIENTWRITE_HEADER|CLIENTWRITE_INFO));
403
  /* BODY is only BODY */
404
0
  DEBUGASSERT(!(type & CLIENTWRITE_BODY) || (type == CLIENTWRITE_BODY));
405
  /* INFO is only INFO */
406
0
  DEBUGASSERT(!(type & CLIENTWRITE_INFO) || (type == CLIENTWRITE_INFO));
407
408
0
  if(type == CLIENTWRITE_BODY) {
409
0
    if(data->req.ignorebody)
410
0
      return CURLE_OK;
411
412
0
    if(data->req.writer_stack && !data->set.http_ce_skip)
413
0
      return Curl_unencode_write(data, data->req.writer_stack, ptr, len);
414
0
  }
415
0
  return chop_write(data, type, FALSE, ptr, len);
416
0
}
417
418
CURLcode Curl_client_unpause(struct Curl_easy *data)
419
0
{
420
0
  CURLcode result = CURLE_OK;
421
422
0
  if(data->state.tempcount) {
423
    /* there are buffers for sending that can be delivered as the receive
424
       pausing is lifted! */
425
0
    unsigned int i;
426
0
    unsigned int count = data->state.tempcount;
427
0
    struct tempbuf writebuf[3]; /* there can only be three */
428
429
    /* copy the structs to allow for immediate re-pausing */
430
0
    for(i = 0; i < data->state.tempcount; i++) {
431
0
      writebuf[i] = data->state.tempwrite[i];
432
0
      Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER);
433
0
    }
434
0
    data->state.tempcount = 0;
435
436
0
    for(i = 0; i < count; i++) {
437
      /* even if one function returns error, this loops through and frees
438
         all buffers */
439
0
      if(!result)
440
0
        result = chop_write(data, writebuf[i].type,
441
0
                            !writebuf[i].paused_body,
442
0
                            Curl_dyn_ptr(&writebuf[i].b),
443
0
                            Curl_dyn_len(&writebuf[i].b));
444
0
      Curl_dyn_free(&writebuf[i].b);
445
0
    }
446
0
  }
447
0
  return result;
448
0
}
449
450
void Curl_client_cleanup(struct Curl_easy *data)
451
0
{
452
0
  struct contenc_writer *writer = data->req.writer_stack;
453
0
  size_t i;
454
455
0
  while(writer) {
456
0
    data->req.writer_stack = writer->downstream;
457
0
    writer->handler->close_writer(data, writer);
458
0
    free(writer);
459
0
    writer = data->req.writer_stack;
460
0
  }
461
462
0
  for(i = 0; i < data->state.tempcount; i++) {
463
0
    Curl_dyn_free(&data->state.tempwrite[i].b);
464
0
  }
465
0
  data->state.tempcount = 0;
466
467
0
}
468
469
/* Real client writer: no downstream. */
470
static CURLcode client_cew_init(struct Curl_easy *data,
471
                                struct contenc_writer *writer)
472
0
{
473
0
  (void) data;
474
0
  (void)writer;
475
0
  return CURLE_OK;
476
0
}
477
478
static CURLcode client_cew_write(struct Curl_easy *data,
479
                                 struct contenc_writer *writer,
480
                                 const char *buf, size_t nbytes)
481
0
{
482
0
  (void)writer;
483
0
  if(!nbytes || data->req.ignorebody)
484
0
    return CURLE_OK;
485
0
  return chop_write(data, CLIENTWRITE_BODY, FALSE, (char *)buf, nbytes);
486
0
}
487
488
static void client_cew_close(struct Curl_easy *data,
489
                             struct contenc_writer *writer)
490
0
{
491
0
  (void) data;
492
0
  (void) writer;
493
0
}
494
495
static const struct content_encoding client_cew = {
496
  NULL,
497
  NULL,
498
  client_cew_init,
499
  client_cew_write,
500
  client_cew_close,
501
  sizeof(struct contenc_writer)
502
};
503
504
/* Create an unencoding writer stage using the given handler. */
505
CURLcode Curl_client_create_writer(struct contenc_writer **pwriter,
506
                                   struct Curl_easy *data,
507
                                   const struct content_encoding *ce_handler,
508
                                   int order)
509
0
{
510
0
  struct contenc_writer *writer;
511
0
  CURLcode result = CURLE_OUT_OF_MEMORY;
512
513
0
  DEBUGASSERT(ce_handler->writersize >= sizeof(struct contenc_writer));
514
0
  writer = (struct contenc_writer *) calloc(1, ce_handler->writersize);
515
0
  if(!writer)
516
0
    goto out;
517
518
0
  writer->handler = ce_handler;
519
0
  writer->order = order;
520
0
  result = ce_handler->init_writer(data, writer);
521
522
0
out:
523
0
  *pwriter = result? NULL : writer;
524
0
  if(result)
525
0
    free(writer);
526
0
  return result;
527
0
}
528
529
void Curl_client_free_writer(struct Curl_easy *data,
530
                             struct contenc_writer *writer)
531
0
{
532
0
  if(writer) {
533
0
    writer->handler->close_writer(data, writer);
534
0
    free(writer);
535
0
  }
536
0
}
537
538
/* allow no more than 5 "chained" compression steps */
539
0
#define MAX_ENCODE_STACK 5
540
541
542
static CURLcode init_writer_stack(struct Curl_easy *data)
543
0
{
544
0
  DEBUGASSERT(!data->req.writer_stack);
545
0
  return Curl_client_create_writer(&data->req.writer_stack,
546
0
                                   data, &client_cew, 0);
547
0
}
548
549
CURLcode Curl_client_add_writer(struct Curl_easy *data,
550
                                struct contenc_writer *writer)
551
0
{
552
0
  CURLcode result;
553
554
0
  if(!data->req.writer_stack) {
555
0
    result = init_writer_stack(data);
556
0
    if(result)
557
0
      return result;
558
0
  }
559
560
0
  if(data->req.writer_stack_depth++ >= MAX_ENCODE_STACK) {
561
0
    failf(data, "Reject response due to more than %u content encodings",
562
0
          MAX_ENCODE_STACK);
563
0
    return CURLE_BAD_CONTENT_ENCODING;
564
0
  }
565
566
  /* Stack the unencoding stage. */
567
0
  if(writer->order >= data->req.writer_stack->order) {
568
0
    writer->downstream = data->req.writer_stack;
569
0
    data->req.writer_stack = writer;
570
0
  }
571
0
  else {
572
0
    struct contenc_writer *w = data->req.writer_stack;
573
0
    while(w->downstream && writer->order < w->downstream->order)
574
0
      w = w->downstream;
575
0
    writer->downstream = w->downstream;
576
0
    w->downstream = writer;
577
0
  }
578
0
  return CURLE_OK;
579
0
}
580
581
582
/*
583
 * Internal read-from-socket function. This is meant to deal with plain
584
 * sockets, SSL sockets and kerberos sockets.
585
 *
586
 * Returns a regular CURLcode value.
587
 */
588
CURLcode Curl_read(struct Curl_easy *data,   /* transfer */
589
                   curl_socket_t sockfd,     /* read from this socket */
590
                   char *buf,                /* store read data here */
591
                   size_t sizerequested,     /* max amount to read */
592
                   ssize_t *n)               /* amount bytes read */
593
0
{
594
0
  CURLcode result = CURLE_RECV_ERROR;
595
0
  ssize_t nread = 0;
596
0
  size_t bytesfromsocket = 0;
597
0
  char *buffertofill = NULL;
598
0
  struct connectdata *conn = data->conn;
599
600
  /* Set 'num' to 0 or 1, depending on which socket that has been sent here.
601
     If it is the second socket, we set num to 1. Otherwise to 0. This lets
602
     us use the correct ssl handle. */
603
0
  int num = (sockfd == conn->sock[SECONDARYSOCKET]);
604
605
0
  *n = 0; /* reset amount to zero */
606
607
0
  bytesfromsocket = CURLMIN(sizerequested, (size_t)data->set.buffer_size);
608
0
  buffertofill = buf;
609
610
0
  nread = conn->recv[num](data, num, buffertofill, bytesfromsocket, &result);
611
0
  if(nread < 0)
612
0
    goto out;
613
614
0
  *n += nread;
615
0
  result = CURLE_OK;
616
0
out:
617
0
  return result;
618
0
}