Coverage Report

Created: 2026-09-01 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl_fuzzer/legacy_fuzzer.cc
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Max Dymond, <cmeister2@gmail.com>, 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
 ***************************************************************************/
22
23
#include <stdlib.h>
24
#include <signal.h>
25
#include <string.h>
26
#include <unistd.h>
27
#include <curl/curl.h>
28
#include "curl_fuzzer.h"
29
#include "legacy_fuzzer.h"
30
#include "legacy_protocol_allowlist.h"
31
#include "legacy_tlv_mutator.h"
32
33
/**
34
 * Run one legacy TLV input independently of the exported libFuzzer symbol.
35
 * Keeping the implementation behind a normal C++ function lets every
36
 * protocol binary expose its own same-named source entrypoint, which is how
37
 * Fuzz Introspector attributes a binary's runtime coverage to its call tree.
38
 */
39
int LegacyFuzzerTestOneInput(const uint8_t *data, size_t size)
40
14.5k
{
41
14.5k
  int rc = 0;
42
14.5k
  int tlv_rc;
43
14.5k
  FUZZ_DATA fuzz;
44
14.5k
  TLV tlv;
45
46
  /* Ignore SIGPIPE errors. We'll handle the errors ourselves. */
47
14.5k
  signal(SIGPIPE, SIG_IGN);
48
49
  /* Have to set all fields to zero before getting to the terminate function */
50
14.5k
  memset(&fuzz, 0, sizeof(FUZZ_DATA));
51
52
14.5k
  if(size < sizeof(TLV_RAW)) {
53
    /* Not enough data for a single TLV - don't continue */
54
4
    goto EXIT_LABEL;
55
4
  }
56
57
  /* Try to initialize the fuzz data */
58
14.5k
  FTRY(fuzz_initialize_fuzz_data(&fuzz, data, size));
59
60
14.5k
  for(tlv_rc = fuzz_get_first_tlv(&fuzz, &tlv);
61
74.1k
      tlv_rc == 0;
62
61.3k
      tlv_rc = fuzz_get_next_tlv(&fuzz, &tlv)) {
63
64
    /* Have the TLV in hand. Parse the TLV. */
65
61.3k
    rc = fuzz_parse_tlv(&fuzz, &tlv);
66
67
61.3k
    if(rc != 0) {
68
      /* Failed to parse the TLV. Can't continue. */
69
1.77k
      goto EXIT_LABEL;
70
1.77k
    }
71
61.3k
  }
72
73
12.7k
  if(tlv_rc != TLV_RC_NO_MORE_TLVS) {
74
    /* A TLV call failed. Can't continue. */
75
162
    goto EXIT_LABEL;
76
162
  }
77
78
  /* Set up the standard easy options. */
79
12.5k
  FTRY(fuzz_set_easy_options(&fuzz));
80
81
  /**
82
   * Add in more curl options that have been accumulated over possibly
83
   * multiple TLVs.
84
   */
85
12.5k
  if(fuzz.header_list != NULL) {
86
448
    curl_easy_setopt(fuzz.easy, CURLOPT_HTTPHEADER, fuzz.header_list);
87
448
  }
88
89
12.5k
  if(fuzz.mail_recipients_list != NULL) {
90
116
    curl_easy_setopt(fuzz.easy, CURLOPT_MAIL_RCPT, fuzz.mail_recipients_list);
91
116
  }
92
93
12.5k
  if(fuzz.mime != NULL) {
94
259
    curl_easy_setopt(fuzz.easy, CURLOPT_MIMEPOST, fuzz.mime);
95
259
  }
96
97
12.5k
  if (fuzz.httppost != NULL) {
98
58
    curl_easy_setopt(fuzz.easy, CURLOPT_HTTPPOST, fuzz.httppost);
99
58
  }
100
101
  /* Run the transfer. */
102
12.5k
  fuzz_handle_transfer(&fuzz);
103
104
14.5k
EXIT_LABEL:
105
106
14.5k
  fuzz_terminate_fuzz_data(&fuzz);
107
108
  /* This function must always return 0. Non-zero codes are reserved. */
109
14.5k
  return 0;
110
12.5k
}
111
112
/**
113
 * Utility function to convert 4 bytes to a u32 predictably.
114
 */
