1 /* mswindows.c -- Windows-specific support
2 Copyright (C) 1996-2011, 2014-2015, 2018-2021 Free Software
3 Foundation, Inc.
4
5 This file is part of GNU Wget.
6
7 GNU Wget is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 GNU Wget is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Wget. If not, see <http://www.gnu.org/licenses/>.
19
20 Additional permission under GNU GPL version 3 section 7
21
22 If you modify this program, or any covered work, by linking or
23 combining it with the OpenSSL project's OpenSSL library (or a
24 modified version of that library), containing parts covered by the
25 terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
26 grants you additional permission to convey the resulting work.
27 Corresponding Source for a non-source form of such a combination
28 shall include the source code for the parts of OpenSSL used as well
29 as that of the covered work. */
30
31 #define INHIBIT_WRAP /* avoid wrapping of socket, bind, ... */
32
33 #include "wget.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <math.h>
40
41
42 #include "utils.h"
43 #include "url.h"
44 #include "exits.h"
45
46 #ifndef ES_SYSTEM_REQUIRED
47 #define ES_SYSTEM_REQUIRED 0x00000001
48 #endif
49
50 #ifndef ES_CONTINUOUS
51 #define ES_CONTINUOUS 0x80000000
52 #endif
53
54
55 /* Windows version of xsleep in utils.c. */
56
57 void
xsleep(double seconds)58 xsleep (double seconds)
59 {
60 #if defined(HAVE_USLEEP) && defined(HAVE_SLEEP)
61 if (seconds >= 1)
62 {
63 /* Explained in utils.c. */
64 sleep (seconds);
65 seconds -= (long) seconds;
66 }
67 usleep (seconds * 1000000);
68 #else /* not HAVE_USLEEP */
69 SleepEx ((DWORD) (seconds * 1000 + .5), FALSE);
70 #endif /* not HAVE_USLEEP */
71 }
72
73 void
windows_main(char ** exec_name)74 windows_main (char **exec_name)
75 {
76 char *p;
77
78 /* Remove .EXE from filename if it has one. */
79 *exec_name = xstrdup (*exec_name);
80 p = strrchr (*exec_name, '.');
81 if (p)
82 *p = '\0';
83 }
84
85 static void
ws_cleanup(void)86 ws_cleanup (void)
87 {
88 xfree (exec_name);
89 WSACleanup ();
90 }
91
92 #if defined(CTRLBREAK_BACKGND) || defined(CTRLC_BACKGND)
93 static void
ws_hangup(const char * reason)94 ws_hangup (const char *reason)
95 {
96 fprintf (stderr, _("Continuing in background.\n"));
97 redirect_output (true, reason);
98
99 /* Detach process from the current console. Under Windows 9x, if we
100 were launched from a 16-bit process (which is usually the case;
101 command.com is 16-bit) the parent process should resume right away.
102 Under NT or if launched from a 32-process under 9x, this is a futile
103 gesture as the parent will wait for us to terminate before resuming. */
104 FreeConsole ();
105 }
106 #endif
107
108 /* Construct the name for a named section (a.k.a. `file mapping') object.
109 The returned string is dynamically allocated and needs to be xfree()'d. */
110 static char *
make_section_name(DWORD pid)111 make_section_name (DWORD pid)
112 {
113 return aprintf ("gnu_wget_fake_fork_%lu", pid);
114 }
115
116 /* This structure is used to hold all the data that is exchanged between
117 parent and child. */
118 struct fake_fork_info
119 {
120 HANDLE event;
121 bool logfile_changed;
122 char lfilename[MAX_PATH + 1];
123 };
124
125 /* Determines if we are the child and if so performs the child logic.
126 Return values:
127 < 0 error
128 0 parent
129 > 0 child
130 */
131 static int
fake_fork_child(void)132 fake_fork_child (void)
133 {
134 HANDLE section, event;
135 struct fake_fork_info *info;
136 char *name;
137
138 name = make_section_name (GetCurrentProcessId ());
139 section = OpenFileMapping (FILE_MAP_WRITE, FALSE, name);
140 xfree (name);
141 /* It seems that Windows 9x and NT set last-error inconsistently when
142 OpenFileMapping() fails; so we assume it failed because the section
143 object does not exist. */
144 if (!section)
145 return 0; /* We are the parent. */
146
147 info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
148 if (!info)
149 {
150 CloseHandle (section);
151 return -1;
152 }
153
154 event = info->event;
155
156 info->logfile_changed = false;
157 if (!opt.lfilename && (!opt.quiet || opt.server_response))
158 {
159 /* See utils:fork_to_background for explanation. */
160 FILE *new_log_fp = unique_create (DEFAULT_LOGFILE, false, &opt.lfilename);
161 if (new_log_fp)
162 {
163 info->logfile_changed = true;
164 snprintf (info->lfilename, sizeof (info->lfilename), "%s",
165 opt.lfilename);
166 fclose (new_log_fp);
167 }
168 }
169
170 UnmapViewOfFile (info);
171 CloseHandle (section);
172
173 /* Inform the parent that we've done our part. */
174 if (!SetEvent (event))
175 return -1;
176
177 CloseHandle (event);
178 return 1; /* We are the child. */
179 }
180
181 /* Windows doesn't support the fork() call; so we fake it by invoking
182 another copy of Wget with the same arguments with which we were
183 invoked. The child copy of Wget should perform the same initialization
184 sequence as the parent; so we should have two processes that are
185 essentially identical. We create a specially named section object that
186 allows the child to distinguish itself from the parent and is used to
187 exchange information between the two processes. We use an event object
188 for synchronization. */
189 static void
fake_fork(void)190 fake_fork (void)
191 {
192 char exe[MAX_PATH + 1];
193 DWORD exe_len, le;
194 SECURITY_ATTRIBUTES sa;
195 HANDLE section, event, h[2];
196 STARTUPINFO si;
197 PROCESS_INFORMATION pi;
198 struct fake_fork_info *info;
199 char *name;
200 BOOL rv;
201
202 section = pi.hProcess = pi.hThread = NULL;
203
204 /* Get the fully qualified name of our executable. This is more reliable
205 than using argv[0]. */
206 exe_len = GetModuleFileName (GetModuleHandle (NULL), exe, sizeof (exe));
207 if (!exe_len || (exe_len >= sizeof (exe)))
208 return;
209
210 sa.nLength = sizeof (sa);
211 sa.lpSecurityDescriptor = NULL;
212 sa.bInheritHandle = TRUE;
213
214 /* Create an anonymous inheritable event object that starts out
215 non-signaled. */
216 event = CreateEvent (&sa, FALSE, FALSE, NULL);
217 if (!event)
218 return;
219
220 /* Create the child process detached form the current console and in a
221 suspended state. */
222 xzero (si);
223 si.cb = sizeof (si);
224 rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
225 CREATE_SUSPENDED | DETACHED_PROCESS,
226 NULL, NULL, &si, &pi);
227 if (!rv)
228 goto cleanup;
229
230 /* Create a named section object with a name based on the process id of
231 the child. */
232 name = make_section_name (pi.dwProcessId);
233 section =
234 CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
235 sizeof (struct fake_fork_info), name);
236 le = GetLastError();
237 xfree (name);
238 /* Fail if the section object already exists (should not happen). */
239 if (!section || (le == ERROR_ALREADY_EXISTS))
240 {
241 rv = FALSE;
242 goto cleanup;
243 }
244
245 /* Copy the event handle into the section object. */
246 info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
247 if (!info)
248 {
249 rv = FALSE;
250 goto cleanup;
251 }
252
253 info->event = event;
254
255 UnmapViewOfFile (info);
256
257 /* Start the child process. */
258 rv = ResumeThread (pi.hThread);
259 if (!rv)
260 {
261 TerminateProcess (pi.hProcess, (DWORD) -1);
262 goto cleanup;
263 }
264
265 /* Wait for the child to signal to us that it has done its part. If it
266 terminates before signaling us it's an error. */
267
268 h[0] = event;
269 h[1] = pi.hProcess;
270 rv = WAIT_OBJECT_0 == WaitForMultipleObjects (2, h, FALSE, 5 * 60 * 1000);
271 if (!rv)
272 goto cleanup;
273
274 info = MapViewOfFile (section, FILE_MAP_READ, 0, 0, 0);
275 if (!info)
276 {
277 rv = FALSE;
278 goto cleanup;
279 }
280
281 /* Ensure string is properly terminated. */
282 if (info->logfile_changed &&
283 !memchr (info->lfilename, '\0', sizeof (info->lfilename)))
284 {
285 rv = FALSE;
286 goto cleanup;
287 }
288
289 printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
290 if (info->logfile_changed)
291 printf (_("Output will be written to %s.\n"), quote (info->lfilename));
292
293 UnmapViewOfFile (info);
294
295 cleanup:
296
297 if (event)
298 CloseHandle (event);
299 if (section)
300 CloseHandle (section);
301 if (pi.hThread)
302 CloseHandle (pi.hThread);
303 if (pi.hProcess)
304 CloseHandle (pi.hProcess);
305
306 /* We're the parent. If all is well, terminate. */
307 if (rv)
308 exit (WGET_EXIT_SUCCESS);
309
310 /* We failed, return. */
311 }
312
313 /* This is the corresponding Windows implementation of the
314 fork_to_background() function in utils.c. */
315 bool
fork_to_background(void)316 fork_to_background (void)
317 {
318 int rv;
319
320 rv = fake_fork_child ();
321 if (rv < 0)
322 {
323 fprintf (stderr, _("fake_fork_child() failed\n"));
324 abort ();
325 }
326 else if (rv == 0)
327 {
328 /* We're the parent. */
329 fake_fork ();
330 /* If fake_fork() returns, it failed. */
331 fprintf (stderr, _("fake_fork() failed\n"));
332 abort ();
333 }
334 /* If we get here, we're the child. */
335 return false;
336 }
337
338 static BOOL WINAPI
ws_handler(DWORD dwEvent)339 ws_handler (DWORD dwEvent)
340 {
341 switch (dwEvent)
342 {
343 #ifdef CTRLC_BACKGND
344 case CTRL_C_EVENT:
345 ws_hangup ("CTRL+C");
346 return TRUE;
347 #endif
348 #ifdef CTRLBREAK_BACKGND
349 case CTRL_BREAK_EVENT:
350 ws_hangup ("CTRL+Break");
351 return TRUE;
352 #endif
353 default:
354 return FALSE;
355 }
356 }
357
358 static char *title_buf = NULL;
359 static char *curr_url = NULL;
360 static int old_percentage = -1;
361
362 /* Updates the console title with the URL of the current file being
363 transferred. */
364 void
ws_changetitle(const char * url)365 ws_changetitle (const char *url)
366 {
367 xfree (title_buf);
368 xfree (curr_url);
369 title_buf = xmalloc (strlen (url) + 20);
370 curr_url = xstrdup (url);
371 old_percentage = -1;
372 sprintf (title_buf, "Wget %s", curr_url);
373 SetConsoleTitle (title_buf);
374 }
375
376 /* Updates the console title with the percentage of the current file
377 transferred. */
378 void
ws_percenttitle(double percentage_float)379 ws_percenttitle (double percentage_float)
380 {
381 int percentage;
382
383 if (!title_buf || !curr_url)
384 return;
385
386 percentage = (int) percentage_float;
387
388 /* Clamp percentage value. */
389 if (percentage < 0)
390 percentage = 0;
391 if (percentage > 100)
392 percentage = 100;
393
394 /* Only update the title when the percentage has changed. */
395 if (percentage == old_percentage)
396 return;
397
398 old_percentage = percentage;
399
400 sprintf (title_buf, "Wget [%d%%] %s", percentage, curr_url);
401 SetConsoleTitle (title_buf);
402 }
403
404 /* Returns a pointer to the fully qualified name of the directory that
405 contains the Wget binary (wget.exe). The returned path does not have a
406 trailing path separator. Returns NULL on failure. */
407 char *
ws_mypath(void)408 ws_mypath (void)
409 {
410 static char *wspathsave = NULL;
411
412 if (!wspathsave)
413 {
414 char buf[MAX_PATH + 1];
415 char *p;
416 DWORD len;
417
418 len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
419 if (!len || (len >= sizeof (buf)))
420 return NULL;
421
422 p = strrchr (buf, PATH_SEPARATOR);
423 if (!p)
424 return NULL;
425
426 *p = '\0';
427 wspathsave = xstrdup (buf);
428 }
429
430 return wspathsave;
431 }
432
433 /* Prevent Windows entering sleep/hibernation-mode while Wget is doing
434 a lengthy transfer. Windows does not, by default, consider network
435 activity in console-programs as activity! Works on Win-98/ME/2K
436 and up. */
437 static void
set_sleep_mode(void)438 set_sleep_mode (void)
439 {
440 typedef DWORD (WINAPI *func_t) (DWORD);
441 func_t set_exec_state;
442
443 set_exec_state =
444 (func_t) GetProcAddress (GetModuleHandle ("KERNEL32.DLL"),
445 "SetThreadExecutionState");
446
447 if (set_exec_state)
448 set_exec_state (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
449 }
450
451 /* Perform Windows specific initialization. */
452 void
ws_startup(void)453 ws_startup (void)
454 {
455 WSADATA data;
456 WORD requested = MAKEWORD (1, 1);
457 int err = WSAStartup (requested, &data);
458 if (err != 0)
459 {
460 fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
461 exec_name);
462 exit (WGET_EXIT_GENERIC_ERROR);
463 }
464
465 if (data.wVersion < requested)
466 {
467 fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
468 exec_name);
469 WSACleanup ();
470 exit (WGET_EXIT_GENERIC_ERROR);
471 }
472
473 atexit (ws_cleanup);
474 set_sleep_mode ();
475 SetConsoleCtrlHandler (ws_handler, TRUE);
476 }
477
478 /* run_with_timeout Windows implementation. */
479
480 /* Stack size 0 uses default thread stack-size (reserve+commit).
481 Determined by what's in the PE header. */
482 #define THREAD_STACK_SIZE 0
483
484 struct thread_data
485 {
486 void (*fun) (void *);
487 void *arg;
488 DWORD ws_error;
489 };
490
491 /* The callback that runs FUN(ARG) in a separate thread. This
492 function exists for two reasons: a) to not require FUN to be
493 declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
494 which are per-thread. The latter is useful when FUN calls Winsock
495 functions, which is how run_with_timeout is used in Wget.
496
497 [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
498 the default (wcc386 -3r). */
499
500 static DWORD WINAPI
thread_helper(void * arg)501 thread_helper (void *arg)
502 {
503 struct thread_data *td = (struct thread_data *) arg;
504
505 /* Initialize Winsock error to what it was in the parent. That way
506 the subsequent call to WSAGetLastError will return the same value
507 if td->fun doesn't change Winsock error state. */
508 WSASetLastError (td->ws_error);
509
510 td->fun (td->arg);
511
512 /* Return Winsock error to the caller, in case FUN ran Winsock
513 code. */
514 td->ws_error = WSAGetLastError ();
515 return 0;
516 }
517
518 /* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
519 seconds. Returns true if the function was interrupted with a
520 timeout, false otherwise.
521
522 This works by running FUN in a separate thread and terminating the
523 thread if it doesn't finish in the specified time. */
524
525 bool
run_with_timeout(double seconds,void (* fun)(void *),void * arg)526 run_with_timeout (double seconds, void (*fun) (void *), void *arg)
527 {
528 HANDLE thread_hnd;
529 struct thread_data thread_arg;
530 DWORD thread_id;
531 bool rc;
532
533 DEBUGP (("seconds %.2f, ", seconds));
534
535 if (seconds == 0)
536 {
537 blocking_fallback:
538 fun (arg);
539 return false;
540 }
541
542 thread_arg.fun = fun;
543 thread_arg.arg = arg;
544 thread_arg.ws_error = WSAGetLastError ();
545 thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
546 &thread_arg, 0, &thread_id);
547 if (!thread_hnd)
548 {
549 DEBUGP (("CreateThread() failed; [%#lx]\n",
550 (unsigned long) GetLastError ()));
551 goto blocking_fallback;
552 }
553
554 if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
555 == WAIT_OBJECT_0)
556 {
557 /* Propagate error state (which is per-thread) to this thread,
558 so the caller can inspect it. */
559 WSASetLastError (thread_arg.ws_error);
560 DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
561 rc = false;
562 }
563 else
564 {
565 TerminateThread (thread_hnd, 1);
566 rc = true;
567 }
568
569 CloseHandle (thread_hnd); /* Clear-up after TerminateThread(). */
570 thread_hnd = NULL;
571 return rc;
572 }
573
574
575 #ifdef ENABLE_IPV6
576 /* An inet_ntop implementation that uses WSAAddressToString.
577 Prototype complies with POSIX 1003.1-2004. This is only used under
578 IPv6 because Wget prints IPv4 addresses using inet_ntoa. */
579
580 const char *
inet_ntop(int af,const void * src,char * dst,socklen_t cnt)581 inet_ntop (int af, const void *src, char *dst, socklen_t cnt)
582 {
583 /* struct sockaddr can't accommodate struct sockaddr_in6. */
584 union {
585 struct sockaddr_in6 sin6;
586 struct sockaddr_in sin;
587 } sa;
588 DWORD dstlen = cnt;
589 size_t srcsize;
590
591 xzero (sa);
592 switch (af)
593 {
594 case AF_INET:
595 sa.sin.sin_family = AF_INET;
596 sa.sin.sin_addr = *(struct in_addr *) src;
597 srcsize = sizeof (sa.sin);
598 break;
599 case AF_INET6:
600 sa.sin6.sin6_family = AF_INET6;
601 sa.sin6.sin6_addr = *(struct in6_addr *) src;
602 srcsize = sizeof (sa.sin6);
603 break;
604 default:
605 abort ();
606 }
607
608 if (WSAAddressToString ((struct sockaddr *) &sa, srcsize, NULL, dst, &dstlen) != 0)
609 {
610 errno = WSAGetLastError();
611 return NULL;
612 }
613 return (const char *) dst;
614 }
615 #endif
616
617
618 void
set_windows_fd_as_blocking_socket(int fd)619 set_windows_fd_as_blocking_socket (int fd)
620 {
621 /* 04/2011
622 gnulib select() converts blocking sockets to nonblocking in windows
623 discussed here:
624 http://old.nabble.com/blocking-socket-is-nonblocking-after-calling-gnulib-
625 select%28%29-in-windows-td31432857.html
626
627 wget uses blocking sockets so we must convert them back to blocking.
628 */
629 int ret = 0;
630 int wsagle = 0;
631 const int zero = 0;
632
633 do
634 {
635 if(wsagle == WSAEINPROGRESS)
636 Sleep(1); /* use windows sleep */
637
638 WSASetLastError (0);
639 ret = ioctl (fd, FIONBIO, &zero);
640 wsagle = WSAGetLastError ();
641 }
642 while (ret && (wsagle == WSAEINPROGRESS));
643
644 if(ret)
645 {
646 fprintf (stderr,
647 _("ioctl() failed. The socket could not be set as blocking.\n") );
648 DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
649 abort ();
650 }
651 return;
652 }
653