lib2032.c (7184B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 #include "first.h" 25 26 #include "memdebug.h" 27 28 #define MAX_EASY_HANDLES 3 29 30 static int ntlm_counter[MAX_EASY_HANDLES]; 31 static CURL *ntlm_easy[MAX_EASY_HANDLES]; 32 static curl_socket_t ntlm_sockets[MAX_EASY_HANDLES]; 33 static CURLcode ntlmcb_res = CURLE_OK; 34 35 static size_t callback(char *ptr, size_t size, size_t nmemb, void *data) 36 { 37 ssize_t idx = ((CURL **) data) - ntlm_easy; 38 curl_socket_t sock; 39 long longdata; 40 CURLcode code; 41 const size_t failure = (size && nmemb) ? 0 : 1; 42 (void)ptr; 43 44 ntlm_counter[idx] += (int)(size * nmemb); 45 46 /* Get socket being used for this easy handle, otherwise CURL_SOCKET_BAD */ 47 code = curl_easy_getinfo(ntlm_easy[idx], CURLINFO_LASTSOCKET, &longdata); 48 49 if(CURLE_OK != code) { 50 curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed, " 51 "with code %d (%s)\n", 52 __FILE__, __LINE__, (int)code, curl_easy_strerror(code)); 53 ntlmcb_res = TEST_ERR_MAJOR_BAD; 54 return failure; 55 } 56 if(longdata == -1L) 57 sock = CURL_SOCKET_BAD; 58 else 59 sock = (curl_socket_t)longdata; 60 61 if(sock != CURL_SOCKET_BAD) { 62 /* Track relationship between this easy handle and the socket. */ 63 if(ntlm_sockets[idx] == CURL_SOCKET_BAD) { 64 /* An easy handle without previous socket, record the socket. */ 65 ntlm_sockets[idx] = sock; 66 } 67 else if(sock != ntlm_sockets[idx]) { 68 /* An easy handle with a socket different to previously 69 tracked one, log and fail right away. Known bug #37. */ 70 curl_mfprintf(stderr, "Handle %d started on socket %d and moved to %d\n", 71 curlx_sztosi(idx), (int)ntlm_sockets[idx], (int)sock); 72 ntlmcb_res = TEST_ERR_MAJOR_BAD; 73 return failure; 74 } 75 } 76 return size * nmemb; 77 } 78 79 static CURLcode test_lib2032(char *URL) /* libntlmconnect */ 80 { 81 enum HandleState { 82 ReadyForNewHandle, 83 NeedSocketForNewHandle, 84 NoMoreHandles 85 }; 86 87 CURLcode res = CURLE_OK; 88 CURLM *multi = NULL; 89 int running; 90 int i; 91 int num_handles = 0; 92 enum HandleState state = ReadyForNewHandle; 93 size_t urllen = strlen(URL) + 4 + 1; 94 char *full_url = malloc(urllen); 95 96 start_test_timing(); 97 98 if(!full_url) { 99 curl_mfprintf(stderr, "Not enough memory for full url\n"); 100 return TEST_ERR_MAJOR_BAD; 101 } 102 103 for(i = 0; i < MAX_EASY_HANDLES; ++i) { 104 ntlm_easy[i] = NULL; 105 ntlm_sockets[i] = CURL_SOCKET_BAD; 106 } 107 108 res_global_init(CURL_GLOBAL_ALL); 109 if(res) { 110 free(full_url); 111 return res; 112 } 113 114 multi_init(multi); 115 116 for(;;) { 117 struct timeval interval; 118 fd_set fdread; 119 fd_set fdwrite; 120 fd_set fdexcep; 121 long timeout = -99; 122 int maxfd = -99; 123 bool found_new_socket = FALSE; 124 125 /* Start a new handle if we aren't at the max */ 126 if(state == ReadyForNewHandle) { 127 easy_init(ntlm_easy[num_handles]); 128 129 if(num_handles % 3 == 2) { 130 curl_msnprintf(full_url, urllen, "%s0200", URL); 131 easy_setopt(ntlm_easy[num_handles], CURLOPT_HTTPAUTH, CURLAUTH_NTLM); 132 } 133 else { 134 curl_msnprintf(full_url, urllen, "%s0100", URL); 135 easy_setopt(ntlm_easy[num_handles], CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 136 } 137 easy_setopt(ntlm_easy[num_handles], CURLOPT_FRESH_CONNECT, 1L); 138 easy_setopt(ntlm_easy[num_handles], CURLOPT_URL, full_url); 139 easy_setopt(ntlm_easy[num_handles], CURLOPT_VERBOSE, 1L); 140 easy_setopt(ntlm_easy[num_handles], CURLOPT_HTTPGET, 1L); 141 easy_setopt(ntlm_easy[num_handles], CURLOPT_USERPWD, 142 "testuser:testpass"); 143 easy_setopt(ntlm_easy[num_handles], CURLOPT_WRITEFUNCTION, callback); 144 easy_setopt(ntlm_easy[num_handles], CURLOPT_WRITEDATA, 145 ntlm_easy + num_handles); 146 easy_setopt(ntlm_easy[num_handles], CURLOPT_HEADER, 1L); 147 148 multi_add_handle(multi, ntlm_easy[num_handles]); 149 num_handles += 1; 150 state = NeedSocketForNewHandle; 151 res = ntlmcb_res; 152 } 153 154 multi_perform(multi, &running); 155 156 curl_mfprintf(stderr, "%s:%d running %d state %d\n", 157 __FILE__, __LINE__, running, state); 158 159 abort_on_test_timeout(); 160 161 if(!running && state == NoMoreHandles) 162 break; /* done */ 163 164 FD_ZERO(&fdread); 165 FD_ZERO(&fdwrite); 166 FD_ZERO(&fdexcep); 167 168 multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd); 169 170 /* At this point, maxfd is guaranteed to be greater or equal than -1. */ 171 172 if(state == NeedSocketForNewHandle) { 173 if(maxfd != -1 && !found_new_socket) { 174 curl_mfprintf(stderr, 175 "Warning: socket did not open immediately for new " 176 "handle (trying again)\n"); 177 continue; 178 } 179 state = num_handles < MAX_EASY_HANDLES ? ReadyForNewHandle 180 : NoMoreHandles; 181 curl_mfprintf(stderr, "%s:%d new state %d\n", 182 __FILE__, __LINE__, state); 183 } 184 185 multi_timeout(multi, &timeout); 186 187 /* At this point, timeout is guaranteed to be greater or equal than -1. */ 188 189 curl_mfprintf(stderr, "%s:%d num_handles %d timeout %ld running %d\n", 190 __FILE__, __LINE__, num_handles, timeout, running); 191 192 if(timeout != -1L) { 193 int itimeout; 194 #if LONG_MAX > INT_MAX 195 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout; 196 #else 197 itimeout = (int)timeout; 198 #endif 199 interval.tv_sec = itimeout/1000; 200 interval.tv_usec = (itimeout%1000)*1000; 201 } 202 else { 203 interval.tv_sec = 0; 204 interval.tv_usec = 5000; 205 206 /* if there's no timeout and we get here on the last handle, we may 207 already have read the last part of the stream so waiting makes no 208 sense */ 209 if(!running && num_handles == MAX_EASY_HANDLES) { 210 break; 211 } 212 } 213 214 select_test(maxfd + 1, &fdread, &fdwrite, &fdexcep, &interval); 215 216 abort_on_test_timeout(); 217 } 218 219 test_cleanup: 220 221 /* proper cleanup sequence - type PB */ 222 223 for(i = 0; i < MAX_EASY_HANDLES; i++) { 224 curl_mprintf("Data connection %d: %d\n", i, ntlm_counter[i]); 225 curl_multi_remove_handle(multi, ntlm_easy[i]); 226 curl_easy_cleanup(ntlm_easy[i]); 227 } 228 229 curl_multi_cleanup(multi); 230 curl_global_cleanup(); 231 232 free(full_url); 233 234 return res; 235 }