--------------------- PatchSet 5326 Date: 2002/10/09 14:36:40 Author: rbcollins Branch: rbcollins_cxxtest Tag: (none) Log: and more Members: src/Makefile.am:1.29.2.22->1.29.2.23 src/tools.c:1.28.6.1->1.28.6.2(DEAD) src/tools.cc:1.1->1.1.2.1 src/url.c:1.11->1.11.6.1(DEAD) src/url.cc:1.1->1.1.2.1 src/useragent.c:1.7->1.7.56.1(DEAD) src/useragent.cc:1.1->1.1.2.1 Index: squid/src/Makefile.am =================================================================== RCS file: /cvsroot/squid-sf//squid/src/Makefile.am,v retrieving revision 1.29.2.22 retrieving revision 1.29.2.23 diff -u -r1.29.2.22 -r1.29.2.23 --- squid/src/Makefile.am 9 Oct 2002 14:14:17 -0000 1.29.2.22 +++ squid/src/Makefile.am 9 Oct 2002 14:36:40 -0000 1.29.2.23 @@ -217,12 +217,12 @@ store_swapmeta.cc \ store_swapout.c \ structs.h \ - tools.c \ + tools.cc \ typedefs.h \ $(UNLINKDSOURCE) \ - url.c \ + url.cc \ urn.cc \ - useragent.c \ + useragent.cc \ wais.cc \ wccp.cc \ whois.cc \ --- squid/src/tools.c Wed Feb 14 01:07:38 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,1098 +0,0 @@ - -/* - * $Id: tools.c,v 1.28.6.1 2002/10/06 04:01:15 rbcollins Exp $ - * - * DEBUG: section 21 Misc Functions - * AUTHOR: Harvest Derived - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid.h" - -#define DEAD_MSG "\ -The Squid Cache (version %s) died.\n\ -\n\ -You've encountered a fatal error in the Squid Cache version %s.\n\ -If a core file was created (possibly in the swap directory),\n\ -please execute 'gdb squid core' or 'dbx squid core', then type 'where',\n\ -and report the trace back to squid-bugs@squid-cache.org.\n\ -\n\ -Thanks!\n" - -static void fatal_common(const char *); -static void fatalvf(const char *fmt, va_list args); -static void mail_warranty(void); -#if MEM_GEN_TRACE -extern void log_trace_done(); -extern void log_trace_init(char *); -#endif - -#ifdef _SQUID_LINUX_ -/* Workaround for crappy glic header files */ -extern int backtrace(void *, int); -extern void backtrace_symbols_fd(void *, int, int); -extern int setresuid(uid_t, uid_t, uid_t); -#endif /* _SQUID_LINUX */ - -extern void (*failure_notify) (const char *); - -MemPool *dlink_node_pool = NULL; - -void -releaseServerSockets(void) -{ - int i; - /* Release the main ports as early as possible */ - for (i = 0; i < NHttpSockets; i++) { - if (HttpSockets[i] >= 0) - close(HttpSockets[i]); - } - if (theInIcpConnection >= 0) - close(theInIcpConnection); - if (theOutIcpConnection >= 0 && theOutIcpConnection != theInIcpConnection) - close(theOutIcpConnection); -} - -static char * -dead_msg(void) -{ - LOCAL_ARRAY(char, msg, 1024); - snprintf(msg, 1024, DEAD_MSG, version_string, version_string); - return msg; -} - -static void -mail_warranty(void) -{ - FILE *fp = NULL; - static char command[256]; -#if HAVE_MKSTEMP - char filename[] = "/tmp/squid-XXXXXX"; - int tfd = mkstemp(filename); - if (tfd < 0) - return; - if ((fp = fdopen(tfd, "w")) == NULL) - return; -#else - char *filename; - if ((filename = tempnam(NULL, appname)) == NULL) - return; - if ((fp = fopen(filename, "w")) == NULL) - return; -#endif - fprintf(fp, "From: %s\n", appname); - fprintf(fp, "To: %s\n", Config.adminEmail); - fprintf(fp, "Subject: %s\n", dead_msg()); - fclose(fp); - snprintf(command, 256, "mail %s < %s", Config.adminEmail, filename); - system(command); /* XXX should avoid system(3) */ - unlink(filename); -} - -void -dumpMallocStats(void) -{ -#if HAVE_MSTATS && HAVE_GNUMALLOC_H - struct mstats ms = mstats(); - fprintf(debug_log, "\ttotal space in arena: %6d KB\n", - (int) (ms.bytes_total >> 10)); - fprintf(debug_log, "\tTotal free: %6d KB %d%%\n", - (int) (ms.bytes_free >> 10), - percent(ms.bytes_free, ms.bytes_total)); -#elif HAVE_MALLINFO && HAVE_STRUCT_MALLINFO - struct mallinfo mp; - int t; - if (!do_mallinfo) - return; - mp = mallinfo(); - fprintf(debug_log, "Memory usage for %s via mallinfo():\n", appname); - fprintf(debug_log, "\ttotal space in arena: %6d KB\n", - mp.arena >> 10); - fprintf(debug_log, "\tOrdinary blocks: %6d KB %6d blks\n", - mp.uordblks >> 10, mp.ordblks); - fprintf(debug_log, "\tSmall blocks: %6d KB %6d blks\n", - mp.usmblks >> 10, mp.smblks); - fprintf(debug_log, "\tHolding blocks: %6d KB %6d blks\n", - mp.hblkhd >> 10, mp.hblks); - fprintf(debug_log, "\tFree Small blocks: %6d KB\n", - mp.fsmblks >> 10); - fprintf(debug_log, "\tFree Ordinary blocks: %6d KB\n", - mp.fordblks >> 10); - t = mp.uordblks + mp.usmblks + mp.hblkhd; - fprintf(debug_log, "\tTotal in use: %6d KB %d%%\n", - t >> 10, percent(t, mp.arena)); - t = mp.fsmblks + mp.fordblks; - fprintf(debug_log, "\tTotal free: %6d KB %d%%\n", - t >> 10, percent(t, mp.arena)); -#if HAVE_STRUCT_MALLINFO_MXFAST - fprintf(debug_log, "\tmax size of small blocks:\t%d\n", - mp.mxfast); - fprintf(debug_log, "\tnumber of small blocks in a holding block:\t%d\n", - mp.nlblks); - fprintf(debug_log, "\tsmall block rounding factor:\t%d\n", - mp.grain); - fprintf(debug_log, "\tspace (including overhead) allocated in ord. blks:\t%d\n", - mp.uordbytes); - fprintf(debug_log, "\tnumber of ordinary blocks allocated:\t%d\n", - mp.allocated); - fprintf(debug_log, "\tbytes used in maintaining the free tree:\t%d\n", - mp.treeoverhead); -#endif /* HAVE_STRUCT_MALLINFO_MXFAST */ -#endif /* HAVE_MALLINFO */ -} - -void -squid_getrusage(struct rusage *r) -{ - memset(r, '\0', sizeof(struct rusage)); -#if HAVE_GETRUSAGE && defined(RUSAGE_SELF) -#ifdef _SQUID_SOLARIS_ - /* Solaris 2.5 has getrusage() permission bug -- Arjan de Vet */ - enter_suid(); -#endif - getrusage(RUSAGE_SELF, r); -#ifdef _SQUID_SOLARIS_ - leave_suid(); -#endif -#endif -} - -double -rusage_cputime(struct rusage *r) -{ - return (double) r->ru_stime.tv_sec + - (double) r->ru_utime.tv_sec + - (double) r->ru_stime.tv_usec / 1000000.0 + - (double) r->ru_utime.tv_usec / 1000000.0; -} - -/* Hack for some HP-UX preprocessors */ -#ifndef HAVE_GETPAGESIZE -#define HAVE_GETPAGESIZE 0 -#endif - -int -rusage_maxrss(struct rusage *r) -{ -#if defined(_SQUID_SGI_) && _ABIAPI - return r->ru_pad[0]; -#elif defined(_SQUID_SGI_) - return r->ru_maxrss; -#elif defined(_SQUID_OSF_) - return r->ru_maxrss; -#elif defined(BSD4_4) - return r->ru_maxrss; -#elif defined(HAVE_GETPAGESIZE) && HAVE_GETPAGESIZE != 0 - return (r->ru_maxrss * getpagesize()) >> 10; -#elif defined(PAGESIZE) - return (r->ru_maxrss * PAGESIZE) >> 10; -#else - return r->ru_maxrss; -#endif -} - -int -rusage_pagefaults(struct rusage *r) -{ -#if defined(_SQUID_SGI_) && _ABIAPI - return r->ru_pad[5]; -#else - return r->ru_majflt; -#endif -} - - -void -PrintRusage(void) -{ - struct rusage rusage; - squid_getrusage(&rusage); - fprintf(debug_log, "CPU Usage: %.3f seconds = %.3f user + %.3f sys\n", - rusage_cputime(&rusage), - rusage.ru_utime.tv_sec + ((double) rusage.ru_utime.tv_usec / 1000000.0), - rusage.ru_stime.tv_sec + ((double) rusage.ru_stime.tv_usec / 1000000.0)); - fprintf(debug_log, "Maximum Resident Size: %d KB\n", - rusage_maxrss(&rusage)); - fprintf(debug_log, "Page faults with physical i/o: %d\n", - rusage_pagefaults(&rusage)); -} - - -void -death(int sig) -{ - if (sig == SIGSEGV) - fprintf(debug_log, "FATAL: Received Segment Violation...dying.\n"); - else if (sig == SIGBUS) - fprintf(debug_log, "FATAL: Received Bus Error...dying.\n"); - else - fprintf(debug_log, "FATAL: Received signal %d...dying.\n", sig); - -#ifdef PRINT_STACK_TRACE -#ifdef _SQUID_HPUX_ - { - extern void U_STACK_TRACE(void); /* link with -lcl */ - fflush(debug_log); - dup2(fileno(debug_log), 2); - U_STACK_TRACE(); - } -#endif /* _SQUID_HPUX_ */ -#ifdef _SQUID_SOLARIS_ - { /* get ftp://opcom.sun.ca/pub/tars/opcom_stack.tar.gz and */ - extern void opcom_stack_trace(void); /* link with -lopcom_stack */ - fflush(debug_log); - dup2(fileno(debug_log), fileno(stdout)); - opcom_stack_trace(); - fflush(stdout); - } -#endif /* _SQUID_SOLARIS_ */ -#if HAVE_BACKTRACE_SYMBOLS_FD - { - static void *(callarray[8192]); - int n; - n = backtrace(callarray, 8192); - backtrace_symbols_fd(callarray, n, fileno(debug_log)); - } -#endif -#endif /* PRINT_STACK_TRACE */ - -#if SA_RESETHAND == 0 - signal(SIGSEGV, SIG_DFL); - signal(SIGBUS, SIG_DFL); - signal(sig, SIG_DFL); -#endif - releaseServerSockets(); - storeDirWriteCleanLogs(0); - PrintRusage(); - dumpMallocStats(); - if (squid_curtime - SQUID_RELEASE_TIME < 864000) { - /* skip if more than 10 days old */ - if (Config.adminEmail) - mail_warranty(); - else - puts(dead_msg()); - } - abort(); -} - - -void -sigusr2_handle(int sig) -{ - static int state = 0; - /* no debug() here; bad things happen if the signal is delivered during _db_print() */ - if (state == 0) { -#ifndef MEM_GEN_TRACE - _db_init(Config.Log.log, "ALL,10"); -#else - log_trace_done(); -#endif - state = 1; - } else { -#ifndef MEM_GEN_TRACE - _db_init(Config.Log.log, Config.debugOptions); -#else - log_trace_init("/tmp/squid.alloc"); -#endif - state = 0; - } -#if !HAVE_SIGACTION - signal(sig, sigusr2_handle); /* reinstall */ -#endif -} - -static void -fatal_common(const char *message) -{ -#if HAVE_SYSLOG - syslog(LOG_ALERT, "%s", message); -#endif - fprintf(debug_log, "FATAL: %s\n", message); - if (opt_debug_stderr > 0 && debug_log != stderr) - fprintf(stderr, "FATAL: %s\n", message); - fprintf(debug_log, "Squid Cache (Version %s): Terminated abnormally.\n", - version_string); - fflush(debug_log); - PrintRusage(); - dumpMallocStats(); -} - -/* fatal */ -void -fatal(const char *message) -{ - releaseServerSockets(); - /* check for store_dirs_rebuilding because fatal() is often - * used in early initialization phases, long before we ever - * get to the store log. */ - if (0 == store_dirs_rebuilding) - storeDirWriteCleanLogs(0); - fatal_common(message); - if (shutting_down) - exit(0); - else - abort(); -} - -/* printf-style interface for fatal */ -#if STDC_HEADERS -void -fatalf(const char *fmt,...) -{ - va_list args; - va_start(args, fmt); -#else -void -fatalf(va_alist) - va_dcl -{ - va_list args; - const char *fmt = NULL; - va_start(args); - fmt = va_arg(args, char *); -#endif - fatalvf(fmt, args); - va_end(args); -} - - -/* used by fatalf */ -static void -fatalvf(const char *fmt, va_list args) -{ - static char fatal_str[BUFSIZ]; - vsnprintf(fatal_str, sizeof(fatal_str), fmt, args); - fatal(fatal_str); -} - -/* fatal with dumping core */ -void -fatal_dump(const char *message) -{ - failure_notify = NULL; - releaseServerSockets(); - if (message) - fatal_common(message); - if (opt_catch_signals) - storeDirWriteCleanLogs(0); - abort(); -} - -void -debug_trap(const char *message) -{ - if (!opt_catch_signals) - fatal_dump(message); - _db_print("WARNING: %s\n", message); -} - -void -sig_child(int sig) -{ -#ifdef _SQUID_NEXT_ - union wait status; -#else - int status; -#endif - pid_t pid; - - do { -#ifdef _SQUID_NEXT_ - pid = wait3(&status, WNOHANG, NULL); -#else - pid = waitpid(-1, &status, WNOHANG); -#endif - /* no debug() here; bad things happen if the signal is delivered during _db_print() */ -#if HAVE_SIGACTION - } while (pid > 0); -#else - } while (pid > 0 || (pid < 0 && errno == EINTR)); - signal(sig, sig_child); -#endif -} - -const char * -getMyHostname(void) -{ - LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1); - static int present = 0; - const struct hostent *h = NULL; - struct in_addr sa; - if (Config.visibleHostname != NULL) - return Config.visibleHostname; - if (present) - return host; - host[0] = '\0'; - memcpy(&sa, &any_addr, sizeof(sa)); - if (Config.Sockaddr.http && sa.s_addr == any_addr.s_addr) - memcpy(&sa, &Config.Sockaddr.http->s.sin_addr, sizeof(sa)); -#if USE_SSL - if (Config.Sockaddr.https && sa.s_addr == any_addr.s_addr) - memcpy(&sa, &Config.Sockaddr.https->s.sin_addr, sizeof(sa)); -#endif - /* - * If the first http_port address has a specific address, try a - * reverse DNS lookup on it. - */ - if (sa.s_addr != any_addr.s_addr) { - h = gethostbyaddr((char *) &sa, - sizeof(sa), AF_INET); - if (h != NULL) { - /* DNS lookup successful */ - /* use the official name from DNS lookup */ - xstrncpy(host, h->h_name, SQUIDHOSTNAMELEN); - debug(50, 4) ("getMyHostname: resolved %s to '%s'\n", - inet_ntoa(sa), - host); - present = 1; - if (strchr(host, '.')) - return host; - - } - debug(50, 1) ("WARNING: failed to resolve %s to a fully qualified hostname\n", - inet_ntoa(sa)); - } - /* - * Get the host name and store it in host to return - */ - if (gethostname(host, SQUIDHOSTNAMELEN) < 0) { - debug(50, 1) ("WARNING: gethostname failed: %s\n", xstrerror()); - } else if ((h = gethostbyname(host)) == NULL) { - debug(50, 1) ("WARNING: gethostbyname failed for %s\n", host); - } else { - debug(50, 6) ("getMyHostname: '%s' resolved into '%s'\n", - host, h->h_name); - /* DNS lookup successful */ - /* use the official name from DNS lookup */ - xstrncpy(host, h->h_name, SQUIDHOSTNAMELEN); - present = 1; - if (strchr(host, '.')) - return host; - } - fatal("Could not determine fully qualified hostname. Please set 'visible_hostname'\n"); - return NULL; /* keep compiler happy */ -} - -const char * -uniqueHostname(void) -{ - return Config.uniqueHostname ? Config.uniqueHostname : getMyHostname(); -} - -void -safeunlink(const char *s, int quiet) -{ - statCounter.syscalls.disk.unlinks++; - if (unlink(s) < 0 && !quiet) - debug(50, 1) ("safeunlink: Couldn't delete %s: %s\n", s, xstrerror()); -} - -/* leave a privilegied section. (Give up any privilegies) - * Routines that need privilegies can rap themselves in enter_suid() - * and leave_suid() - * To give upp all posibilites to gain privilegies use no_suid() - */ -void -leave_suid(void) -{ - debug(21, 3) ("leave_suid: PID %d called\n", (int) getpid()); - if (geteuid() != 0) - return; - /* Started as a root, check suid option */ - if (Config.effectiveUser == NULL) - return; -#if HAVE_SETGROUPS - setgroups(1, &Config2.effectiveGroupID); -#endif - if (setgid(Config2.effectiveGroupID) < 0) - debug(50, 0) ("ALERT: setgid: %s\n", xstrerror()); - debug(21, 3) ("leave_suid: PID %d giving up root, becoming '%s'\n", - (int) getpid(), Config.effectiveUser); -#if HAVE_SETRESUID - if (setresuid(Config2.effectiveUserID, Config2.effectiveUserID, 0) < 0) - debug(50, 0) ("ALERT: setresuid: %s\n", xstrerror()); -#elif HAVE_SETEUID - if (seteuid(Config2.effectiveUserID) < 0) - debug(50, 0) ("ALERT: seteuid: %s\n", xstrerror()); -#else - if (setuid(Config2.effectiveUserID) < 0) - debug(50, 0) ("ALERT: setuid: %s\n", xstrerror()); -#endif -} - -/* Enter a privilegied section */ -void -enter_suid(void) -{ - debug(21, 3) ("enter_suid: PID %d taking root priveleges\n", (int) getpid()); -#if HAVE_SETRESUID - setresuid(-1, 0, -1); -#else - setuid(0); -#endif -} - -/* Give up the posibility to gain privilegies. - * this should be used before starting a sub process - */ -void -no_suid(void) -{ - uid_t uid; - leave_suid(); - uid = geteuid(); - debug(21, 3) ("leave_suid: PID %d giving up root priveleges forever\n", (int) getpid()); -#if HAVE_SETRESUID - if (setresuid(uid, uid, uid) < 0) - debug(50, 1) ("no_suid: setresuid: %s\n", xstrerror()); -#else - setuid(0); - if (setuid(uid) < 0) - debug(50, 1) ("no_suid: setuid: %s\n", xstrerror()); -#endif -} - -void -writePidFile(void) -{ - int fd; - const char *f = NULL; - mode_t old_umask; - char buf[32]; - if ((f = Config.pidFilename) == NULL) - return; - if (!strcmp(Config.pidFilename, "none")) - return; - enter_suid(); - old_umask = umask(022); - fd = file_open(f, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT); - umask(old_umask); - leave_suid(); - if (fd < 0) { - debug(50, 0) ("%s: %s\n", f, xstrerror()); - debug_trap("Could not write pid file"); - return; - } - snprintf(buf, 32, "%d\n", (int) getpid()); - FD_WRITE_METHOD(fd, buf, strlen(buf)); - file_close(fd); -} - - -pid_t -readPidFile(void) -{ - FILE *pid_fp = NULL; - const char *f = Config.pidFilename; - pid_t pid = -1; - int i; - - if (f == NULL || !strcmp(Config.pidFilename, "none")) { - fprintf(stderr, "%s: ERROR: No pid file name defined\n", appname); - exit(1); - } - pid_fp = fopen(f, "r"); - if (pid_fp != NULL) { - pid = 0; - if (fscanf(pid_fp, "%d", &i) == 1) - pid = (pid_t) i; - fclose(pid_fp); - } else { - if (errno != ENOENT) { - fprintf(stderr, "%s: ERROR: Could not read pid file\n", appname); - fprintf(stderr, "\t%s: %s\n", f, xstrerror()); - exit(1); - } - } - return pid; -} - - -void -setMaxFD(void) -{ -#if HAVE_SETRLIMIT - /* try to use as many file descriptors as possible */ - /* System V uses RLIMIT_NOFILE and BSD uses RLIMIT_OFILE */ - struct rlimit rl; -#if defined(RLIMIT_NOFILE) - if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { - debug(50, 0) ("setrlimit: RLIMIT_NOFILE: %s\n", xstrerror()); - } else { - rl.rlim_cur = Squid_MaxFD; - if (rl.rlim_cur > rl.rlim_max) - Squid_MaxFD = rl.rlim_cur = rl.rlim_max; - if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { - snprintf(tmp_error_buf, ERROR_BUF_SZ, - "setrlimit: RLIMIT_NOFILE: %s", xstrerror()); - fatal_dump(tmp_error_buf); - } - } -#elif defined(RLIMIT_OFILE) - if (getrlimit(RLIMIT_OFILE, &rl) < 0) { - debug(50, 0) ("setrlimit: RLIMIT_NOFILE: %s\n", xstrerror()); - } else { - rl.rlim_cur = Squid_MaxFD; - if (rl.rlim_cur > rl.rlim_max) - Squid_MaxFD = rl.rlim_cur = rl.rlim_max; - if (setrlimit(RLIMIT_OFILE, &rl) < 0) { - snprintf(tmp_error_buf, ERROR_BUF_SZ, - "setrlimit: RLIMIT_OFILE: %s", xstrerror()); - fatal_dump(tmp_error_buf); - } - } -#endif -#else /* HAVE_SETRLIMIT */ - debug(21, 1) ("setMaxFD: Cannot increase: setrlimit() not supported on this system\n"); -#endif /* HAVE_SETRLIMIT */ - -#if HAVE_SETRLIMIT && defined(RLIMIT_DATA) - if (getrlimit(RLIMIT_DATA, &rl) < 0) { - debug(50, 0) ("getrlimit: RLIMIT_DATA: %s\n", xstrerror()); - } else if (rl.rlim_max > rl.rlim_cur) { - rl.rlim_cur = rl.rlim_max; /* set it to the max */ - if (setrlimit(RLIMIT_DATA, &rl) < 0) { - snprintf(tmp_error_buf, ERROR_BUF_SZ, - "setrlimit: RLIMIT_DATA: %s", xstrerror()); - fatal_dump(tmp_error_buf); - } - } -#endif /* RLIMIT_DATA */ -#if HAVE_SETRLIMIT && defined(RLIMIT_VMEM) - if (getrlimit(RLIMIT_VMEM, &rl) < 0) { - debug(50, 0) ("getrlimit: RLIMIT_VMEM: %s\n", xstrerror()); - } else if (rl.rlim_max > rl.rlim_cur) { - rl.rlim_cur = rl.rlim_max; /* set it to the max */ - if (setrlimit(RLIMIT_VMEM, &rl) < 0) { - snprintf(tmp_error_buf, ERROR_BUF_SZ, - "setrlimit: RLIMIT_VMEM: %s", xstrerror()); - fatal_dump(tmp_error_buf); - } - } -#endif /* RLIMIT_VMEM */ -} - -time_t -getCurrentTime(void) -{ -#if GETTIMEOFDAY_NO_TZP - gettimeofday(¤t_time); -#else - gettimeofday(¤t_time, NULL); -#endif - current_dtime = (double) current_time.tv_sec + - (double) current_time.tv_usec / 1000000.0; - return squid_curtime = current_time.tv_sec; -} - -int -percent(int a, int b) -{ - return b ? ((int) (100.0 * a / b + 0.5)) : 0; -} - -double -dpercent(double a, double b) -{ - return b ? (100.0 * a / b) : 0.0; -} - -void -squid_signal(int sig, SIGHDLR * func, int flags) -{ -#if HAVE_SIGACTION - struct sigaction sa; - sa.sa_handler = func; - sa.sa_flags = flags; - sigemptyset(&sa.sa_mask); - if (sigaction(sig, &sa, NULL) < 0) - debug(50, 0) ("sigaction: sig=%d func=%p: %s\n", sig, func, xstrerror()); -#else - signal(sig, func); -#endif -} - -struct in_addr -inaddrFromHostent(const struct hostent *hp) -{ - struct in_addr s; - xmemcpy(&s.s_addr, hp->h_addr, sizeof(s.s_addr)); - return s; -} - -double -doubleAverage(double cur, double new, int N, int max) -{ - if (N > max) - N = max; - return (cur * (N - 1.0) + new) / N; -} - -int -intAverage(int cur, int new, int n, int max) -{ - if (n > max) - n = max; - return (cur * (n - 1) + new) / n; -} - -void -logsFlush(void) -{ - if (debug_log) - fflush(debug_log); -} - -const char * -checkNullString(const char *p) -{ - return p ? p : "(NULL)"; -} - -dlink_node * -dlinkNodeNew() -{ - if (dlink_node_pool == NULL) - dlink_node_pool = memPoolCreate("Dlink list nodes", sizeof(dlink_node)); - /* where should we call memPoolDestroy(dlink_node_pool); */ - return memPoolAlloc(dlink_node_pool); -} - -/* the node needs to be unlinked FIRST */ -void -dlinkNodeDelete(dlink_node * m) -{ - if (m == NULL) - return; - memPoolFree(dlink_node_pool, m); -} - -void -dlinkAdd(void *data, dlink_node * m, dlink_list * list) -{ - m->data = data; - m->prev = NULL; - m->next = list->head; - if (list->head) - list->head->prev = m; - list->head = m; - if (list->tail == NULL) - list->tail = m; -} - -void -dlinkAddAfter(void *data, dlink_node * m, dlink_node * n, dlink_list * list) -{ - m->data = data; - m->prev = n; - m->next = n->next; - if (n->next) - n->next->prev = m; - else { - assert(list->tail == n); - list->tail = m; - } - n->next = m; -} - -void -dlinkAddTail(void *data, dlink_node * m, dlink_list * list) -{ - m->data = data; - m->next = NULL; - m->prev = list->tail; - if (list->tail) - list->tail->next = m; - list->tail = m; - if (list->head == NULL) - list->head = m; -} - -void -dlinkDelete(dlink_node * m, dlink_list * list) -{ - if (m->next) - m->next->prev = m->prev; - if (m->prev) - m->prev->next = m->next; - if (m == list->head) - list->head = m->next; - if (m == list->tail) - list->tail = m->prev; - m->next = m->prev = NULL; -} - -void -kb_incr(kb_t * k, size_t v) -{ - k->bytes += v; - k->kb += (k->bytes >> 10); - k->bytes &= 0x3FF; -} - -void -debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm) -{ - MemBuf mb; - Packer p; - assert(label && obj && pm); - memBufDefInit(&mb); - packerToMemInit(&p, &mb); - (*pm) (obj, &p); - debug(section, level) ("%s%s", label, mb.buf); - packerClean(&p); - memBufClean(&mb); -} - -int -stringHasWhitespace(const char *s) -{ - return strpbrk(s, w_space) != NULL; -} - -void -linklistPush(link_list ** L, void *p) -{ - link_list *l = memAllocate(MEM_LINK_LIST); - l->next = NULL; - l->ptr = p; - while (*L) - L = &(*L)->next; - *L = l; -} - -void * -linklistShift(link_list ** L) -{ - void *p; - link_list *l; - if (NULL == *L) - return NULL; - l = *L; - p = l->ptr; - *L = (*L)->next; - memFree(l, MEM_LINK_LIST); - return p; -} - -/* - * Same as rename(2) but complains if something goes wrong; - * the caller is responsible for handing and explaining the - * consequences of errors. - */ -int -xrename(const char *from, const char *to) -{ - debug(21, 2) ("xrename: renaming %s to %s\n", from, to); -#ifdef _SQUID_MSWIN_ - remove(to); -#endif - if (0 == rename(from, to)) - return 0; - debug(21, errno == ENOENT ? 2 : 1) ("xrename: Cannot rename %s to %s: %s\n", - from, to, xstrerror()); - return -1; -} - -int -stringHasCntl(const char *s) -{ - unsigned char c; - while ((c = (unsigned char) *s++) != '\0') { - if (c <= 0x1f) - return 1; - if (c >= 0x7f && c <= 0x9f) - return 1; - } - return 0; -} - -/* - * isPowTen returns true if its argument is an integer power of - * 10. Its used for logging of certain error messages that can - * occur often, but that we don't want to fill cache.log with. - */ -int -isPowTen(int count) -{ - double x = log(count) / log(10.0); - if (0.0 != x - (double) (int) x) - return 0; - return 1; -} - -void -parseEtcHosts(void) -{ - FILE *fp; - char buf[1024]; - char buf2[512]; - char *nt = buf; - char *lt = buf; - - if (NULL == Config.etcHostsPath) - return; - if (0 == strcmp(Config.etcHostsPath, "none")) - return; - fp = fopen(Config.etcHostsPath, "r"); - if (fp == NULL) { - debug(1, 1) ("parseEtcHosts: %s: %s\n", - Config.etcHostsPath, xstrerror()); - return; - } -#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_) - setmode(fileno(fp), O_TEXT); -#endif - while (fgets(buf, 1024, fp)) { /* for each line */ - wordlist *hosts = NULL; - char *addr; - if (buf[0] == '#') /* MS-windows likes to add comments */ - continue; - lt = buf; - addr = buf; - debug(1, 5) ("etc_hosts: line is '%s'\n", buf); - nt = strpbrk(lt, w_space); - if (nt == NULL) /* empty line */ - continue; - *nt = '\0'; /* null-terminate the address */ - debug(1, 5) ("etc_hosts: address is '%s'\n", addr); - lt = nt + 1; - while ((nt = strpbrk(lt, w_space))) { - char *host = NULL; - if (nt == lt) { /* multiple spaces */ - debug(1, 5) ("etc_hosts: multiple spaces, skipping\n"); - lt = nt + 1; - continue; - } - *nt = '\0'; - debug(1, 5) ("etc_hosts: got hostname '%s'\n", lt); - if (Config.appendDomain && !strchr(lt, '.')) { - /* I know it's ugly, but it's only at reconfig */ - strncpy(buf2, lt, 512); - strncat(buf2, Config.appendDomain, 512 - strlen(lt)); - host = buf2; - } else { - host = lt; - } - if (ipcacheAddEntryFromHosts(host, addr) != 0) - goto skip; /* invalid address, continuing is useless */ - wordlistAdd(&hosts, host); - lt = nt + 1; - } - fqdncacheAddEntryFromHosts(addr, hosts); - skip: - wordlistDestroy(&hosts); - } -} - -int -getMyPort(void) -{ - if (Config.Sockaddr.http) - return ntohs(Config.Sockaddr.http->s.sin_port); -#if USE_SSL - if (Config.Sockaddr.https) - return ntohs(Config.Sockaddr.https->s.sin_port); -#endif - fatal("No port defined"); - return 0; /* NOT REACHED */ -} - -/* - * Similar to strtok, but has some rudimentary knowledge - * of quoting - */ -char * -strwordtok(char *buf, char **t) -{ - unsigned char *word = NULL; - unsigned char *p = (unsigned char *) buf; - unsigned char *d; - unsigned char ch; - int quoted = 0; - if (!p) - p = (unsigned char *) *t; - if (!p) - goto error; - while (*p && isspace(*p)) - p++; - if (!*p) - goto error; - word = d = p; - while ((ch = *p)) { - switch (ch) { - case '\\': - p++; - *d++ = ch = *p; - if (ch) - p++; - break; - case '"': - quoted = !quoted; - p++; - default: - if (!quoted && isspace(*p)) { - p++; - goto done; - } - *d++ = *p++; - break; - } - } - done: - *d++ = '\0'; - error: - *t = (char *) p; - return (char *) word; -} - -/* - * Inverse of strwordtok. Quotes a word if needed - */ -void -strwordquote(MemBuf * mb, const char *str) -{ - int quoted = 0; - if (strchr(str, ' ')) { - quoted = 1; - memBufAppend(mb, "\"", 1); - } - while (*str) { - int l = strcspn(str, "\"\\"); - memBufAppend(mb, str, l); - str += l; - while (*str == '"' || *str == '\\') { - memBufAppend(mb, "\\", 1); - memBufAppend(mb, str, 1); - str++; - } - } - if (quoted) - memBufAppend(mb, "\"", 1); -} --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/tools.cc Wed Feb 14 01:07:38 2007 @@ -0,0 +1,1098 @@ + +/* + * $Id: tools.cc,v 1.1.2.1 2002/10/09 14:36:47 rbcollins Exp $ + * + * DEBUG: section 21 Misc Functions + * AUTHOR: Harvest Derived + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + +#define DEAD_MSG "\ +The Squid Cache (version %s) died.\n\ +\n\ +You've encountered a fatal error in the Squid Cache version %s.\n\ +If a core file was created (possibly in the swap directory),\n\ +please execute 'gdb squid core' or 'dbx squid core', then type 'where',\n\ +and report the trace back to squid-bugs@squid-cache.org.\n\ +\n\ +Thanks!\n" + +static void fatal_common(const char *); +static void fatalvf(const char *fmt, va_list args); +static void mail_warranty(void); +#if MEM_GEN_TRACE +extern void log_trace_done(); +extern void log_trace_init(char *); +#endif + +#ifdef _SQUID_LINUX_ +/* Workaround for crappy glic header files */ +SQUIDCEXTERN int backtrace(void *, int); +SQUIDCEXTERN void backtrace_symbols_fd(void *, int, int); +SQUIDCEXTERN int setresuid(uid_t, uid_t, uid_t); +#endif /* _SQUID_LINUX */ + +extern void (*failure_notify) (const char *); + +MemPool *dlink_node_pool = NULL; + +void +releaseServerSockets(void) +{ + int i; + /* Release the main ports as early as possible */ + for (i = 0; i < NHttpSockets; i++) { + if (HttpSockets[i] >= 0) + close(HttpSockets[i]); + } + if (theInIcpConnection >= 0) + close(theInIcpConnection); + if (theOutIcpConnection >= 0 && theOutIcpConnection != theInIcpConnection) + close(theOutIcpConnection); +} + +static char * +dead_msg(void) +{ + LOCAL_ARRAY(char, msg, 1024); + snprintf(msg, 1024, DEAD_MSG, version_string, version_string); + return msg; +} + +static void +mail_warranty(void) +{ + FILE *fp = NULL; + static char command[256]; +#if HAVE_MKSTEMP + char filename[] = "/tmp/squid-XXXXXX"; + int tfd = mkstemp(filename); + if (tfd < 0) + return; + if ((fp = fdopen(tfd, "w")) == NULL) + return; +#else + char *filename; + if ((filename = tempnam(NULL, appname)) == NULL) + return; + if ((fp = fopen(filename, "w")) == NULL) + return; +#endif + fprintf(fp, "From: %s\n", appname); + fprintf(fp, "To: %s\n", Config.adminEmail); + fprintf(fp, "Subject: %s\n", dead_msg()); + fclose(fp); + snprintf(command, 256, "mail %s < %s", Config.adminEmail, filename); + system(command); /* XXX should avoid system(3) */ + unlink(filename); +} + +void +dumpMallocStats(void) +{ +#if HAVE_MSTATS && HAVE_GNUMALLOC_H + struct mstats ms = mstats(); + fprintf(debug_log, "\ttotal space in arena: %6d KB\n", + (int) (ms.bytes_total >> 10)); + fprintf(debug_log, "\tTotal free: %6d KB %d%%\n", + (int) (ms.bytes_free >> 10), + percent(ms.bytes_free, ms.bytes_total)); +#elif HAVE_MALLINFO && HAVE_STRUCT_MALLINFO + struct mallinfo mp; + int t; + if (!do_mallinfo) + return; + mp = mallinfo(); + fprintf(debug_log, "Memory usage for %s via mallinfo():\n", appname); + fprintf(debug_log, "\ttotal space in arena: %6d KB\n", + mp.arena >> 10); + fprintf(debug_log, "\tOrdinary blocks: %6d KB %6d blks\n", + mp.uordblks >> 10, mp.ordblks); + fprintf(debug_log, "\tSmall blocks: %6d KB %6d blks\n", + mp.usmblks >> 10, mp.smblks); + fprintf(debug_log, "\tHolding blocks: %6d KB %6d blks\n", + mp.hblkhd >> 10, mp.hblks); + fprintf(debug_log, "\tFree Small blocks: %6d KB\n", + mp.fsmblks >> 10); + fprintf(debug_log, "\tFree Ordinary blocks: %6d KB\n", + mp.fordblks >> 10); + t = mp.uordblks + mp.usmblks + mp.hblkhd; + fprintf(debug_log, "\tTotal in use: %6d KB %d%%\n", + t >> 10, percent(t, mp.arena)); + t = mp.fsmblks + mp.fordblks; + fprintf(debug_log, "\tTotal free: %6d KB %d%%\n", + t >> 10, percent(t, mp.arena)); +#if HAVE_STRUCT_MALLINFO_MXFAST + fprintf(debug_log, "\tmax size of small blocks:\t%d\n", + mp.mxfast); + fprintf(debug_log, "\tnumber of small blocks in a holding block:\t%d\n", + mp.nlblks); + fprintf(debug_log, "\tsmall block rounding factor:\t%d\n", + mp.grain); + fprintf(debug_log, "\tspace (including overhead) allocated in ord. blks:\t%d\n", + mp.uordbytes); + fprintf(debug_log, "\tnumber of ordinary blocks allocated:\t%d\n", + mp.allocated); + fprintf(debug_log, "\tbytes used in maintaining the free tree:\t%d\n", + mp.treeoverhead); +#endif /* HAVE_STRUCT_MALLINFO_MXFAST */ +#endif /* HAVE_MALLINFO */ +} + +void +squid_getrusage(struct rusage *r) +{ + memset(r, '\0', sizeof(struct rusage)); +#if HAVE_GETRUSAGE && defined(RUSAGE_SELF) +#ifdef _SQUID_SOLARIS_ + /* Solaris 2.5 has getrusage() permission bug -- Arjan de Vet */ + enter_suid(); +#endif + getrusage(RUSAGE_SELF, r); +#ifdef _SQUID_SOLARIS_ + leave_suid(); +#endif +#endif +} + +double +rusage_cputime(struct rusage *r) +{ + return (double) r->ru_stime.tv_sec + + (double) r->ru_utime.tv_sec + + (double) r->ru_stime.tv_usec / 1000000.0 + + (double) r->ru_utime.tv_usec / 1000000.0; +} + +/* Hack for some HP-UX preprocessors */ +#ifndef HAVE_GETPAGESIZE +#define HAVE_GETPAGESIZE 0 +#endif + +int +rusage_maxrss(struct rusage *r) +{ +#if defined(_SQUID_SGI_) && _ABIAPI + return r->ru_pad[0]; +#elif defined(_SQUID_SGI_) + return r->ru_maxrss; +#elif defined(_SQUID_OSF_) + return r->ru_maxrss; +#elif defined(BSD4_4) + return r->ru_maxrss; +#elif defined(HAVE_GETPAGESIZE) && HAVE_GETPAGESIZE != 0 + return (r->ru_maxrss * getpagesize()) >> 10; +#elif defined(PAGESIZE) + return (r->ru_maxrss * PAGESIZE) >> 10; +#else + return r->ru_maxrss; +#endif +} + +int +rusage_pagefaults(struct rusage *r) +{ +#if defined(_SQUID_SGI_) && _ABIAPI + return r->ru_pad[5]; +#else + return r->ru_majflt; +#endif +} + + +void +PrintRusage(void) +{ + struct rusage rusage; + squid_getrusage(&rusage); + fprintf(debug_log, "CPU Usage: %.3f seconds = %.3f user + %.3f sys\n", + rusage_cputime(&rusage), + rusage.ru_utime.tv_sec + ((double) rusage.ru_utime.tv_usec / 1000000.0), + rusage.ru_stime.tv_sec + ((double) rusage.ru_stime.tv_usec / 1000000.0)); + fprintf(debug_log, "Maximum Resident Size: %d KB\n", + rusage_maxrss(&rusage)); + fprintf(debug_log, "Page faults with physical i/o: %d\n", + rusage_pagefaults(&rusage)); +} + + +void +death(int sig) +{ + if (sig == SIGSEGV) + fprintf(debug_log, "FATAL: Received Segment Violation...dying.\n"); + else if (sig == SIGBUS) + fprintf(debug_log, "FATAL: Received Bus Error...dying.\n"); + else + fprintf(debug_log, "FATAL: Received signal %d...dying.\n", sig); + +#ifdef PRINT_STACK_TRACE +#ifdef _SQUID_HPUX_ + { + extern void U_STACK_TRACE(void); /* link with -lcl */ + fflush(debug_log); + dup2(fileno(debug_log), 2); + U_STACK_TRACE(); + } +#endif /* _SQUID_HPUX_ */ +#ifdef _SQUID_SOLARIS_ + { /* get ftp://opcom.sun.ca/pub/tars/opcom_stack.tar.gz and */ + extern void opcom_stack_trace(void); /* link with -lopcom_stack */ + fflush(debug_log); + dup2(fileno(debug_log), fileno(stdout)); + opcom_stack_trace(); + fflush(stdout); + } +#endif /* _SQUID_SOLARIS_ */ +#if HAVE_BACKTRACE_SYMBOLS_FD + { + static void *(callarray[8192]); + int n; + n = backtrace(callarray, 8192); + backtrace_symbols_fd(callarray, n, fileno(debug_log)); + } +#endif +#endif /* PRINT_STACK_TRACE */ + +#if SA_RESETHAND == 0 + signal(SIGSEGV, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(sig, SIG_DFL); +#endif + releaseServerSockets(); + storeDirWriteCleanLogs(0); + PrintRusage(); + dumpMallocStats(); + if (squid_curtime - SQUID_RELEASE_TIME < 864000) { + /* skip if more than 10 days old */ + if (Config.adminEmail) + mail_warranty(); + else + puts(dead_msg()); + } + abort(); +} + + +void +sigusr2_handle(int sig) +{ + static int state = 0; + /* no debug() here; bad things happen if the signal is delivered during _db_print() */ + if (state == 0) { +#ifndef MEM_GEN_TRACE + _db_init(Config.Log.log, "ALL,10"); +#else + log_trace_done(); +#endif + state = 1; + } else { +#ifndef MEM_GEN_TRACE + _db_init(Config.Log.log, Config.debugOptions); +#else + log_trace_init("/tmp/squid.alloc"); +#endif + state = 0; + } +#if !HAVE_SIGACTION + signal(sig, sigusr2_handle); /* reinstall */ +#endif +} + +static void +fatal_common(const char *message) +{ +#if HAVE_SYSLOG + syslog(LOG_ALERT, "%s", message); +#endif + fprintf(debug_log, "FATAL: %s\n", message); + if (opt_debug_stderr > 0 && debug_log != stderr) + fprintf(stderr, "FATAL: %s\n", message); + fprintf(debug_log, "Squid Cache (Version %s): Terminated abnormally.\n", + version_string); + fflush(debug_log); + PrintRusage(); + dumpMallocStats(); +} + +/* fatal */ +void +fatal(const char *message) +{ + releaseServerSockets(); + /* check for store_dirs_rebuilding because fatal() is often + * used in early initialization phases, long before we ever + * get to the store log. */ + if (0 == store_dirs_rebuilding) + storeDirWriteCleanLogs(0); + fatal_common(message); + if (shutting_down) + exit(0); + else + abort(); +} + +/* printf-style interface for fatal */ +#if STDC_HEADERS +void +fatalf(const char *fmt,...) +{ + va_list args; + va_start(args, fmt); +#else +void +fatalf(va_alist) + va_dcl +{ + va_list args; + const char *fmt = NULL; + va_start(args); + fmt = va_arg(args, char *); +#endif + fatalvf(fmt, args); + va_end(args); +} + + +/* used by fatalf */ +static void +fatalvf(const char *fmt, va_list args) +{ + static char fatal_str[BUFSIZ]; + vsnprintf(fatal_str, sizeof(fatal_str), fmt, args); + fatal(fatal_str); +} + +/* fatal with dumping core */ +void +fatal_dump(const char *message) +{ + failure_notify = NULL; + releaseServerSockets(); + if (message) + fatal_common(message); + if (opt_catch_signals) + storeDirWriteCleanLogs(0); + abort(); +} + +void +debug_trap(const char *message) +{ + if (!opt_catch_signals) + fatal_dump(message); + _db_print("WARNING: %s\n", message); +} + +void +sig_child(int sig) +{ +#ifdef _SQUID_NEXT_ + union wait status; +#else + int status; +#endif + pid_t pid; + + do { +#ifdef _SQUID_NEXT_ + pid = wait3(&status, WNOHANG, NULL); +#else + pid = waitpid(-1, &status, WNOHANG); +#endif + /* no debug() here; bad things happen if the signal is delivered during _db_print() */ +#if HAVE_SIGACTION + } while (pid > 0); +#else + } while (pid > 0 || (pid < 0 && errno == EINTR)); + signal(sig, sig_child); +#endif +} + +const char * +getMyHostname(void) +{ + LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1); + static int present = 0; + const struct hostent *h = NULL; + struct in_addr sa; + if (Config.visibleHostname != NULL) + return Config.visibleHostname; + if (present) + return host; + host[0] = '\0'; + memcpy(&sa, &any_addr, sizeof(sa)); + if (Config.Sockaddr.http && sa.s_addr == any_addr.s_addr) + memcpy(&sa, &Config.Sockaddr.http->s.sin_addr, sizeof(sa)); +#if USE_SSL + if (Config.Sockaddr.https && sa.s_addr == any_addr.s_addr) + memcpy(&sa, &Config.Sockaddr.https->s.sin_addr, sizeof(sa)); +#endif + /* + * If the first http_port address has a specific address, try a + * reverse DNS lookup on it. + */ + if (sa.s_addr != any_addr.s_addr) { + h = gethostbyaddr((char *) &sa, + sizeof(sa), AF_INET); + if (h != NULL) { + /* DNS lookup successful */ + /* use the official name from DNS lookup */ + xstrncpy(host, h->h_name, SQUIDHOSTNAMELEN); + debug(50, 4) ("getMyHostname: resolved %s to '%s'\n", + inet_ntoa(sa), + host); + present = 1; + if (strchr(host, '.')) + return host; + + } + debug(50, 1) ("WARNING: failed to resolve %s to a fully qualified hostname\n", + inet_ntoa(sa)); + } + /* + * Get the host name and store it in host to return + */ + if (gethostname(host, SQUIDHOSTNAMELEN) < 0) { + debug(50, 1) ("WARNING: gethostname failed: %s\n", xstrerror()); + } else if ((h = gethostbyname(host)) == NULL) { + debug(50, 1) ("WARNING: gethostbyname failed for %s\n", host); + } else { + debug(50, 6) ("getMyHostname: '%s' resolved into '%s'\n", + host, h->h_name); + /* DNS lookup successful */ + /* use the official name from DNS lookup */ + xstrncpy(host, h->h_name, SQUIDHOSTNAMELEN); + present = 1; + if (strchr(host, '.')) + return host; + } + fatal("Could not determine fully qualified hostname. Please set 'visible_hostname'\n"); + return NULL; /* keep compiler happy */ +} + +const char * +uniqueHostname(void) +{ + return Config.uniqueHostname ? Config.uniqueHostname : getMyHostname(); +} + +void +safeunlink(const char *s, int quiet) +{ + statCounter.syscalls.disk.unlinks++; + if (unlink(s) < 0 && !quiet) + debug(50, 1) ("safeunlink: Couldn't delete %s: %s\n", s, xstrerror()); +} + +/* leave a privilegied section. (Give up any privilegies) + * Routines that need privilegies can rap themselves in enter_suid() + * and leave_suid() + * To give upp all posibilites to gain privilegies use no_suid() + */ +void +leave_suid(void) +{ + debug(21, 3) ("leave_suid: PID %d called\n", (int) getpid()); + if (geteuid() != 0) + return; + /* Started as a root, check suid option */ + if (Config.effectiveUser == NULL) + return; +#if HAVE_SETGROUPS + setgroups(1, &Config2.effectiveGroupID); +#endif + if (setgid(Config2.effectiveGroupID) < 0) + debug(50, 0) ("ALERT: setgid: %s\n", xstrerror()); + debug(21, 3) ("leave_suid: PID %d giving up root, becoming '%s'\n", + (int) getpid(), Config.effectiveUser); +#if HAVE_SETRESUID + if (setresuid(Config2.effectiveUserID, Config2.effectiveUserID, 0) < 0) + debug(50, 0) ("ALERT: setresuid: %s\n", xstrerror()); +#elif HAVE_SETEUID + if (seteuid(Config2.effectiveUserID) < 0) + debug(50, 0) ("ALERT: seteuid: %s\n", xstrerror()); +#else + if (setuid(Config2.effectiveUserID) < 0) + debug(50, 0) ("ALERT: setuid: %s\n", xstrerror()); +#endif +} + +/* Enter a privilegied section */ +void +enter_suid(void) +{ + debug(21, 3) ("enter_suid: PID %d taking root priveleges\n", (int) getpid()); +#if HAVE_SETRESUID + setresuid((uid_t)-1, 0, (uid_t)-1); +#else + setuid(0); +#endif +} + +/* Give up the posibility to gain privilegies. + * this should be used before starting a sub process + */ +void +no_suid(void) +{ + uid_t uid; + leave_suid(); + uid = geteuid(); + debug(21, 3) ("leave_suid: PID %d giving up root priveleges forever\n", (int) getpid()); +#if HAVE_SETRESUID + if (setresuid(uid, uid, uid) < 0) + debug(50, 1) ("no_suid: setresuid: %s\n", xstrerror()); +#else + setuid(0); + if (setuid(uid) < 0) + debug(50, 1) ("no_suid: setuid: %s\n", xstrerror()); +#endif +} + +void +writePidFile(void) +{ + int fd; + const char *f = NULL; + mode_t old_umask; + char buf[32]; + if ((f = Config.pidFilename) == NULL) + return; + if (!strcmp(Config.pidFilename, "none")) + return; + enter_suid(); + old_umask = umask(022); + fd = file_open(f, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT); + umask(old_umask); + leave_suid(); + if (fd < 0) { + debug(50, 0) ("%s: %s\n", f, xstrerror()); + debug_trap("Could not write pid file"); + return; + } + snprintf(buf, 32, "%d\n", (int) getpid()); + FD_WRITE_METHOD(fd, buf, strlen(buf)); + file_close(fd); +} + + +pid_t +readPidFile(void) +{ + FILE *pid_fp = NULL; + const char *f = Config.pidFilename; + pid_t pid = -1; + int i; + + if (f == NULL || !strcmp(Config.pidFilename, "none")) { + fprintf(stderr, "%s: ERROR: No pid file name defined\n", appname); + exit(1); + } + pid_fp = fopen(f, "r"); + if (pid_fp != NULL) { + pid = 0; + if (fscanf(pid_fp, "%d", &i) == 1) + pid = (pid_t) i; + fclose(pid_fp); + } else { + if (errno != ENOENT) { + fprintf(stderr, "%s: ERROR: Could not read pid file\n", appname); + fprintf(stderr, "\t%s: %s\n", f, xstrerror()); + exit(1); + } + } + return pid; +} + + +void +setMaxFD(void) +{ +#if HAVE_SETRLIMIT + /* try to use as many file descriptors as possible */ + /* System V uses RLIMIT_NOFILE and BSD uses RLIMIT_OFILE */ + struct rlimit rl; +#if defined(RLIMIT_NOFILE) + if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { + debug(50, 0) ("setrlimit: RLIMIT_NOFILE: %s\n", xstrerror()); + } else { + rl.rlim_cur = Squid_MaxFD; + if (rl.rlim_cur > rl.rlim_max) + Squid_MaxFD = rl.rlim_cur = rl.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { + snprintf(tmp_error_buf, ERROR_BUF_SZ, + "setrlimit: RLIMIT_NOFILE: %s", xstrerror()); + fatal_dump(tmp_error_buf); + } + } +#elif defined(RLIMIT_OFILE) + if (getrlimit(RLIMIT_OFILE, &rl) < 0) { + debug(50, 0) ("setrlimit: RLIMIT_NOFILE: %s\n", xstrerror()); + } else { + rl.rlim_cur = Squid_MaxFD; + if (rl.rlim_cur > rl.rlim_max) + Squid_MaxFD = rl.rlim_cur = rl.rlim_max; + if (setrlimit(RLIMIT_OFILE, &rl) < 0) { + snprintf(tmp_error_buf, ERROR_BUF_SZ, + "setrlimit: RLIMIT_OFILE: %s", xstrerror()); + fatal_dump(tmp_error_buf); + } + } +#endif +#else /* HAVE_SETRLIMIT */ + debug(21, 1) ("setMaxFD: Cannot increase: setrlimit() not supported on this system\n"); +#endif /* HAVE_SETRLIMIT */ + +#if HAVE_SETRLIMIT && defined(RLIMIT_DATA) + if (getrlimit(RLIMIT_DATA, &rl) < 0) { + debug(50, 0) ("getrlimit: RLIMIT_DATA: %s\n", xstrerror()); + } else if (rl.rlim_max > rl.rlim_cur) { + rl.rlim_cur = rl.rlim_max; /* set it to the max */ + if (setrlimit(RLIMIT_DATA, &rl) < 0) { + snprintf(tmp_error_buf, ERROR_BUF_SZ, + "setrlimit: RLIMIT_DATA: %s", xstrerror()); + fatal_dump(tmp_error_buf); + } + } +#endif /* RLIMIT_DATA */ +#if HAVE_SETRLIMIT && defined(RLIMIT_VMEM) + if (getrlimit(RLIMIT_VMEM, &rl) < 0) { + debug(50, 0) ("getrlimit: RLIMIT_VMEM: %s\n", xstrerror()); + } else if (rl.rlim_max > rl.rlim_cur) { + rl.rlim_cur = rl.rlim_max; /* set it to the max */ + if (setrlimit(RLIMIT_VMEM, &rl) < 0) { + snprintf(tmp_error_buf, ERROR_BUF_SZ, + "setrlimit: RLIMIT_VMEM: %s", xstrerror()); + fatal_dump(tmp_error_buf); + } + } +#endif /* RLIMIT_VMEM */ +} + +time_t +getCurrentTime(void) +{ +#if GETTIMEOFDAY_NO_TZP + gettimeofday(¤t_time); +#else + gettimeofday(¤t_time, NULL); +#endif + current_dtime = (double) current_time.tv_sec + + (double) current_time.tv_usec / 1000000.0; + return squid_curtime = current_time.tv_sec; +} + +int +percent(int a, int b) +{ + return b ? ((int) (100.0 * a / b + 0.5)) : 0; +} + +double +dpercent(double a, double b) +{ + return b ? (100.0 * a / b) : 0.0; +} + +void +squid_signal(int sig, SIGHDLR * func, int flags) +{ +#if HAVE_SIGACTION + struct sigaction sa; + sa.sa_handler = func; + sa.sa_flags = flags; + sigemptyset(&sa.sa_mask); + if (sigaction(sig, &sa, NULL) < 0) + debug(50, 0) ("sigaction: sig=%d func=%p: %s\n", sig, func, xstrerror()); +#else + signal(sig, func); +#endif +} + +struct in_addr +inaddrFromHostent(const struct hostent *hp) +{ + struct in_addr s; + xmemcpy(&s.s_addr, hp->h_addr, sizeof(s.s_addr)); + return s; +} + +double +doubleAverage(double cur, double newD, int N, int max) +{ + if (N > max) + N = max; + return (cur * (N - 1.0) + newD) / N; +} + +int +intAverage(int cur, int newI, int n, int max) +{ + if (n > max) + n = max; + return (cur * (n - 1) + newI) / n; +} + +void +logsFlush(void) +{ + if (debug_log) + fflush(debug_log); +} + +const char * +checkNullString(const char *p) +{ + return p ? p : "(NULL)"; +} + +dlink_node * +dlinkNodeNew() +{ + if (dlink_node_pool == NULL) + dlink_node_pool = memPoolCreate("Dlink list nodes", sizeof(dlink_node)); + /* where should we call memPoolDestroy(dlink_node_pool); */ + return (dlink_node *)memPoolAlloc(dlink_node_pool); +} + +/* the node needs to be unlinked FIRST */ +void +dlinkNodeDelete(dlink_node * m) +{ + if (m == NULL) + return; + memPoolFree(dlink_node_pool, m); +} + +void +dlinkAdd(void *data, dlink_node * m, dlink_list * list) +{ + m->data = data; + m->prev = NULL; + m->next = list->head; + if (list->head) + list->head->prev = m; + list->head = m; + if (list->tail == NULL) + list->tail = m; +} + +void +dlinkAddAfter(void *data, dlink_node * m, dlink_node * n, dlink_list * list) +{ + m->data = data; + m->prev = n; + m->next = n->next; + if (n->next) + n->next->prev = m; + else { + assert(list->tail == n); + list->tail = m; + } + n->next = m; +} + +void +dlinkAddTail(void *data, dlink_node * m, dlink_list * list) +{ + m->data = data; + m->next = NULL; + m->prev = list->tail; + if (list->tail) + list->tail->next = m; + list->tail = m; + if (list->head == NULL) + list->head = m; +} + +void +dlinkDelete(dlink_node * m, dlink_list * list) +{ + if (m->next) + m->next->prev = m->prev; + if (m->prev) + m->prev->next = m->next; + if (m == list->head) + list->head = m->next; + if (m == list->tail) + list->tail = m->prev; + m->next = m->prev = NULL; +} + +void +kb_incr(kb_t * k, size_t v) +{ + k->bytes += v; + k->kb += (k->bytes >> 10); + k->bytes &= 0x3FF; +} + +void +debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm) +{ + MemBuf mb; + Packer p; + assert(label && obj && pm); + memBufDefInit(&mb); + packerToMemInit(&p, &mb); + (*pm) (obj, &p); + debug(section, level) ("%s%s", label, mb.buf); + packerClean(&p); + memBufClean(&mb); +} + +int +stringHasWhitespace(const char *s) +{ + return strpbrk(s, w_space) != NULL; +} + +void +linklistPush(link_list ** L, void *p) +{ + link_list *l = (link_list *)memAllocate(MEM_LINK_LIST); + l->next = NULL; + l->ptr = p; + while (*L) + L = &(*L)->next; + *L = l; +} + +void * +linklistShift(link_list ** L) +{ + void *p; + link_list *l; + if (NULL == *L) + return NULL; + l = *L; + p = l->ptr; + *L = (*L)->next; + memFree(l, MEM_LINK_LIST); + return p; +} + +/* + * Same as rename(2) but complains if something goes wrong; + * the caller is responsible for handing and explaining the + * consequences of errors. + */ +int +xrename(const char *from, const char *to) +{ + debug(21, 2) ("xrename: renaming %s to %s\n", from, to); +#ifdef _SQUID_MSWIN_ + remove(to); +#endif + if (0 == rename(from, to)) + return 0; + debug(21, errno == ENOENT ? 2 : 1) ("xrename: Cannot rename %s to %s: %s\n", + from, to, xstrerror()); + return -1; +} + +int +stringHasCntl(const char *s) +{ + unsigned char c; + while ((c = (unsigned char) *s++) != '\0') { + if (c <= 0x1f) + return 1; + if (c >= 0x7f && c <= 0x9f) + return 1; + } + return 0; +} + +/* + * isPowTen returns true if its argument is an integer power of + * 10. Its used for logging of certain error messages that can + * occur often, but that we don't want to fill cache.log with. + */ +int +isPowTen(int count) +{ + double x = log(count) / log(10.0); + if (0.0 != x - (double) (int) x) + return 0; + return 1; +} + +void +parseEtcHosts(void) +{ + FILE *fp; + char buf[1024]; + char buf2[512]; + char *nt = buf; + char *lt = buf; + + if (NULL == Config.etcHostsPath) + return; + if (0 == strcmp(Config.etcHostsPath, "none")) + return; + fp = fopen(Config.etcHostsPath, "r"); + if (fp == NULL) { + debug(1, 1) ("parseEtcHosts: %s: %s\n", + Config.etcHostsPath, xstrerror()); + return; + } +#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_) + setmode(fileno(fp), O_TEXT); +#endif + while (fgets(buf, 1024, fp)) { /* for each line */ + wordlist *hosts = NULL; + char *addr; + if (buf[0] == '#') /* MS-windows likes to add comments */ + continue; + lt = buf; + addr = buf; + debug(1, 5) ("etc_hosts: line is '%s'\n", buf); + nt = strpbrk(lt, w_space); + if (nt == NULL) /* empty line */ + continue; + *nt = '\0'; /* null-terminate the address */ + debug(1, 5) ("etc_hosts: address is '%s'\n", addr); + lt = nt + 1; + while ((nt = strpbrk(lt, w_space))) { + char *host = NULL; + if (nt == lt) { /* multiple spaces */ + debug(1, 5) ("etc_hosts: multiple spaces, skipping\n"); + lt = nt + 1; + continue; + } + *nt = '\0'; + debug(1, 5) ("etc_hosts: got hostname '%s'\n", lt); + if (Config.appendDomain && !strchr(lt, '.')) { + /* I know it's ugly, but it's only at reconfig */ + strncpy(buf2, lt, 512); + strncat(buf2, Config.appendDomain, 512 - strlen(lt)); + host = buf2; + } else { + host = lt; + } + if (ipcacheAddEntryFromHosts(host, addr) != 0) + goto skip; /* invalid address, continuing is useless */ + wordlistAdd(&hosts, host); + lt = nt + 1; + } + fqdncacheAddEntryFromHosts(addr, hosts); + skip: + wordlistDestroy(&hosts); + } +} + +int +getMyPort(void) +{ + if (Config.Sockaddr.http) + return ntohs(Config.Sockaddr.http->s.sin_port); +#if USE_SSL + if (Config.Sockaddr.https) + return ntohs(Config.Sockaddr.https->s.sin_port); +#endif + fatal("No port defined"); + return 0; /* NOT REACHED */ +} + +/* + * Similar to strtok, but has some rudimentary knowledge + * of quoting + */ +char * +strwordtok(char *buf, char **t) +{ + unsigned char *word = NULL; + unsigned char *p = (unsigned char *) buf; + unsigned char *d; + unsigned char ch; + int quoted = 0; + if (!p) + p = (unsigned char *) *t; + if (!p) + goto error; + while (*p && isspace(*p)) + p++; + if (!*p) + goto error; + word = d = p; + while ((ch = *p)) { + switch (ch) { + case '\\': + p++; + *d++ = ch = *p; + if (ch) + p++; + break; + case '"': + quoted = !quoted; + p++; + default: + if (!quoted && isspace(*p)) { + p++; + goto done; + } + *d++ = *p++; + break; + } + } + done: + *d++ = '\0'; + error: + *t = (char *) p; + return (char *) word; +} + +/* + * Inverse of strwordtok. Quotes a word if needed + */ +void +strwordquote(MemBuf * mb, const char *str) +{ + int quoted = 0; + if (strchr(str, ' ')) { + quoted = 1; + memBufAppend(mb, "\"", 1); + } + while (*str) { + int l = strcspn(str, "\"\\"); + memBufAppend(mb, str, l); + str += l; + while (*str == '"' || *str == '\\') { + memBufAppend(mb, "\\", 1); + memBufAppend(mb, str, 1); + str++; + } + } + if (quoted) + memBufAppend(mb, "\"", 1); +} --- squid/src/url.c Wed Feb 14 01:07:38 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,637 +0,0 @@ - -/* - * $Id: url.c,v 1.11 2002/09/12 20:56:25 squidadm Exp $ - * - * DEBUG: section 23 URL Parsing - * AUTHOR: Duane Wessels - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid.h" - -const char *RequestMethodStr[] = -{ - "NONE", - "GET", - "POST", - "PUT", - "HEAD", - "CONNECT", - "TRACE", - "PURGE", - "OPTIONS", - "DELETE", - "PROPFIND", - "PROPPATCH", - "MKCOL", - "COPY", - "MOVE", - "LOCK", - "UNLOCK", - "BMOVE", - "BDELETE", - "BPROPFIND", - "BPROPPATCH", - "BCOPY", - "SEARCH", - "SUBSCRIBE", - "UNSUBSCRIBE", - "POLL", - "%EXT00", - "%EXT01", - "%EXT02", - "%EXT03", - "%EXT04", - "%EXT05", - "%EXT06", - "%EXT07", - "%EXT08", - "%EXT09", - "%EXT10", - "%EXT11", - "%EXT12", - "%EXT13", - "%EXT14", - "%EXT15", - "%EXT16", - "%EXT17", - "%EXT18", - "%EXT19", - "ERROR" -}; - -const char *ProtocolStr[] = -{ - "NONE", - "http", - "ftp", - "gopher", - "wais", - "cache_object", - "icp", -#if USE_HTCP - "htcp", -#endif - "urn", - "whois", - "internal", - "https", - "TOTAL" -}; - -static request_t *urnParse(method_t method, char *urn); -static const char *const valid_hostname_chars = -#if ALLOW_HOSTNAME_UNDERSCORES -"ABCDEFGHIJKLMNOPQRSTUVWXYZ" -"abcdefghijklmnopqrstuvwxyz" -"0123456789-._"; -#else -"ABCDEFGHIJKLMNOPQRSTUVWXYZ" -"abcdefghijklmnopqrstuvwxyz" -"0123456789-."; -#endif - -/* convert %xx in url string to a character - * Allocate a new string and return a pointer to converted string */ - -char * -url_convert_hex(char *org_url, int allocate) -{ - static char code[] = "00"; - char *url = NULL; - char *s = NULL; - char *t = NULL; - url = allocate ? (char *) xstrdup(org_url) : org_url; - if ((int) strlen(url) < 3 || !strchr(url, '%')) - return url; - for (s = t = url; *s; s++) { - if (*s == '%' && *(s + 1) && *(s + 2)) { - code[0] = *(++s); - code[1] = *(++s); - *t++ = (char) strtol(code, NULL, 16); - } else { - *t++ = *s; - } - } - do { - *t++ = *s; - } while (*s++); - return url; -} - -void -urlInitialize(void) -{ - debug(23, 5) ("urlInitialize: Initializing...\n"); - assert(sizeof(ProtocolStr) == (PROTO_MAX + 1) * sizeof(char *)); - memset(&null_request_flags, '\0', sizeof(null_request_flags)); - /* - * These test that our matchDomainName() function works the - * way we expect it to. - */ - assert(0 == matchDomainName("foo.com", "foo.com")); - assert(0 == matchDomainName(".foo.com", "foo.com")); - assert(0 == matchDomainName("foo.com", ".foo.com")); - assert(0 == matchDomainName(".foo.com", ".foo.com")); - assert(0 == matchDomainName("x.foo.com", ".foo.com")); - assert(0 != matchDomainName("x.foo.com", "foo.com")); - assert(0 != matchDomainName("foo.com", "x.foo.com")); - assert(0 != matchDomainName("bar.com", "foo.com")); - assert(0 != matchDomainName(".bar.com", "foo.com")); - assert(0 != matchDomainName(".bar.com", ".foo.com")); - assert(0 != matchDomainName("bar.com", ".foo.com")); - assert(0 < matchDomainName("zzz.com", "foo.com")); - assert(0 > matchDomainName("aaa.com", "foo.com")); - assert(0 == matchDomainName("FOO.com", "foo.COM")); - assert(0 < matchDomainName("bfoo.com", "afoo.com")); - assert(0 > matchDomainName("afoo.com", "bfoo.com")); - assert(0 < matchDomainName("x-foo.com", ".foo.com")); - /* more cases? */ -} - -method_t -urlParseMethod(const char *s) -{ - method_t method = METHOD_NONE; - /* - * This check for '%' makes sure that we don't - * match one of the extension method placeholders, - * which have the form %EXT[0-9][0-9] - */ - if (*s == '%') - return METHOD_NONE; - for (method++; method < METHOD_ENUM_END; method++) { - if (0 == strcasecmp(s, RequestMethodStr[method])) - return method; - } - return METHOD_NONE; -} - - -protocol_t -urlParseProtocol(const char *s) -{ - /* test common stuff first */ - if (strcasecmp(s, "http") == 0) - return PROTO_HTTP; - if (strcasecmp(s, "ftp") == 0) - return PROTO_FTP; - if (strcasecmp(s, "https") == 0) - return PROTO_HTTPS; - if (strcasecmp(s, "file") == 0) - return PROTO_FTP; - if (strcasecmp(s, "gopher") == 0) - return PROTO_GOPHER; - if (strcasecmp(s, "wais") == 0) - return PROTO_WAIS; - if (strcasecmp(s, "cache_object") == 0) - return PROTO_CACHEOBJ; - if (strcasecmp(s, "urn") == 0) - return PROTO_URN; - if (strcasecmp(s, "whois") == 0) - return PROTO_WHOIS; - if (strcasecmp(s, "internal") == 0) - return PROTO_INTERNAL; - return PROTO_NONE; -} - - -int -urlDefaultPort(protocol_t p) -{ - switch (p) { - case PROTO_HTTP: - return 80; - case PROTO_HTTPS: - return 443; - case PROTO_FTP: - return 21; - case PROTO_GOPHER: - return 70; - case PROTO_WAIS: - return 210; - case PROTO_CACHEOBJ: - case PROTO_INTERNAL: - return CACHE_HTTP_PORT; - case PROTO_WHOIS: - return 43; - default: - return 0; - } -} - -request_t * -urlParse(method_t method, char *url) -{ - LOCAL_ARRAY(char, proto, MAX_URL); - LOCAL_ARRAY(char, login, MAX_URL); - LOCAL_ARRAY(char, host, MAX_URL); - LOCAL_ARRAY(char, urlpath, MAX_URL); - request_t *request = NULL; - char *t = NULL; - char *q = NULL; - int port; - protocol_t protocol = PROTO_NONE; - int l; - proto[0] = host[0] = urlpath[0] = login[0] = '\0'; - - if ((l = strlen(url)) + Config.appendDomainLen > (MAX_URL - 1)) { - /* terminate so it doesn't overflow other buffers */ - *(url + (MAX_URL >> 1)) = '\0'; - debug(23, 1) ("urlParse: URL too large (%d bytes)\n", l); - return NULL; - } - if (method == METHOD_CONNECT) { - port = CONNECT_PORT; - if (sscanf(url, "%[^:]:%d", host, &port) < 1) - return NULL; - } else if (!strncmp(url, "urn:", 4)) { - return urnParse(method, url); - } else { - if (sscanf(url, "%[^:]://%[^/]%[^\r\n]", proto, host, urlpath) < 2) - return NULL; - protocol = urlParseProtocol(proto); - port = urlDefaultPort(protocol); - /* Is there any login informaiton? */ - if ((t = strrchr(host, '@'))) { - strcpy((char *) login, (char *) host); - t = strrchr(login, '@'); - *t = 0; - strcpy((char *) host, t + 1); - } - if ((t = strrchr(host, ':'))) { - *t++ = '\0'; - if (*t != '\0') - port = atoi(t); - } - } - for (t = host; *t; t++) - *t = xtolower(*t); - if (stringHasWhitespace(host)) { - if (URI_WHITESPACE_STRIP == Config.uri_whitespace) { - t = q = host; - while (*t) { - if (!xisspace(*t)) - *q++ = *t; - t++; - } - *q = '\0'; - } - } - if (Config.onoff.check_hostnames && strspn(host, valid_hostname_chars) != strlen(host)) { - debug(23, 1) ("urlParse: Illegal character in hostname '%s'\n", host); - return NULL; - } - /* remove trailing dots from hostnames */ - while ((l = strlen(host)) > 0 && host[--l] == '.') - host[l] = '\0'; - /* remove duplicate dots */ - while ((t = strstr(host, ".."))) - xmemmove(t, t + 1, strlen(t)); - if (Config.appendDomain && !strchr(host, '.')) - strncat(host, Config.appendDomain, SQUIDHOSTNAMELEN); - if (port < 1 || port > 65535) { - debug(23, 3) ("urlParse: Invalid port '%d'\n", port); - return NULL; - } -#ifdef HARDCODE_DENY_PORTS - /* These ports are filtered in the default squid.conf, but - * maybe someone wants them hardcoded... */ - if (port == 7 || port == 9 || port == 19) { - debug(23, 0) ("urlParse: Deny access to port %d\n", port); - return NULL; - } -#endif - if (stringHasWhitespace(urlpath)) { - debug(23, 2) ("urlParse: URI has whitespace: {%s}\n", url); - switch (Config.uri_whitespace) { - case URI_WHITESPACE_DENY: - return NULL; - case URI_WHITESPACE_ALLOW: - break; - case URI_WHITESPACE_ENCODE: - t = rfc1738_escape_unescaped(urlpath); - xstrncpy(urlpath, t, MAX_URL); - break; - case URI_WHITESPACE_CHOP: - *(urlpath + strcspn(urlpath, w_space)) = '\0'; - break; - case URI_WHITESPACE_STRIP: - default: - t = q = urlpath; - while (*t) { - if (!xisspace(*t)) - *q++ = *t; - t++; - } - *q = '\0'; - } - } - request = requestCreate(method, protocol, urlpath); - xstrncpy(request->host, host, SQUIDHOSTNAMELEN); - xstrncpy(request->login, login, MAX_LOGIN_SZ); - request->port = (u_short) port; - return request; -} - -static request_t * -urnParse(method_t method, char *urn) -{ - debug(50, 5) ("urnParse: %s\n", urn); - return requestCreate(method, PROTO_URN, urn + 4); -} - -const char * -urlCanonical(request_t * request) -{ - LOCAL_ARRAY(char, portbuf, 32); - LOCAL_ARRAY(char, urlbuf, MAX_URL); - if (request->canonical) - return request->canonical; - if (request->protocol == PROTO_URN) { - snprintf(urlbuf, MAX_URL, "urn:%s", strBuf(request->urlpath)); - } else { - switch (request->method) { - case METHOD_CONNECT: - snprintf(urlbuf, MAX_URL, "%s:%d", request->host, request->port); - break; - default: - portbuf[0] = '\0'; - if (request->port != urlDefaultPort(request->protocol)) - snprintf(portbuf, 32, ":%d", request->port); - snprintf(urlbuf, MAX_URL, "%s://%s%s%s%s%s", - ProtocolStr[request->protocol], - request->login, - *request->login ? "@" : null_string, - request->host, - portbuf, - strBuf(request->urlpath)); - break; - } - } - return (request->canonical = xstrdup(urlbuf)); -} - -char * -urlCanonicalClean(const request_t * request) -{ - LOCAL_ARRAY(char, buf, MAX_URL); - LOCAL_ARRAY(char, portbuf, 32); - LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ + 1); - char *t; - if (request->protocol == PROTO_URN) { - snprintf(buf, MAX_URL, "urn:%s", strBuf(request->urlpath)); - } else { - switch (request->method) { - case METHOD_CONNECT: - snprintf(buf, MAX_URL, "%s:%d", request->host, request->port); - break; - default: - portbuf[0] = '\0'; - if (request->port != urlDefaultPort(request->protocol)) - snprintf(portbuf, 32, ":%d", request->port); - loginbuf[0] = '\0'; - if ((int) strlen(request->login) > 0) { - strcpy(loginbuf, request->login); - if ((t = strchr(loginbuf, ':'))) - *t = '\0'; - strcat(loginbuf, "@"); - } - snprintf(buf, MAX_URL, "%s://%s%s%s%s", - ProtocolStr[request->protocol], - loginbuf, - request->host, - portbuf, - strBuf(request->urlpath)); - /* - * strip arguments AFTER a question-mark - */ - if (Config.onoff.strip_query_terms) - if ((t = strchr(buf, '?'))) - *(++t) = '\0'; - break; - } - } - if (stringHasCntl(buf)) - xstrncpy(buf, rfc1738_escape_unescaped(buf), MAX_URL); - return buf; -} - -/* - * matchDomainName() compares a hostname with a domainname according - * to the following rules: - * - * HOST DOMAIN MATCH? - * ------------- ------------- ------ - * foo.com foo.com YES - * .foo.com foo.com YES - * x.foo.com foo.com NO - * foo.com .foo.com YES - * .foo.com .foo.com YES - * x.foo.com .foo.com YES - * - * We strip leading dots on hosts (but not domains!) so that - * ".foo.com" is is always the same as "foo.com". - * - * Return values: - * 0 means the host matches the domain - * 1 means the host is greater than the domain - * -1 means the host is less than the domain - */ - -int -matchDomainName(const char *h, const char *d) -{ - int dl; - int hl; - while ('.' == *h) - h++; - hl = strlen(h); - dl = strlen(d); - /* - * Start at the ends of the two strings and work towards the - * beginning. - */ - while (xtolower(h[--hl]) == xtolower(d[--dl])) { - if (hl == 0 && dl == 0) { - /* - * We made it all the way to the beginning of both - * strings without finding any difference. - */ - return 0; - } - if (0 == hl) { - /* - * The host string is shorter than the domain string. - * There is only one case when this can be a match. - * If the domain is just one character longer, and if - * that character is a leading '.' then we call it a - * match. - */ - if (1 == dl && '.' == d[0]) - return 0; - else - return -1; - } - if (0 == dl) { - /* - * The domain string is shorter than the host string. - * This is a match only if the first domain character - * is a leading '.'. - */ - if ('.' == d[0]) - return 0; - else - return 1; - } - } - /* - * We found different characters in the same position (from the end). - */ - /* - * If one of those character is '.' then its special. In order - * for splay tree sorting to work properly, "x-foo.com" must - * be greater than ".foo.com" even though '-' is less than '.'. - */ - if ('.' == d[dl]) - return 1; - if ('.' == h[hl]) - return -1; - return (xtolower(h[hl]) - xtolower(d[dl])); -} - -int -urlCheckRequest(const request_t * r) -{ - int rc = 0; - /* protocol "independent" methods */ - if (r->method == METHOD_CONNECT) - return 1; - if (r->method == METHOD_TRACE) - return 1; - if (r->method == METHOD_PURGE) - return 1; - /* does method match the protocol? */ - switch (r->protocol) { - case PROTO_URN: - case PROTO_HTTP: - case PROTO_CACHEOBJ: - rc = 1; - break; - case PROTO_FTP: - if (r->method == METHOD_PUT) - rc = 1; - case PROTO_GOPHER: - case PROTO_WAIS: - case PROTO_WHOIS: - if (r->method == METHOD_GET) - rc = 1; - else if (r->method == METHOD_HEAD) - rc = 1; - break; - case PROTO_HTTPS: -#ifdef USE_SSL - rc = 1; - break; -#else - /* - * Squid can't originate an SSL connection, so it should - * never receive an "https:" URL. It should always be - * CONNECT instead. - */ - rc = 0; -#endif - default: - break; - } - return rc; -} - -/* - * Quick-n-dirty host extraction from a URL. Steps: - * Look for a colon - * Skip any '/' after the colon - * Copy the next SQUID_MAXHOSTNAMELEN bytes to host[] - * Look for an ending '/' or ':' and terminate - * Look for login info preceeded by '@' - */ -char * -urlHostname(const char *url) -{ - LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN); - char *t; - host[0] = '\0'; - if (NULL == (t = strchr(url, ':'))) - return NULL; - t++; - while (*t != '\0' && *t == '/') - t++; - xstrncpy(host, t, SQUIDHOSTNAMELEN); - if ((t = strchr(host, '/'))) - *t = '\0'; - if ((t = strchr(host, ':'))) - *t = '\0'; - if ((t = strrchr(host, '@'))) { - t++; - xmemmove(host, t, strlen(t) + 1); - } - return host; -} - -static void -urlExtMethodAdd(const char *mstr) -{ - method_t method = 0; - for (method++; method < METHOD_ENUM_END; method++) { - if (0 == strcmp(mstr, RequestMethodStr[method])) { - debug(23, 2) ("Extension method '%s' already exists\n", mstr); - return; - } - if (0 != strncmp("%EXT", RequestMethodStr[method], 4)) - continue; - /* Don't free statically allocated "%EXTnn" string */ - RequestMethodStr[method] = xstrdup(mstr); - debug(23, 1) ("Extension method '%s' added, enum=%d\n", mstr, (int) method); - return; - } - debug(23, 1) ("WARNING: Could not add new extension method '%s' due to lack of array space\n", mstr); -} - -void -urlExtMethodConfigure(void) -{ - wordlist *w = Config.ext_methods; - while (w) { - char *s; - for (s = w->key; *s; s++) - *s = xtoupper(*s); - urlExtMethodAdd(w->key); - w = w->next; - } -} --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/url.cc Wed Feb 14 01:07:38 2007 @@ -0,0 +1,644 @@ + +/* + * $Id: url.cc,v 1.1.2.1 2002/10/09 14:36:48 rbcollins Exp $ + * + * DEBUG: section 23 URL Parsing + * AUTHOR: Duane Wessels + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + +const char *RequestMethodStr[] = +{ + "NONE", + "GET", + "POST", + "PUT", + "HEAD", + "CONNECT", + "TRACE", + "PURGE", + "OPTIONS", + "DELETE", + "PROPFIND", + "PROPPATCH", + "MKCOL", + "COPY", + "MOVE", + "LOCK", + "UNLOCK", + "BMOVE", + "BDELETE", + "BPROPFIND", + "BPROPPATCH", + "BCOPY", + "SEARCH", + "SUBSCRIBE", + "UNSUBSCRIBE", + "POLL", + "%EXT00", + "%EXT01", + "%EXT02", + "%EXT03", + "%EXT04", + "%EXT05", + "%EXT06", + "%EXT07", + "%EXT08", + "%EXT09", + "%EXT10", + "%EXT11", + "%EXT12", + "%EXT13", + "%EXT14", + "%EXT15", + "%EXT16", + "%EXT17", + "%EXT18", + "%EXT19", + "ERROR" +}; + +const char *ProtocolStr[] = +{ + "NONE", + "http", + "ftp", + "gopher", + "wais", + "cache_object", + "icp", +#if USE_HTCP + "htcp", +#endif + "urn", + "whois", + "internal", + "https", + "TOTAL" +}; + +static request_t *urnParse(method_t method, char *urn); +static const char *const valid_hostname_chars = +#if ALLOW_HOSTNAME_UNDERSCORES +"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +"abcdefghijklmnopqrstuvwxyz" +"0123456789-._"; +#else +"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +"abcdefghijklmnopqrstuvwxyz" +"0123456789-."; +#endif + +/* convert %xx in url string to a character + * Allocate a new string and return a pointer to converted string */ + +char * +url_convert_hex(char *org_url, int allocate) +{ + static char code[] = "00"; + char *url = NULL; + char *s = NULL; + char *t = NULL; + url = allocate ? (char *) xstrdup(org_url) : org_url; + if ((int) strlen(url) < 3 || !strchr(url, '%')) + return url; + for (s = t = url; *s; s++) { + if (*s == '%' && *(s + 1) && *(s + 2)) { + code[0] = *(++s); + code[1] = *(++s); + *t++ = (char) strtol(code, NULL, 16); + } else { + *t++ = *s; + } + } + do { + *t++ = *s; + } while (*s++); + return url; +} + +void +urlInitialize(void) +{ + debug(23, 5) ("urlInitialize: Initializing...\n"); + assert(sizeof(ProtocolStr) == (PROTO_MAX + 1) * sizeof(char *)); + memset(&null_request_flags, '\0', sizeof(null_request_flags)); + /* + * These test that our matchDomainName() function works the + * way we expect it to. + */ + assert(0 == matchDomainName("foo.com", "foo.com")); + assert(0 == matchDomainName(".foo.com", "foo.com")); + assert(0 == matchDomainName("foo.com", ".foo.com")); + assert(0 == matchDomainName(".foo.com", ".foo.com")); + assert(0 == matchDomainName("x.foo.com", ".foo.com")); + assert(0 != matchDomainName("x.foo.com", "foo.com")); + assert(0 != matchDomainName("foo.com", "x.foo.com")); + assert(0 != matchDomainName("bar.com", "foo.com")); + assert(0 != matchDomainName(".bar.com", "foo.com")); + assert(0 != matchDomainName(".bar.com", ".foo.com")); + assert(0 != matchDomainName("bar.com", ".foo.com")); + assert(0 < matchDomainName("zzz.com", "foo.com")); + assert(0 > matchDomainName("aaa.com", "foo.com")); + assert(0 == matchDomainName("FOO.com", "foo.COM")); + assert(0 < matchDomainName("bfoo.com", "afoo.com")); + assert(0 > matchDomainName("afoo.com", "bfoo.com")); + assert(0 < matchDomainName("x-foo.com", ".foo.com")); + /* more cases? */ +} + +method_t &operator++ (method_t &aMethod) +{ + aMethod = (method_t)(++(int)aMethod); + return aMethod; +} + + +method_t +urlParseMethod(const char *s) +{ + method_t method = METHOD_NONE; + /* + * This check for '%' makes sure that we don't + * match one of the extension method placeholders, + * which have the form %EXT[0-9][0-9] + */ + if (*s == '%') + return METHOD_NONE; + for (++method; method < METHOD_ENUM_END; ++method) { + if (0 == strcasecmp(s, RequestMethodStr[method])) + return method; + } + return METHOD_NONE; +} + + +protocol_t +urlParseProtocol(const char *s) +{ + /* test common stuff first */ + if (strcasecmp(s, "http") == 0) + return PROTO_HTTP; + if (strcasecmp(s, "ftp") == 0) + return PROTO_FTP; + if (strcasecmp(s, "https") == 0) + return PROTO_HTTPS; + if (strcasecmp(s, "file") == 0) + return PROTO_FTP; + if (strcasecmp(s, "gopher") == 0) + return PROTO_GOPHER; + if (strcasecmp(s, "wais") == 0) + return PROTO_WAIS; + if (strcasecmp(s, "cache_object") == 0) + return PROTO_CACHEOBJ; + if (strcasecmp(s, "urn") == 0) + return PROTO_URN; + if (strcasecmp(s, "whois") == 0) + return PROTO_WHOIS; + if (strcasecmp(s, "internal") == 0) + return PROTO_INTERNAL; + return PROTO_NONE; +} + + +int +urlDefaultPort(protocol_t p) +{ + switch (p) { + case PROTO_HTTP: + return 80; + case PROTO_HTTPS: + return 443; + case PROTO_FTP: + return 21; + case PROTO_GOPHER: + return 70; + case PROTO_WAIS: + return 210; + case PROTO_CACHEOBJ: + case PROTO_INTERNAL: + return CACHE_HTTP_PORT; + case PROTO_WHOIS: + return 43; + default: + return 0; + } +} + +request_t * +urlParse(method_t method, char *url) +{ + LOCAL_ARRAY(char, proto, MAX_URL); + LOCAL_ARRAY(char, login, MAX_URL); + LOCAL_ARRAY(char, host, MAX_URL); + LOCAL_ARRAY(char, urlpath, MAX_URL); + request_t *request = NULL; + char *t = NULL; + char *q = NULL; + int port; + protocol_t protocol = PROTO_NONE; + int l; + proto[0] = host[0] = urlpath[0] = login[0] = '\0'; + + if ((l = strlen(url)) + Config.appendDomainLen > (MAX_URL - 1)) { + /* terminate so it doesn't overflow other buffers */ + *(url + (MAX_URL >> 1)) = '\0'; + debug(23, 1) ("urlParse: URL too large (%d bytes)\n", l); + return NULL; + } + if (method == METHOD_CONNECT) { + port = CONNECT_PORT; + if (sscanf(url, "%[^:]:%d", host, &port) < 1) + return NULL; + } else if (!strncmp(url, "urn:", 4)) { + return urnParse(method, url); + } else { + if (sscanf(url, "%[^:]://%[^/]%[^\r\n]", proto, host, urlpath) < 2) + return NULL; + protocol = urlParseProtocol(proto); + port = urlDefaultPort(protocol); + /* Is there any login informaiton? */ + if ((t = strrchr(host, '@'))) { + strcpy((char *) login, (char *) host); + t = strrchr(login, '@'); + *t = 0; + strcpy((char *) host, t + 1); + } + if ((t = strrchr(host, ':'))) { + *t++ = '\0'; + if (*t != '\0') + port = atoi(t); + } + } + for (t = host; *t; t++) + *t = xtolower(*t); + if (stringHasWhitespace(host)) { + if (URI_WHITESPACE_STRIP == Config.uri_whitespace) { + t = q = host; + while (*t) { + if (!xisspace(*t)) + *q++ = *t; + t++; + } + *q = '\0'; + } + } + if (Config.onoff.check_hostnames && strspn(host, valid_hostname_chars) != strlen(host)) { + debug(23, 1) ("urlParse: Illegal character in hostname '%s'\n", host); + return NULL; + } + /* remove trailing dots from hostnames */ + while ((l = strlen(host)) > 0 && host[--l] == '.') + host[l] = '\0'; + /* remove duplicate dots */ + while ((t = strstr(host, ".."))) + xmemmove(t, t + 1, strlen(t)); + if (Config.appendDomain && !strchr(host, '.')) + strncat(host, Config.appendDomain, SQUIDHOSTNAMELEN); + if (port < 1 || port > 65535) { + debug(23, 3) ("urlParse: Invalid port '%d'\n", port); + return NULL; + } +#ifdef HARDCODE_DENY_PORTS + /* These ports are filtered in the default squid.conf, but + * maybe someone wants them hardcoded... */ + if (port == 7 || port == 9 || port == 19) { + debug(23, 0) ("urlParse: Deny access to port %d\n", port); + return NULL; + } +#endif + if (stringHasWhitespace(urlpath)) { + debug(23, 2) ("urlParse: URI has whitespace: {%s}\n", url); + switch (Config.uri_whitespace) { + case URI_WHITESPACE_DENY: + return NULL; + case URI_WHITESPACE_ALLOW: + break; + case URI_WHITESPACE_ENCODE: + t = rfc1738_escape_unescaped(urlpath); + xstrncpy(urlpath, t, MAX_URL); + break; + case URI_WHITESPACE_CHOP: + *(urlpath + strcspn(urlpath, w_space)) = '\0'; + break; + case URI_WHITESPACE_STRIP: + default: + t = q = urlpath; + while (*t) { + if (!xisspace(*t)) + *q++ = *t; + t++; + } + *q = '\0'; + } + } + request = requestCreate(method, protocol, urlpath); + xstrncpy(request->host, host, SQUIDHOSTNAMELEN); + xstrncpy(request->login, login, MAX_LOGIN_SZ); + request->port = (u_short) port; + return request; +} + +static request_t * +urnParse(method_t method, char *urn) +{ + debug(50, 5) ("urnParse: %s\n", urn); + return requestCreate(method, PROTO_URN, urn + 4); +} + +const char * +urlCanonical(request_t * request) +{ + LOCAL_ARRAY(char, portbuf, 32); + LOCAL_ARRAY(char, urlbuf, MAX_URL); + if (request->canonical) + return request->canonical; + if (request->protocol == PROTO_URN) { + snprintf(urlbuf, MAX_URL, "urn:%s", strBuf(request->urlpath)); + } else { + switch (request->method) { + case METHOD_CONNECT: + snprintf(urlbuf, MAX_URL, "%s:%d", request->host, request->port); + break; + default: + portbuf[0] = '\0'; + if (request->port != urlDefaultPort(request->protocol)) + snprintf(portbuf, 32, ":%d", request->port); + snprintf(urlbuf, MAX_URL, "%s://%s%s%s%s%s", + ProtocolStr[request->protocol], + request->login, + *request->login ? "@" : null_string, + request->host, + portbuf, + strBuf(request->urlpath)); + break; + } + } + return (request->canonical = xstrdup(urlbuf)); +} + +char * +urlCanonicalClean(const request_t * request) +{ + LOCAL_ARRAY(char, buf, MAX_URL); + LOCAL_ARRAY(char, portbuf, 32); + LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ + 1); + char *t; + if (request->protocol == PROTO_URN) { + snprintf(buf, MAX_URL, "urn:%s", strBuf(request->urlpath)); + } else { + switch (request->method) { + case METHOD_CONNECT: + snprintf(buf, MAX_URL, "%s:%d", request->host, request->port); + break; + default: + portbuf[0] = '\0'; + if (request->port != urlDefaultPort(request->protocol)) + snprintf(portbuf, 32, ":%d", request->port); + loginbuf[0] = '\0'; + if ((int) strlen(request->login) > 0) { + strcpy(loginbuf, request->login); + if ((t = strchr(loginbuf, ':'))) + *t = '\0'; + strcat(loginbuf, "@"); + } + snprintf(buf, MAX_URL, "%s://%s%s%s%s", + ProtocolStr[request->protocol], + loginbuf, + request->host, + portbuf, + strBuf(request->urlpath)); + /* + * strip arguments AFTER a question-mark + */ + if (Config.onoff.strip_query_terms) + if ((t = strchr(buf, '?'))) + *(++t) = '\0'; + break; + } + } + if (stringHasCntl(buf)) + xstrncpy(buf, rfc1738_escape_unescaped(buf), MAX_URL); + return buf; +} + +/* + * matchDomainName() compares a hostname with a domainname according + * to the following rules: + * + * HOST DOMAIN MATCH? + * ------------- ------------- ------ + * foo.com foo.com YES + * .foo.com foo.com YES + * x.foo.com foo.com NO + * foo.com .foo.com YES + * .foo.com .foo.com YES + * x.foo.com .foo.com YES + * + * We strip leading dots on hosts (but not domains!) so that + * ".foo.com" is is always the same as "foo.com". + * + * Return values: + * 0 means the host matches the domain + * 1 means the host is greater than the domain + * -1 means the host is less than the domain + */ + +int +matchDomainName(const char *h, const char *d) +{ + int dl; + int hl; + while ('.' == *h) + h++; + hl = strlen(h); + dl = strlen(d); + /* + * Start at the ends of the two strings and work towards the + * beginning. + */ + while (xtolower(h[--hl]) == xtolower(d[--dl])) { + if (hl == 0 && dl == 0) { + /* + * We made it all the way to the beginning of both + * strings without finding any difference. + */ + return 0; + } + if (0 == hl) { + /* + * The host string is shorter than the domain string. + * There is only one case when this can be a match. + * If the domain is just one character longer, and if + * that character is a leading '.' then we call it a + * match. + */ + if (1 == dl && '.' == d[0]) + return 0; + else + return -1; + } + if (0 == dl) { + /* + * The domain string is shorter than the host string. + * This is a match only if the first domain character + * is a leading '.'. + */ + if ('.' == d[0]) + return 0; + else + return 1; + } + } + /* + * We found different characters in the same position (from the end). + */ + /* + * If one of those character is '.' then its special. In order + * for splay tree sorting to work properly, "x-foo.com" must + * be greater than ".foo.com" even though '-' is less than '.'. + */ + if ('.' == d[dl]) + return 1; + if ('.' == h[hl]) + return -1; + return (xtolower(h[hl]) - xtolower(d[dl])); +} + +int +urlCheckRequest(const request_t * r) +{ + int rc = 0; + /* protocol "independent" methods */ + if (r->method == METHOD_CONNECT) + return 1; + if (r->method == METHOD_TRACE) + return 1; + if (r->method == METHOD_PURGE) + return 1; + /* does method match the protocol? */ + switch (r->protocol) { + case PROTO_URN: + case PROTO_HTTP: + case PROTO_CACHEOBJ: + rc = 1; + break; + case PROTO_FTP: + if (r->method == METHOD_PUT) + rc = 1; + case PROTO_GOPHER: + case PROTO_WAIS: + case PROTO_WHOIS: + if (r->method == METHOD_GET) + rc = 1; + else if (r->method == METHOD_HEAD) + rc = 1; + break; + case PROTO_HTTPS: +#ifdef USE_SSL + rc = 1; + break; +#else + /* + * Squid can't originate an SSL connection, so it should + * never receive an "https:" URL. It should always be + * CONNECT instead. + */ + rc = 0; +#endif + default: + break; + } + return rc; +} + +/* + * Quick-n-dirty host extraction from a URL. Steps: + * Look for a colon + * Skip any '/' after the colon + * Copy the next SQUID_MAXHOSTNAMELEN bytes to host[] + * Look for an ending '/' or ':' and terminate + * Look for login info preceeded by '@' + */ +char * +urlHostname(const char *url) +{ + LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN); + char *t; + host[0] = '\0'; + if (NULL == (t = strchr(url, ':'))) + return NULL; + t++; + while (*t != '\0' && *t == '/') + t++; + xstrncpy(host, t, SQUIDHOSTNAMELEN); + if ((t = strchr(host, '/'))) + *t = '\0'; + if ((t = strchr(host, ':'))) + *t = '\0'; + if ((t = strrchr(host, '@'))) { + t++; + xmemmove(host, t, strlen(t) + 1); + } + return host; +} + +static void +urlExtMethodAdd(const char *mstr) +{ + method_t method = METHOD_NONE; + for (++method; method < METHOD_ENUM_END; ++method) { + if (0 == strcmp(mstr, RequestMethodStr[method])) { + debug(23, 2) ("Extension method '%s' already exists\n", mstr); + return; + } + if (0 != strncmp("%EXT", RequestMethodStr[method], 4)) + continue; + /* Don't free statically allocated "%EXTnn" string */ + RequestMethodStr[method] = xstrdup(mstr); + debug(23, 1) ("Extension method '%s' added, enum=%d\n", mstr, (int) method); + return; + } + debug(23, 1) ("WARNING: Could not add new extension method '%s' due to lack of array space\n", mstr); +} + +void +urlExtMethodConfigure(void) +{ + wordlist *w = Config.ext_methods; + while (w) { + char *s; + for (s = w->key; *s; s++) + *s = xtoupper(*s); + urlExtMethodAdd(w->key); + w = w->next; + } +} --- squid/src/useragent.c Wed Feb 14 01:07:38 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,95 +0,0 @@ - -/* - * $Id: useragent.c,v 1.7 2001/07/17 06:57:31 squidadm Exp $ - * - * DEBUG: section 40 User-Agent logging - * AUTHOR: Joe Ramey - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid.h" - -#if USE_USERAGENT_LOG -static Logfile *useragentlog = NULL; -#endif - -void -useragentOpenLog(void) -{ -#if USE_USERAGENT_LOG - assert(NULL == useragentlog); - if (!Config.Log.useragent || (0 == strcmp(Config.Log.useragent, "none"))) { - debug(40, 1) ("User-Agent logging is disabled.\n"); - return; - } - useragentlog = logfileOpen(Config.Log.useragent, 0, 1); -#endif -} - -void -useragentRotateLog(void) -{ -#if USE_USERAGENT_LOG - if (NULL == useragentlog) - return; - logfileRotate(useragentlog); -#endif -} - -void -logUserAgent(const char *client, const char *agent) -{ -#if USE_USERAGENT_LOG - static time_t last_time = 0; - static char time_str[128]; - const char *s; - if (NULL == useragentlog) - return; - if (squid_curtime != last_time) { - s = mkhttpdlogtime(&squid_curtime); - strcpy(time_str, s); - last_time = squid_curtime; - } - logfilePrintf(useragentlog, "%s [%s] \"%s\"\n", - client, - time_str, - agent); -#endif -} - -void -useragentLogClose(void) -{ -#if USE_USERAGENT_LOG - if (NULL == useragentlog) - return; - logfileClose(useragentlog); - useragentlog = NULL; -#endif -} --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/useragent.cc Wed Feb 14 01:07:38 2007 @@ -0,0 +1,95 @@ + +/* + * $Id: useragent.cc,v 1.1.2.1 2002/10/09 14:36:50 rbcollins Exp $ + * + * DEBUG: section 40 User-Agent logging + * AUTHOR: Joe Ramey + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + +#if USE_USERAGENT_LOG +static Logfile *useragentlog = NULL; +#endif + +void +useragentOpenLog(void) +{ +#if USE_USERAGENT_LOG + assert(NULL == useragentlog); + if (!Config.Log.useragent || (0 == strcmp(Config.Log.useragent, "none"))) { + debug(40, 1) ("User-Agent logging is disabled.\n"); + return; + } + useragentlog = logfileOpen(Config.Log.useragent, 0, 1); +#endif +} + +void +useragentRotateLog(void) +{ +#if USE_USERAGENT_LOG + if (NULL == useragentlog) + return; + logfileRotate(useragentlog); +#endif +} + +void +logUserAgent(const char *client, const char *agent) +{ +#if USE_USERAGENT_LOG + static time_t last_time = 0; + static char time_str[128]; + const char *s; + if (NULL == useragentlog) + return; + if (squid_curtime != last_time) { + s = mkhttpdlogtime(&squid_curtime); + strcpy(time_str, s); + last_time = squid_curtime; + } + logfilePrintf(useragentlog, "%s [%s] \"%s\"\n", + client, + time_str, + agent); +#endif +} + +void +useragentLogClose(void) +{ +#if USE_USERAGENT_LOG + if (NULL == useragentlog) + return; + logfileClose(useragentlog); + useragentlog = NULL; +#endif +}