Coverage Report

Created: 2025-10-27 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mhd2/src/mhd2/response_from.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
2
/*
3
  This file is part of GNU libmicrohttpd.
4
  Copyright (C) 2021-2024 Evgeny Grin (Karlson2k)
5
6
  GNU libmicrohttpd is free software; you can redistribute it and/or
7
  modify it under the terms of the GNU Lesser General Public
8
  License as published by the Free Software Foundation; either
9
  version 2.1 of the License, or (at your option) any later version.
10
11
  GNU libmicrohttpd is distributed in the hope that it will be useful,
12
  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
  Lesser General Public License for more details.
15
16
  Alternatively, you can redistribute GNU libmicrohttpd and/or
17
  modify it under the terms of the GNU General Public License as
18
  published by the Free Software Foundation; either version 2 of
19
  the License, or (at your option) any later version, together
20
  with the eCos exception, as follows:
21
22
    As a special exception, if other files instantiate templates or
23
    use macros or inline functions from this file, or you compile this
24
    file and link it with other works to produce a work based on this
25
    file, this file does not by itself cause the resulting work to be
26
    covered by the GNU General Public License. However the source code
27
    for this file must still be made available in accordance with
28
    section (3) of the GNU General Public License v2.
29
30
    This exception does not invalidate any other reasons why a work
31
    based on this file might be covered by the GNU General Public
32
    License.
33
34
  You should have received copies of the GNU Lesser General Public
35
  License and the GNU General Public License along with this library;
36
  if not, see <https://www.gnu.org/licenses/>.
37
*/
38
39
/**
40
 * @file src/mhd2/response_from.c
41
 * @brief  The definitions of MHD_response_from_X() functions and related
42
 *         internal functions
43
 * @author Karlson2k (Evgeny Grin)
44
 */
45
46
#include "mhd_sys_options.h"
47
48
#include "response_from.h"
49
50
#include <string.h>
51
52
#include "sys_bool_type.h"
53
#include "sys_base_types.h"
54
55
#include "compat_calloc.h"
56
#include "sys_malloc.h"
57
#include "sys_file_fd.h"
58
59
#include "mhd_public_api.h"
60
61
#include "mhd_locks.h"
62
#include "mhd_response.h"
63
#include "response_options.h"
64
65
#include "mhd_assert.h"
66
67
#include "mhd_limits.h"
68
69
static struct MHD_Response *
70
response_create_basic (enum MHD_HTTP_StatusCode sc,
71
                       uint_fast64_t cntn_size,
72
                       MHD_FreeCallback free_cb,
73
                       void *free_cb_cls)
74
2.10k
{
75
2.10k
  struct MHD_Response *restrict r;
76
2.10k
  struct ResponseOptions *restrict s;
77
78
2.10k
  if ((100 > sc) || (999 < sc))
79
0
    return NULL;
80
81
2.10k
  r = (struct MHD_Response *)
82
2.10k
      mhd_calloc (1,
83
2.10k
                  sizeof(struct MHD_Response));
84
2.10k
  if (NULL != r)
85
2.10k
  {
86
2.10k
    s = (struct ResponseOptions *)
87
2.10k
        mhd_calloc (1,
88
2.10k
                    sizeof(struct ResponseOptions));
89
2.10k
    if (NULL != s)
90
2.10k
    {
91
#ifndef HAVE_NULL_PTR_ALL_ZEROS
92
      mhd_DLINKEDL_INIT_LIST (r, headers);
93
#ifdef MHD_SUPPORT_AUTH_DIGEST
94
      mhd_DLINKEDL_INIT_LIST (r, auth_d_hdrs);
95
#endif
96
      r->free.cb = NULL;
97
      r->free.cls = NULL;
98
      r->special_resp.spec_hdr = NULL;
99
100
      s->termination_callback.v_ended_cb = NULL;
101
      s->termination_callback.v_ended_cb_cls = NULL;
102
#endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
103
104
2.10k
      r->sc = sc;
105
2.10k
      r->cntn_size = cntn_size;
106
2.10k
      r->free.cb = free_cb;
107
2.10k
      r->free.cls = free_cb_cls;
108
2.10k
      r->settings = s;
109
110
2.10k
      return r; /* Success exit point */
111
2.10k
    }
112
0
    free (r);
113
0
  }
114
0
  return NULL; /* Failure exit point */
115
2.10k
}
116
117
118
MHD_INTERNAL
119
MHD_FN_PAR_NONNULL_ (1) void
120
mhd_response_deinit_content_data (struct MHD_Response *restrict r)
121
2.10k
{
122
2.10k
  mhd_assert (mhd_RESPONSE_CONTENT_DATA_INVALID != r->cntn_dtype);
123
2.10k
  if (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype)
124
0
    free (r->cntn.iovec.iov);
125
2.10k
  else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype)
