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