response_from.c (13035B)
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 { 75 struct MHD_Response *restrict r; 76 struct ResponseOptions *restrict s; 77 78 if ((100 > sc) || (999 < sc)) 79 return NULL; 80 81 r = (struct MHD_Response *) 82 mhd_calloc (1, 83 sizeof(struct MHD_Response)); 84 if (NULL != r) 85 { 86 s = (struct ResponseOptions *) 87 mhd_calloc (1, 88 sizeof(struct ResponseOptions)); 89 if (NULL != s) 90 { 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 r->sc = sc; 105 r->cntn_size = cntn_size; 106 r->free.cb = free_cb; 107 r->free.cls = free_cb_cls; 108 r->settings = s; 109 110 return r; /* Success exit point */ 111 } 112 free (r); 113 } 114 return NULL; /* Failure exit point */ 115 } 116 117 118 MHD_INTERNAL 119 MHD_FN_PAR_NONNULL_ (1) void 120 mhd_response_deinit_content_data (struct MHD_Response *restrict r) 121 { 122 mhd_assert (mhd_RESPONSE_CONTENT_DATA_INVALID != r->cntn_dtype); 123 if (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype) 124 free (r->cntn.iovec.iov); 125 else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype) 126 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 if (NULL != r->free.cb) 130 r->free.cb (r->free.cls); 131 } 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 { 141 struct MHD_Response *restrict res; 142 res = response_create_basic (sc, size, dyn_cont_fc, dyn_cont_cls); 143 if (NULL != res) 144 { 145 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_CALLBACK; 146 res->cntn.dyn.cb = dyn_cont; 147 res->cntn.dyn.cls = dyn_cont_cls; 148 } 149 return res; 150 } 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 { 164 struct MHD_Response *restrict res; 165 166 #if SIZEOF_SIZE_T >= 8 167 if (MHD_SIZE_UNKNOWN == buffer_size) 168 return NULL; 169 #endif /* SIZEOF_SIZE_T >= 8 */ 170 171 res = response_create_basic (sc, buffer_size, free_cb, free_cb_cls); 172 if (NULL != res) 173 { 174 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER; 175 res->cntn.buf = (0 != buffer_size) ? 176 (const unsigned char *) buffer : empty_buf; 177 } 178 return res; 179 } 180 181 182 static void 183 response_cntn_free_buf (void *ptr) 184 { 185 free (ptr); 186 } 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 { 196 struct MHD_Response *restrict res; 197 const unsigned char *buf_copy; 198 unsigned char *new_buf; 199 200 #if SIZEOF_SIZE_T >= 8 201 if (MHD_SIZE_UNKNOWN == buffer_size) 202 return NULL; 203 #endif /* SIZEOF_SIZE_T >= 8 */ 204 205 if (0 != buffer_size) 206 { 207 new_buf = (unsigned char *) malloc (buffer_size); 208 if (NULL == new_buf) 209 return NULL; 210 memcpy (new_buf, buffer, buffer_size); 211 res = response_create_basic (sc, buffer_size, 212 response_cntn_free_buf, new_buf); 213 buf_copy = new_buf; 214 } 215 else 216 { 217 new_buf = NULL; 218 buf_copy = empty_buf; 219 res = response_create_basic (sc, 0, (MHD_FreeCallback) NULL, NULL); 220 } 221 222 if (NULL != res) 223 { 224 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER; 225 res->cntn.buf = buf_copy; 226 return res; /* Success exit point */ 227 } 228 229 /* Cleanup path */ 230 if (NULL != new_buf) 231 free (new_buf); 232 return NULL; 233 } 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 { 244 unsigned int i; 245 size_t i_cp = 0; /**< Index in the copy of iov */ 246 uint_fast64_t total_size = 0; 247 248 /* Calculate final size, number of valid elements, and check 'iov' */ 249 for (i = 0; i < iov_count; ++i) 250 { 251 if (0 == iov[i].iov_len) 252 continue; /* skip zero-sized elements */ 253 if (NULL == iov[i].iov_base) 254 return NULL; /* NULL pointer with non-zero size */ 255 256 total_size += iov[i].iov_len; 257 if ((total_size < iov[i].iov_len) || (0 > (ssize_t) total_size) 258 || (((size_t) total_size) != total_size)) 259 return NULL; /* Larger than send function may report as success */ 260 #if defined(MHD_SOCKETS_KIND_POSIX) || ! defined(_WIN64) 261 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 } 276 if (0 == total_size) 277 { 278 struct MHD_Response *restrict res; 279 280 res = response_create_basic (sc, 0, free_cb, free_cb_cls); 281 if (NULL != res) 282 { 283 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER; 284 res->cntn.buf = empty_buf; 285 } 286 return res; 287 } 288 if (MHD_SIZE_UNKNOWN == total_size) 289 return NULL; 290 291 mhd_assert (0 < i_cp); 292 if (1) 293 { /* for local variables local scope only */ 294 struct MHD_Response *restrict res; 295 mhd_iovec *iov_copy; 296 size_t num_copy_elements = i_cp; 297 298 iov_copy = (mhd_iovec *) 299 mhd_calloc (num_copy_elements, 300 sizeof(mhd_iovec)); 301 if (NULL == iov_copy) 302 return NULL; 303 304 i_cp = 0; 305 for (i = 0; i < iov_count; ++i) 306 { 307 size_t element_size = iov[i].iov_len; 308 const unsigned char *buf = (const unsigned char *) iov[i].iov_base; 309 310 if (0 == element_size) 311 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 iov_copy[i_cp].iov_base = (mhd_IOV_ELMN_PTR_TYPE) mhd_DROP_CONST (buf); 324 iov_copy[i_cp].iov_len = (mhd_iov_elmn_size) element_size; 325 i_cp++; 326 } 327 mhd_assert (num_copy_elements == i_cp); 328 mhd_assert (0 < i_cp); 329 330 res = response_create_basic (sc, total_size, free_cb, free_cb_cls); 331 if (NULL != res) 332 { 333 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_IOVEC; 334 res->cntn.iovec.iov = iov_copy; 335 res->cntn.iovec.cnt = i_cp; 336 return res; /* Success exit point */ 337 } 338 339 /* Below is a cleanup path */ 340 free (iov_copy); 341 } 342 return NULL; 343 } 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 { 353 struct MHD_Response *restrict res; 354 if (offset == MHD_SIZE_UNKNOWN) 355 return NULL; 356 if (size != MHD_SIZE_UNKNOWN) 357 { 358 if (size > ((size + offset) & 0xFFFFFFFFFFFFFFFFU)) 359 return NULL; 360 } 361 res = response_create_basic (sc, size, (MHD_FreeCallback) NULL, NULL); 362 if (NULL != res) 363 { 364 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE; 365 res->cntn.file.fd = fd; 366 res->cntn.file.offset = offset; 367 #ifdef mhd_USE_SENDFILE 368 res->cntn.file.use_sf = (size < MHD_SIZE_UNKNOWN); 369 #endif 370 res->cntn.file.is_pipe = false; /* Not necessary */ 371 } 372 return res; 373 } 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 { 381 struct MHD_Response *restrict res; 382 res = response_create_basic (sc, 383 MHD_SIZE_UNKNOWN, 384 (MHD_FreeCallback) NULL, 385 NULL); 386 if (NULL != res) 387 { 388 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE; 389 res->cntn.file.fd = fd; 390 res->cntn.file.offset = 0; /* Not necessary */ 391 #ifdef mhd_USE_SENDFILE 392 res->cntn.file.use_sf = false; /* Not necessary */ 393 #endif 394 res->cntn.file.is_pipe = true; 395 } 396 return res; 397 } 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 { 420 struct MHD_Response *restrict res; 421 422 mhd_assert (100 <= sc); 423 mhd_assert (600 > sc); 424 mhd_assert ((NULL != cntn) || (0 == cntn_len)); 425 mhd_assert ((NULL != spec_hdr) || (0 == spec_hdr_len)); 426 427 res = (struct MHD_Response *) 428 mhd_calloc (1, 429 sizeof(struct MHD_Response)); 430 if (NULL == res) 431 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 res->sc = (enum MHD_HTTP_StatusCode) sc; 440 res->cntn_size = cntn_len; 441 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER; 442 res->cntn.buf = (const unsigned char *) ((0 != cntn_len) ? cntn : ""); 443 res->cfg.close_forced = true; 444 res->cfg.int_err_resp = true; 445 res->special_resp.spec_hdr_len = spec_hdr_len; 446 res->special_resp.spec_hdr = spec_hdr; 447 res->frozen = true; 448 449 return res; 450 }