test_https_get_iovec.c (11436B)
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 47 static uint16_t global_port; 48 49 /* Use large enough pieces (>16KB) to test partially consumed 50 * data as TLS doesn't take more than 16KB by a single call. */ 51 #define TESTSTR_IOVLEN 20480 52 #define TESTSTR_IOVCNT 30 53 #define TESTSTR_SIZE (TESTSTR_IOVCNT * TESTSTR_IOVLEN) 54 55 56 static void 57 iov_free_callback (void *cls) 58 { 59 free (cls); 60 } 61 62 63 static int 64 check_read_data (const void *ptr, size_t len) 65 { 66 const int *buf; 67 size_t i; 68 69 if (len % sizeof(int)) 70 return -1; 71 72 buf = (const int *) ptr; 73 74 for (i = 0; i < len / sizeof(int); ++i) 75 { 76 if (buf[i] != (int) i) 77 return -1; 78 } 79 80 return 0; 81 } 82 83 84 static enum MHD_Result 85 iovec_ahc (void *cls, 86 struct MHD_Connection *connection, 87 const char *url, 88 const char *method, 89 const char *version, 90 const char *upload_data, 91 size_t *upload_data_size, 92 void **req_cls) 93 { 94 static int aptr; 95 struct MHD_Response *response; 96 enum MHD_Result ret; 97 int *data; 98 struct MHD_IoVec iov[TESTSTR_IOVCNT]; 99 int i; 100 int j; 101 (void) cls; (void) url; (void) version; /* Unused. Silent compiler warning. */ 102 (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 103 104 if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) 105 return MHD_NO; /* unexpected method */ 106 if (&aptr != *req_cls) 107 { 108 /* do never respond on first call */ 109 *req_cls = &aptr; 110 return MHD_YES; 111 } 112 *req_cls = NULL; /* reset when done */ 113 114 /* Create some test data. */ 115 if (NULL == (data = malloc (TESTSTR_SIZE))) 116 return MHD_NO; 117 118 for (j = 0; j < TESTSTR_IOVCNT; ++j) 119 { 120 int *chunk; 121 /* Assign chunks of memory area in the reverse order 122 * to make non-continous set of data therefore 123 * possible buffer overruns could be detected */ 124 chunk = data + (((TESTSTR_IOVCNT - 1) - (unsigned int) j) 125 * (TESTSTR_SIZE / TESTSTR_IOVCNT / sizeof(int))); 126 iov[j].iov_base = chunk; 127 iov[j].iov_len = TESTSTR_SIZE / TESTSTR_IOVCNT; 128 129 for (i = 0; i < (int) (TESTSTR_IOVLEN / sizeof(int)); ++i) 130 chunk[i] = i + (j * (int) (TESTSTR_IOVLEN / sizeof(int))); 131 } 132 133 response = MHD_create_response_from_iovec (iov, 134 TESTSTR_IOVCNT, 135 &iov_free_callback, 136 data); 137 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 138 MHD_destroy_response (response); 139 return ret; 140 } 141 142 143 static unsigned int 144 test_iovec_transfer (void *cls, 145 uint16_t port, 146 const char *cipher_suite, 147 int proto_version) 148 { 149 size_t len; 150 unsigned int ret = 0; 151 struct CBC cbc; 152 char url[255]; 153 (void) cls; /* Unused. Silent compiler warning. */ 154 155 len = TESTSTR_SIZE; 156 if (NULL == (cbc.buf = malloc (sizeof (char) * len))) 157 { 158 fprintf (stderr, MHD_E_MEM); 159 return 1; 160 } 161 cbc.size = len; 162 cbc.pos = 0; 163 164 if (gen_test_uri (url, 165 sizeof (url), 166 port)) 167 { 168 ret = 1; 169 goto cleanup; 170 } 171 172 if (CURLE_OK != 173 send_curl_req (url, &cbc, cipher_suite, proto_version)) 174 { 175 ret = 1; 176 goto cleanup; 177 } 178 179 if ((cbc.pos != TESTSTR_SIZE) || 180 (0 != check_read_data (cbc.buf, cbc.pos))) 181 { 182 fprintf (stderr, "Error: local file & received file differ.\n"); 183 ret = 1; 184 } 185 cleanup: 186 free (cbc.buf); 187 return ret; 188 } 189 190 191 /* perform a HTTP GET request via SSL/TLS */ 192 static unsigned int 193 test_secure_get (FILE *test_fd, 194 const char *cipher_suite, 195 int proto_version) 196 { 197 unsigned int ret; 198 struct MHD_Daemon *d; 199 uint16_t port; 200 201 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 202 port = 0; 203 else 204 port = 3045; 205 206 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 207 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS 208 | MHD_USE_ERROR_LOG, port, 209 NULL, NULL, 210 &iovec_ahc, NULL, 211 MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, 212 MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, 213 MHD_OPTION_END); 214 215 if (d == NULL) 216 { 217 fprintf (stderr, MHD_E_SERVER_INIT); 218 return 1; 219 } 220 if (0 == port) 221 { 222 const union MHD_DaemonInfo *dinfo; 223 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 224 if ((NULL == dinfo) || (0 == dinfo->port) ) 225 { 226 MHD_stop_daemon (d); 227 return 1; 228 } 229 port = dinfo->port; 230 } 231 232 ret = test_iovec_transfer (test_fd, 233 port, 234 cipher_suite, 235 proto_version); 236 237 MHD_stop_daemon (d); 238 return ret; 239 } 240 241 242 static enum MHD_Result 243 ahc_empty (void *cls, 244 struct MHD_Connection *connection, 245 const char *url, 246 const char *method, 247 const char *version, 248 const char *upload_data, 249 size_t *upload_data_size, 250 void **req_cls) 251 { 252 static int ptr; 253 struct MHD_Response *response; 254 enum MHD_Result ret; 255 struct MHD_IoVec iov; 256 (void) cls; 257 (void) url; 258 (void) url; 259 (void) version; /* Unused. Silent compiler warning. */ 260 (void) upload_data; 261 (void) upload_data_size; /* Unused. Silent compiler warning. */ 262 263 if (0 != strcmp (MHD_HTTP_METHOD_GET, 264 method)) 265 return MHD_NO; /* unexpected method */ 266 if (&ptr != *req_cls) 267 { 268 *req_cls = &ptr; 269 return MHD_YES; 270 } 271 *req_cls = NULL; 272 273 iov.iov_base = NULL; 274 iov.iov_len = 0; 275 276 response = MHD_create_response_from_iovec (&iov, 277 1, 278 NULL, 279 NULL); 280 ret = MHD_queue_response (connection, 281 MHD_HTTP_OK, 282 response); 283 MHD_destroy_response (response); 284 if (ret == MHD_NO) 285 { 286 fprintf (stderr, "Failed to queue response.\n"); 287 _exit (20); 288 } 289 return ret; 290 } 291 292 293 static int 294 curlExcessFound (CURL *c, 295 curl_infotype type, 296 char *data, 297 size_t size, 298 void *cls) 299 { 300 static const char *excess_found = "Excess found"; 301 const size_t str_size = strlen (excess_found); 302 (void) c; /* Unused. Silence compiler warning. */ 303 304 #ifdef _DEBUG 305 if ((CURLINFO_TEXT == type) || 306 (CURLINFO_HEADER_IN == type) || 307 (CURLINFO_HEADER_OUT == type)) 308 fprintf (stderr, "%.*s", (int) size, data); 309 #endif /* _DEBUG */ 310 if ((CURLINFO_TEXT == type) 311 && (size >= str_size) 312 && (0 == strncmp (excess_found, data, str_size))) 313 *(int *) cls = 1; 314 return 0; 315 } 316 317 318 static unsigned int 319 testEmptyGet (unsigned int poll_flag) 320 { 321 struct MHD_Daemon *d; 322 CURL *c; 323 char buf[2048]; 324 struct CBC cbc; 325 CURLcode errornum; 326 int excess_found = 0; 327 328 329 if ( (0 == global_port) && 330 (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) 331 { 332 global_port = 1225; 333 334 } 335 336 cbc.buf = buf; 337 cbc.size = 2048; 338 cbc.pos = 0; 339 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG 340 | poll_flag | MHD_USE_TLS, 341 global_port, NULL, NULL, 342 &ahc_empty, NULL, 343 MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, 344 MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, 345 MHD_OPTION_END); 346 if (d == NULL) 347 return 4194304; 348 if (0 == global_port) 349 { 350 const union MHD_DaemonInfo *dinfo; 351 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 352 if ((NULL == dinfo) || (0 == dinfo->port) ) 353 { 354 MHD_stop_daemon (d); return 32; 355 } 356 global_port = dinfo->port; 357 } 358 c = curl_easy_init (); 359 #ifdef _DEBUG 360 curl_easy_setopt (c, CURLOPT_VERBOSE, 1L); 361 #endif 362 curl_easy_setopt (c, CURLOPT_URL, "https://127.0.0.1/"); 363 curl_easy_setopt (c, CURLOPT_PORT, (long) global_port); 364 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 365 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 366 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 367 curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION, &curlExcessFound); 368 curl_easy_setopt (c, CURLOPT_DEBUGDATA, &excess_found); 369 curl_easy_setopt (c, CURLOPT_VERBOSE, 1L); 370 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 371 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 372 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 373 curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0L); 374 curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L); 375 /* NOTE: use of CONNECTTIMEOUT without also 376 setting NOSIGNAL results in really weird 377 crashes on my system!*/ 378 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 379 if (CURLE_OK != (errornum = curl_easy_perform (c))) 380 { 381 fprintf (stderr, 382 "curl_easy_perform failed: `%s'\n", 383 curl_easy_strerror (errornum)); 384 curl_easy_cleanup (c); 385 MHD_stop_daemon (d); 386 return 8388608; 387 } 388 curl_easy_cleanup (c); 389 MHD_stop_daemon (d); 390 if (cbc.pos != 0) 391 return 16777216; 392 if (excess_found) 393 return 33554432; 394 return 0; 395 } 396 397 398 int 399 main (int argc, char *const *argv) 400 { 401 unsigned int errorCount = 0; 402 (void) argc; (void) argv; /* Unused. Silent compiler warning. */ 403 404 #ifdef MHD_HTTPS_REQUIRE_GCRYPT 405 gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); 406 #ifdef GCRYCTL_INITIALIZATION_FINISHED 407 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); 408 #endif 409 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */ 410 if (! testsuite_curl_global_init ()) 411 return 99; 412 if (NULL == curl_version_info (CURLVERSION_NOW)->ssl_version) 413 { 414 fprintf (stderr, "Curl does not support SSL. Cannot run the test.\n"); 415 curl_global_cleanup (); 416 return 77; 417 } 418 419 errorCount += 420 test_secure_get (NULL, NULL, CURL_SSLVERSION_DEFAULT); 421 errorCount += testEmptyGet (0); 422 curl_global_cleanup (); 423 424 return errorCount != 0 ? 1 : 0; 425 }