126
1.16k
    close (r->cntn.file.fd);
127
  /* For #mhd_RESPONSE_CONTENT_DATA_BUFFER clean-up performed by callback
128
     for both modes: internal copy and external cleanup */
129
2.10k
  if (NULL != r->free.cb)
130
0
    r->free.cb (r->free.cls);
131
2.10k
}
132
133
134
MHD_EXTERN_ struct MHD_Response *
135
MHD_response_from_callback (enum MHD_HTTP_StatusCode sc,
136
                            uint_fast64_t size,
137
                            MHD_DynamicContentCreator dyn_cont,
138
                            void *dyn_cont_cls,
139
                            MHD_FreeCallback dyn_cont_fc)
140
0
{
141
0
  struct MHD_Response *restrict res;
142
0
  res = response_create_basic (sc, size, dyn_cont_fc, dyn_cont_cls);
143
0
  if (NULL != res)
144
0
  {
145
0
    res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_CALLBACK;
146
0
    res->cntn.dyn.cb = dyn_cont;
147
0
    res->cntn.dyn.cls = dyn_cont_cls;
148
0
  }
149
0
  return res;
150
0
}
151
152
153
static const unsigned char empty_buf[1] = { 0 };
154
155
MHD_EXTERN_
156
MHD_FN_PAR_IN_SIZE_ (3,2) struct MHD_Response *
157
MHD_response_from_buffer (
158
  enum MHD_HTTP_StatusCode sc,
159
  size_t buffer_size,
160
  const char *buffer,
161
  MHD_FreeCallback free_cb,
162
  void *free_cb_cls)
163
941
{
164
941
  struct MHD_Response *restrict res;
165
166
941
#if SIZEOF_SIZE_T >= 8
167
941
  if (MHD_SIZE_UNKNOWN == buffer_size)
168
0
    return NULL;
169
941
#endif /* SIZEOF_SIZE_T >= 8 */
170
171
941
  res = response_create_basic (sc, buffer_size, free_cb, free_cb_cls);
172
941
  if (NULL != res)
173
941
  {
174
941
    res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
175
941
    res->cntn.buf = (0 != buffer_size) ?
176
895
                    (const unsigned char *) buffer : empty_buf;
177
941
  }
178
941
  return res;
179
941
}
180
181
182
static void
183
response_cntn_free_buf (void *ptr)
184
0
{
185
0
  free (ptr);
186
0
}
187
188
189
MHD_EXTERN_
190
MHD_FN_PAR_IN_SIZE_ (3,2) struct MHD_Response *
191
MHD_response_from_buffer_copy (
192
  enum MHD_HTTP_StatusCode sc,
193
  size_t buffer_size,
194
  const char buffer[MHD_FN_PAR_DYN_ARR_SIZE_ (buffer_size)])
195
0
{
196
0
  struct MHD_Response *restrict res;
197
0
  const unsigned char *buf_copy;
198
0
  unsigned char *new_buf;
199
200
0
#if SIZEOF_SIZE_T >= 8
201
0
  if (MHD_SIZE_UNKNOWN == buffer_size)
202
0
    return NULL;
203
0
#endif /* SIZEOF_SIZE_T >= 8 */
204
205
0
  if (0 != buffer_size)
206
0
  {
207
0
    new_buf = (unsigned char *) malloc (buffer_size);
208
0
    if (NULL == new_buf)
209
0
      return NULL;
210
0
    memcpy (new_buf, buffer, buffer_size);
211
0
    res = response_create_basic (sc, buffer_size,
212
0
                                 response_cntn_free_buf, new_buf);
213
0
    buf_copy = new_buf;
214
0
  }
215
0
  else
216
0
  {
217
0
    new_buf = NULL;
218
0
    buf_copy = empty_buf;
219
0
    res = response_create_basic (sc, 0, (MHD_FreeCallback) NULL, NULL);
220
0
  }
221
222
0
  if (NULL != res)
223
0
  {
224
0
    res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
225
0
    res->cntn.buf = buf_copy;
226
0
    return res; /* Success exit point */
227
0
  }
228
229
  /* Cleanup path */
230
0
  if (NULL != new_buf)
231
0
    free (new_buf);
232
0
  return NULL;
233
0
}
234
235
236
MHD_EXTERN_ struct MHD_Response *
237
MHD_response_from_iovec (
238
  enum MHD_HTTP_StatusCode sc,
239
  unsigned int iov_count,
240
  const struct MHD_IoVec iov[MHD_FN_PAR_DYN_ARR_SIZE_ (iov_count)],
241
  MHD_FreeCallback free_cb,
242
  void *free_cb_cls)
