test_digestauth_concurrent.c (22386B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2010 Christian Grothoff 4 Copyright (C) 2016-2022 Evgeny Grin (Karlson2k) 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 2, 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_digestauth_concurrent.c 24 * @brief Testcase for libmicrohttpd concurrent Digest Authorisation 25 * @author Amr Ali 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 29 #include "mhd_options.h" 30 #include "platform.h" 31 #include <curl/curl.h> 32 #include <microhttpd.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <time.h> 36 #if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \ 37 (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB)) 38 #define NEED_GCRYP_INIT 1 39 #include <gcrypt.h> 40 #endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */ 41 #include <errno.h> 42 43 #ifndef WINDOWS 44 #include <sys/socket.h> 45 #include <unistd.h> 46 #else 47 #include <wincrypt.h> 48 #endif 49 50 #include <pthread.h> 51 52 #include "mhd_has_param.h" 53 54 #ifndef CURL_VERSION_BITS 55 #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z)) 56 #endif /* ! CURL_VERSION_BITS */ 57 #ifndef CURL_AT_LEAST_VERSION 58 #define CURL_AT_LEAST_VERSION(x,y,z) \ 59 (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z)) 60 #endif /* ! CURL_AT_LEAST_VERSION */ 61 62 #ifndef _MHD_INSTRMACRO 63 /* Quoted macro parameter */ 64 #define _MHD_INSTRMACRO(a) #a 65 #endif /* ! _MHD_INSTRMACRO */ 66 #ifndef _MHD_STRMACRO 67 /* Quoted expanded macro parameter */ 68 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a) 69 #endif /* ! _MHD_STRMACRO */ 70 71 #if defined(HAVE___FUNC__) 72 #define externalErrorExit(ignore) \ 73 _externalErrorExit_func (NULL, __func__, __LINE__) 74 #define externalErrorExitDesc(errDesc) \ 75 _externalErrorExit_func (errDesc, __func__, __LINE__) 76 #define libcurlErrorExit(ignore) \ 77 _libcurlErrorExit_func (NULL, __func__, __LINE__) 78 #define libcurlErrorExitDesc(errDesc) \ 79 _libcurlErrorExit_func (errDesc, __func__, __LINE__) 80 #define mhdErrorExit(ignore) \ 81 _mhdErrorExit_func (NULL, __func__, __LINE__) 82 #define mhdErrorExitDesc(errDesc) \ 83 _mhdErrorExit_func (errDesc, __func__, __LINE__) 84 #define checkCURLE_OK(libcurlcall) \ 85 _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \ 86 __func__, __LINE__) 87 #elif defined(HAVE___FUNCTION__) 88 #define externalErrorExit(ignore) \ 89 _externalErrorExit_func (NULL, __FUNCTION__, __LINE__) 90 #define externalErrorExitDesc(errDesc) \ 91 _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__) 92 #define libcurlErrorExit(ignore) \ 93 _libcurlErrorExit_func (NULL, __FUNCTION__, __LINE__) 94 #define libcurlErrorExitDesc(errDesc) \ 95 _libcurlErrorExit_func (errDesc, __FUNCTION__, __LINE__) 96 #define mhdErrorExit(ignore) \ 97 _mhdErrorExit_func (NULL, __FUNCTION__, __LINE__) 98 #define mhdErrorExitDesc(errDesc) \ 99 _mhdErrorExit_func (errDesc, __FUNCTION__, __LINE__) 100 #define checkCURLE_OK(libcurlcall) \ 101 _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \ 102 __FUNCTION__, __LINE__) 103 #else 104 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__) 105 #define externalErrorExitDesc(errDesc) \ 106 _externalErrorExit_func (errDesc, NULL, __LINE__) 107 #define libcurlErrorExit(ignore) _libcurlErrorExit_func (NULL, NULL, __LINE__) 108 #define libcurlErrorExitDesc(errDesc) \ 109 _libcurlErrorExit_func (errDesc, NULL, __LINE__) 110 #define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__) 111 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__) 112 #define checkCURLE_OK(libcurlcall) \ 113 _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), NULL, \ 114 __LINE__) 115 #endif 116 117 118 _MHD_NORETURN static void 119 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 120 { 121 fflush (stdout); 122 if ((NULL != errDesc) && (0 != errDesc[0])) 123 fprintf (stderr, "%s", errDesc); 124 else 125 fprintf (stderr, "System or external library call failed"); 126 if ((NULL != funcName) && (0 != funcName[0])) 127 fprintf (stderr, " in %s", funcName); 128 if (0 < lineNum) 129 fprintf (stderr, " at line %d", lineNum); 130 131 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 132 strerror (errno)); 133 #ifdef MHD_WINSOCK_SOCKETS 134 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 135 #endif /* MHD_WINSOCK_SOCKETS */ 136 fflush (stderr); 137 exit (99); 138 } 139 140 141 /* Not actually used in this test */ 142 static char libcurl_errbuf[CURL_ERROR_SIZE] = ""; 143 144 _MHD_NORETURN static void 145 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 146 { 147 fflush (stdout); 148 if ((NULL != errDesc) && (0 != errDesc[0])) 149 fprintf (stderr, "%s", errDesc); 150 else 151 fprintf (stderr, "CURL library call failed"); 152 if ((NULL != funcName) && (0 != funcName[0])) 153 fprintf (stderr, " in %s", funcName); 154 if (0 < lineNum) 155 fprintf (stderr, " at line %d", lineNum); 156 157 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 158 strerror (errno)); 159 #ifdef MHD_WINSOCK_SOCKETS 160 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 161 #endif /* MHD_WINSOCK_SOCKETS */ 162 if (0 != libcurl_errbuf[0]) 163 fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf); 164 165 fflush (stderr); 166 exit (99); 167 } 168 169 170 _MHD_NORETURN static void 171 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 172 { 173 fflush (stdout); 174 if ((NULL != errDesc) && (0 != errDesc[0])) 175 fprintf (stderr, "%s", errDesc); 176 else 177 fprintf (stderr, "MHD unexpected error"); 178 if ((NULL != funcName) && (0 != funcName[0])) 179 fprintf (stderr, " in %s", funcName); 180 if (0 < lineNum) 181 fprintf (stderr, " at line %d", lineNum); 182 183 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 184 strerror (errno)); 185 #ifdef MHD_WINSOCK_SOCKETS 186 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 187 #endif /* MHD_WINSOCK_SOCKETS */ 188 189 fflush (stderr); 190 exit (8); 191 } 192 193 194 #if 0 195 /* Function unused in this test */ 196 static void 197 _checkCURLE_OK_func (CURLcode code, const char *curlFunc, 198 const char *funcName, int lineNum) 199 { 200 if (CURLE_OK == code) 201 return; 202 203 fflush (stdout); 204 if ((NULL != curlFunc) && (0 != curlFunc[0])) 205 fprintf (stderr, "'%s' resulted in '%s'", curlFunc, 206 curl_easy_strerror (code)); 207 else 208 fprintf (stderr, "libcurl function call resulted in '%s'", 209 curl_easy_strerror (code)); 210 if ((NULL != funcName) && (0 != funcName[0])) 211 fprintf (stderr, " in %s", funcName); 212 if (0 < lineNum) 213 fprintf (stderr, " at line %d", lineNum); 214 215 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 216 strerror (errno)); 217 if (0 != libcurl_errbuf[0]) 218 fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf); 219 220 fflush (stderr); 221 exit (9); 222 } 223 224 225 #endif 226 227 228 /* Could be increased to facilitate debugging */ 229 #define TIMEOUTS_VAL 5 230 231 #define MHD_URI_BASE_PATH "/bar%20foo?key=value" 232 233 #define PAGE \ 234 "<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>" 235 236 #define DENIED \ 237 "<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>" 238 239 #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" 240 241 struct CBC 242 { 243 char *buf; 244 size_t pos; 245 size_t size; 246 }; 247 248 static int verbose; 249 250 static size_t 251 copyBuffer (void *ptr, 252 size_t size, 253 size_t nmemb, 254 void *ctx) 255 { 256 struct CBC *cbc = ctx; 257 258 if (cbc->pos + size * nmemb > cbc->size) 259 mhdErrorExitDesc ("Wrong too large data"); /* overflow */ 260 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 261 cbc->pos += size * nmemb; 262 return size * nmemb; 263 } 264 265 266 static enum MHD_Result 267 ahc_echo (void *cls, 268 struct MHD_Connection *connection, 269 const char *url, 270 const char *method, 271 const char *version, 272 const char *upload_data, 273 size_t *upload_data_size, 274 void **req_cls) 275 { 276 struct MHD_Response *response; 277 char *username; 278 const char *password = "testpass"; 279 const char *realm = "test@example.com"; 280 enum MHD_Result ret; 281 enum MHD_DigestAuthResult ret_e; 282 static int already_called_marker; 283 (void) cls; (void) url; /* Unused. Silent compiler warning. */ 284 (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */ 285 (void) upload_data_size; (void) req_cls; /* Unused. Silent compiler warning. */ 286 287 if (&already_called_marker != *req_cls) 288 { /* Called for the first time, request not fully read yet */ 289 *req_cls = &already_called_marker; 290 /* Wait for complete request */ 291 return MHD_YES; 292 } 293 294 username = MHD_digest_auth_get_username (connection); 295 if ( (username == NULL) || 296 (0 != strcmp (username, "testuser")) ) 297 { 298 response = MHD_create_response_from_buffer_static (strlen (DENIED), 299 DENIED); 300 if (NULL == response) 301 mhdErrorExitDesc ("MHD_create_response_from_buffer failed"); 302 ret = MHD_queue_auth_fail_response2 (connection, 303 realm, 304 MY_OPAQUE, 305 response, 306 MHD_NO, 307 MHD_DIGEST_ALG_MD5); 308 if (MHD_YES != ret) 309 mhdErrorExitDesc ("MHD_queue_auth_fail_response2 failed"); 310 MHD_destroy_response (response); 311 return ret; 312 } 313 ret_e = MHD_digest_auth_check3 (connection, 314 realm, 315 username, 316 password, 317 50 * TIMEOUTS_VAL, 318 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, 319 MHD_DIGEST_AUTH_MULT_ALGO3_MD5); 320 MHD_free (username); 321 if (ret_e != MHD_DAUTH_OK) 322 { 323 response = MHD_create_response_from_buffer_static (strlen (DENIED), 324 DENIED); 325 if (NULL == response) 326 mhdErrorExitDesc ("MHD_create_response_from_buffer() failed"); 327 ret = MHD_queue_auth_fail_response2 (connection, 328 realm, 329 MY_OPAQUE, 330 response, 331 (MHD_DAUTH_NONCE_STALE == ret_e) ? 332 MHD_YES : MHD_NO, 333 MHD_DIGEST_ALG_MD5); 334 if (MHD_YES != ret) 335 mhdErrorExitDesc ("MHD_queue_auth_fail_response2() failed"); 336 MHD_destroy_response (response); 337 return ret; 338 } 339 response = MHD_create_response_from_buffer_static (strlen (PAGE), 340 PAGE); 341 if (NULL == response) 342 mhdErrorExitDesc ("MHD_create_response_from_buffer() failed"); 343 ret = MHD_queue_response (connection, 344 MHD_HTTP_OK, 345 response); 346 if (MHD_YES != ret) 347 mhdErrorExitDesc ("MHD_queue_auth_fail_response2() failed"); 348 MHD_destroy_response (response); 349 return ret; 350 } 351 352 353 static CURL * 354 setupCURL (void *cbc, uint16_t port, char *errbuf) 355 { 356 CURL *c; 357 char url[512]; 358 359 if (1) 360 { 361 int res; 362 /* A workaround for some old libcurl versions, which ignore the specified 363 * port by CURLOPT_PORT when digest authorisation is used. */ 364 res = snprintf (url, (sizeof(url) / sizeof(url[0])), 365 "http://127.0.0.1:%u%s", (unsigned int) port, 366 MHD_URI_BASE_PATH); 367 if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res)) 368 externalErrorExitDesc ("Cannot form request URL"); 369 } 370 371 c = curl_easy_init (); 372 if (NULL == c) 373 libcurlErrorExitDesc ("curl_easy_init() failed"); 374 375 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || 376 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, 377 errbuf)) || 378 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, 379 ©Buffer)) || 380 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) || 381 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 382 ((long) TIMEOUTS_VAL))) || 383 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, 384 ((long) TIMEOUTS_VAL))) || 385 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 386 CURL_HTTP_VERSION_1_1)) || 387 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) || 388 #ifdef _DEBUG 389 (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) || 390 #endif /* _DEBUG */ 391 #if CURL_AT_LEAST_VERSION (7, 85, 0) 392 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http")) || 393 #elif CURL_AT_LEAST_VERSION (7, 19, 4) 394 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) || 395 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */ 396 #if CURL_AT_LEAST_VERSION (7, 45, 0) 397 (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) || 398 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */ 399 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))) || 400 (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, url))) 401 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 402 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST)) || 403 (CURLE_OK != curl_easy_setopt (c, CURLOPT_USERPWD, 404 "testuser:testpass"))) 405 libcurlErrorExitDesc ("curl_easy_setopt() authorization options failed"); 406 return c; 407 } 408 409 410 static void 411 getRnd (void *buf, size_t size) 412 { 413 #ifndef WINDOWS 414 int fd; 415 size_t off = 0; 416 417 fd = open ("/dev/urandom", 418 O_RDONLY); 419 if (-1 == fd) 420 externalErrorExitDesc ("Failed to open '/dev/urandom'"); 421 422 do 423 { 424 ssize_t res; 425 res = read (fd, ((uint8_t *) buf) + off, size - off); 426 if (0 > res) 427 externalErrorExitDesc ("Failed to read '/dev/urandom'"); 428 off += (size_t) res; 429 } while (off < size); 430 (void) close (fd); 431 #else 432 HCRYPTPROV cc; 433 BOOL b; 434 435 b = CryptAcquireContext (&cc, 436 NULL, 437 NULL, 438 PROV_RSA_FULL, 439 CRYPT_VERIFYCONTEXT | CRYPT_SILENT); 440 if (b == 0) 441 externalErrorExitDesc ("CryptAcquireContext() failed"); 442 b = CryptGenRandom (cc, (DWORD) size, (BYTE *) buf); 443 if (b == 0) 444 externalErrorExitDesc ("CryptGenRandom() failed"); 445 CryptReleaseContext (cc, 0); 446 #endif /* ! WINDOWS */ 447 } 448 449 450 struct curlWokerInfo 451 { 452 int workerNumber; 453 struct CBC cbc; 454 pthread_t tid; 455 /** 456 * The libcurl handle to run in thread 457 */ 458 CURL *c; 459 char *libcurl_errbuf; 460 /** 461 * Non-zero if worker is finished 462 */ 463 volatile int finished; 464 /** 465 * The number of successful worker results 466 */ 467 volatile unsigned int success; 468 }; 469 470 471 static void * 472 worker_func (void *param) 473 { 474 struct curlWokerInfo *const w = (struct curlWokerInfo *) param; 475 CURLcode req_result; 476 if (NULL == w) 477 externalErrorExit (); 478 479 req_result = curl_easy_perform (w->c); 480 if (CURLE_OK != req_result) 481 { 482 if (0 != w->libcurl_errbuf[0]) 483 fprintf (stderr, "Worker %d: first request failed. " 484 "libcurl error: '%s'.\n" 485 "libcurl error description: '%s'.\n", 486 w->workerNumber, curl_easy_strerror (req_result), 487 w->libcurl_errbuf); 488 else 489 fprintf (stderr, "Worker %d: first request failed. " 490 "libcurl error: '%s'.\n", 491 w->workerNumber, curl_easy_strerror (req_result)); 492 } 493 else 494 { 495 if (w->cbc.pos != strlen (PAGE)) 496 { 497 fprintf (stderr, "Worker %d: Got %u bytes ('%.*s'), expected %u bytes. ", 498 w->workerNumber, 499 (unsigned) w->cbc.pos, (int) w->cbc.pos, w->cbc.buf, 500 (unsigned) strlen (MHD_URI_BASE_PATH)); 501 mhdErrorExitDesc ("Wrong returned data length"); 502 } 503 if (0 != strncmp (PAGE, w->cbc.buf, strlen (PAGE))) 504 { 505 fprintf (stderr, "Worker %d: Got invalid response '%.*s'. ", 506 w->workerNumber, 507 (int) w->cbc.pos, w->cbc.buf); 508 mhdErrorExitDesc ("Wrong returned data"); 509 } 510 if (verbose) 511 printf ("Worker %d: first request successful.\n", w->workerNumber); 512 w->success++; 513 } 514 #ifdef _DEBUG 515 fflush (stderr); 516 fflush (stdout); 517 #endif /* _DEBUG */ 518 519 /* Second request */ 520 w->cbc.pos = 0; 521 req_result = curl_easy_perform (w->c); 522 if (CURLE_OK != req_result) 523 { 524 if (0 != w->libcurl_errbuf[0]) 525 fprintf (stderr, "Worker %d: second request failed. " 526 "libcurl error: '%s'.\n" 527 "libcurl error description: '%s'.\n", 528 w->workerNumber, curl_easy_strerror (req_result), 529 w->libcurl_errbuf); 530 else 531 fprintf (stderr, "Worker %d: second request failed. " 532 "libcurl error: '%s'.\n", 533 w->workerNumber, curl_easy_strerror (req_result)); 534 } 535 else 536 { 537 if (w->cbc.pos != strlen (PAGE)) 538 { 539 fprintf (stderr, "Worker %d: Got %u bytes ('%.*s'), expected %u bytes. ", 540 w->workerNumber, 541 (unsigned) w->cbc.pos, (int) w->cbc.pos, w->cbc.buf, 542 (unsigned) strlen (MHD_URI_BASE_PATH)); 543 mhdErrorExitDesc ("Wrong returned data length"); 544 } 545 if (0 != strncmp (PAGE, w->cbc.buf, strlen (PAGE))) 546 { 547 fprintf (stderr, "Worker %d: Got invalid response '%.*s'. ", 548 w->workerNumber, 549 (int) w->cbc.pos, w->cbc.buf); 550 mhdErrorExitDesc ("Wrong returned data"); 551 } 552 if (verbose) 553 printf ("Worker %d: second request successful.\n", w->workerNumber); 554 w->success++; 555 } 556 #ifdef _DEBUG 557 fflush (stderr); 558 fflush (stdout); 559 #endif /* _DEBUG */ 560 561 w->finished = ! 0; 562 return NULL; 563 } 564 565 566 #define CLIENT_BUF_SIZE 2048 567 568 static unsigned int 569 testDigestAuth (void) 570 { 571 struct MHD_Daemon *d; 572 char rnd[8]; 573 uint16_t port; 574 size_t i; 575 /* Run three workers in parallel so at least two workers would start within 576 * the same monotonic clock second.*/ 577 struct curlWokerInfo workers[3]; 578 unsigned int ret; 579 580 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 581 port = 0; 582 else 583 port = 4200; 584 585 getRnd (rnd, sizeof(rnd)); 586 587 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 588 port, NULL, NULL, 589 &ahc_echo, NULL, 590 MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd, 591 MHD_OPTION_NONCE_NC_SIZE, 300, 592 MHD_OPTION_THREAD_POOL_SIZE, 593 (unsigned int) (sizeof(workers) / sizeof(workers[0])), 594 MHD_OPTION_DIGEST_AUTH_DEFAULT_MAX_NC, (uint32_t) 999, 595 MHD_OPTION_END); 596 if (d == NULL) 597 return 1; 598 if (0 == port) 599 { 600 const union MHD_DaemonInfo *dinfo; 601 602 dinfo = MHD_get_daemon_info (d, 603 MHD_DAEMON_INFO_BIND_PORT); 604 if ( (NULL == dinfo) || 605 (0 == dinfo->port) ) 606 mhdErrorExitDesc ("MHD_get_daemon_info() failed"); 607 port = dinfo->port; 608 } 609 610 /* Initialise all workers */ 611 for (i = 0; i < sizeof(workers) / sizeof(workers[0]); i++) 612 { 613 struct curlWokerInfo *const w = workers + i; 614 w->workerNumber = (int) i + 1; /* Use 1-based numbering */ 615 w->cbc.buf = malloc (CLIENT_BUF_SIZE); 616 if (NULL == w->cbc.buf) 617 externalErrorExitDesc ("malloc() failed"); 618 w->cbc.size = CLIENT_BUF_SIZE; 619 w->cbc.pos = 0; 620 w->libcurl_errbuf = malloc (CURL_ERROR_SIZE); 621 if (NULL == w->libcurl_errbuf) 622 externalErrorExitDesc ("malloc() failed"); 623 w->libcurl_errbuf[0] = 0; 624 w->c = setupCURL (&w->cbc, port, w->libcurl_errbuf); 625 w->finished = 0; 626 w->success = 0; 627 } 628 629 /* Fire already initialised workers */ 630 for (i = 0; i < sizeof(workers) / sizeof(workers[0]); i++) 631 { 632 struct curlWokerInfo *const w = workers + i; 633 if (0 != pthread_create (&w->tid, NULL, &worker_func, w)) 634 externalErrorExitDesc ("pthread_create() failed"); 635 } 636 637 /* Collect results, cleanup workers */ 638 ret = 0; 639 for (i = 0; i < sizeof(workers) / sizeof(workers[0]); i++) 640 { 641 struct curlWokerInfo *const w = workers + i; 642 if (0 != pthread_join (w->tid, NULL)) 643 externalErrorExitDesc ("pthread_join() failed"); 644 curl_easy_cleanup (w->c); 645 free (w->libcurl_errbuf); 646 free (w->cbc.buf); 647 if (! w->finished) 648 externalErrorExitDesc ("The worker thread did't signal 'finished' state"); 649 ret += 2 - w->success; 650 } 651 652 MHD_stop_daemon (d); 653 return ret; 654 } 655 656 657 int 658 main (int argc, char *const *argv) 659 { 660 unsigned int errorCount = 0; 661 (void) argc; (void) argv; /* Unused. Silent compiler warning. */ 662 #if (LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR == 62) 663 if (1) 664 { 665 fprintf (stderr, "libcurl version 7.62.x has bug in processing " 666 "URI with GET arguments for Digest Auth.\n"); 667 fprintf (stderr, "This test cannot be performed.\n"); 668 exit (77); 669 } 670 #endif /* libcurl version 7.62.x */ 671 672 verbose = ! (has_param (argc, argv, "-q") || 673 has_param (argc, argv, "--quiet") || 674 has_param (argc, argv, "-s") || 675 has_param (argc, argv, "--silent")); 676 677 #ifdef NEED_GCRYP_INIT 678 gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); 679 #ifdef GCRYCTL_INITIALIZATION_FINISHED 680 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); 681 #endif /* GCRYCTL_INITIALIZATION_FINISHED */ 682 #endif /* NEED_GCRYP_INIT */ 683 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 684 return 2; 685 errorCount += testDigestAuth (); 686 if (errorCount != 0) 687 fprintf (stderr, "Error (code: %u)\n", errorCount); 688 curl_global_cleanup (); 689 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 690 }