115
uint32_t to_u32(const uint8_t b[4])
116
68.6k
{
117
68.6k
  uint32_t u;
118
  /* Promote into the unsigned result type before shifting. uint8_t otherwise
119
     promotes to signed int, and values with the high bit set make the
120
     left-shift undefined before curl ever observes the fuzzed boundary. */
121
68.6k
  u = (static_cast<uint32_t>(b[0]) << 24) |
122
68.6k
      (static_cast<uint32_t>(b[1]) << 16) |
123
68.6k
      (static_cast<uint32_t>(b[2]) << 8) |
124
68.6k
      static_cast<uint32_t>(b[3]);
125
68.6k
  return u;
126
68.6k
}
127
128
/**
129
 * Utility function to convert 2 bytes to a u16 predictably.
130
 */
131
uint16_t to_u16(const uint8_t b[2])
132
63.3k
{
133
63.3k
  uint16_t u;
134
63.3k
  u = (b[0] << 8) + b[1];
135
63.3k
  return u;
136
63.3k
}
137
138
/**
139
 * Initialize the local fuzz data structure.
140
 */
141
int fuzz_initialize_fuzz_data(FUZZ_DATA *fuzz,
142
                              const uint8_t *data,
143
                              size_t data_len)
144
14.5k
{
145
14.5k
  int rc = 0;
146
14.5k
  int ii;
147
148
  /* Initialize the fuzz data. */
149
14.5k
  memset(fuzz, 0, sizeof(FUZZ_DATA));
150
151
  /* Create an easy handle. This will have all of the settings configured on
152
     it. */
153
14.5k
  fuzz->easy = curl_easy_init();
154
14.5k
  FCHECK(fuzz->easy != NULL);
155
156
  /* Set up the state parser */
157
14.5k
  fuzz->state.data = data;
158
14.5k
  fuzz->state.data_len = data_len;
159
160
  /* Set up the state of the server sockets. */
161
43.5k
  for(ii = 0; ii < FUZZ_NUM_CONNECTIONS; ii++) {
162
29.0k
    fuzz->sockman[ii].index = ii;
163
29.0k
    fuzz->sockman[ii].fd_state = FUZZ_SOCK_CLOSED;
164
29.0k
  }
165
166
  /* Check for verbose mode. */
167
14.5k
  fuzz->verbose = (getenv("FUZZ_VERBOSE") != NULL);
168
169
14.5k
  FCHECK(setenv("CURL_HSTS_HTTP", "1", 0) == 0);
170
14.5k
  FCHECK(setenv("CURL_ALTSVC_HTTP", "1", 0) == 0);
171
172
14.5k
EXIT_LABEL:
173
174
14.5k
  return rc;
175
14.5k
}
176
177
/**
178
 * Reapply resolver-sensitive string options with their canonical loopback
179
 * values before starting a transfer.
180
 *
181
 * Custom mutation and crossover already finalize generated buffers, but an
182
 * initial corpus entry or standalone reproducer is executed without passing
183
 * through either callback. The option tracker identifies only values that the
184
 * TLV parser successfully applied, so this does not enable routing options that
185
 * were absent from the input. DNS_INTERFACE is deliberately excluded: with
186
 * c-ares it is a device name passed to ares_set_local_dev(), not a hostname.
187
 */