243
0
{
244
0
  unsigned int i;
245
0
  size_t i_cp = 0;   /**< Index in the copy of iov */
246
0
  uint_fast64_t total_size = 0;
247
248
  /* Calculate final size, number of valid elements, and check 'iov' */
249
0
  for (i = 0; i < iov_count; ++i)
250
0
  {
251
0
    if (0 == iov[i].iov_len)
252
0
      continue;     /* skip zero-sized elements */
253
0
    if (NULL == iov[i].iov_base)
254
0
      return NULL;  /* NULL pointer with non-zero size */
255
256
0
    total_size += iov[i].iov_len;
257
0
    if ((total_size < iov[i].iov_len) || (0 > (ssize_t) total_size)
258
0
        || (((size_t) total_size) != total_size))
259
0
      return NULL; /* Larger than send function may report as success */
260
0
#if defined(MHD_SOCKETS_KIND_POSIX) || ! defined(_WIN64)
261
0
    i_cp++;
262
#else  /* ! MHD_SOCKETS_KIND_POSIX && _WIN64 */
263
    if (1)
264
    {
265
      size_t i_add;
266
267
      i_add = (size_t) (iov[i].iov_len / mhd_IOV_ELMN_MAX_SIZE);
268
      if (0 != iov[i].iov_len % mhd_IOV_ELMN_MAX_SIZE)
269
        i_add++;
270
      i_cp += i_add;
271
      if (i_cp < i_add)
272
        return NULL; /* Counter overflow */
273
    }
274
#endif /* ! MHD_SOCKETS_KIND_POSIX && _WIN64 */
275
0
  }
276
0
  if (0 == total_size)
277
0
  {
278
0
    struct MHD_Response *restrict res;
279
280
0
    res = response_create_basic (sc, 0, free_cb, free_cb_cls);
281
0
    if (NULL != res)
282
0
    {
283
0
      res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
284
0
      res->cntn.buf = empty_buf;
285
0
    }
286
0
    return res;
287
0
  }
288
0
  if (MHD_SIZE_UNKNOWN == total_size)
289
0
    return NULL;
290
291
0
  mhd_assert (0 < i_cp);
292
0
  if (1)
293
0
  { /* for local variables local scope only */
294
0
    struct MHD_Response *restrict res;
295
0
    mhd_iovec *iov_copy;
296
0
    size_t num_copy_elements = i_cp;
297
298
0
    iov_copy = (mhd_iovec *)
299
0
               mhd_calloc (num_copy_elements,
300
0
                           sizeof(mhd_iovec));
301
0
    if (NULL == iov_copy)
302
0
      return NULL;
303
304
0
    i_cp = 0;
305
0
    for (i = 0; i < iov_count; ++i)
306
0
    {
307
0
      size_t element_size = iov[i].iov_len;
308
0
      const unsigned char *buf = (const unsigned char *) iov[i].iov_base;
309
310
0
      if (0 == element_size)
311
0
        continue;         /* skip zero-sized elements */
312
#if defined(MHD_SOCKETS_KIND_WINSOCK) && defined(_WIN64)
313
      while (mhd_IOV_ELMN_MAX_SIZE < element_size)
314
      {
315
        iov_copy[i_cp].iov_base =
316
          (mhd_IOV_ELMN_PTR_TYPE) mhd_DROP_CONST (buf);
317
        iov_copy[i_cp].iov_len = mhd_IOV_ELMN_MAX_SIZE;
318
        buf += mhd_IOV_ELMN_MAX_SIZE;
319
        element_size -= mhd_IOV_ELMN_MAX_SIZE;
320
        i_cp++;
321
      }
322
#endif /* MHD_SOCKETS_KIND_WINSOCK && _WIN64 */
323
0
      iov_copy[i_cp].iov_base = (mhd_IOV_ELMN_PTR_TYPE) mhd_DROP_CONST (buf);
324
0
      iov_copy[i_cp].iov_len = (mhd_iov_elmn_size) element_size;
325
0
      i_cp++;
326
0
    }
327
0
    mhd_assert (num_copy_elements == i_cp);
328
0
    mhd_assert (0 < i_cp);
329
330
0
    res = response_create_basic (sc, total_size, free_cb, free_cb_cls);
331
0
    if (NULL != res)
332
0
    {
333
0
      res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_IOVEC;
334
0
      res->cntn.iovec.iov = iov_copy;
335
0
      res->cntn.iovec.cnt = i_cp;
336
0
      return res; /* Success exit point */
337
0
    }
338
339
    /* Below is a cleanup path */
340
0
    free (iov_copy);
341
0
  }
342
0
  return NULL;
343
0
}
344
345
346
MHD_EXTERN_
347
MHD_FN_PAR_FD_READ_ (2) struct MHD_Response *
348
MHD_response_from_fd (enum MHD_HTTP_StatusCode sc,
349
                      int fd,
350
                      uint_fast64_t offset,
351
                      uint_fast64_t size)
