diff options
author | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2022-07-25 10:17:53 +0300 |
---|---|---|
committer | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2022-07-28 07:21:36 +0300 |
commit | 2fd1a01fe010ea47654fd6b3053af619fb96dc37 (patch) | |
tree | 47122c5924ea59a1c01847915fe63c91d04717bd | |
parent | c03c57c9d2d95bd739ab8a149597658d1ec95478 (diff) | |
download | libmicrohttpd-2fd1a01fe010ea47654fd6b3053af619fb96dc37.tar.gz libmicrohttpd-2fd1a01fe010ea47654fd6b3053af619fb96dc37.zip |
test_digestauth2: added new group of tests for Digest Auth checking
-rw-r--r-- | src/testcurl/.gitignore | 5 | ||||
-rw-r--r-- | src/testcurl/Makefile.am | 22 | ||||
-rw-r--r-- | src/testcurl/test_digestauth2.c | 1159 |
3 files changed, 1185 insertions, 1 deletions
diff --git a/src/testcurl/.gitignore b/src/testcurl/.gitignore index e23f52c9..26e59bfc 100644 --- a/src/testcurl/.gitignore +++ b/src/testcurl/.gitignore | |||
@@ -157,3 +157,8 @@ core | |||
157 | /test_basicauth_preauth_oldapi | 157 | /test_basicauth_preauth_oldapi |
158 | /test_digestauth_emu_ext | 158 | /test_digestauth_emu_ext |
159 | /test_digestauth_emu_ext_oldapi | 159 | /test_digestauth_emu_ext_oldapi |
160 | /test_digestauth2 | ||
161 | /test_digestauth2_oldapi | ||
162 | /test_digestauth2_userhash | ||
163 | /test_digestauth2_sha256 | ||
164 | /test_digestauth2_sha256_userhash | ||
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am index e46bbb2d..749e31e2 100644 --- a/src/testcurl/Makefile.am +++ b/src/testcurl/Makefile.am | |||
@@ -169,7 +169,12 @@ THREAD_ONLY_TESTS += \ | |||
169 | 169 | ||
170 | check_PROGRAMS += \ | 170 | check_PROGRAMS += \ |
171 | test_digestauth_emu_ext \ | 171 | test_digestauth_emu_ext \ |
172 | test_digestauth_emu_ext_oldapi | 172 | test_digestauth_emu_ext_oldapi \ |
173 | test_digestauth2 \ | ||
174 | test_digestauth2_oldapi \ | ||
175 | test_digestauth2_userhash \ | ||
176 | test_digestauth2_sha256 \ | ||
177 | test_digestauth2_sha256_userhash | ||
173 | endif | 178 | endif |
174 | 179 | ||
175 | if HEAVY_TESTS | 180 | if HEAVY_TESTS |
@@ -279,6 +284,21 @@ test_digestauth_emu_ext_SOURCES = \ | |||
279 | test_digestauth_emu_ext_oldapi_SOURCES = \ | 284 | test_digestauth_emu_ext_oldapi_SOURCES = \ |
280 | test_digestauth_emu_ext.c | 285 | test_digestauth_emu_ext.c |
281 | 286 | ||
287 | test_digestauth2_SOURCES = \ | ||
288 | test_digestauth2.c mhd_has_param.h mhd_has_in_name.h | ||
289 | |||
290 | test_digestauth2_oldapi_SOURCES = \ | ||
291 | test_digestauth2.c mhd_has_param.h mhd_has_in_name.h | ||
292 | |||
293 | test_digestauth2_userhash_SOURCES = \ | ||
294 | test_digestauth2.c mhd_has_param.h mhd_has_in_name.h | ||
295 | |||
296 | test_digestauth2_sha256_SOURCES = \ | ||
297 | test_digestauth2.c mhd_has_param.h mhd_has_in_name.h | ||
298 | |||
299 | test_digestauth2_sha256_userhash_SOURCES = \ | ||
300 | test_digestauth2.c mhd_has_param.h mhd_has_in_name.h | ||
301 | |||
282 | test_get_iovec_SOURCES = \ | 302 | test_get_iovec_SOURCES = \ |
283 | test_get_iovec.c mhd_has_in_name.h | 303 | test_get_iovec.c mhd_has_in_name.h |
284 | 304 | ||
diff --git a/src/testcurl/test_digestauth2.c b/src/testcurl/test_digestauth2.c new file mode 100644 index 00000000..0d6b844a --- /dev/null +++ b/src/testcurl/test_digestauth2.c | |||
@@ -0,0 +1,1159 @@ | |||
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_digest2.c | ||
24 | * @brief Testcase for MHD Digest Authorisation | ||
25 | * @author Karlson2k (Evgeny Grin) | ||
26 | */ | ||
27 | |||
28 | #include "MHD_config.h" | ||
29 | #include "platform.h" | ||
30 | #include <curl/curl.h> | ||
31 | #include <microhttpd.h> | ||
32 | #include <stdlib.h> | ||
33 | #include <string.h> | ||
34 | #include <time.h> | ||
35 | |||
36 | #ifndef _WIN32 | ||
37 | #include <sys/socket.h> | ||
38 | #include <unistd.h> | ||
39 | #else | ||
40 | #include <wincrypt.h> | ||
41 | #endif | ||
42 | |||
43 | #include "mhd_has_param.h" | ||
44 | #include "mhd_has_in_name.h" | ||
45 | |||
46 | #ifndef MHD_STATICSTR_LEN_ | ||
47 | /** | ||
48 | * Determine length of static string / macro strings at compile time. | ||
49 | */ | ||
50 | #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) | ||
51 | #endif /* ! MHD_STATICSTR_LEN_ */ | ||
52 | |||
53 | #ifndef CURL_VERSION_BITS | ||
54 | #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z)) | ||
55 | #endif /* ! CURL_VERSION_BITS */ | ||
56 | #ifndef CURL_AT_LEAST_VERSION | ||
57 | #define CURL_AT_LEAST_VERSION(x,y,z) \ | ||
58 | (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) | ||
59 | #endif /* ! CURL_AT_LEAST_VERSION */ | ||
60 | |||
61 | #ifndef _MHD_INSTRMACRO | ||
62 | /* Quoted macro parameter */ | ||
63 | #define _MHD_INSTRMACRO(a) #a | ||
64 | #endif /* ! _MHD_INSTRMACRO */ | ||
65 | #ifndef _MHD_STRMACRO | ||
66 | /* Quoted expanded macro parameter */ | ||
67 | #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a) | ||
68 | #endif /* ! _MHD_STRMACRO */ | ||
69 | |||
70 | #if defined(HAVE___FUNC__) | ||
71 | #define externalErrorExit(ignore) \ | ||
72 | _externalErrorExit_func(NULL, __func__, __LINE__) | ||
73 | #define externalErrorExitDesc(errDesc) \ | ||
74 | _externalErrorExit_func(errDesc, __func__, __LINE__) | ||
75 | #define libcurlErrorExit(ignore) \ | ||
76 | _libcurlErrorExit_func(NULL, __func__, __LINE__) | ||
77 | #define libcurlErrorExitDesc(errDesc) \ | ||
78 | _libcurlErrorExit_func(errDesc, __func__, __LINE__) | ||
79 | #define mhdErrorExit(ignore) \ | ||
80 | _mhdErrorExit_func(NULL, __func__, __LINE__) | ||
81 | #define mhdErrorExitDesc(errDesc) \ | ||
82 | _mhdErrorExit_func(errDesc, __func__, __LINE__) | ||
83 | #define checkCURLE_OK(libcurlcall) \ | ||
84 | _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \ | ||
85 | __func__, __LINE__) | ||
86 | #elif defined(HAVE___FUNCTION__) | ||
87 | #define externalErrorExit(ignore) \ | ||
88 | _externalErrorExit_func(NULL, __FUNCTION__, __LINE__) | ||
89 | #define externalErrorExitDesc(errDesc) \ | ||
90 | _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__) | ||
91 | #define libcurlErrorExit(ignore) \ | ||
92 | _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__) | ||
93 | #define libcurlErrorExitDesc(errDesc) \ | ||
94 | _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__) | ||
95 | #define mhdErrorExit(ignore) \ | ||
96 | _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__) | ||
97 | #define mhdErrorExitDesc(errDesc) \ | ||
98 | _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__) | ||
99 | #define checkCURLE_OK(libcurlcall) \ | ||
100 | _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \ | ||
101 | __FUNCTION__, __LINE__) | ||
102 | #else | ||
103 | #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__) | ||
104 | #define externalErrorExitDesc(errDesc) \ | ||
105 | _externalErrorExit_func(errDesc, NULL, __LINE__) | ||
106 | #define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__) | ||
107 | #define libcurlErrorExitDesc(errDesc) \ | ||
108 | _libcurlErrorExit_func(errDesc, NULL, __LINE__) | ||
109 | #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__) | ||
110 | #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__) | ||
111 | #define checkCURLE_OK(libcurlcall) \ | ||
112 | _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), NULL, __LINE__) | ||
113 | #endif | ||
114 | |||
115 | |||
116 | _MHD_NORETURN static void | ||
117 | _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) | ||
118 | { | ||
119 | fflush (stdout); | ||
120 | if ((NULL != errDesc) && (0 != errDesc[0])) | ||
121 | fprintf (stderr, "%s", errDesc); | ||
122 | else | ||
123 | fprintf (stderr, "System or external library call failed"); | ||
124 | if ((NULL != funcName) && (0 != funcName[0])) | ||
125 | fprintf (stderr, " in %s", funcName); | ||
126 | if (0 < lineNum) | ||
127 | fprintf (stderr, " at line %d", lineNum); | ||
128 | |||
129 | fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, | ||
130 | strerror (errno)); | ||
131 | #ifdef MHD_WINSOCK_SOCKETS | ||
132 | fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); | ||
133 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
134 | fflush (stderr); | ||
135 | exit (99); | ||
136 | } | ||
137 | |||
138 | |||
139 | /* Not actually used in this test */ | ||
140 | static char libcurl_errbuf[CURL_ERROR_SIZE] = ""; | ||
141 | |||
142 | _MHD_NORETURN static void | ||
143 | _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum) | ||
144 | { | ||
145 | fflush (stdout); | ||
146 | if ((NULL != errDesc) && (0 != errDesc[0])) | ||
147 | fprintf (stderr, "%s", errDesc); | ||
148 | else | ||
149 | fprintf (stderr, "CURL library call failed"); | ||
150 | if ((NULL != funcName) && (0 != funcName[0])) | ||
151 | fprintf (stderr, " in %s", funcName); | ||
152 | if (0 < lineNum) | ||
153 | fprintf (stderr, " at line %d", lineNum); | ||
154 | |||
155 | fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, | ||
156 | strerror (errno)); | ||
157 | #ifdef MHD_WINSOCK_SOCKETS | ||
158 | fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); | ||
159 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
160 | if (0 != libcurl_errbuf[0]) | ||
161 | fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf); | ||
162 | |||
163 | fflush (stderr); | ||
164 | exit (99); | ||
165 | } | ||
166 | |||
167 | |||
168 | _MHD_NORETURN static void | ||
169 | _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) | ||
170 | { | ||
171 | fflush (stdout); | ||
172 | if ((NULL != errDesc) && (0 != errDesc[0])) | ||
173 | fprintf (stderr, "%s", errDesc); | ||
174 | else | ||
175 | fprintf (stderr, "MHD unexpected error"); | ||
176 | if ((NULL != funcName) && (0 != funcName[0])) | ||
177 | fprintf (stderr, " in %s", funcName); | ||
178 | if (0 < lineNum) | ||
179 | fprintf (stderr, " at line %d", lineNum); | ||
180 | |||
181 | fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, | ||
182 | strerror (errno)); | ||
183 | #ifdef MHD_WINSOCK_SOCKETS | ||
184 | fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); | ||
185 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
186 | |||
187 | fflush (stderr); | ||
188 | exit (8); | ||
189 | } | ||
190 | |||
191 | |||
192 | #if 0 | ||
193 | /* Function unused in this test */ | ||
194 | static void | ||
195 | _checkCURLE_OK_func (CURLcode code, const char *curlFunc, | ||
196 | const char *funcName, int lineNum) | ||
197 | { | ||
198 | if (CURLE_OK == code) | ||
199 | return; | ||
200 | |||
201 | fflush (stdout); | ||
202 | if ((NULL != curlFunc) && (0 != curlFunc[0])) | ||
203 | fprintf (stderr, "'%s' resulted in '%s'", curlFunc, | ||
204 | curl_easy_strerror (code)); | ||
205 | else | ||
206 | fprintf (stderr, "libcurl function call resulted in '%s'", | ||
207 | curl_easy_strerror (code)); | ||
208 | if ((NULL != funcName) && (0 != funcName[0])) | ||
209 | fprintf (stderr, " in %s", funcName); | ||
210 | if (0 < lineNum) | ||
211 | fprintf (stderr, " at line %d", lineNum); | ||
212 | |||
213 | fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, | ||
214 | strerror (errno)); | ||
215 | if (0 != libcurl_errbuf[0]) | ||
216 | fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf); | ||
217 | |||
218 | fflush (stderr); | ||
219 | exit (9); | ||
220 | } | ||
221 | |||
222 | |||
223 | #endif | ||
224 | |||
225 | |||
226 | /* Could be increased to facilitate debugging */ | ||
227 | #define TIMEOUTS_VAL 10 | ||
228 | |||
229 | #define MHD_URI_BASE_PATH "/bar%20foo?key=value" | ||
230 | |||
231 | #define REALM "TestRealm" | ||
232 | #define USERNAME1 "test_user" | ||
233 | /* The hex form of MD5("test_user:TestRealm") */ | ||
234 | #define USERHASH1_MD5_HEX "c53c601503ff176f18f623725fba4281" | ||
235 | #define USERHASH1_MD5_BIN 0xc5, 0x3c, 0x60, 0x15, 0x03, 0xff, 0x17, 0x6f, \ | ||
236 | 0x18, 0xf6, 0x23, 0x72, 0x5f, 0xba, 0x42, 0x81 | ||
237 | /* The hex form of SHA-256("test_user:TestRealm") */ | ||
238 | #define USERHASH1_SHA256_HEX \ | ||
239 | "090c7e06b77d6614cf5fe6cafa004d2e5f8fb36ba45a0e35eacb2eb7728f34de" | ||
240 | /* The binary form of SHA-256("test_user:TestRealm") */ | ||
241 | #define USERHASH1_SHA256_BIN 0x09, 0x0c, 0x7e, 0x06, 0xb7, 0x7d, 0x66, 0x14, \ | ||
242 | 0xcf, 0x5f, 0xe6, 0xca, 0xfa, 0x00, 0x4d, 0x2e, 0x5f, 0x8f, 0xb3, 0x6b, \ | ||
243 | 0xa4, 0x5a, 0x0e, 0x35, 0xea, 0xcb, 0x2e, 0xb7, 0x72, 0x8f, 0x34, 0xde | ||
244 | /* "titkos szuperügynök" in UTF-8 */ | ||
245 | #define USERNAME2 "titkos szuper" "\xC3\xBC" "gyn" "\xC3\xB6" "k" | ||
246 | /* percent-encoded username */ | ||
247 | #define USERNAME2_PCTENC "titkos%20szuper%C3%BCgyn%C3%B6k" | ||
248 | #define PASSWORD_VALUE "test pass" | ||
249 | #define OPAQUE_VALUE "opaque+content" /* Base64 character set */ | ||
250 | |||
251 | |||
252 | #define PAGE \ | ||
253 | "<html><head><title>libmicrohttpd demo page</title>" \ | ||
254 | "</head><body>Access granted</body></html>" | ||
255 | |||
256 | #define DENIED \ | ||
257 | "<html><head><title>libmicrohttpd - Access denied</title>" \ | ||
258 | "</head><body>Access denied</body></html>" | ||
259 | |||
260 | /* Global parameters */ | ||
261 | static int verbose; | ||
262 | static int test_oldapi; | ||
263 | static int test_userhash; | ||
264 | static int test_sha256; | ||
265 | static int curl_uses_usehash; | ||
266 | |||
267 | /* Static helper variables */ | ||
268 | static const char userhash1_md5_hex[] = USERHASH1_MD5_HEX; | ||
269 | static const uint8_t userhash1_md5_bin[] = { USERHASH1_MD5_BIN }; | ||
270 | static const char userhash1_sha256_hex[] = USERHASH1_SHA256_HEX; | ||
271 | static const uint8_t userhash1_sha256_bin[] = { USERHASH1_SHA256_BIN }; | ||
272 | static const char *userhash1_hex; | ||
273 | static size_t userhash1_hex_len; | ||
274 | static const uint8_t *userhash1_bin; | ||
275 | static const char *username_ptr; | ||
276 | |||
277 | static void | ||
278 | test_global_init (void) | ||
279 | { | ||
280 | libcurl_errbuf[0] = 0; | ||
281 | |||
282 | if (0 != curl_global_init (CURL_GLOBAL_WIN32)) | ||
283 | externalErrorExit (); | ||
284 | |||
285 | username_ptr = USERNAME1; | ||
286 | if (! test_sha256) | ||
287 | { | ||
288 | userhash1_hex = userhash1_md5_hex; | ||
289 | userhash1_hex_len = MHD_STATICSTR_LEN_ (userhash1_md5_hex); | ||
290 | userhash1_bin = userhash1_md5_bin; | ||
291 | if ((userhash1_hex_len / 2) != \ | ||
292 | (sizeof(userhash1_md5_bin) / sizeof(userhash1_md5_bin[0]))) | ||
293 | externalErrorExitDesc ("Wrong size of the 'userhash1_md5_bin' array"); | ||
294 | } | ||
295 | else | ||
296 | { | ||
297 | userhash1_hex = userhash1_sha256_hex; | ||
298 | userhash1_hex_len = MHD_STATICSTR_LEN_ (userhash1_sha256_hex); | ||
299 | userhash1_bin = userhash1_sha256_bin; | ||
300 | if ((userhash1_hex_len / 2) != \ | ||
301 | (sizeof(userhash1_sha256_bin) \ | ||
302 | / sizeof(userhash1_sha256_bin[0]))) | ||
303 | externalErrorExitDesc ("Wrong size of the 'userhash1_sha256_bin' array"); | ||
304 | } | ||
305 | } | ||
306 | |||
307 | |||
308 | static void | ||
309 | test_global_cleanup (void) | ||
310 | { | ||
311 | curl_global_cleanup (); | ||
312 | } | ||
313 | |||
314 | |||
315 | static int | ||
316 | gen_good_rnd (void *rnd_buf, size_t rnd_buf_size) | ||
317 | { | ||
318 | if (1024 < rnd_buf_size) | ||
319 | externalErrorExitDesc ("Too large amount of random data " \ | ||
320 | "is requested"); | ||
321 | #ifndef _WIN32 | ||
322 | if (1) | ||
323 | { | ||
324 | const int urand_fd = open ("/dev/urandom", O_RDONLY); | ||
325 | if (0 < urand_fd) | ||
326 | { | ||
327 | size_t pos = 0; | ||
328 | do | ||
329 | { | ||
330 | ssize_t res = read (urand_fd, | ||
331 | ((uint8_t *) rnd_buf) + pos, rnd_buf_size - pos); | ||
332 | if (0 > res) | ||
333 | break; | ||
334 | pos += (size_t) res; | ||
335 | } while (rnd_buf_size > pos); | ||
336 | (void) close (urand_fd); | ||
337 | |||
338 | if (rnd_buf_size == pos) | ||
339 | return ! 0; /* Success */ | ||
340 | } | ||
341 | } | ||
342 | #else /* _WIN32 */ | ||
343 | if (1) | ||
344 | { | ||
345 | HCRYPTPROV cpr_hndl; | ||
346 | if (CryptAcquireContextW (&cpr_hndl, NULL, NULL, PROV_RSA_FULL, | ||
347 | CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) | ||
348 | { | ||
349 | if (CryptGenRandom (cpr_hndl, (DWORD) rnd_buf_size, (BYTE *) rnd_buf)) | ||
350 | { | ||
351 | (void) CryptReleaseContext (cpr_hndl, 0); | ||
352 | return ! 0; /* Success */ | ||
353 | } | ||
354 | (void) CryptReleaseContext (cpr_hndl, 0); | ||
355 | } | ||
356 | } | ||
357 | #endif /* _WIN32 */ | ||
358 | return 0; /* Failure */ | ||
359 | } | ||
360 | |||
361 | |||
362 | struct CBC | ||
363 | { | ||
364 | char *buf; | ||
365 | size_t pos; | ||
366 | size_t size; | ||
367 | }; | ||
368 | |||
369 | |||
370 | static size_t | ||
371 | copyBuffer (void *ptr, | ||
372 | size_t size, | ||
373 | size_t nmemb, | ||
374 | void *ctx) | ||
375 | { | ||
376 | struct CBC *cbc = ctx; | ||
377 | |||
378 | if (cbc->pos + size * nmemb > cbc->size) | ||
379 | mhdErrorExitDesc ("Wrong too large data"); /* overflow */ | ||
380 | memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); | ||
381 | cbc->pos += size * nmemb; | ||
382 | return size * nmemb; | ||
383 | } | ||
384 | |||
385 | |||
386 | static enum MHD_Result | ||
387 | ahc_echo (void *cls, | ||
388 | struct MHD_Connection *connection, | ||
389 | const char *url, | ||
390 | const char *method, | ||
391 | const char *version, | ||
392 | const char *upload_data, | ||
393 | size_t *upload_data_size, | ||
394 | void **req_cls) | ||
395 | { | ||
396 | struct MHD_Response *response; | ||
397 | enum MHD_Result res; | ||
398 | static int already_called_marker; | ||
399 | (void) cls; (void) url; /* Unused. Silent compiler warning. */ | ||
400 | (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */ | ||
401 | (void) upload_data_size; /* Unused. Silent compiler warning. */ | ||
402 | |||
403 | if (&already_called_marker != *req_cls) | ||
404 | { /* Called for the first time, request not fully read yet */ | ||
405 | *req_cls = &already_called_marker; | ||
406 | /* Wait for complete request */ | ||
407 | return MHD_YES; | ||
408 | } | ||
409 | |||
410 | if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) | ||
411 | mhdErrorExitDesc ("Unexpected HTTP method"); | ||
412 | |||
413 | response = NULL; | ||
414 | if (! test_oldapi) | ||
415 | { | ||
416 | struct MHD_DigestAuthInfo *dinfo; | ||
417 | const enum MHD_DigestAuthAlgo3 algo3 = | ||
418 | test_sha256 ? MHD_DIGEST_AUTH_ALGO3_SHA256 : MHD_DIGEST_AUTH_ALGO3_MD5; | ||
419 | |||
420 | dinfo = MHD_digest_auth_get_request_info3 (connection); | ||
421 | if (NULL != dinfo) | ||
422 | { | ||
423 | /* Got any kind of Digest response. Check it, it must be valid */ | ||
424 | struct MHD_DigestAuthUsernameInfo *uname; | ||
425 | enum MHD_DigestAuthResult check_res; | ||
426 | |||
427 | if (NULL == dinfo->username) | ||
428 | mhdErrorExitDesc ("'username' is NULL"); | ||
429 | if (curl_uses_usehash) | ||
430 | { | ||
431 | if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH != dinfo->uname_type) | ||
432 | { | ||
433 | fprintf (stderr, "Unexpected 'uname_type'.\n" | ||
434 | "Expected: %d\tRecieved: %d. ", | ||
435 | (int) MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH, | ||
436 | (int) dinfo->uname_type); | ||
437 | mhdErrorExitDesc ("Wrong 'uname_type'"); | ||
438 | } | ||
439 | else if (dinfo->username_len != userhash1_hex_len) | ||
440 | { | ||
441 | fprintf (stderr, "'username_len' does not match.\n" | ||
442 | "Expected: %u\tRecieved: %u. ", | ||
443 | (unsigned) userhash1_hex_len, | ||
444 | (unsigned) dinfo->username_len); | ||
445 | mhdErrorExitDesc ("Wrong 'username_len'"); | ||
446 | } | ||
447 | else if (0 != memcmp (dinfo->username, userhash1_hex, | ||
448 | dinfo->username_len)) | ||
449 | { | ||
450 | fprintf (stderr, "'username' does not match.\n" | ||
451 | "Expected: '%s'\tRecieved: '%.*s'. ", | ||
452 | userhash1_hex, | ||
453 | (int) dinfo->username_len, | ||
454 | dinfo->username); | ||
455 | mhdErrorExitDesc ("Wrong 'username'"); | ||
456 | } | ||
457 | else if (NULL == dinfo->userhash_bin) | ||
458 | mhdErrorExitDesc ("'userhash_bin' is NULL"); | ||
459 | else if (0 != memcmp (dinfo->userhash_bin, userhash1_bin, | ||
460 | dinfo->username_len / 2)) | ||
461 | mhdErrorExitDesc ("Wrong 'userhash_bin'"); | ||
462 | } | ||
463 | else | ||
464 | { | ||
465 | if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD != dinfo->uname_type) | ||
466 | { | ||
467 | fprintf (stderr, "Unexpected 'uname_type'.\n" | ||
468 | "Expected: %d\tRecieved: %d. ", | ||
469 | (int) MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD, | ||
470 | (int) dinfo->uname_type); | ||
471 | mhdErrorExitDesc ("Wrong 'uname_type'"); | ||
472 | } | ||
473 | else if (dinfo->username_len != strlen (username_ptr)) | ||
474 | { | ||
475 | fprintf (stderr, "'username_len' does not match.\n" | ||
476 | "Expected: %u\tRecieved: %u. ", | ||
477 | (unsigned) strlen (username_ptr), | ||
478 | (unsigned) dinfo->username_len); | ||
479 | mhdErrorExitDesc ("Wrong 'username_len'"); | ||
480 | } | ||
481 | else if (0 != memcmp (dinfo->username, username_ptr, | ||
482 | dinfo->username_len)) | ||
483 | { | ||
484 | fprintf (stderr, "'username' does not match.\n" | ||
485 | "Expected: '%s'\tRecieved: '%.*s'. ", | ||
486 | username_ptr, | ||
487 | (int) dinfo->username_len, | ||
488 | dinfo->username); | ||
489 | mhdErrorExitDesc ("Wrong 'username'"); | ||
490 | } | ||
491 | else if (NULL != dinfo->userhash_bin) | ||
492 | mhdErrorExitDesc ("'userhash_bin' is NOT NULL"); | ||
493 | } | ||
494 | if (algo3 != dinfo->algo) | ||
495 | { | ||
496 | fprintf (stderr, "Unexpected 'algo'.\n" | ||
497 | "Expected: %d\tRecieved: %d. ", | ||
498 | (int) algo3, | ||
499 | (int) dinfo->algo); | ||
500 | mhdErrorExitDesc ("Wrong 'algo'"); | ||
501 | } | ||
502 | else if (10 >= dinfo->cnonce_len) | ||
503 | { | ||
504 | fprintf (stderr, "Unexpected small 'cnonce_len': %ld. ", | ||
505 | (long) dinfo->cnonce_len); | ||
506 | mhdErrorExitDesc ("Wrong 'cnonce_len'"); | ||
507 | } | ||
508 | else if (NULL == dinfo->opaque) | ||
509 | mhdErrorExitDesc ("'opaque' is NULL"); | ||
510 | else if (dinfo->opaque_len != MHD_STATICSTR_LEN_ (OPAQUE_VALUE)) | ||
511 | { | ||
512 | fprintf (stderr, "'opaque_len' does not match.\n" | ||
513 | "Expected: %u\tRecieved: %u. ", | ||
514 | (unsigned) MHD_STATICSTR_LEN_ (OPAQUE_VALUE), | ||
515 | (unsigned) dinfo->opaque_len); | ||
516 | mhdErrorExitDesc ("Wrong 'opaque_len'"); | ||
517 | } | ||
518 | else if (0 != memcmp (dinfo->opaque, OPAQUE_VALUE, dinfo->opaque_len)) | ||
519 | { | ||
520 | fprintf (stderr, "'opaque' does not match.\n" | ||
521 | "Expected: '%s'\tRecieved: '%.*s'. ", | ||
522 | OPAQUE_VALUE, | ||
523 | (int) dinfo->opaque_len, | ||
524 | dinfo->opaque); | ||
525 | mhdErrorExitDesc ("Wrong 'opaque'"); | ||
526 | } | ||
527 | else if (MHD_DIGEST_AUTH_QOP_AUTH != dinfo->qop) | ||
528 | { | ||
529 | fprintf (stderr, "Unexpected 'qop'.\n" | ||
530 | "Expected: %d\tRecieved: %d. ", | ||
531 | (int) MHD_DIGEST_AUTH_QOP_AUTH, | ||
532 | (int) dinfo->qop); | ||
533 | mhdErrorExitDesc ("Wrong 'qop'"); | ||
534 | } | ||
535 | else if (NULL == dinfo->realm) | ||
536 | mhdErrorExitDesc ("'realm' is NULL"); | ||
537 | else if (dinfo->realm_len != MHD_STATICSTR_LEN_ (REALM)) | ||
538 | { | ||
539 | fprintf (stderr, "'realm_len' does not match.\n" | ||
540 | "Expected: %u\tRecieved: %u. ", | ||
541 | (unsigned) MHD_STATICSTR_LEN_ (REALM), | ||
542 | (unsigned) dinfo->realm_len); | ||
543 | mhdErrorExitDesc ("Wrong 'realm_len'"); | ||
544 | } | ||
545 | else if (0 != memcmp (dinfo->realm, REALM, dinfo->realm_len)) | ||
546 | { | ||
547 | fprintf (stderr, "'realm' does not match.\n" | ||
548 | "Expected: '%s'\tRecieved: '%.*s'. ", | ||
549 | OPAQUE_VALUE, | ||
550 | (int) dinfo->realm_len, | ||
551 | dinfo->realm); | ||
552 | mhdErrorExitDesc ("Wrong 'realm'"); | ||
553 | } | ||
554 | MHD_free (dinfo); | ||
555 | |||
556 | uname = MHD_digest_auth_get_username3 (connection); | ||
557 | if (NULL == uname) | ||
558 | mhdErrorExitDesc ("MHD_digest_auth_get_username3() returned NULL"); | ||
559 | else if (NULL == uname->username) | ||
560 | mhdErrorExitDesc ("'username' is NULL"); | ||
561 | if (curl_uses_usehash) | ||
562 | { | ||
563 | if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH != uname->uname_type) | ||
564 | { | ||
565 | fprintf (stderr, "Unexpected 'uname_type'.\n" | ||
566 | "Expected: %d\tRecieved: %d. ", | ||
567 | (int) MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH, | ||
568 | (int) uname->uname_type); | ||
569 | mhdErrorExitDesc ("Wrong 'uname_type'"); | ||
570 | } | ||
571 | else if (uname->username_len != userhash1_hex_len) | ||
572 | { | ||
573 | fprintf (stderr, "'username_len' does not match.\n" | ||
574 | "Expected: %u\tRecieved: %u. ", | ||
575 | (unsigned) userhash1_hex_len, | ||
576 | (unsigned) uname->username_len); | ||
577 | mhdErrorExitDesc ("Wrong 'username_len'"); | ||
578 | } | ||
579 | else if (0 != memcmp (uname->username, userhash1_hex, | ||
580 | uname->username_len)) | ||
581 | { | ||
582 | fprintf (stderr, "'username' does not match.\n" | ||
583 | "Expected: '%s'\tRecieved: '%.*s'. ", | ||
584 | userhash1_hex, | ||
585 | (int) uname->username_len, | ||
586 | uname->username); | ||
587 | mhdErrorExitDesc ("Wrong 'username'"); | ||
588 | } | ||
589 | else if (NULL == uname->userhash_bin) | ||
590 | mhdErrorExitDesc ("'userhash_bin' is NULL"); | ||
591 | else if (0 != memcmp (uname->userhash_bin, userhash1_bin, | ||
592 | uname->username_len / 2)) | ||
593 | mhdErrorExitDesc ("Wrong 'userhash_bin'"); | ||
594 | } | ||
595 | else | ||
596 | { | ||
597 | if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD != uname->uname_type) | ||
598 | { | ||
599 | fprintf (stderr, "Unexpected 'uname_type'.\n" | ||
600 | "Expected: %d\tRecieved: %d. ", | ||
601 | (int) MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD, | ||
602 | (int) uname->uname_type); | ||
603 | mhdErrorExitDesc ("Wrong 'uname_type'"); | ||
604 | } | ||
605 | else if (uname->username_len != strlen (username_ptr)) | ||
606 | { | ||
607 | fprintf (stderr, "'username_len' does not match.\n" | ||
608 | "Expected: %u\tRecieved: %u. ", | ||
609 | (unsigned) strlen (username_ptr), | ||
610 | (unsigned) uname->username_len); | ||
611 | mhdErrorExitDesc ("Wrong 'username_len'"); | ||
612 | } | ||
613 | else if (0 != memcmp (uname->username, username_ptr, | ||
614 | uname->username_len)) | ||
615 | { | ||
616 | fprintf (stderr, "'username' does not match.\n" | ||
617 | "Expected: '%s'\tRecieved: '%.*s'. ", | ||
618 | username_ptr, | ||
619 | (int) uname->username_len, | ||
620 | uname->username); | ||
621 | mhdErrorExitDesc ("Wrong 'username'"); | ||
622 | } | ||
623 | else if (NULL != uname->userhash_bin) | ||
624 | mhdErrorExitDesc ("'userhash_bin' is NOT NULL"); | ||
625 | } | ||
626 | MHD_free (uname); | ||
627 | |||
628 | check_res = | ||
629 | MHD_digest_auth_check3 (connection, REALM, username_ptr, | ||
630 | PASSWORD_VALUE, 50 * TIMEOUTS_VAL, | ||
631 | 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, | ||
632 | (enum MHD_DigestAuthMultiAlgo3) algo3); | ||
633 | |||
634 | switch (check_res) | ||
635 | { | ||
636 | /* Valid result */ | ||
637 | case MHD_DAUTH_OK: | ||
638 | if (verbose) | ||
639 | printf ("Got valid auth check result: MHD_DAUTH_OK.\n"); | ||
640 | break; | ||
641 | /* Invalid results */ | ||
642 | case MHD_DAUTH_NONCE_STALE: | ||
643 | mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \ | ||
644 | "MHD_DAUTH_NONCE_STALE"); | ||
645 | break; | ||
646 | case MHD_DAUTH_NONCE_WRONG: | ||
647 | mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \ | ||
648 | "MHD_DAUTH_NONCE_WRONG"); | ||
649 | break; | ||
650 | case MHD_DAUTH_ERROR: | ||
651 | externalErrorExitDesc ("General error returned " \ | ||
652 | "by 'MHD_digest_auth_check3()'"); | ||
653 | break; | ||
654 | case MHD_DAUTH_WRONG_USERNAME: | ||
655 | mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \ | ||
656 | "MHD_DAUTH_WRONG_USERNAME"); | ||
657 | break; | ||
658 | case MHD_DAUTH_RESPONSE_WRONG: | ||
659 | mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \ | ||
660 | "MHD_DAUTH_RESPONSE_WRONG"); | ||
661 | break; | ||
662 | case MHD_DAUTH_WRONG_HEADER: | ||
663 | case MHD_DAUTH_WRONG_REALM: | ||
664 | case MHD_DAUTH_WRONG_URI: | ||
665 | case MHD_DAUTH_WRONG_QOP: | ||
666 | case MHD_DAUTH_WRONG_ALGO: | ||
667 | case MHD_DAUTH_TOO_LARGE: | ||
668 | fprintf (stderr, "'MHD_digest_auth_check3()' returned " | ||
669 | "unexpected result: %d. ", | ||
670 | check_res); | ||
671 | mhdErrorExitDesc ("Wrong returned code"); | ||
672 | break; | ||
673 | default: | ||
674 | fprintf (stderr, "'MHD_digest_auth_check3()' returned " | ||
675 | "impossible result code: %d. ", | ||
676 | check_res); | ||
677 | mhdErrorExitDesc ("Impossible returned code"); | ||
678 | } | ||
679 | |||
680 | response = | ||
681 | MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE), | ||
682 | (const void *) PAGE); | ||
683 | if (NULL == response) | ||
684 | mhdErrorExitDesc ("Response creation failed"); | ||
685 | |||
686 | if (MHD_YES != | ||
687 | MHD_queue_response (connection, MHD_HTTP_OK, response)) | ||
688 | mhdErrorExitDesc ("'MHD_queue_response()' failed"); | ||
689 | } | ||
690 | else | ||
691 | { | ||
692 | /* No Digest auth header */ | ||
693 | response = | ||
694 | MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED), | ||
695 | (const void *) DENIED); | ||
696 | if (NULL == response) | ||
697 | mhdErrorExitDesc ("Response creation failed"); | ||
698 | res = | ||
699 | MHD_queue_auth_required_response3 (connection, REALM, OPAQUE_VALUE, | ||
700 | "/", response, 0, | ||
701 | MHD_DIGEST_AUTH_MULT_QOP_AUTH, | ||
702 | (enum MHD_DigestAuthMultiAlgo3) algo3, | ||
703 | test_userhash, 0); | ||
704 | if (MHD_YES != res) | ||
705 | mhdErrorExitDesc ("'MHD_queue_auth_required_response3()' failed"); | ||
706 | } | ||
707 | } | ||
708 | else | ||
709 | { | ||
710 | /* Use old API */ | ||
711 | char *username; | ||
712 | int check_res; | ||
713 | |||
714 | username = MHD_digest_auth_get_username (connection); | ||
715 | if (NULL != username) | ||
716 | { /* Has a valid username in header */ | ||
717 | if (0 != strcmp (username, username_ptr)) | ||
718 | { | ||
719 | fprintf (stderr, "'username' does not match.\n" | ||
720 | "Expected: '%s'\tRecieved: '%s'. ", | ||
721 | username_ptr, | ||
722 | username); | ||
723 | mhdErrorExitDesc ("Wrong 'username'"); | ||
724 | } | ||
725 | MHD_free (username); | ||
726 | |||
727 | check_res = MHD_digest_auth_check (connection, REALM, username_ptr, | ||
728 | PASSWORD_VALUE, 50 * TIMEOUTS_VAL); | ||
729 | |||
730 | if (MHD_YES != check_res) | ||
731 | { | ||
732 | fprintf (stderr, "'MHD_digest_auth_check()' returned unexpected" | ||
733 | " result: %d. ", check_res); | ||
734 | mhdErrorExitDesc ("Wrong 'MHD_digest_auth_check()' result"); | ||
735 | } | ||
736 | response = | ||
737 | MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE), | ||
738 | (const void *) PAGE); | ||
739 | if (NULL == response) | ||
740 | mhdErrorExitDesc ("Response creation failed"); | ||
741 | |||
742 | if (MHD_YES != | ||
743 | MHD_queue_response (connection, MHD_HTTP_OK, response)) | ||
744 | mhdErrorExitDesc ("'MHD_queue_response()' failed"); | ||
745 | } | ||
746 | else | ||
747 | { | ||
748 | /* Has no valid username in header */ | ||
749 | response = | ||
750 | MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED), | ||
751 | (const void *) DENIED); | ||
752 | if (NULL == response) | ||
753 | mhdErrorExitDesc ("Response creation failed"); | ||
754 | |||
755 | res = MHD_queue_auth_fail_response (connection, REALM, OPAQUE_VALUE, | ||
756 | response, 0); | ||
757 | if (MHD_YES != res) | ||
758 | mhdErrorExitDesc ("'MHD_queue_auth_fail_response()' failed"); | ||
759 | } | ||
760 | } | ||
761 | MHD_destroy_response (response); | ||
762 | return MHD_YES; | ||
763 | } | ||
764 | |||
765 | |||
766 | static CURL * | ||
767 | setupCURL (void *cbc, int port) | ||
768 | { | ||
769 | CURL *c; | ||
770 | char url[512]; | ||
771 | |||
772 | if (1) | ||
773 | { | ||
774 | int res; | ||
775 | /* A workaround for some old libcurl versions, which ignore the specified | ||
776 | * port by CURLOPT_PORT when authorisation is used. */ | ||
777 | res = snprintf (url, (sizeof(url) / sizeof(url[0])), | ||
778 | "http://127.0.0.1:%d%s", port, MHD_URI_BASE_PATH); | ||
779 | if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res)) | ||
780 | externalErrorExitDesc ("Cannot form request URL"); | ||
781 | } | ||
782 | |||
783 | c = curl_easy_init (); | ||
784 | if (NULL == c) | ||
785 | libcurlErrorExitDesc ("curl_easy_init() failed"); | ||
786 | |||
787 | if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || | ||
788 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, | ||
789 | ©Buffer)) || | ||
790 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) || | ||
791 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, | ||
792 | ((long) TIMEOUTS_VAL))) || | ||
793 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, | ||
794 | CURL_HTTP_VERSION_1_1)) || | ||
795 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, | ||
796 | ((long) TIMEOUTS_VAL))) || | ||
797 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, | ||
798 | libcurl_errbuf)) || | ||
799 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L)) || | ||
800 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPAUTH, | ||
801 | (long) CURLAUTH_DIGEST)) || | ||
802 | #if CURL_AT_LEAST_VERSION (7,19,1) | ||
803 | /* Need version 7.19.1 for separate username and password */ | ||
804 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_USERNAME, username_ptr)) || | ||
805 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_PASSWORD, PASSWORD_VALUE)) || | ||
806 | #endif /* CURL_AT_LEAST_VERSION(7,19,1) */ | ||
807 | #ifdef _DEBUG | ||
808 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) || | ||
809 | #endif /* _DEBUG */ | ||
810 | #if CURL_AT_LEAST_VERSION (7, 19, 4) | ||
811 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) || | ||
812 | #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */ | ||
813 | #if CURL_AT_LEAST_VERSION (7, 45, 0) | ||
814 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) || | ||
815 | #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */ | ||
816 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))) || | ||
817 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, url))) | ||
818 | libcurlErrorExitDesc ("curl_easy_setopt() failed"); | ||
819 | return c; | ||
820 | } | ||
821 | |||
822 | |||
823 | static CURLcode | ||
824 | performQueryExternal (struct MHD_Daemon *d, CURL *c) | ||
825 | { | ||
826 | CURLM *multi; | ||
827 | time_t start; | ||
828 | struct timeval tv; | ||
829 | CURLcode ret; | ||
830 | |||
831 | ret = CURLE_FAILED_INIT; /* will be replaced with real result */ | ||
832 | multi = NULL; | ||
833 | multi = curl_multi_init (); | ||
834 | if (multi == NULL) | ||
835 | libcurlErrorExitDesc ("curl_multi_init() failed"); | ||
836 | if (CURLM_OK != curl_multi_add_handle (multi, c)) | ||
837 | libcurlErrorExitDesc ("curl_multi_add_handle() failed"); | ||
838 | |||
839 | start = time (NULL); | ||
840 | while (time (NULL) - start <= TIMEOUTS_VAL) | ||
841 | { | ||
842 | fd_set rs; | ||
843 | fd_set ws; | ||
844 | fd_set es; | ||
845 | MHD_socket maxMhdSk; | ||
846 | int maxCurlSk; | ||
847 | int running; | ||
848 | |||
849 | maxMhdSk = MHD_INVALID_SOCKET; | ||
850 | maxCurlSk = -1; | ||
851 | FD_ZERO (&rs); | ||
852 | FD_ZERO (&ws); | ||
853 | FD_ZERO (&es); | ||
854 | if (NULL != multi) | ||
855 | { | ||
856 | curl_multi_perform (multi, &running); | ||
857 | if (0 == running) | ||
858 | { | ||
859 | struct CURLMsg *msg; | ||
860 | int msgLeft; | ||
861 | int totalMsgs = 0; | ||
862 | do | ||
863 | { | ||
864 | msg = curl_multi_info_read (multi, &msgLeft); | ||
865 | if (NULL == msg) | ||
866 | libcurlErrorExitDesc ("curl_multi_info_read() failed"); | ||
867 | totalMsgs++; | ||
868 | if (CURLMSG_DONE == msg->msg) | ||
869 | ret = msg->data.result; | ||
870 | } while (msgLeft > 0); | ||
871 | if (1 != totalMsgs) | ||
872 | { | ||
873 | fprintf (stderr, | ||
874 | "curl_multi_info_read returned wrong " | ||
875 | "number of results (%d).\n", | ||
876 | totalMsgs); | ||
877 | externalErrorExit (); | ||
878 | } | ||
879 | curl_multi_remove_handle (multi, c); | ||
880 | curl_multi_cleanup (multi); | ||
881 | multi = NULL; | ||
882 | } | ||
883 | else | ||
884 | { | ||
885 | if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk)) | ||
886 | libcurlErrorExitDesc ("curl_multi_fdset() failed"); | ||
887 | } | ||
888 | } | ||
889 | if (NULL == multi) | ||
890 | { /* libcurl has finished, check whether MHD still needs to perform cleanup */ | ||
891 | if (0 != MHD_get_timeout64s (d)) | ||
892 | break; /* MHD finished as well */ | ||
893 | } | ||
894 | if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk)) | ||
895 | mhdErrorExitDesc ("MHD_get_fdset() failed"); | ||
896 | tv.tv_sec = 0; | ||
897 | tv.tv_usec = 200000; | ||
898 | #ifdef MHD_POSIX_SOCKETS | ||
899 | if (maxMhdSk > maxCurlSk) | ||
900 | maxCurlSk = maxMhdSk; | ||
901 | #endif /* MHD_POSIX_SOCKETS */ | ||
902 | if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv)) | ||
903 | { | ||
904 | #ifdef MHD_POSIX_SOCKETS | ||
905 | if (EINTR != errno) | ||
906 | externalErrorExitDesc ("Unexpected select() error"); | ||
907 | #else | ||
908 | if ((WSAEINVAL != WSAGetLastError ()) || | ||
909 | (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) | ||
910 | externalErrorExitDesc ("Unexpected select() error"); | ||
911 | Sleep (200); | ||
912 | #endif | ||
913 | } | ||
914 | if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es)) | ||
915 | mhdErrorExitDesc ("MHD_run_from_select() failed"); | ||
916 | } | ||
917 | |||
918 | return ret; | ||
919 | } | ||
920 | |||
921 | |||
922 | /** | ||
923 | * Check request result | ||
924 | * @param curl_code the CURL easy return code | ||
925 | * @param pcbc the pointer struct CBC | ||
926 | * @return non-zero if success, zero if failed | ||
927 | */ | ||
928 | static unsigned int | ||
929 | check_result (CURLcode curl_code, CURL *c, struct CBC *pcbc) | ||
930 | { | ||
931 | long code; | ||
932 | if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code)) | ||
933 | libcurlErrorExit (); | ||
934 | |||
935 | if (MHD_HTTP_OK != code) | ||
936 | { | ||
937 | fprintf (stderr, "Request returned wrong code: %ld.\n", | ||
938 | code); | ||
939 | return 0; | ||
940 | } | ||
941 | |||
942 | if (CURLE_OK != curl_code) | ||
943 | { | ||
944 | fflush (stdout); | ||
945 | if (0 != libcurl_errbuf[0]) | ||
946 | fprintf (stderr, "Request failed. " | ||
947 | "libcurl error: '%s'.\n" | ||
948 | "libcurl error description: '%s'.\n", | ||
949 | curl_easy_strerror (curl_code), | ||
950 | libcurl_errbuf); | ||
951 | else | ||
952 | fprintf (stderr, "Request failed. " | ||
953 | "libcurl error: '%s'.\n", | ||
954 | curl_easy_strerror (curl_code)); | ||
955 | fflush (stderr); | ||
956 | return 0; | ||
957 | } | ||
958 | |||
959 | if (pcbc->pos != MHD_STATICSTR_LEN_ (PAGE)) | ||
960 | { | ||
961 | fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ", | ||
962 | (unsigned) pcbc->pos, (int) pcbc->pos, pcbc->buf, | ||
963 | (unsigned) MHD_STATICSTR_LEN_ (PAGE)); | ||
964 | mhdErrorExitDesc ("Wrong returned data length"); | ||
965 | } | ||
966 | if (0 != memcmp (PAGE, pcbc->buf, pcbc->pos)) | ||
967 | { | ||
968 | fprintf (stderr, "Got invalid response '%.*s'. ", | ||
969 | (int) pcbc->pos, pcbc->buf); | ||
970 | mhdErrorExitDesc ("Wrong returned data"); | ||
971 | } | ||
972 | return 1; | ||
973 | } | ||
974 | |||
975 | |||
976 | static unsigned int | ||
977 | testDigestAuth (void) | ||
978 | { | ||
979 | struct MHD_Daemon *d; | ||
980 | uint16_t port; | ||
981 | uint8_t salt[8]; | ||
982 | struct CBC cbc; | ||
983 | char buf[2048]; | ||
984 | CURL *c; | ||
985 | int failed = 0; | ||
986 | |||
987 | if (! gen_good_rnd (salt, sizeof(salt))) | ||
988 | { | ||
989 | fprintf (stderr, "WARNING: the random buffer (used as salt value) is not " | ||
990 | "initialised completely, nonce generation may be " | ||
991 | "predictable in this test.\n"); | ||
992 | fflush (stderr); | ||
993 | } | ||
994 | |||
995 | if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) | ||
996 | port = 0; | ||
997 | else | ||
998 | port = 4210; | ||
999 | |||
1000 | d = MHD_start_daemon (MHD_USE_ERROR_LOG, | ||
1001 | port, NULL, NULL, | ||
1002 | &ahc_echo, NULL, | ||
1003 | MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (salt), salt, | ||
1004 | MHD_OPTION_NONCE_NC_SIZE, 300, | ||
1005 | MHD_OPTION_END); | ||
1006 | if (d == NULL) | ||
1007 | return 1; | ||
1008 | if (0 == port) | ||
1009 | { | ||
1010 | const union MHD_DaemonInfo *dinfo; | ||
1011 | |||
1012 | dinfo = MHD_get_daemon_info (d, | ||
1013 | MHD_DAEMON_INFO_BIND_PORT); | ||
1014 | if ( (NULL == dinfo) || | ||
1015 | (0 == dinfo->port) ) | ||
1016 | mhdErrorExitDesc ("MHD_get_daemon_info() failed"); | ||
1017 | port = dinfo->port; | ||
1018 | } | ||
1019 | |||
1020 | /* First request */ | ||
1021 | cbc.buf = buf; | ||
1022 | cbc.size = sizeof (buf); | ||
1023 | cbc.pos = 0; | ||
1024 | memset (cbc.buf, 0, cbc.size); | ||
1025 | c = setupCURL (&cbc, port); | ||
1026 | if (check_result (performQueryExternal (d, c), c, &cbc)) | ||
1027 | { | ||
1028 | if (verbose) | ||
1029 | printf ("Got expected response.\n"); | ||
1030 | } | ||
1031 | else | ||
1032 | { | ||
1033 | fprintf (stderr, "Request FAILED.\n"); | ||
1034 | failed = 1; | ||
1035 | } | ||
1036 | curl_easy_cleanup (c); | ||
1037 | |||
1038 | MHD_stop_daemon (d); | ||
1039 | return failed ? 1 : 0; | ||
1040 | } | ||
1041 | |||
1042 | |||
1043 | int | ||
1044 | main (int argc, char *const *argv) | ||
1045 | { | ||
1046 | #if ! CURL_AT_LEAST_VERSION (7,19,1) | ||
1047 | (void) argc; (void) argv; /* Unused. Silent compiler warning. */ | ||
1048 | /* Need version 7.19.1 for separate username and password */ | ||
1049 | fprintf (stderr, "Required libcurl version 7.19.1 at least" | ||
1050 | " to run this test.\n"); | ||
1051 | return 77; | ||
1052 | #else /* CURL_AT_LEAST_VERSION(7,19,1) */ | ||
1053 | unsigned int errorCount = 0; | ||
1054 | const curl_version_info_data *const curl_info = | ||
1055 | curl_version_info (CURLVERSION_NOW); | ||
1056 | int curl_sspi; | ||
1057 | (void) argc; (void) argv; /* Unused. Silent compiler warning. */ | ||
1058 | |||
1059 | /* Test type and test parameters */ | ||
1060 | verbose = ! (has_param (argc, argv, "-q") || | ||
1061 | has_param (argc, argv, "--quiet") || | ||
1062 | has_param (argc, argv, "-s") || | ||
1063 | has_param (argc, argv, "--silent")); | ||
1064 | test_oldapi = has_in_name (argv[0], "_oldapi"); | ||
1065 | test_userhash = has_in_name (argv[0], "_userhash"); | ||
1066 | test_sha256 = has_in_name (argv[0], "_sha256"); | ||
1067 | |||
1068 | if (test_oldapi) | ||
1069 | { /* Wrong test types combination */ | ||
1070 | if (test_userhash || test_sha256) | ||
1071 | return 99; | ||
1072 | } | ||
1073 | |||
1074 | /* Curl version and known bugs checks */ | ||
1075 | curl_sspi = 0; | ||
1076 | #ifdef CURL_VERSION_SSPI | ||
1077 | if (0 != (curl_info->features & CURL_VERSION_SSPI)) | ||
1078 | curl_sspi = 1; | ||
1079 | #endif /* CURL_VERSION_SSPI */ | ||
1080 | |||
1081 | if ((CURL_VERSION_BITS (7,63,0) > curl_info->version_num) && | ||
1082 | (CURL_VERSION_BITS (7,62,0) <= curl_info->version_num) ) | ||
1083 | { | ||
1084 | fprintf (stderr, "libcurl version 7.62.x has bug in processing" | ||
1085 | "URI with GET argements for Digest Auth.\n"); | ||
1086 | fprintf (stderr, "This test with libcurl %u.%u.%u cannot be performed.\n", | ||
1087 | 0xFF & (curl_info->version_num >> 16), | ||
1088 | 0xFF & (curl_info->version_num >> 8), | ||
1089 | 0xFF & (curl_info->version_num >> 0)); | ||
1090 | return 77; | ||
1091 | } | ||
1092 | if (test_userhash) | ||
1093 | { | ||
1094 | if (curl_sspi) | ||
1095 | { | ||
1096 | printf ("WARNING: Windows SSPI API does not support 'userhash'.\n"); | ||
1097 | printf ("This test just checks Digest Auth compatibility with " | ||
1098 | "the clients without 'userhash' support " | ||
1099 | "when 'userhash=true' is specified by MHD.\n"); | ||
1100 | curl_uses_usehash = 0; | ||
1101 | } | ||
1102 | else if (CURL_VERSION_BITS (7,57,0) > curl_info->version_num) | ||
1103 | { | ||
1104 | printf ("WARNING: libcurl before version 7.57.0 does not " | ||
1105 | "support 'userhash'.\n"); | ||
1106 | printf ("This test just checks Digest Auth compatibility with " | ||
1107 | "libcurl version %u.%u.%u without 'userhash' support " | ||
1108 | "when 'userhash=true' is specified by MHD.\n", | ||
1109 | 0xFF & (curl_info->version_num >> 16), | ||
1110 | 0xFF & (curl_info->version_num >> 8), | ||
1111 | 0xFF & (curl_info->version_num >> 0)); | ||
1112 | curl_uses_usehash = 0; | ||
1113 | } | ||
1114 | else if (CURL_VERSION_BITS (7,81,0) > curl_info->version_num) | ||
1115 | { | ||
1116 | fprintf (stderr, "Required libcurl version 7.81.0 at least" | ||
1117 | " to run this test with userhash.\n"); | ||
1118 | fprintf (stderr, "This libcurl version %u.%u.%u has broken digest" | ||
1119 | "calculation when userhash is used.\n", | ||
1120 | 0xFF & (curl_info->version_num >> 16), | ||
1121 | 0xFF & (curl_info->version_num >> 8), | ||
1122 | 0xFF & (curl_info->version_num >> 0)); | ||
1123 | return 77; | ||
1124 | } | ||
1125 | else | ||
1126 | curl_uses_usehash = ! 0; | ||
1127 | } | ||
1128 | else | ||
1129 | curl_uses_usehash = 0; | ||
1130 | |||
1131 | if (test_sha256) | ||
1132 | { | ||
1133 | if (curl_sspi) | ||
1134 | { | ||
1135 | fprintf (stderr, "Windows SSPI API does not support SHA-256 digests.\n"); | ||
1136 | return 77; | ||
1137 | } | ||
1138 | else if (CURL_VERSION_BITS (7,57,0) > curl_info->version_num) | ||
1139 | { | ||
1140 | fprintf (stderr, "Required libcurl version 7.57.0 at least" | ||
1141 | " to run this test with SHA-256.\n"); | ||
1142 | fprintf (stderr, "This libcurl version %u.%u.%u " | ||
1143 | "does not support SHA-256.\n", | ||
1144 | 0xFF & (curl_info->version_num >> 16), | ||
1145 | 0xFF & (curl_info->version_num >> 8), | ||
1146 | 0xFF & (curl_info->version_num >> 0)); | ||
1147 | return 77; | ||
1148 | } | ||
1149 | } | ||
1150 | |||
1151 | test_global_init (); | ||
1152 | |||
1153 | errorCount += testDigestAuth (); | ||
1154 | if (errorCount != 0) | ||
1155 | fprintf (stderr, "Error (code: %u)\n", errorCount); | ||
1156 | test_global_cleanup (); | ||
1157 | return (0 == errorCount) ? 0 : 1; /* 0 == pass */ | ||
1158 | #endif /* CURL_AT_LEAST_VERSION(7,19,1) */ | ||
1159 | } | ||