1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* Inspired by previous work by Romeo Anghelache & Eric Stern. */
10 
11 #include "squid.h"
12 #include "Debug.h"
13 #include "globals.h"
14 #include "protos.h"
15 #include "SquidConfig.h"
16 #include "tools.h"
17 #include "WinSvc.h"
18 
19 #if _SQUID_WINDOWS_
20 #if !defined(_MSWSOCK_)
21 #include <mswsock.h>
22 #endif
23 #include <process.h>
24 #if defined(_MSC_VER) /* Microsoft C Compiler ONLY */
25 #include <crtdbg.h>
26 #endif
27 #endif
28 
29 /* forward declarations */
30 static void WIN32_Exit(void);
31 static unsigned int GetOSVersion();
32 void WIN32_svcstatusupdate(DWORD, DWORD);
33 void WINAPI WIN32_svcHandler(DWORD);
34 extern "C" void WINAPI SquidWinSvcMain(DWORD, char **);
35 
36 #if USE_WIN32_SERVICE
37 static void WIN32_Abort(int);
38 static int WIN32_StoreKey(const char *, DWORD, unsigned char *, int);
39 static int WIN32_create_key(void);
40 static void WIN32_build_argv (char *);
41 #endif
42 
43 #if defined(_MSC_VER) /* Microsoft C Compiler ONLY */
44 void Squid_Win32InvalidParameterHandler(const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, uintptr_t);
45 #endif
46 static int Win32SockInit(void);
47 static void Win32SockCleanup(void);
48 SQUIDCEXTERN LPCRITICAL_SECTION dbg_mutex;
49 void WIN32_ExceptionHandlerCleanup(void);
50 static int s_iInitCount = 0;
51 static HANDLE NotifyAddrChange_thread = INVALID_HANDLE_VALUE;
52 
53 #undef NotifyAddrChange
54 typedef DWORD(WINAPI * PFNotifyAddrChange) (OUT PHANDLE, IN LPOVERLAPPED);
55 #define NOTIFYADDRCHANGE "NotifyAddrChange"
56 
57 #if USE_WIN32_SERVICE
58 static SERVICE_STATUS svcStatus;
59 static SERVICE_STATUS_HANDLE svcHandle;
60 static int WIN32_argc;
61 static char ** WIN32_argv;
62 static char * WIN32_module_name;
63 
64 #define VENDOR "squid-cache.org"
65 static char VENDORString[] = VENDOR;
66 #define SOFTWARENAME PACKAGE_NAME
67 static char SOFTWARENAMEString[] = SOFTWARENAME;
68 #define SOFTWARE "SOFTWARE"
69 static char SOFTWAREString[] = SOFTWARE;
70 #define COMMANDLINE "CommandLine"
71 #define CONFIGFILE  "ConfigFile"
72 #undef ChangeServiceConfig2
73 typedef BOOL (WINAPI * PFChangeServiceConfig2) (SC_HANDLE, DWORD, LPVOID);
74 #ifdef UNICODE
75 #define CHANGESERVICECONFIG2 "ChangeServiceConfig2W"
76 #else
77 #define CHANGESERVICECONFIG2 "ChangeServiceConfig2A"
78 #endif
79 static SC_ACTION Squid_SCAction[] = { { SC_ACTION_RESTART, 60000 } };
80 static char Squid_ServiceDescriptionString[] = SOFTWARENAME " " VERSION " WWW Proxy Server";
81 static SERVICE_DESCRIPTION Squid_ServiceDescription = { Squid_ServiceDescriptionString };
82 static SERVICE_FAILURE_ACTIONS Squid_ServiceFailureActions = { INFINITE, NULL, NULL, 1, Squid_SCAction };
83 static char REGKEY[256] = SOFTWARE "\\" VENDOR "\\" SOFTWARENAME "\\";
84 static char *keys[] = {
85     SOFTWAREString,     /* key[0] */
86     VENDORString,       /* key[1] */
87     SOFTWARENAMEString,   /* key[2] */
88     NULL,       /* key[3] */
89     NULL        /* key[4] */
90 };
91 
92 static int Squid_Aborting = 0;
93 #endif
94 
95 /* ====================================================================== */
96 /* LOCAL FUNCTIONS */
97 /* ====================================================================== */
98 
99 #if USE_WIN32_SERVICE
100 static int
WIN32_create_key(void)101 WIN32_create_key(void)
102 {
103     int index;
104     HKEY hKey;
105     HKEY hKeyNext;
106     int retval;
107     LONG rv;
108 
109     hKey = HKEY_LOCAL_MACHINE;
110     index = 0;
111     retval = 0;
112 
113     /* Walk the tree, creating at each stage if necessary */
114 
115     while (keys[index]) {
116         unsigned long result;
117         rv = RegCreateKeyEx(hKey, keys[index],  /* subkey */
118                             0,          /* reserved */
119                             NULL,       /* class */
120                             REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKeyNext, &result);
121 
122         if (rv != ERROR_SUCCESS) {
123             fprintf(stderr, "RegCreateKeyEx(%s),%d\n", keys[index], (int) rv);
124             retval = -4;
125         }
126 
127         /* Close the old key */
128         rv = RegCloseKey(hKey);
129 
130         if (rv != ERROR_SUCCESS) {
131             fprintf(stderr, "RegCloseKey %d\n", (int) rv);
132 
133             if (retval == 0) {
134                 /* Keep error status from RegCreateKeyEx, if any */
135                 retval = -4;
136             }
137         }
138 
139         if (retval) {
140             break;
141         }
142 
143         hKey = hKeyNext;
144         ++index;
145     }
146 
147     if (keys[index] == NULL) {
148         /* Close the final key we opened, if we walked the entire
149          * tree
150          */
151         rv = RegCloseKey(hKey);
152 
153         if (rv != ERROR_SUCCESS) {
154             fprintf(stderr, "RegCloseKey %d\n", (int) rv);
155 
156             if (retval == 0) {
157                 /* Keep error status from RegCreateKeyEx, if any */
158                 retval = -4;
159             }
160         }
161     }
162 
163     return retval;
164 }
165 
166 static int
WIN32_StoreKey(const char * key,DWORD type,unsigned char * value,int value_size)167 WIN32_StoreKey(const char *key, DWORD type, unsigned char *value,
168                int value_size)
169 {
170     LONG rv;
171     HKEY hKey;
172     int retval;
173 
174     rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY, 0, KEY_WRITE, &hKey);
175 
176     if (rv == ERROR_FILE_NOT_FOUND) {
177         /* Key could not be opened -- try to create it
178          */
179 
180         if (WIN32_create_key() < 0) {
181             /* Creation failed (error already reported) */
182             return -4;
183         }
184 
185         /* Now it has been created we should be able to open it
186          */
187         rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY, 0, KEY_WRITE, &hKey);
188 
189         if (rv == ERROR_FILE_NOT_FOUND) {
190             fprintf(stderr, "Registry does not contain key %s after creation\n",
191                     REGKEY);
192             return -1;
193         }
194     }
195 
196     if (rv != ERROR_SUCCESS) {
197         fprintf(stderr, "RegOpenKeyEx HKLM\\%s, %d\n", REGKEY, (int) rv);
198         return -4;
199     }
200 
201     /* Now set the value and data */
202     rv = RegSetValueEx(hKey, key,   /* value key name */
203                        0,           /* reserved */
204                        type,            /* type */
205                        value,           /* value data */
206                        (DWORD) value_size); /* for size of "value" */
207 
208     retval = 0;         /* Return value */
209 
210     if (rv != ERROR_SUCCESS) {
211         fprintf(stderr, "RegQueryValueEx(key %s),%d\n", key, (int) rv);
212         retval = -4;
213     } else {
214         fprintf(stderr, "Registry stored HKLM\\%s\\%s value %s\n",
215                 REGKEY,
216                 key,
217                 type == REG_SZ ? value : (unsigned char *) "(not displayable)");
218     }
219 
220     /* Make sure we close the key even if there was an error storing
221      * the data
222      */
223     rv = RegCloseKey(hKey);
224 
225     if (rv != ERROR_SUCCESS) {
226         fprintf(stderr, "RegCloseKey HKLM\\%s, %d\n", REGKEY, (int) rv);
227 
228         if (retval == 0) {
229             /* Keep error status from RegQueryValueEx, if any */
230             retval = -4;
231         }
232     }
233 
234     return retval;
235 }
236 
237 /* Build argv, argc from string passed from Windows.  */
WIN32_build_argv(char * cmd)238 static void WIN32_build_argv(char *cmd)
239 {
240     int argvlen = 0;
241     char *word;
242 
243     WIN32_argc = 1;
244     WIN32_argv = (char **) xmalloc ((WIN32_argc+1) * sizeof (char *));
245     WIN32_argv[0]=xstrdup(WIN32_module_name);
246     /* Scan command line until there is nothing left. */
247 
248     while (*cmd) {
249         /* Ignore spaces */
250 
251         if (xisspace(*cmd)) {
252             ++cmd;
253             continue;
254         }
255 
256         /* Found the beginning of an argument. */
257         word = cmd;
258 
259         while (*cmd) {
260             ++cmd;      /* Skip over this character */
261 
262             if (xisspace(*cmd)) /* End of argument if space */
263                 break;
264         }
265 
266         if (*cmd)
267             *cmd++ = '\0';      /* Terminate `word' */
268 
269         /* See if we need to allocate more space for argv */
270         if (WIN32_argc >= argvlen) {
271             argvlen = WIN32_argc + 1;
272             WIN32_argv = (char **) xrealloc (WIN32_argv, (1 + argvlen) * sizeof (char *));
273         }
274 
275         /* Add word to argv file. */
276         WIN32_argv[WIN32_argc++] = word;
277     }
278 
279     WIN32_argv[WIN32_argc] = NULL;
280 }
281 
282 #endif /* USE_WIN32_SERVICE */
283 
284 static unsigned int
GetOSVersion()285 GetOSVersion()
286 {
287     OSVERSIONINFOEX osvi;
288     BOOL bOsVersionInfoEx;
289 
290     safe_free(WIN32_OS_string);
291     memset(&osvi, '\0', sizeof(OSVERSIONINFOEX));
292     /* Try calling GetVersionEx using the OSVERSIONINFOEX structure.
293      * If that fails, try using the OSVERSIONINFO structure.
294      */
295 
296     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
297 
298     if (!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) & osvi))) {
299         osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
300         if (!GetVersionEx((OSVERSIONINFO *) & osvi))
301             goto GetVerError;
302     }
303     switch (osvi.dwPlatformId) {
304     case VER_PLATFORM_WIN32_NT:
305         if (osvi.dwMajorVersion <= 4) {
306             WIN32_OS_string = xstrdup("Windows NT");
307             return _WIN_OS_WINNT;
308         }
309         if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 0)) {
310             WIN32_OS_string = xstrdup("Windows 2000");
311             return _WIN_OS_WIN2K;
312         }
313         if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 1)) {
314             WIN32_OS_string = xstrdup("Windows XP");
315             return _WIN_OS_WINXP;
316         }
317         if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 2)) {
318             WIN32_OS_string = xstrdup("Windows Server 2003");
319             return _WIN_OS_WINNET;
320         }
321         if ((osvi.dwMajorVersion == 6) && (osvi.dwMinorVersion == 0)) {
322             if (osvi.wProductType == VER_NT_WORKSTATION)
323                 WIN32_OS_string = xstrdup("Windows Vista");
324             else
325                 WIN32_OS_string = xstrdup("Windows Server 2008");
326             return _WIN_OS_WINLON;
327         }
328         if ((osvi.dwMajorVersion == 6) && (osvi.dwMinorVersion == 1)) {
329             if (osvi.wProductType == VER_NT_WORKSTATION)
330                 WIN32_OS_string = xstrdup("Windows 7");
331             else
332                 WIN32_OS_string = xstrdup("Windows Server 2008 R2");
333             return _WIN_OS_WIN7;
334         }
335         if (((osvi.dwMajorVersion > 6)) || ((osvi.dwMajorVersion == 6) && (osvi.dwMinorVersion > 1))) {
336             if (osvi.wProductType == VER_NT_WORKSTATION)
337                 WIN32_OS_string = xstrdup("Unknown Windows version, assuming Windows 7 capabilities");
338             else
339                 WIN32_OS_string = xstrdup("Unknown Windows version, assuming Windows Server 2008 R2 capabilities");
340             return _WIN_OS_WIN7;
341         }
342         break;
343     case VER_PLATFORM_WIN32_WINDOWS:
344         if ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0)) {
345             WIN32_OS_string = xstrdup("Windows 95");
346             return _WIN_OS_WIN95;
347         }
348         if ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 10)) {
349             WIN32_OS_string = xstrdup("Windows 98");
350             return _WIN_OS_WIN98;
351         }
352         if ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 90)) {
353             WIN32_OS_string = xstrdup("Windows Me");
354             return _WIN_OS_WINME;
355         }
356         break;
357     case VER_PLATFORM_WIN32s:
358         WIN32_OS_string = xstrdup("Windows 3.1 with WIN32S");
359         return _WIN_OS_WIN32S;
360         break;
361     default:
362         break;
363     }
364 GetVerError:
365     WIN32_OS_string = xstrdup("Unknown Windows system");
366     return _WIN_OS_UNKNOWN;
367 }
368 
369 /* ====================================================================== */
370 /* PUBLIC FUNCTIONS */
371 /* ====================================================================== */
372 
373 #if USE_WIN32_SERVICE
374 void
WIN32_Abort(int sig)375 WIN32_Abort(int sig)
376 {
377     svcStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
378     svcStatus.dwServiceSpecificExitCode = 1;
379     Squid_Aborting = 1;
380     WIN32_Exit();
381 }
382 #endif
383 
384 void
WIN32_IpAddrChangeMonitorExit()385 WIN32_IpAddrChangeMonitorExit()
386 {
387     DWORD status = ERROR_SUCCESS;
388 
389     if (NotifyAddrChange_thread != INVALID_HANDLE_VALUE) {
390         TerminateThread(NotifyAddrChange_thread, status);
391         CloseHandle(NotifyAddrChange_thread);
392     }
393 }
394 
395 void
WIN32_Exit()396 WIN32_Exit()
397 {
398     Win32SockCleanup();
399 #if USE_WIN32_SERVICE
400 
401     if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) {
402         if (!Squid_Aborting) {
403             svcStatus.dwCurrentState = SERVICE_STOPPED;
404             SetServiceStatus(svcHandle, &svcStatus);
405         }
406     }
407 
408 #endif
409     if (dbg_mutex)
410         DeleteCriticalSection(dbg_mutex);
411 
412     WIN32_ExceptionHandlerCleanup();
413     WIN32_IpAddrChangeMonitorExit();
414     _exit(0);
415 }
416 
417 static DWORD WINAPI
WIN32_IpAddrChangeMonitor(LPVOID lpParam)418 WIN32_IpAddrChangeMonitor(LPVOID lpParam)
419 {
420     DWORD Result;
421     HMODULE IPHLPAPIHandle;
422     PFNotifyAddrChange NotifyAddrChange;
423 
424     if ((IPHLPAPIHandle = GetModuleHandle("IPHLPAPI")) == NULL)
425         IPHLPAPIHandle = LoadLibrary("IPHLPAPI");
426     NotifyAddrChange = (PFNotifyAddrChange) GetProcAddress(IPHLPAPIHandle, NOTIFYADDRCHANGE);
427 
428     while (1) {
429         Result = NotifyAddrChange(NULL, NULL);
430         if (Result != NO_ERROR) {
431             debugs(1, DBG_IMPORTANT, "NotifyAddrChange error " << Result);
432             return 1;
433         }
434         debugs(1, DBG_IMPORTANT, "Notification of IP address change received, requesting Squid reconfiguration ...");
435         reconfigure(SIGHUP);
436     }
437     return 0;
438 }
439 
440 DWORD
WIN32_IpAddrChangeMonitorInit()441 WIN32_IpAddrChangeMonitorInit()
442 {
443     DWORD status = ERROR_SUCCESS;
444     DWORD threadID = 0, ThrdParam = 0;
445 
446     if ((WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) && (Config.onoff.WIN32_IpAddrChangeMonitor)) {
447         NotifyAddrChange_thread = CreateThread(NULL, 0, WIN32_IpAddrChangeMonitor,
448                                                &ThrdParam, 0, &threadID);
449         if (NotifyAddrChange_thread == NULL) {
450             status = GetLastError();
451             NotifyAddrChange_thread = INVALID_HANDLE_VALUE;
452             debugs(1, DBG_IMPORTANT, "Failed to start IP monitor thread.");
453         } else
454             debugs(1, 2, "Starting IP monitor thread [" << threadID << "] ...");
455     }
456     return status;
457 }
458 
WIN32_Subsystem_Init(int * argc,char *** argv)459 int WIN32_Subsystem_Init(int * argc, char *** argv)
460 {
461 #if defined(_MSC_VER) /* Microsoft C Compiler ONLY */
462     _invalid_parameter_handler oldHandler, newHandler;
463 #endif
464 
465     WIN32_OS_version = GetOSVersion();
466 
467     if ((WIN32_OS_version == _WIN_OS_UNKNOWN) || (WIN32_OS_version == _WIN_OS_WIN32S))
468         return 1;
469 
470     if (atexit(WIN32_Exit) != 0)
471         return 1;
472 
473 #if defined(_MSC_VER) /* Microsoft C Compiler ONLY */
474 
475     newHandler = Squid_Win32InvalidParameterHandler;
476 
477     oldHandler = _set_invalid_parameter_handler(newHandler);
478 
479     _CrtSetReportMode(_CRT_ASSERT, 0);
480 
481 #endif
482 #if USE_WIN32_SERVICE
483 
484     if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) {
485         char path[512];
486         HKEY hndKey;
487 
488         if (signal(SIGABRT, WIN32_Abort) == SIG_ERR)
489             return 1;
490 
491         /* Register the service Handler function */
492         svcHandle = RegisterServiceCtrlHandler(service_name.c_str(), WIN32_svcHandler);
493 
494         if (svcHandle == 0)
495             return 1;
496 
497         /* Set Process work dir to directory cointaining squid.exe */
498         GetModuleFileName(NULL, path, 512);
499 
500         WIN32_module_name=xstrdup(path);
501 
502         path[strlen(path) - 10] = '\0';
503 
504         if (SetCurrentDirectory(path) == 0)
505             return 1;
506 
507         safe_free(ConfigFile);
508 
509         /* get config file from Windows Registry */
510         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY, 0, KEY_QUERY_VALUE, &hndKey) == ERROR_SUCCESS) {
511             DWORD Type = 0;
512             DWORD Size = 0;
513             LONG Result;
514             Result = RegQueryValueEx(hndKey, CONFIGFILE, NULL, &Type, NULL, &Size);
515 
516             if (Result == ERROR_SUCCESS && Size) {
517                 ConfigFile = static_cast<char *>(xmalloc(Size));
518                 RegQueryValueEx(hndKey, CONFIGFILE, NULL, &Type, (unsigned char *)ConfigFile, &Size);
519             } else
520                 ConfigFile = xstrdup(DEFAULT_CONFIG_FILE);
521 
522             Size = 0;
523 
524             Type = 0;
525 
526             Result = RegQueryValueEx(hndKey, COMMANDLINE, NULL, &Type, NULL, &Size);
527 
528             if (Result == ERROR_SUCCESS && Size) {
529                 WIN32_Service_Command_Line = static_cast<char *>(xmalloc(Size));
530                 RegQueryValueEx(hndKey, COMMANDLINE, NULL, &Type, (unsigned char *)WIN32_Service_Command_Line, &Size);
531             } else
532                 WIN32_Service_Command_Line = xstrdup("");
533 
534             RegCloseKey(hndKey);
535         } else {
536             ConfigFile = xstrdup(DEFAULT_CONFIG_FILE);
537             WIN32_Service_Command_Line = xstrdup("");
538         }
539 
540         WIN32_build_argv(WIN32_Service_Command_Line);
541         *argc = WIN32_argc;
542         *argv = WIN32_argv;
543         /* Set Service Status to SERVICE_START_PENDING */
544         svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
545         svcStatus.dwCurrentState = SERVICE_START_PENDING;
546         svcStatus.dwControlsAccepted =
547             SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
548         svcStatus.dwWin32ExitCode = 0;
549         svcStatus.dwServiceSpecificExitCode = 0;
550         svcStatus.dwCheckPoint = 0;
551         svcStatus.dwWaitHint = 10000;
552         SetServiceStatus(svcHandle, &svcStatus);
553 
554         _setmaxstdio(Squid_MaxFD);
555 
556     }
557 
558 #endif /* USE_WIN32_SERVICE */
559     if (Win32SockInit() < 0)
560         return 1;
561 
562     return 0;
563 }
564 
565 #if USE_WIN32_SERVICE
566 void
WIN32_svcstatusupdate(DWORD svcstate,DWORD WaitHint)567 WIN32_svcstatusupdate(DWORD svcstate, DWORD WaitHint)
568 {
569     if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) {
570         ++svcStatus.dwCheckPoint;
571         svcStatus.dwWaitHint = WaitHint;
572         svcStatus.dwCurrentState = svcstate;
573         SetServiceStatus(svcHandle, &svcStatus);
574     }
575 }
576 
577 VOID WINAPI
WIN32_svcHandler(DWORD Opcode)578 WIN32_svcHandler(DWORD Opcode)
579 {
580     DWORD status;
581 
582     switch (Opcode) {
583 
584     case _WIN_SQUID_SERVICE_CONTROL_STOP:
585 
586     case _WIN_SQUID_SERVICE_CONTROL_SHUTDOWN:
587         /* Do whatever it takes to stop here. */
588         svcStatus.dwWin32ExitCode = 0;
589         svcStatus.dwCurrentState = SERVICE_STOP_PENDING;
590         svcStatus.dwCheckPoint = 0;
591         svcStatus.dwWaitHint = 10000;
592         shut_down(SIGTERM);
593 
594         if (!SetServiceStatus(svcHandle, &svcStatus)) {
595             status = GetLastError();
596             debugs(1, DBG_IMPORTANT, "SetServiceStatus error " << status);
597         }
598 
599         debugs(1, DBG_IMPORTANT, "Leaving Squid service");
600         return;
601 
602     case _WIN_SQUID_SERVICE_CONTROL_INTERROGATE:
603         /* Fall through to send current status. */
604 
605         if (!SetServiceStatus(svcHandle, &svcStatus)) {
606             status = GetLastError();
607             debugs(1, DBG_IMPORTANT, "SetServiceStatus error " << status);
608         }
609 
610         break;
611 
612     case _WIN_SQUID_SERVICE_CONTROL_ROTATE:
613         rotate_logs(SIGUSR1);
614         break;
615 
616     case _WIN_SQUID_SERVICE_CONTROL_RECONFIGURE:
617         reconfigure(SIGHUP);
618         break;
619 
620     case _WIN_SQUID_SERVICE_CONTROL_DEBUG:
621         sigusr2_handle(SIGUSR2);
622         break;
623 
624     case _WIN_SQUID_SERVICE_CONTROL_INTERRUPT:
625         /* Do whatever it takes to stop here. */
626         svcStatus.dwWin32ExitCode = 0;
627         svcStatus.dwCurrentState = SERVICE_STOP_PENDING;
628         svcStatus.dwCheckPoint = 0;
629         svcStatus.dwWaitHint = 10000;
630         shut_down(SIGINT);
631 
632         if (!SetServiceStatus(svcHandle, &svcStatus)) {
633             status = GetLastError();
634             debugs(1, DBG_IMPORTANT, "SetServiceStatus error " << status);
635         }
636 
637         debugs(1, DBG_IMPORTANT, "Leaving Squid service");
638         break;
639 
640     default:
641         debugs(1, DBG_IMPORTANT, "Unrecognized opcode " << Opcode);
642     }
643 
644     return;
645 }
646 
647 void
WIN32_RemoveService()648 WIN32_RemoveService()
649 {
650     SC_HANDLE schService;
651     SC_HANDLE schSCManager;
652 
653     if (service_name.isEmpty())
654         service_name = SBuf(APP_SHORTNAME);
655 
656     const char *service =  service_name.c_str();
657     strcat(REGKEY, service);
658 
659     keys[4] = const_cast<char*>(service);
660 
661     schSCManager = OpenSCManager(NULL,  /* machine (NULL == local)    */
662                                  NULL,          /* database (NULL == default) */
663                                  SC_MANAGER_ALL_ACCESS  /* access required            */
664                                 );
665 
666     if (!schSCManager)
667         fprintf(stderr, "OpenSCManager failed\n");
668     else {
669         schService = OpenService(schSCManager, service, SERVICE_ALL_ACCESS);
670 
671         if (schService == NULL)
672             fprintf(stderr, "OpenService failed\n");
673 
674         /* Could not open the service */
675         else {
676             /* try to stop the service */
677 
678             if (ControlService(schService, _WIN_SQUID_SERVICE_CONTROL_STOP,
679                                &svcStatus)) {
680                 sleep(1);
681 
682                 while (QueryServiceStatus(schService, &svcStatus)) {
683                     if (svcStatus.dwCurrentState == SERVICE_STOP_PENDING)
684                         sleep(1);
685                     else
686                         break;
687                 }
688             }
689 
690             /* now remove the service */
691             if (DeleteService(schService) == 0)
692                 fprintf(stderr, "DeleteService failed.\n");
693             else
694                 printf("Service " SQUIDSBUFPH " deleted successfully.\n", SQUIDSBUFPRINT(service_name));
695 
696             CloseServiceHandle(schService);
697         }
698 
699         CloseServiceHandle(schSCManager);
700     }
701 }
702 
703 void
WIN32_SetServiceCommandLine()704 WIN32_SetServiceCommandLine()
705 {
706     if (service_name.isEmpty())
707         service_name = SBuf(APP_SHORTNAME);
708 
709     const char *service = service_name.c_str();
710     strcat(REGKEY, service);
711 
712     keys[4] = const_cast<char*>(service);
713 
714     /* Now store the Service Command Line in the registry */
715     WIN32_StoreKey(COMMANDLINE, REG_SZ, (unsigned char *) WIN32_Command_Line, strlen(WIN32_Command_Line) + 1);
716 }
717 
718 void
WIN32_InstallService()719 WIN32_InstallService()
720 {
721     SC_HANDLE schService;
722     SC_HANDLE schSCManager;
723     char ServicePath[512];
724     char szPath[512];
725     int lenpath;
726 
727     if (service_name.isEmpty())
728         service_name = SBuf(APP_SHORTNAME);
729 
730     const char *service = service_name.c_str();
731     strcat(REGKEY, service);
732 
733     keys[4] = const_cast<char*>(service);
734 
735     if ((lenpath = GetModuleFileName(NULL, ServicePath, 512)) == 0) {
736         fprintf(stderr, "Can't get executable path\n");
737         exit(1);
738     }
739 
740     snprintf(szPath, sizeof(szPath), "%s %s:" SQUIDSBUFPH, ServicePath, _WIN_SQUID_SERVICE_OPTION, SQUIDSBUFPRINT(service_name));
741     schSCManager = OpenSCManager(NULL,  /* machine (NULL == local)    */
742                                  NULL,          /* database (NULL == default) */
743                                  SC_MANAGER_ALL_ACCESS  /* access required            */
744                                 );
745 
746     if (!schSCManager) {
747         fprintf(stderr, "OpenSCManager failed\n");
748         exit(1);
749     } else {
750         schService = CreateService(schSCManager,    /* SCManager database     */
751                                    service,             /* name of service        */
752                                    service,             /* name to display        */
753                                    SERVICE_ALL_ACCESS,              /* desired access         */
754                                    SERVICE_WIN32_OWN_PROCESS,           /* service type           */
755                                    SERVICE_AUTO_START,              /* start type             */
756                                    SERVICE_ERROR_NORMAL,            /* error control type     */
757                                    (const char *) szPath,           /* service's binary       */
758                                    NULL,                    /* no load ordering group */
759                                    NULL,                    /* no tag identifier      */
760                                    "Tcpip\0AFD\0",              /* dependencies           */
761                                    NULL,                    /* LocalSystem account    */
762                                    NULL);                   /* no password            */
763 
764         if (schService) {
765             if (WIN32_OS_version > _WIN_OS_WINNT) {
766                 HMODULE ADVAPI32Handle;
767                 PFChangeServiceConfig2 ChangeServiceConfig2;
768                 DWORD dwInfoLevel = SERVICE_CONFIG_DESCRIPTION;
769 
770                 ADVAPI32Handle = GetModuleHandle("advapi32");
771                 ChangeServiceConfig2 = (PFChangeServiceConfig2) GetProcAddress(ADVAPI32Handle, CHANGESERVICECONFIG2);
772                 ChangeServiceConfig2(schService, dwInfoLevel, &Squid_ServiceDescription);
773                 dwInfoLevel = SERVICE_CONFIG_FAILURE_ACTIONS;
774                 ChangeServiceConfig2(schService, dwInfoLevel, &Squid_ServiceFailureActions);
775             }
776 
777             CloseServiceHandle(schService);
778             /* Now store the config file location in the registry */
779 
780             if (!ConfigFile)
781                 ConfigFile = xstrdup(DEFAULT_CONFIG_FILE);
782 
783             WIN32_StoreKey(CONFIGFILE, REG_SZ, (unsigned char *) ConfigFile, strlen(ConfigFile) + 1);
784 
785             printf("Squid Cache version %s for %s\n", version_string, CONFIG_HOST_TYPE);
786             printf("installed successfully as " SQUIDSBUFPH " Windows System Service.\n", SQUIDSBUFPRINT(service_name));
787             printf("To run, start it from the Services Applet of Control Panel.\n");
788             printf("Don't forget to edit squid.conf before starting it.\n\n");
789         } else {
790             fprintf(stderr, "CreateService failed\n");
791             exit(1);
792         }
793 
794         CloseServiceHandle(schSCManager);
795     }
796 }
797 
798 void
WIN32_sendSignal(int WIN32_signal)799 WIN32_sendSignal(int WIN32_signal)
800 {
801     SERVICE_STATUS ssStatus;
802     DWORD fdwAccess, fdwControl;
803     SC_HANDLE schService;
804     SC_HANDLE schSCManager;
805 
806     if (service_name.isEmpty())
807         service_name = SBuf(APP_SHORTNAME);
808 
809     schSCManager = OpenSCManager(NULL,  /* machine (NULL == local)    */
810                                  NULL,          /* database (NULL == default) */
811                                  SC_MANAGER_ALL_ACCESS  /* access required            */
812                                 );
813 
814     if (!schSCManager) {
815         fprintf(stderr, "OpenSCManager failed\n");
816         exit(1);
817     }
818 
819     /* The required service object access depends on the control. */
820     switch (WIN32_signal) {
821 
822     case 0:         /* SIGNULL */
823         fdwAccess = SERVICE_INTERROGATE;
824         fdwControl = _WIN_SQUID_SERVICE_CONTROL_INTERROGATE;
825         break;
826 
827     case SIGUSR1:
828         fdwAccess = SERVICE_USER_DEFINED_CONTROL;
829         fdwControl = _WIN_SQUID_SERVICE_CONTROL_ROTATE;
830         break;
831 
832     case SIGUSR2:
833         fdwAccess = SERVICE_USER_DEFINED_CONTROL;
834         fdwControl = _WIN_SQUID_SERVICE_CONTROL_DEBUG;
835         break;
836 
837     case SIGHUP:
838         fdwAccess = SERVICE_USER_DEFINED_CONTROL;
839         fdwControl = _WIN_SQUID_SERVICE_CONTROL_RECONFIGURE;
840         break;
841 
842     case SIGTERM:
843         fdwAccess = SERVICE_STOP;
844         fdwControl = _WIN_SQUID_SERVICE_CONTROL_STOP;
845         break;
846 
847     case SIGINT:
848 
849     case SIGKILL:
850         fdwAccess = SERVICE_USER_DEFINED_CONTROL;
851         fdwControl = _WIN_SQUID_SERVICE_CONTROL_INTERRUPT;
852         break;
853 
854     default:
855         exit(1);
856     }
857 
858     /* Open a handle to the service. */
859     schService = OpenService(schSCManager,  /* SCManager database */
860                              service_name.c_str(),  /* name of service    */
861                              fdwAccess);        /* specify access     */
862 
863     if (schService == NULL) {
864         fprintf(stderr, "%s: ERROR: Could not open Service " SQUIDSBUFPH "\n", APP_SHORTNAME, SQUIDSBUFPRINT(service_name));
865         exit(1);
866     } else {
867         /* Send a control value to the service. */
868 
869         if (!ControlService(schService, /* handle of service      */
870                             fdwControl, /* control value to send  */
871                             &ssStatus)) {   /* address of status info */
872             fprintf(stderr, "%s: ERROR: Could not Control Service " SQUIDSBUFPH "\n",
873                     APP_SHORTNAME, SQUIDSBUFPRINT(service_name));
874             exit(1);
875         } else {
876             /* Print the service status. */
877             printf("\nStatus of " SQUIDSBUFPH " Service:\n", SQUIDSBUFPRINT(service_name));
878             printf("  Service Type: 0x%lx\n", ssStatus.dwServiceType);
879             printf("  Current State: 0x%lx\n", ssStatus.dwCurrentState);
880             printf("  Controls Accepted: 0x%lx\n", ssStatus.dwControlsAccepted);
881             printf("  Exit Code: %ld\n", ssStatus.dwWin32ExitCode);
882             printf("  Service Specific Exit Code: %ld\n",
883                    ssStatus.dwServiceSpecificExitCode);
884             printf("  Check Point: %ld\n", ssStatus.dwCheckPoint);
885             printf("  Wait Hint: %ld\n", ssStatus.dwWaitHint);
886         }
887 
888         CloseServiceHandle(schService);
889     }
890 
891     CloseServiceHandle(schSCManager);
892 }
893 
WIN32_StartService(int argc,char ** argv)894 int WIN32_StartService(int argc, char **argv)
895 {
896     SERVICE_TABLE_ENTRY DispatchTable[] = {
897         {NULL, SquidWinSvcMain},
898         {NULL, NULL}
899     };
900     char *c;
901     char stderr_path[256];
902 
903     strcpy(stderr_path, argv[0]);
904     strcat(stderr_path,".log");
905     freopen(stderr_path, "w", stderr);
906     setmode(fileno(stderr), O_TEXT);
907     WIN32_run_mode = _WIN_SQUID_RUN_MODE_SERVICE;
908 
909     if (!(c=strchr(argv[1],':'))) {
910         fprintf(stderr, "Bad Service Parameter: %s\n", argv[1]);
911         return 1;
912     }
913 
914     service_name = SBuf(c+1);
915     const char *service = service_name.c_str();
916     DispatchTable[0].lpServiceName = const_cast<char*>(service);
917     strcat(REGKEY, service);
918     keys[4] = const_cast<char*>(service);
919 
920     if (!StartServiceCtrlDispatcher(DispatchTable)) {
921         fprintf(stderr, "StartServiceCtrlDispatcher error = %ld\n", GetLastError());
922         return 1;
923     }
924 
925     return 0;
926 }
927 
928 #endif /* USE_WIN32_SERVICE */
929 
Win32SockInit(void)930 static int Win32SockInit(void)
931 {
932     int iVersionRequested;
933     WSADATA wsaData;
934     int err, opt;
935     int optlen = sizeof(opt);
936 
937     if (s_iInitCount > 0) {
938         ++s_iInitCount;
939         return (0);
940     } else if (s_iInitCount < 0)
941         return (s_iInitCount);
942 
943     /* s_iInitCount == 0. Do the initailization */
944     iVersionRequested = MAKEWORD(2, 0);
945 
946     err = WSAStartup((WORD) iVersionRequested, &wsaData);
947 
948     if (err) {
949         s_iInitCount = -1;
950         return (s_iInitCount);
951     }
952 
953     if (LOBYTE(wsaData.wVersion) != 2 ||
954             HIBYTE(wsaData.wVersion) != 0) {
955         s_iInitCount = -2;
956         WSACleanup();
957         return (s_iInitCount);
958     }
959 
960     if (WIN32_OS_version !=_WIN_OS_WINNT) {
961         if (::getsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&opt, &optlen)) {
962             s_iInitCount = -3;
963             WSACleanup();
964             return (s_iInitCount);
965         } else {
966             opt = opt | SO_SYNCHRONOUS_NONALERT;
967 
968             if (::setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *) &opt, optlen)) {
969                 s_iInitCount = -3;
970                 WSACleanup();
971                 return (s_iInitCount);
972             }
973         }
974     }
975 
976     WIN32_Socks_initialized = 1;
977     ++s_iInitCount;
978     return (s_iInitCount);
979 }
980 
Win32SockCleanup(void)981 static void Win32SockCleanup(void)
982 {
983     if (--s_iInitCount == 0)
984         WSACleanup();
985 
986     return;
987 }
988 
Squid_Win32InvalidParameterHandler(const wchar_t * expression,const wchar_t * function,const wchar_t * file,unsigned int line,uintptr_t pReserved)989 void Squid_Win32InvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved)
990 {
991     return;
992 }
993 
994