tool_doswin.c (27808B)
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 "tool_setup.h" 25 26 #if defined(_WIN32) || defined(MSDOS) 27 28 #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) 29 # include <libgen.h> 30 #endif 31 32 #ifdef _WIN32 33 # include <stdlib.h> 34 # include <tlhelp32.h> 35 # include "tool_cfgable.h" 36 # include "tool_libinfo.h" 37 #endif 38 39 #include "tool_bname.h" 40 #include "tool_doswin.h" 41 #include "tool_msgs.h" 42 43 #include "memdebug.h" /* keep this as LAST include */ 44 45 #ifdef _WIN32 46 # undef PATH_MAX 47 # define PATH_MAX MAX_PATH 48 #elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */ 49 # define _use_lfn(f) (0) /* long filenames never available */ 50 #elif defined(__DJGPP__) 51 # include <fcntl.h> /* _use_lfn(f) prototype */ 52 #endif 53 54 #ifdef MSDOS 55 56 #ifndef S_ISCHR 57 # ifdef S_IFCHR 58 # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) 59 # else 60 # define S_ISCHR(m) (0) /* cannot tell if file is a device */ 61 # endif 62 #endif 63 64 /* only used by msdosify() */ 65 static SANITIZEcode truncate_dryrun(const char *path, 66 const size_t truncate_pos); 67 static SANITIZEcode msdosify(char **const sanitized, const char *file_name, 68 int flags); 69 #endif 70 static SANITIZEcode rename_if_reserved_dos(char **const sanitized, 71 const char *file_name, 72 int flags); 73 74 75 /* 76 Sanitize a file or path name. 77 78 All banned characters are replaced by underscores, for example: 79 f?*foo => f__foo 80 f:foo::$DATA => f_foo__$DATA 81 f:\foo:bar => f__foo_bar 82 f:\foo:bar => f:\foo:bar (flag SANITIZE_ALLOW_PATH) 83 84 This function was implemented according to the guidelines in 'Naming Files, 85 Paths, and Namespaces' section 'Naming Conventions'. 86 https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx 87 88 Flags 89 ----- 90 SANITIZE_ALLOW_PATH: Allow path separators and colons. 91 Without this flag path separators and colons are sanitized. 92 93 SANITIZE_ALLOW_RESERVED: Allow reserved device names. 94 Without this flag a reserved device name is renamed (COM1 => _COM1) unless it 95 is in a UNC prefixed path. 96 97 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. 98 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. 99 */ 100 SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name, 101 int flags) 102 { 103 char *p, *target; 104 size_t len; 105 SANITIZEcode sc; 106 size_t max_sanitized_len; 107 108 if(!sanitized) 109 return SANITIZE_ERR_BAD_ARGUMENT; 110 111 *sanitized = NULL; 112 113 if(!file_name) 114 return SANITIZE_ERR_BAD_ARGUMENT; 115 116 if(flags & SANITIZE_ALLOW_PATH) { 117 #ifndef MSDOS 118 if(file_name[0] == '\\' && file_name[1] == '\\') 119 /* UNC prefixed path \\ (eg \\?\C:\foo) */ 120 max_sanitized_len = 32767-1; 121 else 122 #endif 123 max_sanitized_len = PATH_MAX-1; 124 } 125 else 126 /* The maximum length of a filename. FILENAME_MAX is often the same as 127 PATH_MAX, in other words it is 260 and does not discount the path 128 information therefore we should not use it. */ 129 max_sanitized_len = (PATH_MAX-1 > 255) ? 255 : PATH_MAX-1; 130 131 len = strlen(file_name); 132 if(len > max_sanitized_len) 133 return SANITIZE_ERR_INVALID_PATH; 134 135 target = strdup(file_name); 136 if(!target) 137 return SANITIZE_ERR_OUT_OF_MEMORY; 138 139 #ifndef MSDOS 140 if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4)) 141 /* Skip the literal path prefix \\?\ */ 142 p = target + 4; 143 else 144 #endif 145 p = target; 146 147 /* replace control characters and other banned characters */ 148 for(; *p; ++p) { 149 const char *banned; 150 151 if((1 <= *p && *p <= 31) || 152 (!(flags & SANITIZE_ALLOW_PATH) && *p == ':') || 153 (!(flags & SANITIZE_ALLOW_PATH) && (*p == '/' || *p == '\\'))) { 154 *p = '_'; 155 continue; 156 } 157 158 for(banned = "|<>\"?*"; *banned; ++banned) { 159 if(*p == *banned) { 160 *p = '_'; 161 break; 162 } 163 } 164 } 165 166 /* remove trailing spaces and periods if not allowing paths */ 167 if(!(flags & SANITIZE_ALLOW_PATH) && len) { 168 char *clip = NULL; 169 170 p = &target[len]; 171 do { 172 --p; 173 if(*p != ' ' && *p != '.') 174 break; 175 clip = p; 176 } while(p != target); 177 178 if(clip) { 179 *clip = '\0'; 180 } 181 } 182 183 #ifdef MSDOS 184 sc = msdosify(&p, target, flags); 185 free(target); 186 if(sc) 187 return sc; 188 target = p; 189 len = strlen(target); 190 191 if(len > max_sanitized_len) { 192 free(target); 193 return SANITIZE_ERR_INVALID_PATH; 194 } 195 #endif 196 197 if(!(flags & SANITIZE_ALLOW_RESERVED)) { 198 sc = rename_if_reserved_dos(&p, target, flags); 199 free(target); 200 if(sc) 201 return sc; 202 target = p; 203 len = strlen(target); 204 205 if(len > max_sanitized_len) { 206 free(target); 207 return SANITIZE_ERR_INVALID_PATH; 208 } 209 } 210 211 *sanitized = target; 212 return SANITIZE_ERR_OK; 213 } 214 215 #ifdef MSDOS 216 /* 217 Test if truncating a path to a file will leave at least a single character in 218 the filename. Filenames suffixed by an alternate data stream cannot be 219 truncated. This performs a dry run, nothing is modified. 220 221 Good truncate_pos 9: C:\foo\bar => C:\foo\ba 222 Good truncate_pos 6: C:\foo => C:\foo 223 Good truncate_pos 5: C:\foo => C:\fo 224 Bad* truncate_pos 5: C:foo => C:foo 225 Bad truncate_pos 5: C:\foo:ads => C:\fo 226 Bad truncate_pos 9: C:\foo:ads => C:\foo:ad 227 Bad truncate_pos 5: C:\foo\bar => C:\fo 228 Bad truncate_pos 5: C:\foo\ => C:\fo 229 Bad truncate_pos 7: C:\foo\ => C:\foo\ 230 Error truncate_pos 7: C:\foo => (pos out of range) 231 Bad truncate_pos 1: C:\foo\ => C 232 233 * C:foo is ambiguous, C could end up being a drive or file therefore something 234 like C:superlongfilename cannot be truncated. 235 236 Returns 237 SANITIZE_ERR_OK: Good -- 'path' can be truncated 238 SANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated 239 != SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error 240 */ 241 static SANITIZEcode truncate_dryrun(const char *path, 242 const size_t truncate_pos) 243 { 244 size_t len; 245 246 if(!path) 247 return SANITIZE_ERR_BAD_ARGUMENT; 248 249 len = strlen(path); 250 251 if(truncate_pos > len) 252 return SANITIZE_ERR_BAD_ARGUMENT; 253 254 if(!len || !truncate_pos) 255 return SANITIZE_ERR_INVALID_PATH; 256 257 if(strpbrk(&path[truncate_pos - 1], "\\/:")) 258 return SANITIZE_ERR_INVALID_PATH; 259 260 /* C:\foo can be truncated but C:\foo:ads cannot */ 261 if(truncate_pos > 1) { 262 const char *p = &path[truncate_pos - 1]; 263 do { 264 --p; 265 if(*p == ':') 266 return SANITIZE_ERR_INVALID_PATH; 267 } while(p != path && *p != '\\' && *p != '/'); 268 } 269 270 return SANITIZE_ERR_OK; 271 } 272 273 /* The functions msdosify, rename_if_dos_device_name and __crt0_glob_function 274 * were taken with modification from the DJGPP port of tar 1.12. They use 275 * algorithms originally from DJTAR. 276 */ 277 278 /* 279 Extra sanitization MS-DOS for file_name. 280 281 This is a supporting function for sanitize_file_name. 282 283 Warning: This is an MS-DOS legacy function and was purposely written in a way 284 that some path information may pass through. For example drive letter names 285 (C:, D:, etc) are allowed to pass through. For sanitizing a filename use 286 sanitize_file_name. 287 288 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. 289 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. 290 */ 291 static SANITIZEcode msdosify(char **const sanitized, const char *file_name, 292 int flags) 293 { 294 char dos_name[PATH_MAX]; 295 static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */ 296 "|<>/\\\":?*"; /* illegal in DOS & W95 */ 297 static const char *illegal_chars_w95 = &illegal_chars_dos[8]; 298 int idx, dot_idx; 299 const char *s = file_name; 300 char *d = dos_name; 301 const char *const dlimit = dos_name + sizeof(dos_name) - 1; 302 const char *illegal_aliens = illegal_chars_dos; 303 size_t len = sizeof(illegal_chars_dos) - 1; 304 305 if(!sanitized) 306 return SANITIZE_ERR_BAD_ARGUMENT; 307 308 *sanitized = NULL; 309 310 if(!file_name) 311 return SANITIZE_ERR_BAD_ARGUMENT; 312 313 if(strlen(file_name) > PATH_MAX-1) 314 return SANITIZE_ERR_INVALID_PATH; 315 316 /* Support for Windows 9X VFAT systems, when available. */ 317 if(_use_lfn(file_name)) { 318 illegal_aliens = illegal_chars_w95; 319 len -= (illegal_chars_w95 - illegal_chars_dos); 320 } 321 322 /* Get past the drive letter, if any. */ 323 if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') { 324 *d++ = *s++; 325 *d = ((flags & SANITIZE_ALLOW_PATH)) ? ':' : '_'; 326 ++d; ++s; 327 } 328 329 for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) { 330 if(memchr(illegal_aliens, *s, len)) { 331 332 if((flags & SANITIZE_ALLOW_PATH) && *s == ':') 333 *d = ':'; 334 else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\')) 335 *d = *s; 336 /* Dots are special: DOS does not allow them as the leading character, 337 and a filename cannot have more than a single dot. We leave the 338 first non-leading dot alone, unless it comes too close to the 339 beginning of the name: we want sh.lex.c to become sh_lex.c, not 340 sh.lex-c. */ 341 else if(*s == '.') { 342 if((flags & SANITIZE_ALLOW_PATH) && idx == 0 && 343 (s[1] == '/' || s[1] == '\\' || 344 (s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) { 345 /* Copy "./" and "../" verbatim. */ 346 *d++ = *s++; 347 if(d == dlimit) 348 break; 349 if(*s == '.') { 350 *d++ = *s++; 351 if(d == dlimit) 352 break; 353 } 354 *d = *s; 355 } 356 else if(idx == 0) 357 *d = '_'; 358 else if(dot_idx >= 0) { 359 if(dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */ 360 d[dot_idx - idx] = '_'; /* replace previous dot */ 361 *d = '.'; 362 } 363 else 364 *d = '-'; 365 } 366 else 367 *d = '.'; 368 369 if(*s == '.') 370 dot_idx = idx; 371 } 372 else if(*s == '+' && s[1] == '+') { 373 if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */ 374 *d++ = 'x'; 375 if(d == dlimit) 376 break; 377 *d = 'x'; 378 } 379 else { 380 /* libg++ etc. */ 381 if(dlimit - d < 4) { 382 *d++ = 'x'; 383 if(d == dlimit) 384 break; 385 *d = 'x'; 386 } 387 else { 388 memcpy(d, "plus", 4); 389 d += 3; 390 } 391 } 392 s++; 393 idx++; 394 } 395 else 396 *d = '_'; 397 } 398 else 399 *d = *s; 400 if(*s == '/' || *s == '\\') { 401 idx = 0; 402 dot_idx = -1; 403 } 404 else 405 idx++; 406 } 407 *d = '\0'; 408 409 if(*s) { 410 /* dos_name is truncated, check that truncation requirements are met, 411 specifically truncating a filename suffixed by an alternate data stream 412 or truncating the entire filename is not allowed. */ 413 if(strpbrk(s, "\\/:") || truncate_dryrun(dos_name, d - dos_name)) 414 return SANITIZE_ERR_INVALID_PATH; 415 } 416 417 *sanitized = strdup(dos_name); 418 return *sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY; 419 } 420 #endif /* MSDOS */ 421 422 /* 423 Rename file_name if it is a reserved dos device name. 424 425 This is a supporting function for sanitize_file_name. 426 427 Warning: This is an MS-DOS legacy function and was purposely written in a way 428 that some path information may pass through. For example drive letter names 429 (C:, D:, etc) are allowed to pass through. For sanitizing a filename use 430 sanitize_file_name. 431 432 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. 433 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. 434 */ 435 static SANITIZEcode rename_if_reserved_dos(char **const sanitized, 436 const char *file_name, 437 int flags) 438 { 439 /* We could have a file whose name is a device on MS-DOS. Trying to 440 * retrieve such a file would fail at best and wedge us at worst. We need 441 * to rename such files. */ 442 char *p, *base; 443 char fname[PATH_MAX]; 444 #ifdef MSDOS 445 struct_stat st_buf; 446 #endif 447 size_t len; 448 449 if(!sanitized || !file_name) 450 return SANITIZE_ERR_BAD_ARGUMENT; 451 452 *sanitized = NULL; 453 len = strlen(file_name); 454 455 /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */ 456 #ifndef MSDOS 457 if((flags & SANITIZE_ALLOW_PATH) && 458 file_name[0] == '\\' && file_name[1] == '\\') { 459 *sanitized = strdup(file_name); 460 if(!*sanitized) 461 return SANITIZE_ERR_OUT_OF_MEMORY; 462 return SANITIZE_ERR_OK; 463 } 464 #endif 465 466 if(len > PATH_MAX-1) 467 return SANITIZE_ERR_INVALID_PATH; 468 469 memcpy(fname, file_name, len); 470 fname[len] = '\0'; 471 base = basename(fname); 472 473 /* Rename reserved device names that are known to be accessible without \\.\ 474 Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS 475 https://support.microsoft.com/en-us/kb/74496 476 https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx 477 */ 478 for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) { 479 size_t p_len; 480 int x = (curl_strnequal(p, "CON", 3) || 481 curl_strnequal(p, "PRN", 3) || 482 curl_strnequal(p, "AUX", 3) || 483 curl_strnequal(p, "NUL", 3)) ? 3 : 484 (curl_strnequal(p, "CLOCK$", 6)) ? 6 : 485 (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ? 486 (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0; 487 488 if(!x) 489 continue; 490 491 /* the devices may be accessible with an extension or ADS, for 492 example CON.AIR and 'CON . AIR' and CON:AIR access console */ 493 494 for(; p[x] == ' '; ++x) 495 ; 496 497 if(p[x] == '.') { 498 p[x] = '_'; 499 continue; 500 } 501 else if(p[x] == ':') { 502 if(!(flags & SANITIZE_ALLOW_PATH)) { 503 p[x] = '_'; 504 continue; 505 } 506 ++x; 507 } 508 else if(p[x]) /* no match */ 509 continue; 510 511 /* p points to 'CON' or 'CON ' or 'CON:', etc */ 512 p_len = strlen(p); 513 514 /* Prepend a '_' */ 515 if(strlen(fname) == PATH_MAX-1) 516 return SANITIZE_ERR_INVALID_PATH; 517 memmove(p + 1, p, p_len + 1); 518 p[0] = '_'; 519 ++p_len; 520 521 /* if fname was just modified then the basename pointer must be updated */ 522 if(p == fname) 523 base = basename(fname); 524 } 525 526 /* This is the legacy portion from rename_if_dos_device_name that checks for 527 reserved device names. It only works on MS-DOS. On Windows XP the stat 528 check errors with EINVAL if the device name is reserved. On Windows 529 Vista/7/8 it sets mode S_IFREG (regular file or device). According to 530 MSDN stat doc the latter behavior is correct, but that does not help us 531 identify whether it is a reserved device name and not a regular 532 filename. */ 533 #ifdef MSDOS 534 if(base && ((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) { 535 /* Prepend a '_' */ 536 size_t blen = strlen(base); 537 if(blen) { 538 if(strlen(fname) >= PATH_MAX-1) 539 return SANITIZE_ERR_INVALID_PATH; 540 memmove(base + 1, base, blen + 1); 541 base[0] = '_'; 542 } 543 } 544 #endif 545 546 *sanitized = strdup(fname); 547 return *sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY; 548 } 549 550 #ifdef __DJGPP__ 551 /* 552 * Disable program default argument globbing. We do it on our own. 553 */ 554 char **__crt0_glob_function(char *arg) 555 { 556 (void)arg; 557 return (char **)0; 558 } 559 #endif 560 561 #ifdef _WIN32 562 563 #if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) && \ 564 !defined(CURL_DISABLE_CA_SEARCH) && !defined(CURL_CA_SEARCH_SAFE) 565 /* Search and set the CA cert file for Windows. 566 * 567 * Do not call this function if Schannel is the selected SSL backend. We allow 568 * setting CA location for Schannel only when explicitly specified by the user 569 * via CURLOPT_CAINFO / --cacert. 570 * 571 * Function to find CACert bundle on a Win32 platform using SearchPath. 572 * (SearchPath is already declared via inclusions done in setup header file) 573 * (Use the ASCII version instead of the Unicode one!) 574 * The order of the directories it searches is: 575 * 1. application's directory 576 * 2. current working directory 577 * 3. Windows System directory (e.g. C:\Windows\System32) 578 * 4. Windows Directory (e.g. C:\Windows) 579 * 5. all directories along %PATH% 580 * 581 * For WinXP and later search order actually depends on registry value: 582 * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode 583 */ 584 CURLcode FindWin32CACert(struct OperationConfig *config, 585 const TCHAR *bundle_file) 586 { 587 CURLcode result = CURLE_OK; 588 DWORD res_len; 589 TCHAR buf[PATH_MAX]; 590 TCHAR *ptr = NULL; 591 592 buf[0] = TEXT('\0'); 593 594 res_len = SearchPath(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr); 595 if(res_len > 0) { 596 char *mstr = curlx_convert_tchar_to_UTF8(buf); 597 tool_safefree(config->cacert); 598 if(mstr) 599 config->cacert = strdup(mstr); 600 curlx_unicodefree(mstr); 601 if(!config->cacert) 602 result = CURLE_OUT_OF_MEMORY; 603 } 604 605 return result; 606 } 607 #endif 608 609 /* Get a list of all loaded modules with full paths. 610 * Returns slist on success or NULL on error. 611 */ 612 struct curl_slist *GetLoadedModulePaths(void) 613 { 614 struct curl_slist *slist = NULL; 615 #if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) 616 HANDLE hnd = INVALID_HANDLE_VALUE; 617 MODULEENTRY32 mod = {0}; 618 619 mod.dwSize = sizeof(MODULEENTRY32); 620 621 do { 622 hnd = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0); 623 } while(hnd == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_LENGTH); 624 625 if(hnd == INVALID_HANDLE_VALUE) 626 goto error; 627 628 if(!Module32First(hnd, &mod)) 629 goto error; 630 631 do { 632 char *path; /* points to stack allocated buffer */ 633 struct curl_slist *temp; 634 635 #ifdef UNICODE 636 /* sizeof(mod.szExePath) is the max total bytes of wchars. the max total 637 bytes of multibyte chars will not be more than twice that. */ 638 char buffer[sizeof(mod.szExePath) * 2]; 639 if(!WideCharToMultiByte(CP_ACP, 0, mod.szExePath, -1, 640 buffer, sizeof(buffer), NULL, NULL)) 641 goto error; 642 path = buffer; 643 #else 644 path = mod.szExePath; 645 #endif 646 temp = curl_slist_append(slist, path); 647 if(!temp) 648 goto error; 649 slist = temp; 650 } while(Module32Next(hnd, &mod)); 651 652 goto cleanup; 653 654 error: 655 curl_slist_free_all(slist); 656 slist = NULL; 657 cleanup: 658 if(hnd != INVALID_HANDLE_VALUE) 659 CloseHandle(hnd); 660 #endif 661 return slist; 662 } 663 664 bool tool_term_has_bold; 665 666 #if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) 667 /* The terminal settings to restore on exit */ 668 static struct TerminalSettings { 669 HANDLE hStdOut; 670 DWORD dwOutputMode; 671 LONG valid; 672 } TerminalSettings; 673 674 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING 675 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 676 #endif 677 678 static void restore_terminal(void) 679 { 680 if(InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE)) 681 SetConsoleMode(TerminalSettings.hStdOut, TerminalSettings.dwOutputMode); 682 } 683 684 /* This is the console signal handler. 685 * The system calls it in a separate thread. 686 */ 687 static BOOL WINAPI signal_handler(DWORD type) 688 { 689 if(type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT) 690 restore_terminal(); 691 return FALSE; 692 } 693 694 static void init_terminal(void) 695 { 696 TerminalSettings.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); 697 698 /* 699 * Enable VT (Virtual Terminal) output. 700 * Note: VT mode flag can be set on any version of Windows, but VT 701 * processing only performed on Win10 >= version 1709 (OS build 16299) 702 * Creator's Update. Also, ANSI bold on/off supported since then. 703 */ 704 if(TerminalSettings.hStdOut == INVALID_HANDLE_VALUE || 705 !GetConsoleMode(TerminalSettings.hStdOut, 706 &TerminalSettings.dwOutputMode) || 707 !curlx_verify_windows_version(10, 0, 16299, PLATFORM_WINNT, 708 VERSION_GREATER_THAN_EQUAL)) 709 return; 710 711 if((TerminalSettings.dwOutputMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) 712 tool_term_has_bold = true; 713 else { 714 /* The signal handler is set before attempting to change the console mode 715 because otherwise a signal would not be caught after the change but 716 before the handler was installed. */ 717 (void)InterlockedExchange(&TerminalSettings.valid, (LONG)TRUE); 718 if(SetConsoleCtrlHandler(signal_handler, TRUE)) { 719 if(SetConsoleMode(TerminalSettings.hStdOut, 720 (TerminalSettings.dwOutputMode | 721 ENABLE_VIRTUAL_TERMINAL_PROCESSING))) { 722 tool_term_has_bold = true; 723 atexit(restore_terminal); 724 } 725 else { 726 SetConsoleCtrlHandler(signal_handler, FALSE); 727 (void)InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE); 728 } 729 } 730 } 731 } 732 #endif 733 734 CURLcode win32_init(void) 735 { 736 curlx_now_init(); 737 #if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) 738 init_terminal(); 739 #endif 740 741 return CURLE_OK; 742 } 743 744 #if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) 745 /* The following STDIN non - blocking read techniques are heavily inspired 746 by nmap and ncat (https://nmap.org/ncat/) */ 747 struct win_thread_data { 748 /* This is a copy of the true stdin file handle before any redirection. It is 749 read by the thread. */ 750 HANDLE stdin_handle; 751 /* This is the listen socket for the thread. It is closed after the first 752 connection. */ 753 curl_socket_t socket_l; 754 /* This is the global config - used for printing errors and so forth */ 755 struct GlobalConfig *global; 756 }; 757 758 static DWORD WINAPI win_stdin_thread_func(void *thread_data) 759 { 760 struct win_thread_data *tdata = (struct win_thread_data *)thread_data; 761 DWORD n; 762 int nwritten; 763 char buffer[BUFSIZ]; 764 BOOL r; 765 766 SOCKADDR_IN clientAddr; 767 int clientAddrLen = sizeof(clientAddr); 768 769 curl_socket_t socket_w = accept(tdata->socket_l, (SOCKADDR*)&clientAddr, 770 &clientAddrLen); 771 772 if(socket_w == CURL_SOCKET_BAD) { 773 errorf(tdata->global, "accept error: %08lx\n", GetLastError()); 774 goto ThreadCleanup; 775 } 776 777 closesocket(tdata->socket_l); /* sclose here fails test 1498 */ 778 tdata->socket_l = CURL_SOCKET_BAD; 779 if(shutdown(socket_w, SD_RECEIVE) == SOCKET_ERROR) { 780 errorf(tdata->global, "shutdown error: %08lx\n", GetLastError()); 781 goto ThreadCleanup; 782 } 783 for(;;) { 784 r = ReadFile(tdata->stdin_handle, buffer, sizeof(buffer), &n, NULL); 785 if(r == 0) 786 break; 787 if(n == 0) 788 break; 789 nwritten = send(socket_w, buffer, n, 0); 790 if(nwritten == SOCKET_ERROR) 791 break; 792 if((DWORD)nwritten != n) 793 break; 794 } 795 ThreadCleanup: 796 CloseHandle(tdata->stdin_handle); 797 tdata->stdin_handle = NULL; 798 if(tdata->socket_l != CURL_SOCKET_BAD) { 799 sclose(tdata->socket_l); 800 tdata->socket_l = CURL_SOCKET_BAD; 801 } 802 if(socket_w != CURL_SOCKET_BAD) 803 sclose(socket_w); 804 805 if(tdata) { 806 free(tdata); 807 } 808 809 return 0; 810 } 811 812 /* The background thread that reads and buffers the true stdin. */ 813 static HANDLE stdin_thread = NULL; 814 static curl_socket_t socket_r = CURL_SOCKET_BAD; 815 816 curl_socket_t win32_stdin_read_thread(struct GlobalConfig *global) 817 { 818 int result; 819 bool r; 820 int rc = 0, socksize = 0; 821 struct win_thread_data *tdata = NULL; 822 SOCKADDR_IN selfaddr; 823 824 if(socket_r != CURL_SOCKET_BAD) { 825 assert(stdin_thread != NULL); 826 return socket_r; 827 } 828 assert(stdin_thread == NULL); 829 830 do { 831 /* Prepare handles for thread */ 832 tdata = (struct win_thread_data*)calloc(1, sizeof(struct win_thread_data)); 833 if(!tdata) { 834 errorf(global, "calloc() error"); 835 break; 836 } 837 /* Create the listening socket for the thread. When it starts, it will 838 * accept our connection and begin writing STDIN data to the connection. */ 839 tdata->socket_l = WSASocketW(AF_INET, SOCK_STREAM, 840 IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); 841 842 if(tdata->socket_l == CURL_SOCKET_BAD) { 843 errorf(global, "WSASocketW error: %08lx", GetLastError()); 844 break; 845 } 846 847 socksize = sizeof(selfaddr); 848 memset(&selfaddr, 0, socksize); 849 selfaddr.sin_family = AF_INET; 850 selfaddr.sin_addr.S_un.S_addr = htonl(INADDR_LOOPBACK); 851 /* Bind to any available loopback port */ 852 result = bind(tdata->socket_l, (SOCKADDR*)&selfaddr, socksize); 853 if(result == SOCKET_ERROR) { 854 errorf(global, "bind error: %08lx", GetLastError()); 855 break; 856 } 857 858 /* Bind to any available loopback port */ 859 result = getsockname(tdata->socket_l, (SOCKADDR*)&selfaddr, &socksize); 860 if(result == SOCKET_ERROR) { 861 errorf(global, "getsockname error: %08lx", GetLastError()); 862 break; 863 } 864 865 result = listen(tdata->socket_l, 1); 866 if(result == SOCKET_ERROR) { 867 errorf(global, "listen error: %08lx\n", GetLastError()); 868 break; 869 } 870 871 /* Make a copy of the stdin handle to be used by win_stdin_thread_func */ 872 r = DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), 873 GetCurrentProcess(), &tdata->stdin_handle, 874 0, FALSE, DUPLICATE_SAME_ACCESS); 875 876 if(!r) { 877 errorf(global, "DuplicateHandle error: %08lx", GetLastError()); 878 break; 879 } 880 881 /* Start up the thread. We don't bother keeping a reference to it 882 because it runs until program termination. From here on out all reads 883 from the stdin handle or file descriptor 0 will be reading from the 884 socket that is fed by the thread. */ 885 stdin_thread = CreateThread(NULL, 0, win_stdin_thread_func, 886 tdata, 0, NULL); 887 if(!stdin_thread) { 888 errorf(global, "CreateThread error: %08lx", GetLastError()); 889 break; 890 } 891 892 /* Connect to the thread and rearrange our own STDIN handles */ 893 socket_r = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 894 if(socket_r == CURL_SOCKET_BAD) { 895 errorf(global, "socket error: %08lx", GetLastError()); 896 break; 897 } 898 899 /* Hard close the socket on closesocket() */ 900 setsockopt(socket_r, SOL_SOCKET, SO_DONTLINGER, 0, 0); 901 902 if(connect(socket_r, (SOCKADDR*)&selfaddr, socksize) == SOCKET_ERROR) { 903 errorf(global, "connect error: %08lx", GetLastError()); 904 break; 905 } 906 907 if(shutdown(socket_r, SD_SEND) == SOCKET_ERROR) { 908 errorf(global, "shutdown error: %08lx", GetLastError()); 909 break; 910 } 911 912 /* Set the stdin handle to read from the socket. */ 913 if(SetStdHandle(STD_INPUT_HANDLE, (HANDLE)socket_r) == 0) { 914 errorf(global, "SetStdHandle error: %08lx", GetLastError()); 915 break; 916 } 917 918 rc = 1; 919 } while(0); 920 921 if(rc != 1) { 922 if(socket_r != CURL_SOCKET_BAD && tdata) { 923 if(GetStdHandle(STD_INPUT_HANDLE) == (HANDLE)socket_r && 924 tdata->stdin_handle) { 925 /* restore STDIN */ 926 SetStdHandle(STD_INPUT_HANDLE, tdata->stdin_handle); 927 tdata->stdin_handle = NULL; 928 } 929 930 sclose(socket_r); 931 socket_r = CURL_SOCKET_BAD; 932 } 933 934 if(stdin_thread) { 935 TerminateThread(stdin_thread, 1); 936 stdin_thread = NULL; 937 } 938 939 if(tdata) { 940 if(tdata->stdin_handle) 941 CloseHandle(tdata->stdin_handle); 942 if(tdata->socket_l != CURL_SOCKET_BAD) 943 sclose(tdata->socket_l); 944 945 free(tdata); 946 } 947 948 return CURL_SOCKET_BAD; 949 } 950 951 assert(socket_r != CURL_SOCKET_BAD); 952 return socket_r; 953 } 954 955 #endif /* !CURL_WINDOWS_UWP && !UNDER_CE */ 956 957 #endif /* _WIN32 */ 958 959 #endif /* _WIN32 || MSDOS */