188
static int fuzz_finalize_routing_options(FUZZ_DATA *fuzz)
189
12.5k
{
190
12.5k
  int rc = 0;
191
192
12.5k
#define FFINALIZE_ROUTING_OPTION(TLVTYPE, CURLOPTNAME)                         \
193
50.3k
  if(fuzz->options[(CURLOPTNAME) % 1000]) {                                   \
194
1.32k
    const char *canonical =                                                   \
195
1.32k
      legacy_tlv_mutator::CanonicalRoutingValue((TLVTYPE));                   \
196
1.32k
    FCHECK(canonical != NULL);                                                 \
197
1.32k
    FTRY(curl_easy_setopt(fuzz->easy, (CURLOPTNAME), canonical));              \
198
1.32k
  }
199
200
12.5k
  FFINALIZE_ROUTING_OPTION(TLV_TYPE_PROXY, CURLOPT_PROXY);
201
12.5k
  FFINALIZE_ROUTING_OPTION(TLV_TYPE_FTPPORT, CURLOPT_FTPPORT);
202
12.5k
  FFINALIZE_ROUTING_OPTION(TLV_TYPE_INTERFACE, CURLOPT_INTERFACE);
203
12.5k
  FFINALIZE_ROUTING_OPTION(TLV_TYPE_PRE_PROXY, CURLOPT_PRE_PROXY);
204
205
12.5k
#undef FFINALIZE_ROUTING_OPTION
206
207
12.5k
EXIT_LABEL:
208
12.5k
  return rc;
209
12.5k
}
210
211
/**
212
 * Set standard options on the curl easy.
213
 */
214
int fuzz_set_easy_options(FUZZ_DATA *fuzz)
215
12.5k
{
216
12.5k
  int rc = 0;
217
218
  /* Existing seeds and direct reproducers bypass the custom mutator. Close
219
     that path before any transfer can resolve a corpus-provided endpoint. */
220
12.5k
  FTRY(fuzz_finalize_routing_options(fuzz));
221
222
  /* Set some standard options on the CURL easy handle. We need to override the
223
     socket function so that we create our own sockets to present to CURL. */
224
12.5k
  FTRY(curl_easy_setopt(fuzz->easy,
225
12.5k
                        CURLOPT_OPENSOCKETFUNCTION,
226
12.5k
                        fuzz_open_socket));
227
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_OPENSOCKETDATA, fuzz));
228
229
  /* In case something tries to set a socket option, intercept this. */
230
12.5k
  FTRY(curl_easy_setopt(fuzz->easy,
231
12.5k
                        CURLOPT_SOCKOPTFUNCTION,
232
12.5k
                        fuzz_sockopt_callback));
233
234
  /* Set the standard read function callback. */
235
12.5k
  FTRY(curl_easy_setopt(fuzz->easy,
236
12.5k
                        CURLOPT_READFUNCTION,
237
12.5k
                        fuzz_read_callback));
238
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_READDATA, fuzz));
239
240
  /* Set the standard write function callback. */
241
12.5k
  FTRY(curl_easy_setopt(fuzz->easy,
242
12.5k
                        CURLOPT_WRITEFUNCTION,
243
12.5k
                        fuzz_write_callback));
244
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_WRITEDATA, fuzz));
245
246
  /* Set the writable cookie jar path so cookies are tested. */
247
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_COOKIEJAR, FUZZ_COOKIE_JAR_PATH));
248
249
  /* Set the RO cookie file path so cookies are tested. */
250
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_COOKIEFILE, FUZZ_RO_COOKIE_FILE_PATH));
251
252
  /* Set altsvc header cache filepath so that it can be fuzzed. */
253
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_ALTSVC, FUZZ_ALT_SVC_HEADER_CACHE_PATH));
254
255
  /* Set the hsts header cache filepath so that it can be fuzzed. */
256
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_HSTS, FUZZ_HSTS_HEADER_CACHE_PATH));
257
258
  /* Set the Certificate Revocation List file path so it can be fuzzed */
259
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_CRLFILE, FUZZ_CRL_FILE_PATH));
260
261
  /* Loading the host trust store for every WSS mutation dominates the
262
     WebSocket target even when the in-process mock immediately ends the TLS
263
     handshake. Keep this exception local to that target: the other legacy
264
     fuzzers should retain libcurl's verification default so their ordinary
265
     mutations continue to cover certificate setup. An explicit
266
     SSL_VERIFYPEER TLV still restores verification in the WebSocket target. */
267
#ifdef FUZZ_PROTOCOLS_WS
268
  if(!fuzz->options[CURLOPT_SSL_VERIFYPEER % 1000]) {
269
    FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_SSL_VERIFYPEER, 0L));
