/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at https://curl.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * SPDX-License-Identifier: curl * ***************************************************************************/ #include "tool_setup.h" #if defined(_WIN32) || defined(MSDOS) #include "curlx/basename.h" /* for curlx_basename() */ #include "curlx/version_win32.h" /* for curlx_verify_windows_version() */ #ifdef _WIN32 # include #elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */ # define CURL_USE_LFN(f) 0 /* long filenames never available */ #elif defined(__DJGPP__) # include /* for _use_lfn(f) prototype */ # define CURL_USE_LFN(f) _use_lfn(f) #endif #include "tool_cfgable.h" #include "tool_doswin.h" #include "tool_msgs.h" #ifdef MSDOS #ifndef S_ISCHR # ifdef S_IFCHR # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) # else # define S_ISCHR(m) 0 /* cannot tell if file is a device */ # endif #endif /* The functions msdosify, rename_if_dos_device_name and __crt0_glob_function * were taken with modification from the DJGPP port of tar 1.12. They use * algorithms originally from DJTAR. */ #ifdef __DJGPP__ /* * Disable program default argument globbing. We do it on our own. */ char **__crt0_glob_function(char *arg) { (void)arg; return (char **)0; } #endif /* * Test if truncating a path to a file leaves at least a single character * in the filename. Filenames suffixed by an alternate data stream cannot be * truncated. This performs a dry run, nothing is modified. * * Good truncate_pos 9: C:\foo\bar => C:\foo\ba * Good truncate_pos 6: C:\foo => C:\foo * Good truncate_pos 5: C:\foo => C:\fo * Bad* truncate_pos 5: C:foo => C:foo * Bad truncate_pos 5: C:\foo:ads => C:\fo * Bad truncate_pos 9: C:\foo:ads => C:\foo:ad * Bad truncate_pos 5: C:\foo\bar => C:\fo * Bad truncate_pos 5: C:\foo\ => C:\fo * Bad truncate_pos 7: C:\foo\ => C:\foo\ * Error truncate_pos 7: C:\foo => (pos out of range) * Bad truncate_pos 1: C:\foo\ => C * * * C:foo is ambiguous, C could end up being a drive or file therefore * something like C:superlongfilename cannot be truncated. * * Returns * SANITIZE_ERR_OK: Good -- 'path' can be truncated * SANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated * != SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error */ static SANITIZEcode truncate_dryrun(const char *path, const size_t truncate_pos) { size_t len; if(!path) return SANITIZE_ERR_BAD_ARGUMENT; len = strlen(path); if(truncate_pos > len) return SANITIZE_ERR_BAD_ARGUMENT; if(!len || !truncate_pos) return SANITIZE_ERR_INVALID_PATH; if(strpbrk(&path[truncate_pos - 1], "\\/:")) return SANITIZE_ERR_INVALID_PATH; /* C:\foo can be truncated but C:\foo:ads cannot */ if(truncate_pos > 1) { const char *p = &path[truncate_pos - 1]; do { --p; if(*p == ':') return SANITIZE_ERR_INVALID_PATH; } while(p != path && *p != '\\' && *p != '/'); } return SANITIZE_ERR_OK; } /* * Extra sanitization MS-DOS for file_name. * * This is a supporting function for sanitize_file_name. * * Warning: This is an MS-DOS legacy function and was purposely written in * a way that some path information may pass through. For example drive letter * names (C:, D:, etc) are allowed to pass through. For sanitizing a filename * use sanitize_file_name. * * Success: SANITIZE_ERR_OK *sanitized points to a sanitized copy of file_name. * Failure: != SANITIZE_ERR_OK *sanitized is NULL. */ static SANITIZEcode msdosify(char ** const sanitized, const char *file_name, int flags) { char dos_name[PATH_MAX]; static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */ "|<>/\\\":?*"; /* illegal in DOS & W95 */ static const char *illegal_chars_w95 = &illegal_chars_dos[8]; int idx, dot_idx; const char *s = file_name; char *d = dos_name; const char * const dlimit = dos_name + sizeof(dos_name) - 1; const char *illegal_aliens = illegal_chars_dos; size_t len = sizeof(illegal_chars_dos) - 1; if(!sanitized) return SANITIZE_ERR_BAD_ARGUMENT; *sanitized = NULL; if(!file_name) return SANITIZE_ERR_BAD_ARGUMENT; if(strlen(file_name) > PATH_MAX - 1) return SANITIZE_ERR_INVALID_PATH; /* Support for Windows 9X VFAT systems, when available. */ if(CURL_USE_LFN(file_name)) { illegal_aliens = illegal_chars_w95; len -= (illegal_chars_w95 - illegal_chars_dos); } /* Get past the drive letter, if any. */ if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') { *d++ = *s++; *d = (flags & SANITIZE_ALLOW_PATH) ? ':' : '_'; ++d; ++s; } for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) { if(memchr(illegal_aliens, *s, len)) { if((flags & SANITIZE_ALLOW_PATH) && *s == ':') *d = ':'; else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\')) *d = *s; /* Dots are special: DOS does not allow them as the leading character, and a filename cannot have more than a single dot. We leave the first non-leading dot alone, unless it comes too close to the beginning of the name: we want sh.lex.c to become sh_lex.c, not sh.lex-c. */ else if(*s == '.') { if((flags & SANITIZE_ALLOW_PATH) && idx == 0 && (s[1] == '/' || s[1] == '\\' || (s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) { /* Copy "./" and "../" verbatim. */ *d++ = *s++; if(d == dlimit) break; if(*s == '.') { *d++ = *s++; if(d == dlimit) break; } *d = *s; } else if(idx == 0) *d = '_'; else if(dot_idx >= 0) { if(dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */ d[dot_idx - idx] = '_'; /* replace previous dot */ *d = '.'; } else *d = '-'; } else *d = '.'; if(*s == '.') dot_idx = idx; } else if(*s == '+' && s[1] == '+') { if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */ *d++ = 'x'; if(d == dlimit) break; *d = 'x'; } else { /* libg++ etc. */ if(dlimit - d < 4) { *d++ = 'x'; if(d == dlimit) break; *d = 'x'; } else { memcpy(d, "plus", 4); d += 3; } } s++; idx++; } else *d = '_'; } else *d = *s; if(*s == '/' || *s == '\\') { idx = 0; dot_idx = -1; } else idx++; } *d = '\0'; if(*s) { /* dos_name is truncated, check that truncation requirements are met, specifically truncating a filename suffixed by an alternate data stream or truncating the entire filename is not allowed. */ if(strpbrk(s, "\\/:") || truncate_dryrun(dos_name, d - dos_name)) return SANITIZE_ERR_INVALID_PATH; } *sanitized = curlx_strdup(dos_name); return *sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY; } #endif /* MSDOS */ /* * Rename file_name if it is a reserved dos device name. * * This is a supporting function for sanitize_file_name. * * Warning: This is an MS-DOS legacy function and was purposely written in * a way that some path information may pass through. For example drive letter * names (C:, D:, etc) are allowed to pass through. For sanitizing a filename * use sanitize_file_name. * * Success: SANITIZE_ERR_OK *sanitized points to a sanitized copy of file_name. * Failure: != SANITIZE_ERR_OK *sanitized is NULL. */ static SANITIZEcode rename_if_reserved_dos(char ** const sanitized, const char *file_name, int flags) { /* We could have a file whose name is a device on MS-DOS. Trying to retrieve such a file would fail at best and wedge us at worst. We need to rename such files. */ char *p, *base, *buffer; #ifdef MSDOS curlx_struct_stat st_buf; #endif size_t len, bufsize; if(!sanitized || !file_name) return SANITIZE_ERR_BAD_ARGUMENT; *sanitized = NULL; /* Ignore "\\" prefixed paths, they are allowed to use reserved names. */ #ifndef MSDOS if((flags & SANITIZE_ALLOW_PATH) && file_name[0] == '\\' && file_name[1] == '\\') { *sanitized = curlx_strdup(file_name); if(!*sanitized) return SANITIZE_ERR_OUT_OF_MEMORY; return SANITIZE_ERR_OK; } #endif /* The buffer contains two extra bytes to allow for path expansion that occurs if reserved name(s) need an underscore prepended. */ len = strlen(file_name); bufsize = len + 2 + 1; buffer = curlx_malloc(bufsize); if(!buffer) return SANITIZE_ERR_OUT_OF_MEMORY; memcpy(buffer, file_name, len + 1); base = curlx_basename(buffer); /* Rename reserved device names that are known to be accessible without \\.\ Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS https://web.archive.org/web/20160314141551/support.microsoft.com/en-us/kb/74496 https://learn.microsoft.com/windows/win32/fileio/naming-a-file */ for(p = buffer; p; p = (p == buffer && buffer != base ? base : NULL)) { size_t p_len; int x = (curl_strnequal(p, "CON", 3) || curl_strnequal(p, "PRN", 3) || curl_strnequal(p, "AUX", 3) || curl_strnequal(p, "NUL", 3)) ? 3 : (curl_strnequal(p, "CLOCK$", 6)) ? 6 : (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ? (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0; if(!x) continue; /* the devices may be accessible with an extension or ADS, for example CON.AIR and 'CON . AIR' and CON:AIR access console */ for(; p[x] == ' '; ++x) ; if(p[x] == '.') { p[x] = '_'; continue; } else if(p[x] == ':') { if(!(flags & SANITIZE_ALLOW_PATH)) { p[x] = '_'; continue; } ++x; } else if(p[x]) /* no match */ continue; /* p points to 'CON' or 'CON ' or 'CON:', etc */ p_len = strlen(p); /* Prepend a '_' */ memmove(p + 1, p, p_len + 1); p[0] = '_'; ++p_len; ++len; /* the basename pointer must be updated since the path has expanded */ if(p == buffer) base = curlx_basename(buffer); } /* This is the legacy portion from rename_if_dos_device_name that checks for reserved device names. It only works on MS-DOS. On Windows XP the stat check errors with EINVAL if the device name is reserved. On Windows Vista/7/8 it sets mode S_IFREG (regular file or device). According to MSDN stat doc the latter behavior is correct, but that does not help us identify whether it is a reserved device name and not a regular filename. */ #ifdef MSDOS if(base && (curlx_stat(base, &st_buf) == 0) && S_ISCHR(st_buf.st_mode)) { /* Prepend a '_' */ size_t blen = strlen(base); if(blen) { if(len == bufsize - 1) { curlx_free(buffer); return SANITIZE_ERR_INVALID_PATH; } memmove(base + 1, base, blen + 1); base[0] = '_'; ++len; } } #endif *sanitized = buffer; return SANITIZE_ERR_OK; } /* * Sanitize a file or path name. * * All banned characters are replaced by underscores, for example: * f?*foo => f__foo * f:foo::$DATA => f_foo__$DATA * f:\foo:bar => f__foo_bar * f:\foo:bar => f:\foo:bar (flag SANITIZE_ALLOW_PATH) * * This function was implemented according to the guidelines in 'Naming Files, * Paths, and Namespaces' section 'Naming Conventions'. * https://learn.microsoft.com/windows/win32/fileio/naming-a-file * * Flags * ----- * SANITIZE_ALLOW_PATH: Allow path separators and colons. * Without this flag path separators and colons are sanitized. * * SANITIZE_ALLOW_RESERVED: Allow reserved device names. * Without this flag a reserved device name is renamed (COM1 => _COM1). * * To fully block reserved device names requires not passing either flag. * Some less common path styles are allowed to use reserved device names. * For example, a "\\" prefixed path may use reserved device names if paths * are allowed. * * Success: SANITIZE_ERR_OK *sanitized points to a sanitized copy of file_name. * Failure: != SANITIZE_ERR_OK *sanitized is NULL. */ SANITIZEcode sanitize_file_name(char ** const sanitized, const char *file_name, int flags) { char *p, *target; size_t len; SANITIZEcode sc; if(!sanitized) return SANITIZE_ERR_BAD_ARGUMENT; *sanitized = NULL; if(!file_name) return SANITIZE_ERR_BAD_ARGUMENT; len = strlen(file_name); target = curlx_strdup(file_name); if(!target) return SANITIZE_ERR_OUT_OF_MEMORY; #ifndef MSDOS if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4)) /* Skip the literal-path prefix \\?\ */ p = target + 4; else #endif p = target; /* replace control characters and other banned characters */ for(; *p; ++p) { const char *banned; if((1 <= *p && *p <= 31) || (!(flags & SANITIZE_ALLOW_PATH) && *p == ':') || (!(flags & SANITIZE_ALLOW_PATH) && (*p == '/' || *p == '\\'))) { *p = '_'; continue; } for(banned = "|<>\"?*"; *banned; ++banned) { if(*p == *banned) { *p = '_'; break; } } } /* remove trailing spaces and periods if not allowing paths */ if(!(flags & SANITIZE_ALLOW_PATH) && len) { char *clip = NULL; p = &target[len]; do { --p; if(*p != ' ' && *p != '.') break; clip = p; } while(p != target); if(clip) { *clip = '\0'; } } #ifdef MSDOS sc = msdosify(&p, target, flags); curlx_free(target); if(sc) return sc; target = p; #endif if(!(flags & SANITIZE_ALLOW_RESERVED)) { sc = rename_if_reserved_dos(&p, target, flags); curlx_free(target); if(sc) return sc; target = p; } #ifdef DEBUGBUILD if(getenv("CURL_FN_SANITIZE_BAD")) { curlx_free(target); return SANITIZE_ERR_INVALID_PATH; } if(getenv("CURL_FN_SANITIZE_OOM")) { curlx_free(target); return SANITIZE_ERR_OUT_OF_MEMORY; } #endif *sanitized = target; return SANITIZE_ERR_OK; } #ifdef _WIN32 #if !defined(CURL_WINDOWS_UWP) && \ !defined(CURL_DISABLE_CA_SEARCH) && !defined(CURL_CA_SEARCH_SAFE) /* Search and set the CA cert file for Windows. * * Do not call this function if Schannel is the selected SSL backend. We allow * setting CA location for Schannel only when explicitly specified by the user * via CURLOPT_CAINFO / --cacert. * * Function to find CACert bundle on a Win32 platform using SearchPath. * (SearchPath is already declared via inclusions done in setup header file) * (Use the ASCII version instead of the Unicode one!) * The order of the directories it searches is: * 1. application's directory * 2. current working directory * 3. Windows System directory (e.g. C:\Windows\System32) * 4. Windows Directory (e.g. C:\Windows) * 5. all directories along %PATH% * * For WinXP and later search order actually depends on registry value: * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode */ CURLcode FindWin32CACert(struct OperationConfig *config, const TCHAR *bundle_file) { CURLcode result = CURLE_OK; DWORD res_len; TCHAR buf[MAX_PATH]; TCHAR *ptr = NULL; buf[0] = TEXT('\0'); res_len = SearchPath(NULL, bundle_file, NULL, MAX_PATH, buf, &ptr); if(res_len > 0) { curlx_free(config->cacert); config->cacert = curlx_convert_tchar_to_UTF8(buf); if(!config->cacert) result = CURLE_OUT_OF_MEMORY; } return result; } #endif /* Get a list of all loaded modules with full paths. * Returns slist on success or NULL on error. */ struct curl_slist *GetLoadedModulePaths(void) { struct curl_slist *slist = NULL; #ifndef CURL_WINDOWS_UWP HANDLE hnd = INVALID_HANDLE_VALUE; MODULEENTRY32 mod = { 0 }; mod.dwSize = sizeof(MODULEENTRY32); do { hnd = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0); } while(hnd == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_LENGTH); if(hnd == INVALID_HANDLE_VALUE) goto error; if(!Module32First(hnd, &mod)) goto error; do { char *path; /* points to stack allocated buffer */ struct curl_slist *temp; #ifdef UNICODE /* sizeof(mod.szExePath) is the max total bytes of wchars. the max total bytes of multibyte chars is not more than twice that. */ char buffer[sizeof(mod.szExePath) * 2]; if(!WideCharToMultiByte(CP_ACP, 0, mod.szExePath, -1, buffer, sizeof(buffer), NULL, NULL)) goto error; path = buffer; #else path = mod.szExePath; #endif temp = curl_slist_append(slist, path); if(!temp) goto error; slist = temp; } while(Module32Next(hnd, &mod)); goto cleanup; error: curl_slist_free_all(slist); slist = NULL; cleanup: if(hnd != INVALID_HANDLE_VALUE) CloseHandle(hnd); #endif return slist; } bool tool_term_has_bold; #ifndef CURL_WINDOWS_UWP /* The terminal settings to restore on exit */ static struct TerminalSettings { HANDLE hStdOut; DWORD dwOutputMode; LONG valid; } TerminalSettings; /* Offered by mingw-w64 v7+. MS SDK ~10.16299/~VS2017+. */ #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif static void restore_terminal(void) { if(InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE)) SetConsoleMode(TerminalSettings.hStdOut, TerminalSettings.dwOutputMode); } /* This is the console signal handler. * The system calls it in a separate thread. */ static BOOL WINAPI signal_handler(DWORD type) { if(type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT) restore_terminal(); return FALSE; } static void init_terminal(void) { TerminalSettings.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); /* * Enable VT (Virtual Terminal) output. * Note: VT mode flag can be set on any version of Windows, but VT * processing only performed on Win10 >= version 1709 (OS build 16299) * Creator's Update. Also, ANSI bold on/off supported since then. */ if(TerminalSettings.hStdOut == INVALID_HANDLE_VALUE || !GetConsoleMode(TerminalSettings.hStdOut, &TerminalSettings.dwOutputMode) || !curlx_verify_windows_version(10, 0, 16299, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) return; if((TerminalSettings.dwOutputMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) tool_term_has_bold = true; else { /* The signal handler is set before attempting to change the console mode because otherwise a signal would not be caught after the change but before the handler was installed. */ (void)InterlockedExchange(&TerminalSettings.valid, (LONG)TRUE); if(SetConsoleCtrlHandler(signal_handler, TRUE)) { if(SetConsoleMode(TerminalSettings.hStdOut, (TerminalSettings.dwOutputMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))) { tool_term_has_bold = true; atexit(restore_terminal); } else { SetConsoleCtrlHandler(signal_handler, FALSE); (void)InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE); } } } } #ifdef USE_WINSOCK /* The following STDIN non - blocking read techniques are heavily inspired by nmap and ncat (https://nmap.org/ncat/) */ struct win_thread_data { /* This is a copy of the true stdin file handle before any redirection. It is read by the thread. */ HANDLE stdin_handle; /* This is the listen socket for the thread. It is closed after the first connection. */ curl_socket_t socket_l; }; static DWORD WINAPI win_stdin_thread_func(void *thread_data) { struct win_thread_data *tdata = (struct win_thread_data *)thread_data; struct sockaddr_in clientAddr; int clientAddrLen = sizeof(clientAddr); curl_socket_t socket_w = CURL_ACCEPT(tdata->socket_l, (struct sockaddr *)&clientAddr, &clientAddrLen); if(socket_w == CURL_SOCKET_BAD) { errorf("accept error: %d", SOCKERRNO); goto ThreadCleanup; } sclose(tdata->socket_l); tdata->socket_l = CURL_SOCKET_BAD; if(shutdown(socket_w, SHUT_RD)) { errorf("shutdown error: %d", SOCKERRNO); goto ThreadCleanup; } for(;;) { DWORD n; ssize_t nwritten; char buffer[BUFSIZ]; if(!ReadFile(tdata->stdin_handle, buffer, sizeof(buffer), &n, NULL)) break; if(n == 0) break; nwritten = swrite(socket_w, buffer, n); if(nwritten == -1) break; if((DWORD)nwritten != n) break; } ThreadCleanup: CloseHandle(tdata->stdin_handle); tdata->stdin_handle = NULL; if(tdata->socket_l != CURL_SOCKET_BAD) { sclose(tdata->socket_l); tdata->socket_l = CURL_SOCKET_BAD; } if(socket_w != CURL_SOCKET_BAD) sclose(socket_w); curlx_free(tdata); return 0; } /* The background thread that reads and buffers the true stdin. */ curl_socket_t win32_stdin_read_thread(void) { int rc = 0; struct win_thread_data *tdata = NULL; static HANDLE stdin_thread = NULL; static curl_socket_t socket_r = CURL_SOCKET_BAD; if(socket_r != CURL_SOCKET_BAD) { assert(stdin_thread != NULL); return socket_r; } assert(stdin_thread == NULL); do { curl_socklen_t socksize = 0; struct sockaddr_in selfaddr; /* Prepare handles for thread */ tdata = (struct win_thread_data *) curlx_calloc(1, sizeof(struct win_thread_data)); if(!tdata) { errorf("curlx_calloc() error"); break; } /* Create the listening socket for the thread. When it starts, it accepts * our connection and begin writing STDIN data to the connection. */ tdata->socket_l = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(tdata->socket_l == CURL_SOCKET_BAD) { errorf("socket() error: %d", SOCKERRNO); break; } socksize = sizeof(selfaddr); memset(&selfaddr, 0, socksize); selfaddr.sin_family = AF_INET; selfaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* Bind to any available loopback port */ if(bind(tdata->socket_l, (const struct sockaddr *)&selfaddr, socksize)) { errorf("bind error: %d", SOCKERRNO); break; } /* Bind to any available loopback port */ if(getsockname(tdata->socket_l, (struct sockaddr *)&selfaddr, &socksize)) { errorf("getsockname error: %d", SOCKERRNO); break; } if(listen(tdata->socket_l, 1)) { errorf("listen error: %d", SOCKERRNO); break; } /* Make a copy of the stdin handle to be used by win_stdin_thread_func */ if(!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), GetCurrentProcess(), &tdata->stdin_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { errorf("DuplicateHandle error: 0x%08lx", GetLastError()); break; } /* Start up the thread. We do not bother keeping a reference to it because it runs until program termination. From here on out all reads from the stdin handle or file descriptor 0 is reading from the socket that is fed by the thread. */ stdin_thread = CreateThread(NULL, 0, win_stdin_thread_func, tdata, 0, NULL); if(!stdin_thread) { errorf("CreateThread error: 0x%08lx", GetLastError()); break; } tdata = NULL; /* win_stdin_thread_func owns it now */ /* Connect to the thread and rearrange our own STDIN handles */ socket_r = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(socket_r == CURL_SOCKET_BAD) { errorf("socket error: %d", SOCKERRNO); break; } /* Hard close the socket on closesocket() */ setsockopt(socket_r, SOL_SOCKET, SO_DONTLINGER, 0, 0); if(connect(socket_r, (const struct sockaddr *)&selfaddr, socksize)) { errorf("connect error: %d", SOCKERRNO); break; } if(shutdown(socket_r, SHUT_WR)) { errorf("shutdown error: %d", SOCKERRNO); break; } /* Set the stdin handle to read from the socket. */ if(SetStdHandle(STD_INPUT_HANDLE, (HANDLE)socket_r) == 0) { errorf("SetStdHandle error: 0x%08lx", GetLastError()); break; } rc = 1; } while(0); if(rc != 1) { if(socket_r != CURL_SOCKET_BAD && tdata) { if(GetStdHandle(STD_INPUT_HANDLE) == (HANDLE)socket_r && tdata->stdin_handle) { /* restore STDIN */ SetStdHandle(STD_INPUT_HANDLE, tdata->stdin_handle); tdata->stdin_handle = NULL; } sclose(socket_r); socket_r = CURL_SOCKET_BAD; } if(stdin_thread) { TerminateThread(stdin_thread, 1); CloseHandle(stdin_thread); stdin_thread = NULL; } if(tdata) { if(tdata->stdin_handle) CloseHandle(tdata->stdin_handle); if(tdata->socket_l != CURL_SOCKET_BAD) sclose(tdata->socket_l); curlx_free(tdata); } return CURL_SOCKET_BAD; } assert(socket_r != CURL_SOCKET_BAD); return socket_r; } #endif /* USE_WINSOCK */ #endif /* !CURL_WINDOWS_UWP */ CURLcode win32_init(void) { curlx_verify_windows_init(); curlx_now_init(); #ifndef CURL_WINDOWS_UWP init_terminal(); #endif return CURLE_OK; } #endif /* _WIN32 */ #endif /* _WIN32 || MSDOS */