test_https_get_iovec.c (11604B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007-2021 Christian Grothoff 4 Copyright (C) 2016-2022 Evgeny Grin 5 6 libmicrohttpd is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published 8 by the Free Software Foundation; either version 3, or (at your 9 option) any later version. 10 11 libmicrohttpd is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with libmicrohttpd; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 Boston, MA 02110-1301, USA. 20 */ 21 22 /** 23 * @file test_https_get_iovec.c 24 * @brief Testcase for libmicrohttpd HTTPS GET operations using an iovec 25 * @author Sagie Amir 26 * @author Karlson2k (Evgeny Grin) 27 * @author Lawrence Sebald 28 */ 29 30 /* 31 * This testcase is derived from the test_https_get.c testcase. This version 32 * adds the usage of a scatter/gather array for storing the response data. 33 */ 34 35 #include "platform.h" 36 #include "microhttpd.h" 37 #include <limits.h> 38 #include <sys/stat.h> 39 #include <curl/curl.h> 40 #ifdef MHD_HTTPS_REQUIRE_GCRYPT 41 #include <gcrypt.h> 42 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */ 43 #include "tls_test_common.h" 44 #include "tls_test_keys.h" 45 46 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 47 test into a marked, classifiable test error (TESTING.md, P5). */ 48 #include "mhd_panic_tripwire.h" 49 50 51 static uint16_t global_port; 52 53 /* Use large enough pieces (>16KB) to test partially consumed 54 * data as TLS doesn't take more than 16KB by a single call. */ 55 #define TESTSTR_IOVLEN 20480 56 #define TESTSTR_IOVCNT 30 57 #define TESTSTR_SIZE (TESTSTR_IOVCNT * TESTSTR_IOVLEN) 58 59 60 static void 61 iov_free_callback (void *cls) 62 { 63 free (cls); 64 } 65 66 67 static int 68 check_read_data (const void *ptr, size_t len) 69 { 70 const int *buf; 71 size_t i; 72 73 if (len % sizeof(int)) 74 return -1; 75 76 buf = (const int *) ptr; 77 78 for (i = 0; i < len / sizeof(int); ++i) 79 { 80 if (buf[i] != (int) i) 81 return -1; 82 } 83 84 return 0; 85 } 86 87 88 static enum MHD_Result 89 iovec_ahc (void *cls, 90 struct MHD_Connection *connection, 91 const char *url, 92 const char *method, 93 const char *version, 94 const char *upload_data, 95 size_t *upload_data_size, 96 void **req_cls) 97 { 98 static int aptr; 99 struct MHD_Response *response; 100 enum MHD_Result ret; 101 int *data; 102 struct MHD_IoVec iov[TESTSTR_IOVCNT]; 103 int i; 104 int j; 105 (void) cls; (void) url; (void) version; /* Unused. Silent compiler warning. */ 106 (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 107 108 if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) 109 return MHD_NO; /* unexpected method */ 110 if (&aptr != *req_cls) 111 { 112 /* do never respond on first call */ 113 *req_cls = &aptr; 114 return MHD_YES; 115 } 116 *req_cls = NULL; /* reset when done */ 117 118 /* Create some test data. */ 119 if (NULL == (data = malloc (TESTSTR_SIZE))) 120 return MHD_NO; 121 122 for (j = 0; j < TESTSTR_IOVCNT; ++j) 123 { 124 int *chunk; 125 /* Assign chunks of memory area in the reverse order 126 * to make non-continuous set of data therefore 127 * possible buffer overruns could be detected */ 128 chunk = data + (((TESTSTR_IOVCNT - 1) - (unsigned int) j) 129 * (TESTSTR_SIZE / TESTSTR_IOVCNT / sizeof(int))); 130 iov[j].iov_base = chunk; 131 iov[j].iov_len = TESTSTR_SIZE / TESTSTR_IOVCNT; 132 133 for (i = 0; i < (int) (TESTSTR_IOVLEN / sizeof(int)); ++i) 134 chunk[i] = i + (j * (int) (TESTSTR_IOVLEN / sizeof(int))); 135 } 136 137 response = MHD_create_response_from_iovec (iov, 138 TESTSTR_IOVCNT, 139 &iov_free_callback, 140 data); 141 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 142 MHD_destroy_response (response); 143 return ret; 144 } 145 146 147 static unsigned int 148 test_iovec_transfer (void *cls, 149 uint16_t port, 150 const char *cipher_suite, 151 int proto_version) 152 { 153 size_t len; 154 unsigned int ret = 0; 155 struct CBC cbc; 156 char url[255]; 157 (void) cls; /* Unused. Silent compiler warning. */ 158 159 len = TESTSTR_SIZE; 160 if (NULL == (cbc.buf = malloc (sizeof (char) * len))) 161 { 162 fprintf (stderr, MHD_E_MEM); 163 return 1; 164 } 165 cbc.size = len; 166 cbc.pos = 0; 167 168 if (gen_test_uri (url, 169 sizeof (url), 170 port)) 171 { 172 ret = 1; 173 goto cleanup; 174 } 175 176 if (CURLE_OK != 177 send_curl_req (url, &cbc, cipher_suite, proto_version)) 178 { 179 ret = 1; 180 goto cleanup; 181 } 182 183 if ((cbc.pos != TESTSTR_SIZE) || 184 (0 != check_read_data (cbc.buf, cbc.pos))) 185 { 186 fprintf (stderr, "Error: local file & received file differ.\n"); 187 ret = 1; 188 } 189 cleanup: 190 free (cbc.buf); 191 return ret; 192 } 193 194 195 /* perform a HTTP GET request via SSL/TLS */ 196 static unsigned int 197 test_secure_get (FILE *test_fd, 198 const char *cipher_suite, 199 int proto_version) 200 { 201 unsigned int ret; 202 struct MHD_Daemon *d; 203 uint16_t port; 204 205 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 206 port = 0; 207 else 208 port = 3045; 209 210 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 211 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS 212 | MHD_USE_ERROR_LOG, port, 213 NULL, NULL, 214 &iovec_ahc, NULL, 215 MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, 216 MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, 217 MHD_OPTION_END); 218 219 if (d == NULL) 220 { 221 fprintf (stderr, MHD_E_SERVER_INIT); 222 return 1; 223 } 224 if (0 == port) 225 { 226 const union MHD_DaemonInfo *dinfo; 227 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 228 if ((NULL == dinfo) || (0 == dinfo->port) ) 229 { 230 MHD_stop_daemon (d); 231 return 1; 232 } 233 port = dinfo->port; 234 } 235 236 ret = test_iovec_transfer (test_fd, 237 port, 238 cipher_suite, 239 proto_version); 240 241 MHD_stop_daemon (d); 242 return ret; 243 } 244 245 246 static enum MHD_Result 247 ahc_empty (void *cls, 248 struct MHD_Connection *connection, 249 const char *url, 250 const char *method, 251 const char *version, 252 const char *upload_data, 253 size_t *upload_data_size, 254 void **req_cls) 255 { 256 static int ptr; 257 struct MHD_Response *response; 258 enum MHD_Result ret; 259 struct MHD_IoVec iov; 260 (void) cls; 261 (void) url; 262 (void) url; 263 (void) version; /* Unused. Silent compiler warning. */ 264 (void) upload_data; 265 (void) upload_data_size; /* Unused. Silent compiler warning. */ 266 267 if (0 != strcmp (MHD_HTTP_METHOD_GET, 268 method)) 269 return MHD_NO; /* unexpected method */ 270 if (&ptr != *req_cls) 271 { 272 *req_cls = &ptr; 273 return MHD_YES; 274 } 275 *req_cls = NULL; 276 277 iov.iov_base = NULL; 278 iov.iov_len = 0; 279 280 response = MHD_create_response_from_iovec (&iov, 281 1, 282 NULL, 283 NULL); 284 ret = MHD_queue_response (connection, 285 MHD_HTTP_OK, 286 response); 287 MHD_destroy_response (response); 288 if (ret == MHD_NO) 289 { 290 fprintf (stderr, "Failed to queue response.\n"); 291 _exit (20); 292 } 293 return ret; 294 } 295 296 297 static int 298 curlExcessFound (CURL *c, 299 curl_infotype type, 300 char *data, 301 size_t size, 302 void *cls) 303 { 304 static const char *excess_found = "Excess found"; 305 const size_t str_size = strlen (excess_found); 306 (void) c; /* Unused. Silence compiler warning. */ 307 308 #ifdef _DEBUG 309 if ((CURLINFO_TEXT == type) || 310 (CURLINFO_HEADER_IN == type) || 311 (CURLINFO_HEADER_OUT == type)) 312 fprintf (stderr, "%.*s", (int) size, data); 313 #endif /* _DEBUG */ 314 if ((CURLINFO_TEXT == type) 315 && (size >= str_size) 316 && (0 == strncmp (excess_found, data, str_size))) 317 *(int *) cls = 1; 318 return 0; 319 } 320 321 322 static unsigned int 323 testEmptyGet (unsigned int poll_flag) 324 { 325 struct MHD_Daemon *d; 326 CURL *c; 327 char buf[2048]; 328 struct CBC cbc; 329 CURLcode errornum; 330 int excess_found = 0; 331 332 333 if ( (0 == global_port) && 334 (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) 335 { 336 global_port = 1225; 337 338 } 339 340 cbc.buf = buf; 341 cbc.size = 2048; 342 cbc.pos = 0; 343 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG 344 | poll_flag | MHD_USE_TLS, 345 global_port, NULL, NULL, 346 &ahc_empty, NULL, 347 MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, 348 MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, 349 MHD_OPTION_END); 350 if (d == NULL) 351 return 4194304; 352 if (0 == global_port) 353 { 354 const union MHD_DaemonInfo *dinfo; 355 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 356 if ((NULL == dinfo) || (0 == dinfo->port) ) 357 { 358 MHD_stop_daemon (d); return 32; 359 } 360 global_port = dinfo->port; 361 } 362 c = curl_easy_init (); 363 #ifdef _DEBUG 364 curl_easy_setopt (c, CURLOPT_VERBOSE, 1L); 365 #endif 366 curl_easy_setopt (c, CURLOPT_URL, "https://127.0.0.1/"); 367 curl_easy_setopt (c, CURLOPT_PORT, (long) global_port); 368 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 369 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 370 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 371 curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION, &curlExcessFound); 372 curl_easy_setopt (c, CURLOPT_DEBUGDATA, &excess_found); 373 curl_easy_setopt (c, CURLOPT_VERBOSE, 1L); 374 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 375 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 376 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 377 curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0L); 378 curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L); 379 /* NOTE: use of CONNECTTIMEOUT without also 380 setting NOSIGNAL results in really weird 381 crashes on my system!*/ 382 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 383 if (CURLE_OK != (errornum = curl_easy_perform (c))) 384 { 385 fprintf (stderr, 386 "curl_easy_perform failed: `%s'\n", 387 curl_easy_strerror (errornum)); 388 curl_easy_cleanup (c); 389 MHD_stop_daemon (d); 390 return 8388608; 391 } 392 curl_easy_cleanup (c); 393 MHD_stop_daemon (d); 394 if (cbc.pos != 0) 395 return 16777216; 396 if (excess_found) 397 return 33554432; 398 return 0; 399 } 400 401 402 int 403 main (int argc, char *const *argv) 404 { 405 unsigned int errorCount = 0; 406 (void) argc; (void) argv; /* Unused. Silent compiler warning. */ 407 408 #ifdef MHD_HTTPS_REQUIRE_GCRYPT 409 gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); 410 #ifdef GCRYCTL_INITIALIZATION_FINISHED 411 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); 412 #endif 413 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */ 414 if (! testsuite_curl_global_init ()) 415 return 99; 416 if (NULL == curl_version_info (CURLVERSION_NOW)->ssl_version) 417 { 418 fprintf (stderr, "Curl does not support SSL. Cannot run the test.\n"); 419 curl_global_cleanup (); 420 return 77; 421 } 422 423 errorCount += 424 test_secure_get (NULL, NULL, CURL_SSLVERSION_DEFAULT); 425 errorCount += testEmptyGet (0); 426 curl_global_cleanup (); 427 428 return errorCount != 0 ? 1 : 0; 429 }