270
  }
271
#endif
272
273
  /* Set the .netrc file path so it can be fuzzed */
274
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_NETRC_FILE, FUZZ_NETRC_FILE_PATH));
275
276
  /* Time out requests quickly. */
277
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_TIMEOUT_MS, 200L));
278
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_SERVER_RESPONSE_TIMEOUT, 1L));
279
280
  /* Can enable verbose mode by having the environment variable FUZZ_VERBOSE. */
281
12.5k
  if(fuzz->verbose) {
282
0
    FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_VERBOSE, 1L));
283
0
  }
284
285
  /* Force resolution of all addresses to a specific IP address. */
286
12.5k
  fuzz->connect_to_list = curl_slist_append(NULL, "::127.0.1.127:");
287
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_CONNECT_TO, fuzz->connect_to_list));
288
289
  /* Limit the protocols in use by this fuzzer. */
290
12.5k
  FTRY(fuzz_set_allowed_protocols(fuzz));
291
292
12.5k
EXIT_LABEL:
293
294
12.5k
  return rc;
295
12.5k
}
296
297
/**
298
 * Terminate the fuzz data structure, including freeing any allocated memory.
299
 */
300
void fuzz_terminate_fuzz_data(FUZZ_DATA *fuzz)
301
14.5k
{
302
14.5k
  int ii;
303
304
14.5k
  fuzz_free((void **)&fuzz->postfields);
305
306
43.6k
  for(ii = 0; ii < FUZZ_NUM_CONNECTIONS; ii++) {
307
29.0k
    if(fuzz->sockman[ii].fd_state != FUZZ_SOCK_CLOSED) {
308
6.91k
      close(fuzz->sockman[ii].fd);
309
6.91k
      fuzz->sockman[ii].fd_state = FUZZ_SOCK_CLOSED;
310
6.91k
    }
311
29.0k
  }
312
313
14.5k
  if(fuzz->connect_to_list != NULL) {
314
12.5k
    curl_slist_free_all(fuzz->connect_to_list);
315
12.5k
    fuzz->connect_to_list = NULL;
316
12.5k
  }
317
318
14.5k
  if(fuzz->header_list != NULL) {
319
457
    curl_slist_free_all(fuzz->header_list);
320
457
    fuzz->header_list = NULL;
321
457
  }
322
323
14.5k
  if(fuzz->mail_recipients_list != NULL) {
324
121
    curl_slist_free_all(fuzz->mail_recipients_list);
325
121
    fuzz->mail_recipients_list = NULL;
326
121
  }
327
328
14.5k
  if(fuzz->mime != NULL) {
329
272
    curl_mime_free(fuzz->mime);
330
272
    fuzz->mime = NULL;
331
272
  }
332
333
14.5k
  if(fuzz->easy != NULL) {
334
14.5k
    curl_easy_cleanup(fuzz->easy);
335
14.5k
    fuzz->easy = NULL;
336
14.5k
  }
337
338
  /* When you have passed the struct curl_httppost pointer to curl_easy_setopt
339
   * (using the CURLOPT_HTTPPOST option), you must not free the list until after
340
   *  you have called curl_easy_cleanup for the curl handle.
341
   *  https://curl.se/libcurl/c/curl_formadd.html */
342
14.5k
  if (fuzz->httppost != NULL) {
343
61
    curl_formfree(fuzz->httppost);
344
61
    fuzz->httppost = NULL;
345
61
  }
346
347
  // free after httppost and last_post_part.
348
14.5k
  if (fuzz->post_body != NULL) {
349
61
    fuzz_free((void **)&fuzz->post_body);
350
61
  }
351
14.5k
}
352
353
/**
354
 * If a pointer has been allocated, free that pointer.
355
 */
356
void fuzz_free(void **ptr)
357
76.2k
{
358
76.2k
  if(*ptr != NULL) {
359
44.4k
    free(*ptr);
360
44.4k
    *ptr = NULL;
361
44.4k
  }
362
76.2k
}
363
364
/**
365
 * Function for handling the fuzz transfer, including sending responses to
366
 * requests.
367
 */
