response_from.c (14076B)
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_2cls (enum MHD_HTTP_StatusCode sc, 136 uint_fast64_t size, 137 MHD_DynamicContentCreator dyn_cont, 138 void *dyn_cont_cls, 139 MHD_FreeCallback free_cb, 140 void *free_cb_cls) 141 { 142 struct MHD_Response *restrict res; 143 res = response_create_basic (sc, size, free_cb, free_cb_cls); 144 if (NULL != res) 145 { 146 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_CALLBACK; 147 res->cntn.dyn.cb = dyn_cont; 148 res->cntn.dyn.cls = dyn_cont_cls; 149 } 150 else if (NULL != free_cb) 151 free_cb (free_cb_cls); 152 return res; 153 } 154 155 156 static const unsigned char empty_buf[1] = { 0 }; 157 158 MHD_EXTERN_ 159 MHD_FN_PAR_IN_SIZE_ (3, 2) struct MHD_Response * 160 MHD_response_from_buffer ( 161 enum MHD_HTTP_StatusCode sc, 162 size_t buffer_size, 163 const char *buffer, 164 MHD_FreeCallback free_cb, 165 void *free_cb_cls) 166 { 167 struct MHD_Response *restrict res; 168 169 #if SIZEOF_SIZE_T >= 8 170 if (MHD_SIZE_UNKNOWN == buffer_size) 171 { 172 if (NULL != free_cb) 173 free_cb (free_cb_cls); 174 return NULL; 175 } 176 #endif /* SIZEOF_SIZE_T >= 8 */ 177 178 res = response_create_basic (sc, buffer_size, free_cb, free_cb_cls); 179 if (NULL != res) 180 { 181 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER; 182 res->cntn.buf = (0 != buffer_size) ? 183 (const unsigned char *)buffer : empty_buf; 184 } 185 else if (NULL != free_cb) 186 free_cb (free_cb_cls); 187 return res; 188 } 189 190 191 static void 192 response_cntn_free_buf (void *ptr) 193 { 194 free (ptr); 195 } 196 197 198 MHD_EXTERN_ 199 MHD_FN_PAR_IN_SIZE_ (3, 2) struct MHD_Response * 200 MHD_response_from_buffer_copy ( 201 enum MHD_HTTP_StatusCode sc, 202 size_t buffer_size, 203 const char buffer[MHD_FN_PAR_DYN_ARR_SIZE_ (buffer_size)]) 204 { 205 struct MHD_Response *restrict res; 206 const unsigned char *buf_copy; 207 unsigned char *new_buf; 208 209 #if SIZEOF_SIZE_T >= 8 210 if (MHD_SIZE_UNKNOWN == buffer_size) 211 return NULL; 212 #endif /* SIZEOF_SIZE_T >= 8 */ 213 214 if (0 != buffer_size) 215 { 216 new_buf = (unsigned char *)malloc (buffer_size); 217 if (NULL == new_buf) 218 return NULL; 219 memcpy (new_buf, buffer, buffer_size); 220 res = response_create_basic (sc, buffer_size, 221 response_cntn_free_buf, new_buf); 222 buf_copy = new_buf; 223 } 224 else 225 { 226 new_buf = NULL; 227 buf_copy = empty_buf; 228 res = response_create_basic (sc, 0, (MHD_FreeCallback)NULL, NULL); 229 } 230 231 if (NULL != res) 232 { 233 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER; 234 res->cntn.buf = buf_copy; 235 return res; /* Success exit point */ 236 } 237 238 /* Cleanup path */ 239 if (NULL != new_buf) 240 free (new_buf); 241 return NULL; 242 } 243 244 245 static struct MHD_Response * 246 response_from_iovec_inner ( 247 enum MHD_HTTP_StatusCode sc, 248 unsigned int iov_count, 249 const struct MHD_IoVec iov[MHD_FN_PAR_DYN_ARR_SIZE_ (iov_count)], 250 MHD_FreeCallback free_cb, 251 void *free_cb_cls) 252 { 253 unsigned int i; 254 size_t i_cp = 0; /**< Index in the copy of iov */ 255 uint_fast64_t total_size = 0; 256 257 /* Calculate final size, number of valid elements, and check 'iov' */ 258 for (i = 0; i < iov_count; ++i) 259 { 260 if (0 == iov[i].iov_len) 261 continue; /* skip zero-sized elements */ 262 if (NULL == iov[i].iov_base) 263 return NULL; /* NULL pointer with non-zero size */ 264 265 total_size += iov[i].iov_len; 266 if ((total_size < iov[i].iov_len) || (0 > (ssize_t)total_size) 267 || (((size_t)total_size) != total_size)) 268 return NULL; /* Larger than send function may report as success */ 269 #if defined(MHD_SOCKETS_KIND_POSIX) || !defined(_WIN64) 270 i_cp++; 271 #else /* ! MHD_SOCKETS_KIND_POSIX && _WIN64 */ 272 if (1) 273 { 274 size_t i_add; 275 276 i_add = (size_t)(iov[i].iov_len / mhd_IOV_ELMN_MAX_SIZE); 277 if (0 != iov[i].iov_len % mhd_IOV_ELMN_MAX_SIZE) 278 i_add++; 279 i_cp += i_add; 280 if (i_cp < i_add) 281 return NULL; /* Counter overflow */ 282 } 283 #endif /* ! MHD_SOCKETS_KIND_POSIX && _WIN64 */ 284 } 285 if (0 == total_size) 286 { 287 struct MHD_Response *restrict res; 288 289 res = response_create_basic (sc, 0, free_cb, free_cb_cls); 290 if (NULL != res) 291 { 292 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER; 293 res->cntn.buf = empty_buf; 294 } 295 return res; 296 } 297 if (MHD_SIZE_UNKNOWN == total_size) 298 return NULL; 299 300 mhd_assert (0 < i_cp); 301 if (1) 302 { /* for local variables local scope only */ 303 struct MHD_Response *restrict res; 304 mhd_iovec *iov_copy; 305 size_t num_copy_elements = i_cp; 306 307 iov_copy = (mhd_iovec *) 308 mhd_calloc (num_copy_elements, 309 sizeof(mhd_iovec)); 310 if (NULL == iov_copy) 311 return NULL; 312 313 i_cp = 0; 314 for (i = 0; i < iov_count; ++i) 315 { 316 size_t element_size = iov[i].iov_len; 317 const unsigned char *buf = (const unsigned char *)iov[i].iov_base; 318 319 if (0 == element_size) 320 continue; /* skip zero-sized elements */ 321 #if defined(MHD_SOCKETS_KIND_WINSOCK) && defined(_WIN64) 322 while (mhd_IOV_ELMN_MAX_SIZE < element_size) 323 { 324 iov_copy[i_cp].iov_base = 325 (mhd_IOV_ELMN_PTR_TYPE)mhd_DROP_CONST (buf); 326 iov_copy[i_cp].iov_len = mhd_IOV_ELMN_MAX_SIZE; 327 buf += mhd_IOV_ELMN_MAX_SIZE; 328 element_size -= mhd_IOV_ELMN_MAX_SIZE; 329 i_cp++; 330 } 331 #endif /* MHD_SOCKETS_KIND_WINSOCK && _WIN64 */ 332 iov_copy[i_cp].iov_base = (mhd_IOV_ELMN_PTR_TYPE)mhd_DROP_CONST (buf); 333 iov_copy[i_cp].iov_len = (mhd_iov_elmn_size)element_size; 334 i_cp++; 335 } 336 mhd_assert (num_copy_elements == i_cp); 337 mhd_assert (0 < i_cp); 338 339 res = response_create_basic (sc, total_size, free_cb, free_cb_cls); 340 if (NULL != res) 341 { 342 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_IOVEC; 343 res->cntn.iovec.iov = iov_copy; 344 res->cntn.iovec.cnt = i_cp; 345 return res; /* Success exit point */ 346 } 347 348 /* Below is a cleanup path */ 349 free (iov_copy); 350 } 351 return NULL; 352 } 353 354 355 MHD_EXTERN_ struct MHD_Response * 356 MHD_response_from_iovec ( 357 enum MHD_HTTP_StatusCode sc, 358 unsigned int iov_count, 359 const struct MHD_IoVec iov[MHD_FN_PAR_DYN_ARR_SIZE_ (iov_count)], 360 MHD_FreeCallback free_cb, 361 void *free_cb_cls) 362 { 363 struct MHD_Response *res = 364 response_from_iovec_inner (sc, iov_count, iov, free_cb, free_cb_cls); 365 if ((NULL == res) && (NULL != free_cb)) 366 free_cb (free_cb_cls); 367 368 return res; 369 } 370 371 372 static 373 MHD_FN_PAR_FD_READ_ (2) struct MHD_Response * 374 response_from_fd_inner (enum MHD_HTTP_StatusCode sc, 375 int fd, 376 uint_fast64_t offset, 377 uint_fast64_t size) 378 { 379 struct MHD_Response *restrict res; 380 if (offset == MHD_SIZE_UNKNOWN) 381 return NULL; 382 if (size != MHD_SIZE_UNKNOWN) 383 { 384 if (size > ((size + offset) & 0xFFFFFFFFFFFFFFFFU)) 385 return NULL; 386 } 387 res = response_create_basic (sc, size, (MHD_FreeCallback)NULL, NULL); 388 if (NULL != res) 389 { 390 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE; 391 res->cntn.file.fd = fd; 392 res->cntn.file.offset = offset; 393 #ifdef mhd_USE_SENDFILE 394 res->cntn.file.use_sf = (size < MHD_SIZE_UNKNOWN); 395 #endif 396 res->cntn.file.is_pipe = false; /* Not necessary */ 397 } 398 return res; 399 } 400 401 402 MHD_EXTERN_ 403 MHD_FN_PAR_FD_READ_ (2) struct MHD_Response * 404 MHD_response_from_fd (enum MHD_HTTP_StatusCode sc, 405 int fd, 406 uint_fast64_t offset, 407 uint_fast64_t size) 408 { 409 struct MHD_Response *res = 410 response_from_fd_inner (sc, fd, offset, size); 411 if (NULL == res) 412 close (fd); 413 return res; 414 } 415 416 417 MHD_EXTERN_ 418 MHD_FN_PAR_FD_READ_ (2) struct MHD_Response * 419 MHD_response_from_pipe (enum MHD_HTTP_StatusCode sc, 420 int fd) 421 { 422 struct MHD_Response *restrict res; 423 res = response_create_basic (sc, 424 MHD_SIZE_UNKNOWN, 425 (MHD_FreeCallback)NULL, 426 NULL); 427 if (NULL != res) 428 { 429 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE; 430 res->cntn.file.fd = fd; 431 res->cntn.file.offset = 0; /* Not necessary */ 432 #ifdef mhd_USE_SENDFILE 433 res->cntn.file.use_sf = false; /* Not necessary */ 434 #endif 435 res->cntn.file.is_pipe = true; 436 } 437 else 438 close (fd); 439 return res; 440 } 441 442 443 /** 444 * Create special internal response for sending error reply 445 * @param sc the HTTP status code 446 * @param cntn_len the length of the @a cntn 447 * @param cntn the content of the response, could be NULL 448 * @param spec_hdr_len the length of the @a spec_hdr 449 * @param spec_hdr the special header line, without last CRLF, 450 * if not NULL it will be deallocated by free(). 451 * @return the pointer to the created object if succeed, 452 * NULL otherwise 453 * 454 */ 455 MHD_INTERNAL 456 MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (5) struct MHD_Response * 457 mhd_response_special_for_error (unsigned int sc, 458 size_t cntn_len, 459 const char *cntn, 460 size_t spec_hdr_len, 461 char *spec_hdr) 462 { 463 struct MHD_Response *restrict res; 464 465 mhd_assert (100 <= sc); 466 mhd_assert (600 > sc); 467 mhd_assert ((NULL != cntn) || (0 == cntn_len)); 468 mhd_assert ((NULL != spec_hdr) || (0 == spec_hdr_len)); 469 470 res = (struct MHD_Response *) 471 mhd_calloc (1, 472 sizeof(struct MHD_Response)); 473 if (NULL == res) 474 return NULL; 475 476 #ifndef HAVE_NULL_PTR_ALL_ZEROS 477 mhd_DLINKEDL_INIT_LIST (res, headers); 478 res->free.cb = NULL; 479 res->free.cls = NULL; 480 res->special_resp.spec_hdr = NULL; 481 #endif /* ! HAVE_NULL_PTR_ALL_ZEROS */ 482 res->sc = (enum MHD_HTTP_StatusCode)sc; 483 res->cntn_size = cntn_len; 484 res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER; 485 res->cntn.buf = (const unsigned char *)((0 != cntn_len) ? cntn : ""); 486 res->cfg.close_forced = true; 487 res->cfg.int_err_resp = true; 488 res->special_resp.spec_hdr_len = spec_hdr_len; 489 res->special_resp.spec_hdr = spec_hdr; 490 res->frozen = true; 491 492 return res; 493 }