]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - tools/purge/purge.cc
SourceFormat Enforcement
[thirdparty/squid.git] / tools / purge / purge.cc
index 82359572284f8db211626937067f564b60c37e46..558379e1ba9a46bcddf929956e40faa6532356db 100644 (file)
@@ -1,6 +1,3 @@
-//
-// $Id$
-//
 // Author:  Jens-S. V?ckler <voeckler@rvs.uni-hannover.de>
 //
 // File:    purge.cc
 // Initial revision
 //
 //
-#if defined(__GNUC__) || defined(__GNUG__)
-#pragma implementation
-#endif
-
-#include "config.h"
-// for xstrdup
+#include "squid.h"
 #include "util.h"
 
-//#include <assert.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <dirent.h>
-//#include <ctype.h>
 #include <string.h>
-//#include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <fcntl.h>
 #endif
 
 #include <netinet/in.h>
-#include <netinet/tcp.h>  // TCP_NODELAY
+#include <netinet/tcp.h>
 #include <arpa/inet.h>
-#include <netdb.h>        // gethostbyname()
-//#include <regex.h>    //comes via compat.h
+#include <netdb.h>
 
+#include "conffile.hh"
 #include "convert.hh"
-#include "socket.hh"
+#include "copyout.hh"
 #include "signal.hh"
+#include "socket.hh"
 #include "squid-tlv.hh"
-#include "copyout.hh"
-#include "conffile.hh"
 
 #ifndef DEFAULTHOST
 #define DEFAULTHOST "localhost"
 
 volatile sig_atomic_t term_flag = 0; // 'terminate' is a gcc 2.8.x internal...
 char*  linebuffer = 0;
-size_t buffersize = 16834;
+size_t buffersize = 128*1024;
 static char* copydir = 0;
-static unsigned debugFlag = 0;
+static uint32_t debugFlag = 0;
 static unsigned purgeMode = 0;
 static bool iamalive = false;
 static bool reminder = false;
@@ -154,7 +142,6 @@ static bool verbose  = false;
 static bool envelope = false;
 static bool no_fork  = false;
 static const char* programname = 0;
-static const char* RCS_ID = "$Id$";
 
 // ----------------------------------------------------------------------
 
@@ -217,7 +204,8 @@ concat( const char* start, ... )
     // first run: determine size
     unsigned size = strlen(start)+1;
     va_start( ap, start );
-    while ( (s=va_arg(ap,const char*)) != NULL ) size += strlen(s ? s : "");
+    while ( (s=va_arg(ap,const char*)) != NULL )
+        size += strlen(s);
     va_end(ap);
 
     // allocate
@@ -246,7 +234,8 @@ isxstring( const char* s, size_t testlen )
     if ( strlen(s) != testlen ) return false;
 
     size_t i=0;
-    while ( i<testlen && isxdigit(s[i]) ) i++;
+    while ( i<testlen && isxdigit(s[i]) )
+        ++i;
     return (i==testlen);
 }
 
@@ -267,7 +256,7 @@ log_extended( const char* fn, int code, long size, const SquidMetaList* meta )
 
     if ( meta && (findings = meta->search( STORE_META_KEY_MD5 )) ) {
         unsigned char* s = (unsigned char*) findings->data;
-        for ( int j=0; j<16; j++, s++ ) {
+        for ( int j=0; j<16; ++j, ++s ) {
             md5[j*2+0] = hexdigit[ *s >> 4 ];
             md5[j*2+1] = hexdigit[ *s & 15 ];
         }
@@ -292,7 +281,9 @@ log_extended( const char* fn, int code, long size, const SquidMetaList* meta )
                   (unsigned long)temp.timestamp, (unsigned long)temp.lastref,
                   (unsigned long)temp.expires, (unsigned long)temp.lastmod, temp.flags, temp.refcount );
     } else {
-        snprintf( timeb, sizeof(timeb), "%08lx %08lx %08lx %08lx %04x %5hu ", (unsigned long)-1, (unsigned long)-1, (unsigned long)-1, (unsigned long)-1, 0, 0 );
+        unsigned long ul = ULONG_MAX;  // Match type of StoreMetaTLV fields
+        unsigned short hu = 0;  // Match type of StoreMetaTLV refcount fields
+        snprintf( timeb, sizeof(timeb), "%08lx %08lx %08lx %08lx %04x %5d ", ul, ul, ul, ul, 0, hu);
     }
 
     // make sure that there is just one printf()