368
int fuzz_handle_transfer(FUZZ_DATA *fuzz)
369
12.5k
{
370
12.5k
  int rc = 0;
371
12.5k
  CURLM *multi_handle;
372
12.5k
  int still_running; /* keep number of running handles */
373
12.5k
  CURLMsg *msg; /* for picking up messages with the transfer status */
374
12.5k
  int msgs_left; /* how many messages are left */
375
12.5k
  int double_timeout = 0;
376
12.5k
  fd_set fdread;
377
12.5k
  fd_set fdwrite;
378
12.5k
  fd_set fdexcep;
379
12.5k
  struct timeval timeout;
380
12.5k
  int select_rc;
381
12.5k
  CURLMcode mc;
382
12.5k
  int maxfd = -1;
383
12.5k
  long curl_timeo = -1;
384
12.5k
  int ii;
385
12.5k
  FUZZ_SOCKET_MANAGER *sman[FUZZ_NUM_CONNECTIONS];
386
387
37.7k
  for(ii = 0; ii < FUZZ_NUM_CONNECTIONS; ii++) {
388
25.1k
    sman[ii] = &fuzz->sockman[ii];
389
390
    /* Set up the starting index for responses. */
391
25.1k
    sman[ii]->response_index = 1;
392
25.1k
  }
393
394
  /* init a multi stack */
395
12.5k
  multi_handle = curl_multi_init();
396
397
  /* add the individual transfers */
398
12.5k
  curl_multi_add_handle(multi_handle, fuzz->easy);
399
400
  /* Do an initial process. This might end the transfer immediately. */
401
12.5k
  curl_multi_perform(multi_handle, &still_running);
402
12.5k
  FV_PRINTF(fuzz,
403
12.5k
            "FUZZ: Initial perform; still running? %d \n",
404
12.5k
            still_running);
405
406
16.2k
  while(still_running) {
407
    /* Reset the sets of file descriptors. */
408
4.10k
    FD_ZERO(&fdread);
409
4.10k
    FD_ZERO(&fdwrite);
410
4.10k
    FD_ZERO(&fdexcep);
411
412
    /* Set a timeout of 10ms. This is lower than recommended by the multi guide
413
       but we're not going to any remote servers, so everything should complete
414
       very quickly. */
415
4.10k
    timeout.tv_sec = 0;
416
4.10k
    timeout.tv_usec = 10000;
417
418
    /* get file descriptors from the transfers */
419
4.10k
    mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
420
4.10k
    if(mc != CURLM_OK) {
421
0
      fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc);
422
0
      rc = -1;
423
0
      break;
424
0
    }
425
426
12.3k
    for(ii = 0; ii < FUZZ_NUM_CONNECTIONS; ii++) {
427
      /* Add the socket FD into the readable set if connected. */
428
8.21k
      if(sman[ii]->fd_state == FUZZ_SOCK_OPEN) {
429
2.40k
        FD_SET(sman[ii]->fd, &fdread);
430
431
        /* Work out the maximum FD between the cURL file descriptors and the
432
           server FD. */
433
2.40k
        maxfd = FUZZ_MAX(sman[ii]->fd, maxfd);
434
2.40k
      }
435
8.21k
    }
436
437
    /* Work out what file descriptors need work. */
438
4.10k
    rc = fuzz_select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
439
440
4.10k
    if(rc == -1) {
441
      /* Had an issue while selecting a file descriptor. Let's just exit. */
442
0
      FV_PRINTF(fuzz, "FUZZ: select failed, exiting \n");
443
0
      break;
444
0
    }
445
446
    /* Check to see if a server file descriptor is readable. If it is,
447
       then send the next response from the fuzzing data. */
448
4.10k
    int server_data_sent = 0;
449
12.3k
    for(ii = 0; ii < FUZZ_NUM_CONNECTIONS; ii++) {
450
8.21k
      if(sman[ii]->fd_state == FUZZ_SOCK_OPEN &&
451
8.21k
         FD_ISSET(sman[ii]->fd, &fdread)) {
452
2.36k
        rc = fuzz_send_next_response(fuzz, sman[ii]);
453
2.36k
        if(rc != 0) {
454
          /* Failed to send a response. Break out here. */
455
0
          break;
456
0
        }
457
2.36k
        server_data_sent = 1;
458
2.36k
      }
459
8.21k
    }
460
461
    /* Stall detection: exit after two consecutive iterations where no new
462
       data was provided to curl. This handles both select() timeouts and
463
       cases where curl registers a writable fd but cannot make progress
464
       (e.g. HTTP/2 egress stuck with no real peer to drain to). */
465
4.10k
    if(!server_data_sent) {
466
1.74k
      FV_PRINTF(fuzz, "FUZZ: No data sent; stall count %d \n", double_timeout);
467
1.74k
      if(double_timeout == 1) {
468
481
        break;
469
481
      }
470
1.26k
      double_timeout = 1;
471
1.26k
    }
472
2.36k
    else {
473
2.36k
      double_timeout = 0;
474
2.36k
    }
475
476
3.62k
    curl_multi_perform(multi_handle, &still_running);
477
3.62k
  }