352
220
{
353
220
  struct MHD_Response *restrict res;
354
220
  if (offset == MHD_SIZE_UNKNOWN)
355
0
    return NULL;
356
220
  if (size != MHD_SIZE_UNKNOWN)
357
220
  {
358
220
    if (size > ((size + offset) & 0xFFFFFFFFFFFFFFFFU))
359
0
      return NULL;
360
220
  }
361
220
  res = response_create_basic (sc, size, (MHD_FreeCallback) NULL, NULL);
362
220
  if (NULL != res)
363
220
  {
364
220
    res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE;
365
220
    res->cntn.file.fd = fd;
366
220
    res->cntn.file.offset = offset;
367
220
#ifdef mhd_USE_SENDFILE
368
220
    res->cntn.file.use_sf = (size < MHD_SIZE_UNKNOWN);
369
220
#endif
370
220
    res->cntn.file.is_pipe = false; /* Not necessary */
371
220
  }
372
220
  return res;
373
220
}
374
375
376
MHD_EXTERN_
377
MHD_FN_PAR_FD_READ_ (2) struct MHD_Response *
378
MHD_response_from_pipe (enum MHD_HTTP_StatusCode sc,
379
                        int fd)
380
941
{
381
941
  struct MHD_Response *restrict res;
382
941
  res = response_create_basic (sc,
383
941
                               MHD_SIZE_UNKNOWN,
384
941
                               (MHD_FreeCallback) NULL,
385
941
                               NULL);
386
941
  if (NULL != res)
387
941
  {
388
941
    res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE;
389
941
    res->cntn.file.fd = fd;
390
941
    res->cntn.file.offset = 0; /* Not necessary */
391
941
#ifdef mhd_USE_SENDFILE
392
941
    res->cntn.file.use_sf = false; /* Not necessary */
393
941
#endif
394
941
    res->cntn.file.is_pipe = true;
395
941
  }
396
941
  return res;
397
941
}
398
399
400
/**
401
 * Create special internal response for sending error reply
402
 * @param sc the HTTP status code
403
 * @param cntn_len the length of the @a cntn
404
 * @param cntn the content of the response, could be NULL
405
 * @param spec_hdr_len the length of the @a spec_hdr
406
 * @param spec_hdr the special header line, without last CRLF,
407
 *                 if not NULL it will be deallocated by free().
408
 * @return the pointer to the created object if succeed,
409
 *         NULL otherwise
410
 *
411
 */
412
MHD_INTERNAL
413
MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (5) struct MHD_Response *
414
mhd_response_special_for_error (unsigned int sc,
415
                                size_t cntn_len,
416
                                const char *cntn,
417
                                size_t spec_hdr_len,
418
                                char *spec_hdr)
419
0
{
420
0
  struct MHD_Response *restrict res;
421
422
0
  mhd_assert (100 <= sc);
423
0
  mhd_assert (600 > sc);
424
0
  mhd_assert ((NULL != cntn) || (0 == cntn_len));
425
0
  mhd_assert ((NULL != spec_hdr) || (0 == spec_hdr_len));
426
427
0
  res = (struct MHD_Response *)
428
0
        mhd_calloc (1,
429
0
                    sizeof(struct MHD_Response));
430
0
  if (NULL == res)
431
0
    return NULL;
432
433
#ifndef HAVE_NULL_PTR_ALL_ZEROS
434
  mhd_DLINKEDL_INIT_LIST (res, headers);
435
  res->free.cb = NULL;
436
  res->free.cls = NULL;
437
  res->special_resp.spec_hdr = NULL;
438
#endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
439
0
  res->sc = (enum MHD_HTTP_StatusCode) sc;
440
0
  res->cntn_size = cntn_len;
441
0
  res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
442
0
  res->cntn.buf = (const unsigned char *) ((0 != cntn_len) ? cntn : "");
443
0
  res->cfg.close_forced = true;
444
0
  res->cfg.int_err_resp = true;
445
0
  res->special_resp.spec_hdr_len = spec_hdr_len;
446
0
  res->special_resp.spec_hdr = spec_hdr;
447
0
  res->frozen = true;
448
449
0
  return res;
450
0
}