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 /* DEBUG: section 21    Misc Functions */
10 
11 #include "squid.h"
12 #include "anyp/PortCfg.h"
13 #include "base/Subscription.h"
14 #include "client_side.h"
15 #include "fatal.h"
16 #include "fde.h"
17 #include "fqdncache.h"
18 #include "fs_io.h"
19 #include "htcp.h"
20 #include "http/Stream.h"
21 #include "ICP.h"
22 #include "ip/Intercept.h"
23 #include "ip/QosConfig.h"
24 #include "ipc/Coordinator.h"
25 #include "ipc/Kids.h"
26 #include "ipcache.h"
27 #include "MemBuf.h"
28 #include "sbuf/Stream.h"
29 #include "SquidConfig.h"
30 #include "SquidMath.h"
31 #include "SquidTime.h"
32 #include "store/Disks.h"
33 #include "tools.h"
34 #include "wordlist.h"
35 
36 #include <cerrno>
37 #if HAVE_SYS_PRCTL_H
38 #include <sys/prctl.h>
39 #endif
40 #if HAVE_WIN32_PSAPI
41 #include <psapi.h>
42 #endif
43 #if HAVE_SYS_STAT_H
44 #include <sys/stat.h>
45 #endif
46 #if HAVE_SYS_WAIT_H
47 #include <sys/wait.h>
48 #endif
49 #if HAVE_GRP_H
50 #include <grp.h>
51 #endif
52 
53 #define DEAD_MSG "\
54 The Squid Cache (version %s) died.\n\
55 \n\
56 You've encountered a fatal error in the Squid Cache version %s.\n\
57 If a core file was created (possibly in the swap directory),\n\
58 please execute 'gdb squid core' or 'dbx squid core', then type 'where',\n\
59 and report the trace back to squid-bugs@lists.squid-cache.org.\n\
60 \n\
61 Thanks!\n"
62 
63 static void mail_warranty(void);
64 static void restoreCapabilities(bool keep);
65 int DebugSignal = -1;
66 SBuf service_name(APP_SHORTNAME);
67 
68 #if _SQUID_LINUX_
69 /* Workaround for crappy glic header files */
70 SQUIDCEXTERN int backtrace(void *, int);
71 SQUIDCEXTERN void backtrace_symbols_fd(void *, int, int);
72 SQUIDCEXTERN int setresuid(uid_t, uid_t, uid_t);
73 #else /* _SQUID_LINUX_ */
74 /* needed on Opensolaris for backtrace_symbols_fd */
75 #if HAVE_EXECINFO_H
76 #include <execinfo.h>
77 #endif /* HAVE_EXECINFO_H */
78 
79 #endif /* _SQUID_LINUX */
80 
81 void
releaseServerSockets(void)82 releaseServerSockets(void)
83 {
84     // Release the main ports as early as possible
85 
86     // clear http_port, https_port, and ftp_port lists
87     clientConnectionsClose();
88 
89     // clear icp_port's
90     icpClosePorts();
91 
92     // XXX: Why not the HTCP, SNMP, DNS ports as well?
93     // XXX: why does this differ from main closeServerConnections() anyway ?
94 }
95 
96 static char *
dead_msg(void)97 dead_msg(void)
98 {
99     LOCAL_ARRAY(char, msg, 1024);
100     snprintf(msg, 1024, DEAD_MSG, version_string, version_string);
101     return msg;
102 }
103 
104 static void
mail_warranty(void)105 mail_warranty(void)
106 {
107     FILE *fp = NULL;
108     static char command[256];
109 
110     /*
111      * NP: umask() takes the mask of bits we DONT want set.
112      *
113      * We want the current user to have read/write access
114      * and since this file will be passed to mailsystem,
115      * the group and other must have read access.
116      */
117     const mode_t prev_umask=umask(S_IXUSR|S_IXGRP|S_IWGRP|S_IWOTH|S_IXOTH);
118 
119 #if HAVE_MKSTEMP
120     char filename[] = "/tmp/squid-XXXXXX";
121     int tfd = mkstemp(filename);
122     if (tfd < 0 || (fp = fdopen(tfd, "w")) == NULL) {
123         umask(prev_umask);
124         return;
125     }
126 #else
127     char *filename;
128     // XXX tempnam is obsolete since POSIX.2008-1
129     // tmpfile is not an option, we want the created files to stick around
130     if ((filename = tempnam(NULL, APP_SHORTNAME)) == NULL ||
131             (fp = fopen(filename, "w")) == NULL) {
132         umask(prev_umask);
133         return;
134     }
135 #endif
136     umask(prev_umask);
137 
138     if (Config.EmailFrom)
139         fprintf(fp, "From: %s\n", Config.EmailFrom);
140     else
141         fprintf(fp, "From: %s@%s\n", APP_SHORTNAME, uniqueHostname());
142 
143     fprintf(fp, "To: %s\n", Config.adminEmail);
144     fprintf(fp, "Subject: %s\n", dead_msg());
145     fclose(fp);
146 
147     snprintf(command, 256, "%s %s < %s", Config.EmailProgram, Config.adminEmail, filename);
148     if (system(command)) {}     /* XXX should avoid system(3) */
149     unlink(filename);
150 #if !HAVE_MKSTEMP
151     xfree(filename); // tempnam() requires us to free its allocation
152 #endif
153 }
154 
155 void
dumpMallocStats(void)156 dumpMallocStats(void)
157 {
158 #if HAVE_MSTATS && HAVE_GNUMALLOC_H
159 
160     struct mstats ms = mstats();
161     fprintf(debug_log, "\ttotal space in arena:  %6d KB\n",
162             (int) (ms.bytes_total >> 10));
163     fprintf(debug_log, "\tTotal free:            %6d KB %d%%\n",
164             (int) (ms.bytes_free >> 10),
165             Math::intPercent(ms.bytes_free, ms.bytes_total));
166 #endif
167 }
168 
169 void
squid_getrusage(struct rusage * r)170 squid_getrusage(struct rusage *r)
171 {
172     memset(r, '\0', sizeof(struct rusage));
173 #if HAVE_GETRUSAGE && defined(RUSAGE_SELF) && !_SQUID_WINDOWS_
174 #if _SQUID_SOLARIS_
175     /* Solaris 2.5 has getrusage() permission bug -- Arjan de Vet */
176     enter_suid();
177 #endif
178 
179     getrusage(RUSAGE_SELF, r);
180 
181 #if _SQUID_SOLARIS_
182     leave_suid();
183 #endif
184 
185 #elif _SQUID_WINDOWS_ && HAVE_WIN32_PSAPI
186     // Windows has an alternative method if there is no POSIX getrusage defined.
187     if (WIN32_OS_version >= _WIN_OS_WINNT) {
188         /* On Windows NT and later call PSAPI.DLL for process Memory */
189         /* informations -- Guido Serassio                       */
190         HANDLE hProcess;
191         PROCESS_MEMORY_COUNTERS pmc;
192         hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
193                                PROCESS_VM_READ,
194                                FALSE, GetCurrentProcessId());
195         {
196             /* Microsoft CRT doesn't have getrusage function,  */
197             /* so we get process CPU time information from PSAPI.DLL. */
198             FILETIME ftCreate, ftExit, ftKernel, ftUser;
199             if (GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
200                 int64_t *ptUser = (int64_t *)&ftUser;
201                 int64_t tUser64 = *ptUser / 10;
202                 int64_t *ptKernel = (int64_t *)&ftKernel;
203                 int64_t tKernel64 = *ptKernel / 10;
204                 r->ru_utime.tv_sec =(long)(tUser64 / 1000000);
205                 r->ru_stime.tv_sec =(long)(tKernel64 / 1000000);
206                 r->ru_utime.tv_usec =(long)(tUser64 % 1000000);
207                 r->ru_stime.tv_usec =(long)(tKernel64 % 1000000);
208             } else {
209                 CloseHandle( hProcess );
210                 return;
211             }
212         }
213         if (GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc))) {
214             r->ru_maxrss=(DWORD)(pmc.WorkingSetSize / getpagesize());
215             r->ru_majflt=pmc.PageFaultCount;
216         } else {
217             CloseHandle( hProcess );
218             return;
219         }
220 
221         CloseHandle( hProcess );
222     }
223 #endif
224 }
225 
226 double
227 
rusage_cputime(struct rusage * r)228 rusage_cputime(struct rusage *r)
229 {
230     return (double) r->ru_stime.tv_sec +
231            (double) r->ru_utime.tv_sec +
232            (double) r->ru_stime.tv_usec / 1000000.0 +
233            (double) r->ru_utime.tv_usec / 1000000.0;
234 }
235 
236 /* Hack for some HP-UX preprocessors */
237 #ifndef HAVE_GETPAGESIZE
238 #define HAVE_GETPAGESIZE 0
239 #endif
240 
241 int
242 
rusage_maxrss(struct rusage * r)243 rusage_maxrss(struct rusage *r)
244 {
245 #if _SQUID_SGI_ && _ABIAPI
246     return r->ru_pad[0];
247 #elif _SQUID_SGI_|| _SQUID_OSF_ || _SQUID_AIX_ || defined(BSD4_4)
248 
249     return r->ru_maxrss;
250 #elif defined(HAVE_GETPAGESIZE) && HAVE_GETPAGESIZE != 0
251 
252     return (r->ru_maxrss * getpagesize()) >> 10;
253 #elif defined(PAGESIZE)
254 
255     return (r->ru_maxrss * PAGESIZE) >> 10;
256 #else
257 
258     return r->ru_maxrss;
259 #endif
260 }
261 
262 int
263 
rusage_pagefaults(struct rusage * r)264 rusage_pagefaults(struct rusage *r)
265 {
266 #if _SQUID_SGI_ && _ABIAPI
267     return r->ru_pad[5];
268 #else
269 
270     return r->ru_majflt;
271 #endif
272 }
273 
274 void
PrintRusage(void)275 PrintRusage(void)
276 {
277 
278     struct rusage rusage;
279     squid_getrusage(&rusage);
280     fprintf(debug_log, "CPU Usage: %.3f seconds = %.3f user + %.3f sys\n",
281             rusage_cputime(&rusage),
282             rusage.ru_utime.tv_sec + ((double) rusage.ru_utime.tv_usec / 1000000.0),
283             rusage.ru_stime.tv_sec + ((double) rusage.ru_stime.tv_usec / 1000000.0));
284     fprintf(debug_log, "Maximum Resident Size: %d KB\n",
285             rusage_maxrss(&rusage));
286     fprintf(debug_log, "Page faults with physical i/o: %d\n",
287             rusage_pagefaults(&rusage));
288 }
289 
290 void
death(int sig)291 death(int sig)
292 {
293     if (sig == SIGSEGV)
294         debugs(1, DBG_CRITICAL, ForceAlert << "FATAL: Received Segment Violation...dying.");
295     else if (sig == SIGBUS)
296         debugs(1, DBG_CRITICAL, ForceAlert << "FATAL: Received Bus Error...dying.");
297     else
298         debugs(1, DBG_CRITICAL, ForceAlert << "FATAL: Received signal " << sig << "...dying.");
299 
300 #if PRINT_STACK_TRACE
301 #if _SQUID_HPUX_
302     {
303         extern void U_STACK_TRACE(void);    /* link with -lcl */
304         fflush(debug_log);
305         dup2(fileno(debug_log), 2);
306         U_STACK_TRACE();
307     }
308 
309 #endif /* _SQUID_HPUX_ */
310 #if _SQUID_SOLARIS_ && HAVE_LIBOPCOM_STACK
311     {   /* get ftp://opcom.sun.ca/pub/tars/opcom_stack.tar.gz and */
312         extern void opcom_stack_trace(void);    /* link with -lopcom_stack */
313         fflush(debug_log);
314         dup2(fileno(debug_log), fileno(stdout));
315         opcom_stack_trace();
316         fflush(stdout);
317     }
318 
319 #endif /* _SQUID_SOLARIS_and HAVE_LIBOPCOM_STACK */
320 #if HAVE_BACKTRACE_SYMBOLS_FD
321     {
322         static void *callarray[8192];
323         int n;
324         n = backtrace(callarray, 8192);
325         backtrace_symbols_fd(callarray, n, fileno(debug_log));
326     }
327 
328 #endif
329 #endif /* PRINT_STACK_TRACE */
330 
331 #if SA_RESETHAND == 0 && !_SQUID_WINDOWS_
332     signal(SIGSEGV, SIG_DFL);
333 
334     signal(SIGBUS, SIG_DFL);
335 
336     signal(sig, SIG_DFL);
337 
338 #endif
339 
340     releaseServerSockets();
341 
342     storeDirWriteCleanLogs(0);
343 
344     if (!shutting_down) {
345         PrintRusage();
346 
347         dumpMallocStats();
348     }
349 
350     if (squid_curtime - SQUID_RELEASE_TIME < 864000) {
351         /* skip if more than 10 days old */
352 
353         if (Config.adminEmail)
354             mail_warranty();
355 
356         puts(dead_msg());
357     }
358 
359     abort();
360 }
361 
362 void
BroadcastSignalIfAny(int & sig)363 BroadcastSignalIfAny(int& sig)
364 {
365     if (sig > 0) {
366         if (IamMasterProcess()) {
367             for (int i = TheKids.count() - 1; i >= 0; --i) {
368                 const auto &kid = TheKids.get(i);
369                 if (kid.running())
370                     kill(kid.getPid(), sig);
371             }
372         }
373         sig = -1;
374     }
375 }
376 
377 void
sigusr2_handle(int sig)378 sigusr2_handle(int sig)
379 {
380     static int state = 0;
381     /* no debugs() here; bad things happen if the signal is delivered during _db_print() */
382 
383     DebugSignal = sig;
384 
385     if (state == 0) {
386         Debug::parseOptions("ALL,7");
387         state = 1;
388     } else {
389         Debug::parseOptions(Debug::debugOptions);
390         state = 0;
391     }
392 
393 #if !HAVE_SIGACTION
394     /* reinstall */
395     if (signal(sig, sigusr2_handle) == SIG_ERR) {
396         int xerrno = errno;
397         debugs(50, DBG_CRITICAL, "signal: sig=" << sig << " func=sigusr2_handle: " << xstrerr(xerrno));
398     }
399 #endif
400 }
401 
402 void
debug_trap(const char * message)403 debug_trap(const char *message)
404 {
405     if (!opt_catch_signals)
406         fatal_dump(message);
407 
408     debugs(50, DBG_CRITICAL, "WARNING: " << message);
409 }
410 
411 const char *
getMyHostname(void)412 getMyHostname(void)
413 {
414     LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1);
415     static int present = 0;
416     struct addrinfo *AI = NULL;
417     Ip::Address sa;
418 
419     if (Config.visibleHostname != NULL)
420         return Config.visibleHostname;
421 
422     if (present)
423         return host;
424 
425     host[0] = '\0';
426 
427     if (HttpPortList != NULL && sa.isAnyAddr())
428         sa = HttpPortList->s;
429 
430     /*
431      * If the first http_port address has a specific address, try a
432      * reverse DNS lookup on it.
433      */
434     if ( !sa.isAnyAddr() ) {
435 
436         sa.getAddrInfo(AI);
437         /* we are looking for a name. */
438         if (getnameinfo(AI->ai_addr, AI->ai_addrlen, host, SQUIDHOSTNAMELEN, NULL, 0, NI_NAMEREQD ) == 0) {
439             /* DNS lookup successful */
440             /* use the official name from DNS lookup */
441             debugs(50, 4, "getMyHostname: resolved " << sa << " to '" << host << "'");
442 
443             present = 1;
444 
445             Ip::Address::FreeAddr(AI);
446 
447             if (strchr(host, '.'))
448                 return host;
449         }
450 
451         Ip::Address::FreeAddr(AI);
452         debugs(50, 2, "WARNING: failed to resolve " << sa << " to a fully qualified hostname");
453     }
454 
455     // still no host. fallback to gethostname()
456     if (gethostname(host, SQUIDHOSTNAMELEN) < 0) {
457         int xerrno = errno;
458         debugs(50, DBG_IMPORTANT, "WARNING: gethostname failed: " << xstrerr(xerrno));
459     } else {
460         /* Verify that the hostname given resolves properly */
461         struct addrinfo hints;
462         memset(&hints, 0, sizeof(addrinfo));
463         hints.ai_flags = AI_CANONNAME;
464 
465         if (getaddrinfo(host, NULL, NULL, &AI) == 0) {
466             /* DNS lookup successful */
467             /* use the official name from DNS lookup */
468             debugs(50, 6, "getMyHostname: '" << host << "' has DNS resolution.");
469             present = 1;
470 
471             /* AYJ: do we want to flag AI_ALL and cache the result anywhere. ie as our local host IPs? */
472             if (AI)
473                 freeaddrinfo(AI);
474 
475             return host;
476         }
477         int xerrno = errno;
478 
479         if (AI)
480             freeaddrinfo(AI);
481         debugs(50, DBG_IMPORTANT, "WARNING: '" << host << "' rDNS test failed: " << xstrerr(xerrno));
482     }
483 
484     /* throw a configuration error when the Host/IP given has bad DNS/rDNS. */
485     debugs(50, DBG_CRITICAL, "WARNING: Could not determine this machines public hostname. " <<
486            "Please configure one or set 'visible_hostname'.");
487 
488     return ("localhost");
489 }
490 
491 const char *
uniqueHostname(void)492 uniqueHostname(void)
493 {
494     debugs(21, 3, HERE << " Config: '" << Config.uniqueHostname << "'");
495     return Config.uniqueHostname ? Config.uniqueHostname : getMyHostname();
496 }
497 
498 /** leave a priviliged section. (Give up any privilegies)
499  * Routines that need privilegies can rap themselves in enter_suid()
500  * and leave_suid()
501  * To give upp all posibilites to gain privilegies use no_suid()
502  */
503 void
leave_suid(void)504 leave_suid(void)
505 {
506     debugs(21, 3, "leave_suid: PID " << getpid() << " called");
507 
508     if (Config.effectiveGroup) {
509 #if HAVE_SETGROUPS
510         setgroups(1, &Config2.effectiveGroupID);
511 #endif
512 
513         if (setgid(Config2.effectiveGroupID) < 0) {
514             int xerrno = errno;
515             debugs(50, DBG_CRITICAL, "ALERT: setgid: " << xstrerr(xerrno));
516         }
517     }
518 
519     if (geteuid() != 0)
520         return;
521 
522     /* Started as a root, check suid option */
523     if (Config.effectiveUser == NULL)
524         return;
525 
526     debugs(21, 3, "leave_suid: PID " << getpid() << " giving up root, becoming '" << Config.effectiveUser << "'");
527 
528     if (!Config.effectiveGroup) {
529 
530         if (setgid(Config2.effectiveGroupID) < 0) {
531             int xerrno = errno;
532             debugs(50, DBG_CRITICAL, "ALERT: setgid: " << xstrerr(xerrno));
533         }
534 
535         if (initgroups(Config.effectiveUser, Config2.effectiveGroupID) < 0) {
536             debugs(50, DBG_CRITICAL, "ALERT: initgroups: unable to set groups for User " <<
537                    Config.effectiveUser << " and Group " <<
538                    (unsigned) Config2.effectiveGroupID << "");
539         }
540     }
541 
542 #if HAVE_SETRESUID
543     if (setresuid(Config2.effectiveUserID, Config2.effectiveUserID, 0) < 0) {
544         const auto xerrno = errno;
545         fatalf("FATAL: setresuid: %s", xstrerr(xerrno));
546     }
547 
548 #elif HAVE_SETEUID
549     if (seteuid(Config2.effectiveUserID) < 0) {
550         const auto xerrno = errno;
551         fatalf("FATAL: seteuid: %s", xstrerr(xerrno));
552     }
553 
554 #else
555     if (setuid(Config2.effectiveUserID) < 0) {
556         const auto xerrno = errno;
557         fatalf("FATAL: setuid: %s", xstrerr(xerrno));
558     }
559 
560 #endif
561 
562     restoreCapabilities(true);
563 
564 #if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
565     /* Set Linux DUMPABLE flag */
566     if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0) {
567         int xerrno = errno;
568         debugs(50, 2, "ALERT: prctl: " << xstrerr(xerrno));
569     }
570 #endif
571 }
572 
573 /* Enter a privilegied section */
574 void
enter_suid(void)575 enter_suid(void)
576 {
577     debugs(21, 3, "enter_suid: PID " << getpid() << " taking root privileges");
578 #if HAVE_SETRESUID
579     if (setresuid((uid_t)-1, 0, (uid_t)-1) < 0) {
580         const auto xerrno = errno;
581         debugs (21, 3, "enter_suid: setresuid failed: " << xstrerr(xerrno));
582     }
583 #else
584 
585     setuid(0);
586 #endif
587 #if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
588     /* Set Linux DUMPABLE flag */
589 
590     if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0) {
591         int xerrno = errno;
592         debugs(50, 2, "ALERT: prctl: " << xstrerr(xerrno));
593     }
594 #endif
595 }
596 
597 /* Give up the posibility to gain privilegies.
598  * this should be used before starting a sub process
599  */
600 void
no_suid(void)601 no_suid(void)
602 {
603     uid_t uid;
604     leave_suid();
605     uid = geteuid();
606     debugs(21, 3, "no_suid: PID " << getpid() << " giving up root privileges forever");
607 
608     if (setuid(0) < 0 && TheProcessKind != pkHelper) {
609         int xerrno = errno;
610         debugs(50, DBG_IMPORTANT, "WARNING: no_suid: setuid(0): " << xstrerr(xerrno));
611     }
612 
613     if (setuid(uid) < 0) {
614         int xerrno = errno;
615         debugs(50, DBG_IMPORTANT, "ERROR: no_suid: setuid(" << uid << "): " << xstrerr(xerrno));
616     }
617 
618     restoreCapabilities(false);
619 
620 #if HAVE_PRCTL && defined(PR_SET_DUMPABLE)
621     /* Set Linux DUMPABLE flag */
622     if (Config.coredump_dir && prctl(PR_SET_DUMPABLE, 1) != 0) {
623         int xerrno = errno;
624         debugs(50, 2, "ALERT: prctl: " << xstrerr(xerrno));
625     }
626 #endif
627 }
628 
629 bool
IamMasterProcess()630 IamMasterProcess()
631 {
632     return KidIdentifier == 0;
633 }
634 
635 bool
IamWorkerProcess()636 IamWorkerProcess()
637 {
638     // when there is only one process, it has to be the worker
639     if (opt_no_daemon || Config.workers == 0)
640         return true;
641 
642     return TheProcessKind == pkWorker;
643 }
644 
645 bool
IamDiskProcess()646 IamDiskProcess()
647 {
648     return TheProcessKind == pkDisker;
649 }
650 
651 bool
InDaemonMode()652 InDaemonMode()
653 {
654     return !opt_no_daemon && Config.workers > 0;
655 }
656 
657 bool
UsingSmp()658 UsingSmp()
659 {
660     return InDaemonMode() && NumberOfKids() > 1;
661 }
662 
663 bool
IamCoordinatorProcess()664 IamCoordinatorProcess()
665 {
666     return TheProcessKind == pkCoordinator;
667 }
668 
669 bool
IamPrimaryProcess()670 IamPrimaryProcess()
671 {
672     // when there is only one process, it has to be primary
673     if (opt_no_daemon || Config.workers == 0)
674         return true;
675 
676     // when there is a master and worker process, the master delegates
677     // primary functions to its only kid
678     if (NumberOfKids() == 1)
679         return IamWorkerProcess();
680 
681     // in SMP mode, multiple kids delegate primary functions to the coordinator
682     return IamCoordinatorProcess();
683 }
684 
685 int
NumberOfKids()686 NumberOfKids()
687 {
688     // no kids in no-daemon mode
689     if (!InDaemonMode())
690         return 0;
691 
692     // XXX: detect and abort when called before workers/cache_dirs are parsed
693 
694     const int rockDirs = Config.cacheSwap.n_strands;
695 
696     const bool needCoord = Config.workers > 1 || rockDirs > 0;
697     return (needCoord ? 1 : 0) + Config.workers + rockDirs;
698 }
699 
700 SBuf
ProcessRoles()701 ProcessRoles()
702 {
703     SBuf roles;
704     if (IamMasterProcess())
705         roles.append(" master");
706     if (IamCoordinatorProcess())
707         roles.append(" coordinator");
708     if (IamWorkerProcess())
709         roles.append(" worker");
710     if (IamDiskProcess())
711         roles.append(" disker");
712     return roles;
713 }
714 
715 /* A little piece of glue for odd systems */
716 #ifndef RLIMIT_NOFILE
717 #ifdef RLIMIT_OFILE
718 #define RLIMIT_NOFILE RLIMIT_OFILE
719 #endif
720 #endif
721 
722 /** Figure out the number of supported filedescriptors */
723 void
setMaxFD(void)724 setMaxFD(void)
725 {
726 #if HAVE_SETRLIMIT && defined(RLIMIT_NOFILE)
727 
728     /* On Linux with 64-bit file support the sys/resource.h header
729      * uses #define to change the function definition to require rlimit64
730      */
731 #if defined(getrlimit)
732     struct rlimit64 rl; // Assume its a 64-bit redefine anyways.
733 #else
734     struct rlimit rl;
735 #endif
736 
737     if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
738         int xerrno = errno;
739         debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
740     } else if (Config.max_filedescriptors > 0) {
741 #if USE_SELECT || USE_SELECT_WIN32
742         /* select() breaks if this gets set too big */
743         if (Config.max_filedescriptors > FD_SETSIZE) {
744             rl.rlim_cur = FD_SETSIZE;
745             debugs(50, DBG_CRITICAL, "WARNING: 'max_filedescriptors " << Config.max_filedescriptors << "' does not work with select()");
746         } else
747 #endif
748             rl.rlim_cur = Config.max_filedescriptors;
749         if (rl.rlim_cur > rl.rlim_max)
750             rl.rlim_max = rl.rlim_cur;
751         if (setrlimit(RLIMIT_NOFILE, &rl)) {
752             int xerrno = errno;
753             debugs(50, DBG_CRITICAL, "ERROR: setrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
754             getrlimit(RLIMIT_NOFILE, &rl);
755             rl.rlim_cur = rl.rlim_max;
756             if (setrlimit(RLIMIT_NOFILE, &rl)) {
757                 xerrno = errno;
758                 debugs(50, DBG_CRITICAL, "ERROR: setrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
759             }
760         }
761     }
762     if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
763         int xerrno = errno;
764         debugs(50, DBG_CRITICAL, "ERROR: getrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
765     } else {
766         Squid_MaxFD = rl.rlim_cur;
767     }
768 
769 #endif /* HAVE_SETRLIMIT */
770 }
771 
772 void
setSystemLimits(void)773 setSystemLimits(void)
774 {
775 #if HAVE_SETRLIMIT && defined(RLIMIT_NOFILE) && !_SQUID_CYGWIN_
776     /* limit system filedescriptors to our own limit */
777 
778     /* On Linux with 64-bit file support the sys/resource.h header
779      * uses #define to change the function definition to require rlimit64
780      */
781 #if defined(getrlimit)
782     struct rlimit64 rl; // Assume its a 64-bit redefine anyways.
783 #else
784     struct rlimit rl;
785 #endif
786 
787     if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
788         int xerrno = errno;
789         debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_NOFILE: " << xstrerr(xerrno));
790     } else {
791         rl.rlim_cur = Squid_MaxFD;
792         if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
793             int xerrno = errno;
794             snprintf(tmp_error_buf, ERROR_BUF_SZ, "setrlimit: RLIMIT_NOFILE: %s", xstrerr(xerrno));
795             fatal_dump(tmp_error_buf);
796         }
797     }
798 #endif /* HAVE_SETRLIMIT */
799 
800 #if HAVE_SETRLIMIT && defined(RLIMIT_DATA) && !_SQUID_CYGWIN_
801     if (getrlimit(RLIMIT_DATA, &rl) < 0) {
802         int xerrno = errno;
803         debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_DATA: " << xstrerr(xerrno));
804     } else if (rl.rlim_max > rl.rlim_cur) {
805         rl.rlim_cur = rl.rlim_max;  /* set it to the max */
806 
807         if (setrlimit(RLIMIT_DATA, &rl) < 0) {
808             int xerrno = errno;
809             snprintf(tmp_error_buf, ERROR_BUF_SZ, "setrlimit: RLIMIT_DATA: %s", xstrerr(xerrno));
810             fatal_dump(tmp_error_buf);
811         }
812     }
813 #endif /* RLIMIT_DATA */
814     if (Config.max_filedescriptors > Squid_MaxFD) {
815         debugs(50, DBG_IMPORTANT, "NOTICE: Could not increase the number of filedescriptors");
816     }
817 
818 #if HAVE_SETRLIMIT && defined(RLIMIT_VMEM) && !_SQUID_CYGWIN_
819     if (getrlimit(RLIMIT_VMEM, &rl) < 0) {
820         int xerrno = errno;
821         debugs(50, DBG_CRITICAL, "getrlimit: RLIMIT_VMEM: " << xstrerr(xerrno));
822     } else if (rl.rlim_max > rl.rlim_cur) {
823         rl.rlim_cur = rl.rlim_max;  /* set it to the max */
824 
825         if (setrlimit(RLIMIT_VMEM, &rl) < 0) {
826             int xerrno = errno;
827             snprintf(tmp_error_buf, ERROR_BUF_SZ, "setrlimit: RLIMIT_VMEM: %s", xstrerr(xerrno));
828             fatal_dump(tmp_error_buf);
829         }
830     }
831 #endif /* RLIMIT_VMEM */
832 }
833 
834 void
squid_signal(int sig,SIGHDLR * func,int flags)835 squid_signal(int sig, SIGHDLR * func, int flags)
836 {
837 #if HAVE_SIGACTION
838 
839     struct sigaction sa;
840     sa.sa_handler = func;
841     sa.sa_flags = flags;
842     sigemptyset(&sa.sa_mask);
843 
844     if (sigaction(sig, &sa, NULL) < 0) {
845         int xerrno = errno;
846         debugs(50, DBG_CRITICAL, "sigaction: sig=" << sig << " func=" << func << ": " << xstrerr(xerrno));
847     }
848 #else
849 #if _SQUID_WINDOWS_
850     /*
851     On Windows, only SIGINT, SIGILL, SIGFPE, SIGTERM, SIGBREAK, SIGABRT and SIGSEGV signals
852     are supported, so we must care of don't call signal() for other value.
853     The SIGILL, SIGSEGV, and SIGTERM signals are not generated under Windows. They are defined
854     for ANSI compatibility, so both SIGSEGV and SIGBUS are emulated with an Exception Handler.
855     */
856     switch (sig) {
857 
858     case SIGINT:
859 
860     case SIGILL:
861 
862     case SIGFPE:
863 
864     case SIGTERM:
865 
866     case SIGBREAK:
867 
868     case SIGABRT:
869         break;
870 
871     case SIGSEGV:
872         WIN32_ExceptionHandlerInit();
873         break;
874 
875     case SIGBUS:
876         WIN32_ExceptionHandlerInit();
877         return;
878         break;  /* Nor reached */
879 
880     default:
881         return;
882         break;  /* Nor reached */
883     }
884 
885 #endif
886 
887     signal(sig, func);
888 
889 #endif
890 }
891 
892 void
logsFlush(void)893 logsFlush(void)
894 {
895     if (debug_log)
896         fflush(debug_log);
897 }
898 
899 void
debugObj(int section,int level,const char * label,void * obj,ObjPackMethod pm)900 debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm)
901 {
902     assert(label && obj && pm);
903     MemBuf mb;
904     mb.init();
905     (*pm) (obj, &mb);
906     debugs(section, level, "" << label << "" << mb.buf << "");
907     mb.clean();
908 }
909 
910 void
parseEtcHosts(void)911 parseEtcHosts(void)
912 {
913     char buf[1024];
914     char buf2[512];
915     char *nt = buf;
916     char *lt = buf;
917 
918     if (!Config.etcHostsPath)
919         return;
920 
921     if (0 == strcmp(Config.etcHostsPath, "none"))
922         return;
923 
924     FILE *fp = fopen(Config.etcHostsPath, "r");
925 
926     if (!fp) {
927         int xerrno = errno;
928         debugs(1, DBG_IMPORTANT, "parseEtcHosts: '" << Config.etcHostsPath << "' : " << xstrerr(xerrno));
929         return;
930     }
931 
932 #if _SQUID_WINDOWS_
933     setmode(fileno(fp), O_TEXT);
934 #endif
935 
936     while (fgets(buf, 1024, fp)) {  /* for each line */
937 
938         if (buf[0] == '#')  /* MS-windows likes to add comments */
939             continue;
940 
941         strtok(buf, "#");   /* chop everything following a comment marker */
942 
943         lt = buf;
944 
945         char *addr = buf;
946 
947         debugs(1, 5, "etc_hosts: line is '" << buf << "'");
948 
949         nt = strpbrk(lt, w_space);
950 
951         if (nt == NULL)     /* empty line */
952             continue;
953 
954         *nt = '\0';     /* null-terminate the address */
955 
956         debugs(1, 5, "etc_hosts: address is '" << addr << "'");
957 
958         lt = nt + 1;
959 
960         SBufList hosts;
961 
962         while ((nt = strpbrk(lt, w_space))) {
963             char *host = NULL;
964 
965             if (nt == lt) { /* multiple spaces */
966                 debugs(1, 5, "etc_hosts: multiple spaces, skipping");
967                 lt = nt + 1;
968                 continue;
969             }
970 
971             *nt = '\0';
972             debugs(1, 5, "etc_hosts: got hostname '" << lt << "'");
973 
974             /* For IPV6 addresses also check for a colon */
975             if (Config.appendDomain && !strchr(lt, '.') && !strchr(lt, ':')) {
976                 /* I know it's ugly, but it's only at reconfig */
977                 strncpy(buf2, lt, sizeof(buf2)-1);
978                 strncat(buf2, Config.appendDomain, sizeof(buf2) - strlen(lt) - 1);
979                 buf2[sizeof(buf2)-1] = '\0';
980                 host = buf2;
981             } else {
982                 host = lt;
983             }
984 
985             if (ipcacheAddEntryFromHosts(host, addr) != 0) {
986                 /* invalid address, continuing is useless */
987                 hosts.clear();
988                 break;
989             }
990             hosts.emplace_back(SBuf(host));
991 
992             lt = nt + 1;
993         }
994 
995         if (!hosts.empty())
996             fqdncacheAddEntryFromHosts(addr, hosts);
997     }
998 
999     fclose (fp);
1000 }
1001 
1002 int
getMyPort(void)1003 getMyPort(void)
1004 {
1005     AnyP::PortCfgPointer p;
1006     if ((p = HttpPortList) != NULL) {
1007         // skip any special interception ports
1008         while (p != NULL && p->flags.isIntercepted())
1009             p = p->next;
1010         if (p != NULL)
1011             return p->s.port();
1012     }
1013 
1014     if ((p = FtpPortList) != NULL) {
1015         // skip any special interception ports
1016         while (p != NULL && p->flags.isIntercepted())
1017             p = p->next;
1018         if (p != NULL)
1019             return p->s.port();
1020     }
1021 
1022     debugs(21, DBG_CRITICAL, "ERROR: No forward-proxy ports configured.");
1023     return 0; // Invalid port. This will result in invalid URLs on bad configurations.
1024 }
1025 
1026 /*
1027  * Set the umask to at least the given mask. This is in addition
1028  * to the umask set at startup
1029  */
1030 void
setUmask(mode_t mask)1031 setUmask(mode_t mask)
1032 {
1033     // No way to get the current umask value without setting it.
1034     static const mode_t orig_umask = umask(mask); // once, to get
1035     umask(mask | orig_umask); // always, to set
1036 }
1037 
1038 /*
1039  * Inverse of strwordtok. Quotes a word if needed
1040  */
1041 void
strwordquote(MemBuf * mb,const char * str)1042 strwordquote(MemBuf * mb, const char *str)
1043 {
1044     int quoted = 0;
1045 
1046     if (strchr(str, ' ')) {
1047         quoted = 1;
1048         mb->append("\"", 1);
1049     }
1050 
1051     while (*str) {
1052         int l = strcspn(str, "\"\\\n\r");
1053         mb->append(str, l);
1054         str += l;
1055 
1056         switch (*str) {
1057 
1058         case '\n':
1059             mb->append("\\n", 2);
1060             ++str;
1061             break;
1062 
1063         case '\r':
1064             mb->append("\\r", 2);
1065             ++str;
1066             break;
1067 
1068         case '\0':
1069             break;
1070 
1071         default:
1072             mb->append("\\", 1);
1073             mb->append(str, 1);
1074             ++str;
1075             break;
1076         }
1077     }
1078 
1079     if (quoted)
1080         mb->append("\"", 1);
1081 }
1082 
1083 void
keepCapabilities(void)1084 keepCapabilities(void)
1085 {
1086 #if USE_LIBCAP && HAVE_PRCTL && defined(PR_SET_KEEPCAPS)
1087 
1088     if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
1089         Ip::Interceptor.StopTransparency("capability setting has failed.");
1090     }
1091 #endif
1092 }
1093 
1094 static void
restoreCapabilities(bool keep)1095 restoreCapabilities(bool keep)
1096 {
1097     /* NP: keep these two if-endif separate. Non-Linux work perfectly well without Linux syscap support. */
1098 #if USE_LIBCAP
1099     cap_t caps;
1100     if (keep)
1101         caps = cap_get_proc();
1102     else
1103         caps = cap_init();
1104     if (!caps) {
1105         Ip::Interceptor.StopTransparency("Can't get current capabilities");
1106     } else {
1107         int ncaps = 0;
1108         int rc = 0;
1109         cap_value_t cap_list[10];
1110         cap_list[ncaps] = CAP_NET_BIND_SERVICE;
1111         ++ncaps;
1112         if (Ip::Interceptor.TransparentActive() ||
1113 #if USE_LIBNETFILTERCONNTRACK
1114                 // netfilter_conntrack requires CAP_NET_ADMIN to get client's CONNMARK
1115                 Ip::Interceptor.InterceptActive() ||
1116 #endif
1117                 Ip::Qos::TheConfig.isHitNfmarkActive() ||
1118                 Ip::Qos::TheConfig.isAclNfmarkActive() ||
1119                 Ip::Qos::TheConfig.isAclTosActive()) {
1120             cap_list[ncaps] = CAP_NET_ADMIN;
1121             ++ncaps;
1122         }
1123 
1124         cap_clear_flag(caps, CAP_EFFECTIVE);
1125         rc |= cap_set_flag(caps, CAP_EFFECTIVE, ncaps, cap_list, CAP_SET);
1126         rc |= cap_set_flag(caps, CAP_PERMITTED, ncaps, cap_list, CAP_SET);
1127 
1128         if (rc || cap_set_proc(caps) != 0) {
1129             Ip::Interceptor.StopTransparency("Error enabling needed capabilities.");
1130         }
1131         cap_free(caps);
1132     }
1133 #elif _SQUID_LINUX_
1134     Ip::Interceptor.StopTransparency("Missing needed capability support.");
1135 #endif /* HAVE_SYS_CAPABILITY_H */
1136 }
1137 
1138 pid_t
WaitForOnePid(pid_t pid,PidStatus & status,int flags)1139 WaitForOnePid(pid_t pid, PidStatus &status, int flags)
1140 {
1141 #if _SQUID_NEXT_
1142     if (pid < 0)
1143         return wait3(&status, flags, NULL);
1144     return wait4(pid, &status, flags, NULL);
1145 #elif _SQUID_WINDOWS_
1146     return 0; // function not used on Windows
1147 #else
1148     return waitpid(pid, &status, flags);
1149 #endif
1150 }
1151 
1152 #if _SQUID_WINDOWS_
1153 SBuf
WindowsErrorMessage(DWORD errorId)1154 WindowsErrorMessage(DWORD errorId)
1155 {
1156     char *rawMessage = nullptr;
1157     const auto length = FormatMessage(
1158                             FORMAT_MESSAGE_ALLOCATE_BUFFER |
1159                             FORMAT_MESSAGE_FROM_SYSTEM |
1160                             FORMAT_MESSAGE_IGNORE_INSERTS,
1161                             nullptr,
1162                             errorId,
1163                             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
1164                             static_cast<LPTSTR>(&rawMessage),
1165                             0,
1166                             nullptr);
1167     if (!length) {
1168         Must(!rawMessage); // nothing to LocalFree()
1169         return ToSBuf("windows error ", errorId);
1170     }
1171     const auto result = SBuf(rawMessage, length);
1172     LocalFree(rawMessage);
1173     return result;
1174 }
1175 #endif // _SQUID_WINDOWS_
1176 
1177