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