@@ -329,7 +320,6 @@ action( int fd, size_t metasize,
     static const char* schablone = "PURGE %s HTTP/1.0\r\nAccept: */*\r\n\r\n";
     struct stat st;
     long size = ( fstat(fd,&st) == -1 ? -1 : long(st.st_size - metasize) );
-    int status = 0;
 
     // if we want to copy out the file, do that first of all.
     if ( ::copydir && *copydir && size > 0 )
@@ -337,6 +327,7 @@ action( int fd, size_t metasize,
                   fn, url, ::copydir, ::envelope );
 
     // do we need to PURGE the file, yes, if purgemode bit#0 was set.
+    int status = 0;
     if ( ::purgeMode & 0x01 ) {
         unsigned long bufsize = strlen(url) + strlen(schablone) + 4;
         char* buffer = new char[bufsize];
@@ -349,8 +340,8 @@ action( int fd, size_t metasize,
             return false;
         }
 
-        int size = strlen(buffer);
-        if ( write( sockfd, buffer, size ) != size ) {
+        int content_size = strlen(buffer);
+        if ( write( sockfd, buffer, content_size ) != content_size ) {
             // error while talking to squid
             fprintf( stderr, "unable to talk to server: %s\n", strerror(errno) );
             close(sockfd);
@@ -358,15 +349,23 @@ action( int fd, size_t metasize,
             return false;
         }
         memset( buffer+8, 0, 4 );
-        if ( read( sockfd, buffer, bufsize ) < 1 ) {
+        int readLen = read(sockfd, buffer, bufsize);
+        if (readLen < 1) {
             // error while reading squid's answer
             fprintf( stderr, "unable to read answer: %s\n", strerror(errno) );
             close(sockfd);
             delete[] buffer;
             return false;
         }
+        buffer[bufsize-1] = '\0';
         close(sockfd);
-        status = strtol(buffer+8,0,10);
+        int64_t s = strtol(buffer+8,0,10);
+        if (s > 0 && s < 1000)
+            status = s;
+        else {
+            // error while reading squid's answer
+            fprintf( stderr, "invalid HTTP status in reply: %s\n", buffer+8);
+        }
         delete[] buffer;
     }
 
@@ -401,7 +400,9 @@ match( const char* fn, const REList* list )
     if ( debugFlag & 0x01 ) fprintf( stderr, "# [3] %s\n", fn );
     int fd = open( fn, O_RDONLY );
     if ( fd != -1 ) {
-        if ( read(fd,::linebuffer,::buffersize-1) > 60 ) {
+        memset(::linebuffer, 0, ::buffersize);
+        size_t readLen = read(fd,::linebuffer,::buffersize-1);
+        if ( readLen > 60 ) {
             ::linebuffer[ ::buffersize-1 ] = '\0'; // force-terminate string
 
             // check the offset into the start of object data. The offset is
@@ -422,6 +423,14 @@ match( const char* fn, const REList* list )
             while ( offset + addon <= datastart ) {
                 unsigned int size = 0;
                 memcpy( &size, linebuffer+offset+sizeof(char), sizeof(unsigned int) );
+                if (size+offset < size) {
+                    fputs("WARNING: file corruption detected. 32-bit overflow in size field.\n", stderr);
+                    break;
+                }
+                if (size+offset > readLen) {
+                    fputs( "WARNING: Partial meta data loaded.\n", stderr );
+                    break;
+                }
                 meta.append( SquidMetaType(*(linebuffer+offset)),
                              size, linebuffer+offset+addon );
                 offset += ( addon + size );
@@ -495,7 +504,8 @@ filelevel( const char* directory, const REList* list )
     if ( ::iamalive ) {
         static char alivelist[4][3] = { "\\\b", "|\b", "/\b", "-\b" };
         static unsigned short alivecount = 0;
-        assert( write( STDOUT_FILENO, alivelist[alivecount++ & 3], 2 ) == 2 );
+        const int write_success = write(STDOUT_FILENO, alivelist[alivecount++ & 3], 2);
+        assert(write_success == 2);
     }
 
     bool flag = true;
@@ -549,23 +559,23 @@ dirlevel( const char* dirname, const REList* list, bool level=false )
 }
 
 int
-checkForPortOnly( const char* optarg )
+checkForPortOnly( const char* arg )
 // purpose: see if somebody just put in a port instead of a hostname
 // paramtr: optarg (IN): argument from commandline
 // returns: 0..65535 is the valid port number in network byte order,
 //          -1 if not a port
 {
     // if there is a period in there, it must be a valid hostname
-    if ( strchr( optarg, '.' ) != 0 ) return -1;
+    if ( strchr( arg, '.' ) != 0 ) return -1;
 
     // if it is just a number between 0 and 65535, it must be a port
     char* errstr = 0;
-    unsigned long result = strtoul( optarg, &errstr, 0 );
-    if ( result < 65536 && errstr != optarg ) return htons(result);
+    unsigned long result = strtoul( arg, &errstr, 0 );
+    if ( result < 65536 && errstr != arg ) return htons(result);
 
 #if 0
     // one last try, test for a symbolical service name
-    struct servent* service = getservbyname( optarg, "tcp" );
+    struct servent* service = getservbyname( arg, "tcp" );
     return service ? service->s_port : -1;
 #else
     return -1;
@@ -576,14 +586,14 @@ void
 helpMe( void )
 // purpuse: write help message and exit
 {
-    printf( "\n%s\nUsage:\t%s\t[-a] [-c cf] [-d l] [-(f|F) fn | -(e|E) re] "
+    printf( "\nUsage:\t%s\t[-a] [-c cf] [-d l] [-(f|F) fn | -(e|E) re] "
             "[-p h[:p]]\n\t\t[-P #] [-s] [-v] [-C dir [-H]] [-n]\n\n",
-            ::RCS_ID, ::programname );
+            ::programname );
     printf(
         " -a\tdisplay a little rotating thingy to indicate that I am alive (tty only).\n"
         " -c c\tsquid.conf location, default \"%s\".\n"
         " -C dir\tbase directory for content extraction (copy-out mode).\n"
-        " -d l\tdebug level, an OR of different debug options.\n"
+        " -d l\tdebug level, an OR mask of different debug options.\n"
         " -e re\tsingle regular expression per -e instance (use quotes!).\n"
         " -E re\tsingle case sensitive regular expression like -e.\n"
         " -f fn\tname of textfile containing one regular expression per line.\n"
@@ -604,8 +614,8 @@ helpMe( void )
 
 void
 parseCommandline( int argc, char* argv[], REList*& head,
-                  char*& conffile, char*& copydir,
-                  struct in_addr& serverHost, unsigned short& serverPort )
+                  char*& conffile, char*& copyDirPath,
+                  struct in_addr& serverHostIp, unsigned short& serverHostPort )
 // paramtr: argc: see ::main().
 //          argv: see ::main().
 // returns: Does terminate the program on errors!
@@ -616,8 +626,10 @@ parseCommandline( int argc, char* argv[], REList*& head,
     FILE* rfile;
 
     // program basename
-    if ( (ptr = strrchr(argv[0],'/')) == NULL ) ptr=argv[0];
-    else ptr++;
+    if ( (ptr = strrchr(argv[0],'/')) == NULL )
+        ptr=argv[0];
+    else
+        ++ptr;
     ::programname = ptr;
 
     // extract commandline parameters
@@ -630,24 +642,37 @@ parseCommandline( int argc, char* argv[], REList*& head,
             break;
         case 'C':
             if ( optarg && *optarg ) {
-                if ( copydir ) xfree( (void*) copydir );
-                assert( (copydir = xstrdup(optarg)) );
+                if ( copyDirPath ) xfree( (void*) copyDirPath );
+                copyDirPath = xstrdup(optarg);
+                assert(copyDirPath);
             }
             break;
         case 'c':
-            if ( optarg && *optarg ) {
-                if ( *conffile ) xfree((void*) conffile );
-                assert( (conffile = xstrdup(optarg)) );
+            if ( !optarg || !*optarg ) {
+                fprintf( stderr, "%c requires a regex pattern argument!\n", option );
+                exit(1);
             }
+            if ( *conffile ) xfree((void*) conffile);
+            conffile = xstrdup(optarg);
+            assert(conffile);
             break;
 
         case 'd':
-            ::debugFlag = strtoul( optarg, 0, 0 );
+            if ( !optarg || !*optarg ) {
+                fprintf( stderr, "%c expects a mask parameter. Debug disabled.\n", option );
+                ::debugFlag = 0;
+            } else
+                ::debugFlag = (strtoul(optarg, NULL, 0) & 0xFFFFFFFF);
             break;
 
         case 'E':
         case 'e':
-            if ( head == 0 ) tail = head = new REList( optarg, option=='E' );
+            if ( !optarg || !*optarg ) {
+                fprintf( stderr, "%c requires a regex pattern argument!\n", option );
+                exit(1);
+            }
+            if ( head == 0 )
+                tail = head = new REList( optarg, option=='E' );
             else {
                 tail->next = new REList( optarg, option=='E' );
                 tail = tail->next;
@@ -655,12 +680,16 @@ parseCommandline( int argc, char* argv[], REList*& head,
             break;
 
         case 'f':
+            if ( !optarg || !*optarg ) {
+                fprintf( stderr, "%c requires a filename argument!\n", option );
+                exit(1);
+            }
             if ( (rfile = fopen( optarg, "r" )) != NULL ) {
                 unsigned long lineno = 0;
 #define LINESIZE 512
                 char line[LINESIZE];
                 while ( fgets( line, LINESIZE, rfile ) != NULL ) {
-                    lineno++;
+                    ++lineno;
                     int len = strlen(line)-1;
                     if ( len+2 >= LINESIZE ) {
                         fprintf( stderr, "%s:%lu: line too long, sorry.\n",
@@ -669,8 +698,10 @@ parseCommandline( int argc, char* argv[], REList*& head,
                     }
 
                     // remove trailing line breaks
-                    while ( len > 0 && ( line[len] == '\n' || line[len] == '\r' ) )
-                        line[len--] = '\0';
+                    while ( len > 0 && ( line[len] == '\n' || line[len] == '\r' ) ) {
+                        line[len] = '\0';
+                        --len;
+                    }
 
                     // insert into list of expressions
                     if ( head == 0 ) tail = head = new REList(line,option=='F');
@@ -691,6 +722,10 @@ parseCommandline( int argc, char* argv[], REList*& head,
             ::no_fork = ! ::no_fork;
             break;
         case 'p':
+            if ( !optarg || !*optarg ) {
+                fprintf( stderr, "%c requires a port argument!\n", option );
+                exit(1);
+            }
             colon = strchr( optarg, ':' );
             if ( colon == 0 ) {
                 // no colon, only look at host
@@ -700,28 +735,33 @@ parseCommandline( int argc, char* argv[], REList*& head,
                 port = checkForPortOnly( optarg );
                 if ( port == -1 ) {
                     // assume that main() did set the default port
-                    if ( convertHostname(optarg,serverHost) == -1 ) {
+                    if ( convertHostname(optarg,serverHostIp) == -1 ) {
                         fprintf( stderr, "unable to resolve host %s!\n", optarg );
                         exit(1);
                     }
                 } else {
                     // assume that main() did set the default host
-                    serverPort = port;
+                    serverHostPort = port;
                 }
             } else {
                 // colon used, port is extra
-                *colon++ = 0;
-                if ( convertHostname(optarg,serverHost) == -1 ) {
+                *colon = 0;
+                ++colon;
+                if ( convertHostname(optarg,serverHostIp) == -1 ) {
                     fprintf( stderr, "unable to resolve host %s!\n", optarg );
                     exit(1);
                 }
-                if ( convertPortname(colon,serverPort) == -1 ) {
+                if ( convertPortname(colon,serverHostPort) == -1 ) {
                     fprintf( stderr, "unable to resolve port %s!\n", colon );
                     exit(1);
                 }
             }
             break;
         case 'P':
+            if ( !optarg || !*optarg ) {
+                fprintf( stderr, "%c requires a mode argument!\n", option );
+                exit(1);
+            }
             ::purgeMode = ( strtol( optarg, 0, 0 ) & 0x07 );
             break;
         case 's':
@@ -749,13 +789,13 @@ parseCommandline( int argc, char* argv[], REList*& head,
     assert( head != 0 );
 
     // make sure that the copy out directory is there and accessible
-    if ( copydir && *copydir )
-        if ( assert_copydir( copydir ) != 0 ) exit(1);
+    if ( copyDirPath && *copyDirPath )
+        if ( assert_copydir( copyDirPath ) != 0 ) exit(1);
 
     // show results
     if ( showme ) {
-        printf( "#\n# Currently active values for %s:\n# %s\n",
-                ::programname, ::RCS_ID );
+        printf( "#\n# Currently active values for %s:\n",
+                ::programname);
         printf( "# Debug level       : " );
         if ( ::debugFlag ) printf( "%#6.4x", ::debugFlag );
         else printf( "production level" ); // printf omits 0x prefix for 0!
@@ -763,21 +803,22 @@ parseCommandline( int argc, char* argv[], REList*& head,
         puts( ::verbose ? " + extra verbosity" : "" );
 
         printf( "# Copy-out directory: %s ",
-                copydir ? copydir : "copy-out mode disabled" );
-        if ( copydir )
+                copyDirPath ? copyDirPath : "copy-out mode disabled" );
+        if ( copyDirPath )
             printf( "(%s HTTP header)\n", ::envelope ? "prepend" : "no" );
         else
             puts("");
 
         printf( "# Squid config file : %s\n", conffile );
         printf( "# Cacheserveraddress: %s:%u\n",
-                inet_ntoa( serverHost ), ntohs( serverPort ) );
+                inet_ntoa( serverHostIp ), ntohs( serverHostPort ) );
         printf( "# purge mode        : 0x%02x\n", ::purgeMode );
         printf( "# Regular expression: " );
 
         unsigned count(0);
         for ( tail = head; tail != NULL; tail = tail->next ) {
-            if ( count++ ) printf( "#%22u", count );
+            if ( count++ )
+                printf( "#%22u", count );
 #if defined(LINUX) && putc==_IO_putc
             // I HATE BROKEN LINUX HEADERS!
             // purge.o(.text+0x1040): undefined reference to `_IO_putc'