478
479
  /* Remove the easy handle from the multi stack. */
480
12.5k
  curl_multi_remove_handle(multi_handle, fuzz->easy);
481
482
  /* Clean up the multi handle - the top level function will handle the easy
483
     handle. */
484
12.5k
  curl_multi_cleanup(multi_handle);
485
486
12.5k
  return rc;
487
12.5k
}
488
489
/**
490
 * Sends the next fuzzing response to the server file descriptor.
491
 */
492
int fuzz_send_next_response(FUZZ_DATA *fuzz, FUZZ_SOCKET_MANAGER *sman)
493
2.36k
{
494
2.36k
  int rc = 0;
495
2.36k
  ssize_t ret_in;
496
2.36k
  ssize_t ret_out;
497
2.36k
  char buffer[8192];
498
2.36k
  const uint8_t *data;
499
2.36k
  size_t data_len;
500
501
  /* Need to read all data sent by the client so the file descriptor becomes
502
     unreadable. Because the file descriptor is non-blocking we won't just
503
     hang here. */
504
4.83k
  do {
505
4.83k
    ret_in = read(sman->fd, buffer, sizeof(buffer));
506
4.83k
    if(fuzz->verbose && ret_in > 0) {
507
0
      printf("FUZZ[%d]: Received %zu bytes \n==>\n", sman->index, ret_in);
508
0
      fwrite(buffer, ret_in, 1, stdout);
509
0
      printf("\n<==\n");
510
0
    }
511
4.83k
  } while (ret_in > 0);
512
513
  /* Now send a response to the request that the client just made. */
514
2.36k
  FV_PRINTF(fuzz,
515
2.36k
            "FUZZ[%d]: Sending next response: %d \n",
516
2.36k
            sman->index,
517
2.36k
            sman->response_index);
518
2.36k
  data = sman->responses[sman->response_index].data;
519
2.36k
  data_len = sman->responses[sman->response_index].data_len;
520
521
2.36k
  if(data != NULL) {
522
2.36k
    if(write(sman->fd, data, data_len) != (ssize_t)data_len) {
523
      /* Failed to write the data back to the client. Prevent any further
524
         testing. */
525
0
      rc = -1;
526
0
    }
527
2.36k
  }
528
529
  /* Work out if there are any more responses. If not, then shut down the
530
     server. */
531
2.36k
  sman->response_index++;
532
533
2.36k
  if(sman->response_index >= TLV_MAX_NUM_RESPONSES ||
534
2.36k
     sman->responses[sman->response_index].data == NULL) {
535
2.31k
    FV_PRINTF(fuzz,
536
2.31k
              "FUZZ[%d]: Shutting down server socket: %d \n",
537
2.31k
              sman->index,
538
2.31k
              sman->fd);
539
2.31k
    shutdown(sman->fd, SHUT_WR);
540
2.31k
    sman->fd_state = FUZZ_SOCK_SHUTDOWN;
541
2.31k
  }
542
543
2.36k
  return rc;
544
2.36k
}
545
546
/**
547
 * Wrapper for select() so profiling can track it.
548
 */
