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