branch: master
tool_doswin.c
26614 bytesRaw
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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 <tlhelp32.h>
#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 <fcntl.h> /* 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 */