549
int fuzz_select(int nfds,
550
                fd_set *readfds,
551
                fd_set *writefds,
552
                fd_set *exceptfds,
553
4.10k
                struct timeval *timeout) {
554
4.10k
  return select(nfds, readfds, writefds, exceptfds, timeout);
555
4.10k
}
556
557
/**
558
 * Set allowed protocols based on the compile options.
559
 *
560
 * Note that it can only use ONE of the FUZZ_PROTOCOLS_* defines.
561
 */
562
int fuzz_set_allowed_protocols(FUZZ_DATA *fuzz)
563
12.5k
{
564
12.5k
  int rc = 0;
565
12.5k
  const char *allowed_protocols = "";
566
567
#ifdef FUZZ_PROTOCOLS_ALL
568
  /* CURLOPT_PROTOCOLS_STR rejects the complete value if even one requested
569
     protocol was compiled out. Derive the generic target's stable safety
570
     policy from this libcurl build so optional RTMP and SSH backends cannot
571
     prevent every transfer from starting. */
572
  allowed_protocols = legacy_protocol_allowlist::ForCurrentCurl().c_str();
573
#endif
574
#ifdef FUZZ_PROTOCOLS_DICT
575
  allowed_protocols = "dict";
576
#endif
577
#ifdef FUZZ_PROTOCOLS_FILE
578
  allowed_protocols = "file";
579
#endif
580
#ifdef FUZZ_PROTOCOLS_FTP
581
  allowed_protocols = "ftp,ftps";
582
#endif
583
#ifdef FUZZ_PROTOCOLS_GOPHER
584
  allowed_protocols = "gopher,gophers";
585
#endif
586
#ifdef FUZZ_PROTOCOLS_HTTP
587
  allowed_protocols = "http";
588
#endif
589
#ifdef FUZZ_PROTOCOLS_HTTPS
590
  allowed_protocols = "https";
591
#endif
592
#ifdef FUZZ_PROTOCOLS_IMAP
593
  allowed_protocols = "imap,imaps";
594
#endif
595
12.5k
#ifdef FUZZ_PROTOCOLS_LDAP
596
12.5k
  allowed_protocols = "ldap,ldaps";
597
12.5k
#endif
598
#ifdef FUZZ_PROTOCOLS_MQTT
599
  allowed_protocols = "mqtt";
600
#endif
601
#ifdef FUZZ_PROTOCOLS_POP3
602
  allowed_protocols = "pop3,pop3s";
603
#endif
604
#ifdef FUZZ_PROTOCOLS_RTMP
605
  allowed_protocols = "rtmp,rtmpe,rtmps,rtmpt,rtmpte,rtmpts";
606
#endif
607
#ifdef FUZZ_PROTOCOLS_RTSP
608
  allowed_protocols = "rtsp";
609
#endif
610
#ifdef FUZZ_PROTOCOLS_SCP
611
  allowed_protocols = "scp";
612
#endif
613
#ifdef FUZZ_PROTOCOLS_SFTP
614
  allowed_protocols = "sftp";
615
#endif
616
#ifdef FUZZ_PROTOCOLS_SMB
617
  allowed_protocols = "smb,smbs";
618
#endif
619
#ifdef FUZZ_PROTOCOLS_SMTP
620
  allowed_protocols = "smtp,smtps";
621
#endif
622
#ifdef FUZZ_PROTOCOLS_TFTP
623
  allowed_protocols = "tftp";
624
#endif
625
#ifdef FUZZ_PROTOCOLS_WS
626
  // http is required by websockets
627
  allowed_protocols = "http,ws,wss";
628
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_CONNECT_ONLY, 2L));
629
#endif
630
631
12.5k
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_PROTOCOLS_STR, allowed_protocols));
632
633
12.5k
EXIT_LABEL:
634
635
12.5k
  return rc;
636
12.5k
}