A common request I've seen on the squid/squidGuard archives is forced
first pages.  The normal response is that it cannot be done since HTTP is
a stateless protocol.

This patch to squidGuard-1.1.4 allows SG to track states using simple
strings and redirect substitutions.  A destination block can set a
state which can then be tested for in a source block.

For example, the forced first page:

time timetoreset {
    weekly * 00:00-00:01        ## Reset(clear states) at midnight
}

source hasreadfirstpage {       ## This is by user(%i)..
    withstate readfirst%i       ## but could be by IP(%a),
}                               ## hostname(%n), etc.


dest readfirstpage {
    urllist firstpage/urls      ## Contains the url of the first page
    setstate readfirst%i        ## Sets the state when rule is triggered
}

## Notice this destination is never used in an ACL.  It's a hacky way
## to get the states to reset using time definitions.
dest resetstates {
    within timetoreset
    setstate reset              ## reset is a special setstate keyword.
}

acl {
    hasreadfirstpage {          ## if the state matches, pass them
        pass all
    }
    default {
        pass readfirstpage none               ## if at first page, set state
        redirect http://blah.com/firstpage    ## ..else redirect to 1st page
    }
}

This has not been extensively tested so as usual, your milage may vary.
The patch is attached, use level 0 (patch -p0 < squidGuard-states.patch)
diff -Naur squidGuard-1.1.4/STATES-README squidGuard-states/STATES-README
--- squidGuard-1.1.4/STATES-README      Wed Dec 31 16:00:00 1969
+++ squidGuard-states/STATES-README     Tue Oct 16 14:03:22 2001
@@ -0,0 +1,49 @@
+This patch allows squidGuard to remember things between requests.  This allows
+you do do things like have a forced first page without having to change the
+home page on each workstation.
+
+Lets look at an example of a forced first page.
+
+time timetoreset {
+    weekly * 00:00-00:01        ## Reset(clear states) at midnight
+}
+
+source hasreadfirstpage {
+    withstate readfirst%i       ## This is by user(%i)..
+                                ## but could be by IP(%a), 
+                                ## hostname(%n), etc.
+}
+
+dest readfirstpage {
+    urllist firstpage/urls      ## Contains the url of the first page
+    setstate readfirst%i        ## Sets the state when rule is triggered
+}
+
+dest resetstates {
+    within timetoreset
+    setstate reset              ## reset is a special state keyword.  
+}
+
+acl {
+    hasreadfirstpage {          ## if the state matches, pass them
+        pass all
+    }
+    default {
+        pass readfirstpage none               ## if at first page, set state
+        redirect http://blah.com/firstpage    ## ..else redirect to 1st page
+    }
+}
+
+The setstate and withstate commands both accept the same string substitions
+as the redirect command.   Use setstate in dest definitions and withstate
+in source definitions.  A dest ruleset triggers a state to be set and then
+the corrisponding source will match from then on.  
+
+The special keyword 'reset' is used with setstate to reset (clear) the states.
+Unlike other rules, the setstate reset command will execute as soon as the 
+rule becomes active.  In the above example this is when "within timetoreset", 
+midnight in this case, is reached.  
+
+There should be all sorts of tricks you can do with states.  Tips, tricks,
+bugs and/or questions should be directed to [EMAIL PROTECTED]
+
diff -Naur squidGuard-1.1.4/src/Makefile.in squidGuard-states/src/Makefile.in
--- squidGuard-1.1.4/src/Makefile.in    Tue Mar  7 02:06:22 2000
+++ squidGuard-states/src/Makefile.in   Fri Oct 12 16:35:51 2001
@@ -38,7 +38,7 @@
 cfgdir = @sg_cfgdir@
 infodir        = $(prefix)/info
 
-OBJS   = main.o sgLog.o sgDb.o sgDiv.o y.tab.o lex.yy.o
+OBJS   = main.o sgLog.o sgDb.o sgDiv.o y.tab.o lex.yy.o ipc.o
 
 all::
        @echo making $@ in `basename \`pwd\``
@@ -69,6 +69,9 @@
 
 y.tab.o: y.tab.c y.tab.h sg.h
        $(COMPILE) -c y.tab.c
+
+ipc.o: ipc.h ipc.c
+       $(COMPILE) -c ipc.c
 
 lex.yy.c: sg.l sg.h
        $(LEX) sg.l || cp lex.yy.c.flex lex.yy.c
diff -Naur squidGuard-1.1.4/src/ipc.c squidGuard-states/src/ipc.c
--- squidGuard-1.1.4/src/ipc.c  Wed Dec 31 16:00:00 1969
+++ squidGuard-states/src/ipc.c Tue Oct 16 14:39:44 2001
@@ -0,0 +1,78 @@
+#include "ipc.h"
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+SHM *shmInit(key_t key, int size) {
+       SHM *shm;
+       int creator = 0;
+       void *tail;
+
+       shm = (SHM *) calloc(1,sizeof(SHM));
+       if (shm == NULL) return NULL;
+       shm->key = key;
+       shm->free = size;
+       shm->head = NULL;
+       shm->tail = NULL;
+       shm->loghead = NULL;
+       
+       if ((shm->shmid = shmget(key,(int) size+sizeof(void *),0644)) < 0) {           
+                         // Try to connect
+               if ((shm->shmid = shmget(key,(int) size+sizeof(void *),IPC_CREAT | 
+0644)) < 0) {        // Try to create
+                       perror("shmget");
+                       exit(1);
+               }
+               else { creator = 1; }
+       }
+       
+       if ((tail = shmat(shm->shmid, NULL, 0)) == (char *) -1) {                      
+ // Try to attach
+               perror("shmat");
+               exit(1);
+       }
+
+       shm->tail = tail;
+       shm->head = tail += sizeof(void *); // Set head past tail pointer.
+       
+       if (creator) {
+               *(shm->tail) = shm->head;  // Initialize tail pointer
+       }
+       else {
+               shm->loghead = shm->head;
+       }
+
+       return shm;
+}
+
+void *shmMalloc(SHM *shm, int size) {
+       void **tail;
+       void *ptr;
+
+       tail = shm->tail;
+       ptr = *tail;
+
+       if (shm->free < size) return NULL;
+
+       *tail += size;
+       shm->free -= size;
+       if (shm->loghead == NULL && ptr == shm->head) shm->loghead = ptr;
+
+       return ptr;
+}
+
+void *shmDestroy(SHM *shm) {
+       shmdt(shm->tail);
+       shmctl(shm->shmid,IPC_RMID,NULL);
+       free(shm);
+       shm=NULL;
+}
+
+void *shmTop(SHM *shm) {
+       return(shm->loghead);           
+}
+
+void shmClear(SHM *shm) {
+       memset(shm->head,NULL,*(shm->tail)-shm->head);
+       shm->free += *(shm->tail)-shm->head;
+       *(shm->tail) = shm->head;
+       shm->loghead = NULL;
+}
diff -Naur squidGuard-1.1.4/src/ipc.h squidGuard-states/src/ipc.h
--- squidGuard-1.1.4/src/ipc.h  Wed Dec 31 16:00:00 1969
+++ squidGuard-states/src/ipc.h Tue Oct 16 09:24:58 2001
@@ -0,0 +1,20 @@
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+struct sShm {
+       void *head;
+       void **tail;
+       key_t key;
+       size_t free;
+       int shmid;
+       void *loghead;
+};
+
+typedef struct sShm SHM;
+
+SHM *shmInit(key_t key, int size);
+void *shmMalloc(SHM *shm, int size);
+void *shmDestroy(SHM *shm);
+void *shmTop(SHM *shm);
+void shmClear(SHM *shm);
+
diff -Naur squidGuard-1.1.4/src/main.c squidGuard-states/src/main.c
--- squidGuard-1.1.4/src/main.c Mon Mar 27 03:03:45 2000
+++ squidGuard-states/src/main.c        Fri Oct 12 16:47:55 2001
@@ -27,6 +27,7 @@
 struct Destination *lastDest = NULL;
 struct Destination *Dest = NULL;
 
+struct States *State = NULL;  /* stateful redirector */
 struct sgRewrite *lastRewrite = NULL;
 struct sgRewrite *Rewrite = NULL;
 struct sgRegExp *lastRewriteRegExec = NULL;
@@ -63,6 +64,9 @@
 int sgtime = 0;
 char *globalLogDir = NULL;
 
+#if USE_SHMEM
+SHM *sgSHM;
+#endif 
 
 #if __STDC__
 int main(int    argc,
@@ -87,6 +91,10 @@
 #if HAVE_SIGACTION
   struct sigaction act;
 #endif
+#if USE_SHMEM
+  key_t key = 11234;
+#endif
+
   gettimeofday(&start_time, NULL);
   progname = argv[0];
   globalPid = getpid();
@@ -140,6 +148,13 @@
     sgLogError("squidGuard stopped 
(%d.%03d)",stop_time.tv_sec,stop_time.tv_usec/1000);
     exit(0);
   }
+
+#if USE_SHMEM
+  sgLogError("Initializing %d bytes of shared memory",SHM_POOL_SIZE);
+  sgSHM = shmInit(key,SHM_POOL_SIZE);
+  if (sgSHM == NULL) sgLogFatalError("shmInit() failed");
+  State = shmTop(sgSHM);
+#endif
   sgTimeElementSortEvents();
   sgTimeNextEvent();
 #if HAVE_SIGACTION
@@ -177,7 +192,10 @@
       } else {
        strncpy(tmp,squidInfo.src,MAX_BUF-1);
        globalLogFile = NULL;
-       src = sgFindSource(tmp,squidInfo.ident,squidInfo.srcDomain);
+       /* Stateful redirector */
+       //src = sgFindSource(tmp,squidInfo.ident,squidInfo.srcDomain);
+       src = sgFindSource(tmp,&squidInfo);
+       /* End Stateful redirector */
        acl = sgAclCheckSource(src);
        if((redirect = sgAclAccess(src,acl,&squidInfo)) == NULL){
          puts("");
@@ -205,6 +223,10 @@
       gettimeofday(&stop_time, NULL);
       stop_time.tv_sec = stop_time.tv_sec + globalDebugTimeDelta;
       sgLogError("squidGuard stopped 
(%d.%03d)",stop_time.tv_sec,stop_time.tv_usec/1000);
+#if USE_SHMEM
+         sgLogError("Destroying shared memory pool");
+         shmDestroy(sgSHM);
+#endif
       exit(2);
     }
 #endif
@@ -212,9 +234,17 @@
     gettimeofday(&stop_time, NULL);
     stop_time.tv_sec = stop_time.tv_sec + globalDebugTimeDelta;
     sgLogError("squidGuard stopped 
(%d.%03d)",stop_time.tv_sec,stop_time.tv_usec/1000);
+#if USE_SHMEM
+       sgLogError("Destroying shared memory pool");
+       shmDestroy(sgSHM);
+#endif
     exit(0);
 #endif
   }
+#if USE_SHMEM
+  sgLogError("Destroying shared memory pool");
+  shmDestroy(sgSHM);
+#endif
   exit(0);
 }
 
diff -Naur squidGuard-1.1.4/src/sg.h.in squidGuard-states/src/sg.h.in
--- squidGuard-1.1.4/src/sg.h.in        Tue Mar 21 01:40:24 2000
+++ squidGuard-states/src/sg.h.in       Fri Oct 12 16:13:22 2001
@@ -58,6 +58,13 @@
 # define ulong unsigned long
 #endif
 
+/* Shared Memory for Stateful Redirector */
+#define USE_SHMEM    1
+#if USE_SHMEM
+# include "ipc.h"
+# define SHM_POOL_SIZE  1024*24
+#endif
+
 #define T_WEEKLY      1
 #define T_WEEKDAY     2
 #define T_TVAL        3
@@ -169,6 +176,12 @@
   struct sgRewrite *next;
 };
 
+/* Stateful redirector */
+struct States {
+       char *name;
+       struct States *next;
+};
+
 #define SGDBTYPE_DOMAINLIST 1
 #define SGDBTYPE_URLLIST 2
 #define SGDBTYPE_USERLIST 3
@@ -232,6 +245,7 @@
   char *redirect;
   struct Time *time;
   int within;
+  char *setstate;  /* state to set when matched */
   struct LogFile *logfile;
   struct Destination *next;
 };
@@ -245,12 +259,12 @@
   struct sgDb *userDb;
   struct Time *time;
   int within;
+  char *withstate;  /* state required to match */
   struct LogFile *logfile;
   struct Source *next;
 };
 
 
-
 struct Acl {
   char *name;
   int active;
@@ -307,7 +321,8 @@
 void   sgSourceDomain __P((char *));
 void   sgSourceIpList __P((char *));
 struct Source *sgSourceFindName __P((char *));
-struct Source *sgFindSource __P((char *, char *, char *));
+//struct Source *sgFindSource __P((char *, char *, char *)); //stateful redirector
+struct Source *sgFindSource __P((char *, struct SquidInfo *squidInfo));
 void   sgSourceTime __P((char *, int));
 
 void   sgDest __P((char *));
@@ -378,3 +393,11 @@
 void   yyerror __P((char *));
 int    yyparse __P((void));
 int    yylex __P((void));
+
+/* stateful redirector */
+void   sgDestState __P((char *));
+void   sgSourceState __P((char *));
+void   sgSetState __P((char *));
+char   *sgFindState __P((char *));
+void   sgResetState __P(());
+/* end stateful redirector */
diff -Naur squidGuard-1.1.4/src/sg.l squidGuard-states/src/sg.l
--- squidGuard-1.1.4/src/sg.l   Mon Sep 27 04:04:40 1999
+++ squidGuard-states/src/sg.l  Tue Oct  9 16:56:22 2001
@@ -49,6 +49,8 @@
 ^acl         return ACL;
 ^dbhome        {yylval.string = yytext ; return DBHOME ;}
 ^logdir       {yylval.string = yytext ; return LOGDIR ;}
+setstate       return SETSTATE;
+withstate      return WITHSTATE;
 domainlist  return DOMAINLIST;
 urllist     return URLLIST;
 expressionlist return EXPRESSIONLIST;
diff -Naur squidGuard-1.1.4/src/sg.y squidGuard-states/src/sg.y
--- squidGuard-1.1.4/src/sg.y   Mon Mar 27 06:08:39 2000
+++ squidGuard-states/src/sg.y  Tue Oct 16 13:53:10 2001
@@ -32,6 +32,8 @@
 extern struct Destination *lastDest ;
 extern struct Destination *Dest ;
 
+extern struct States *State;   /* stateful redirector */
+
 extern struct sgRewrite *lastRewrite;
 extern struct sgRewrite *Rewrite;
 extern struct sgRegExp *lastRewriteRegExec;
@@ -59,6 +61,10 @@
 
 extern int globalDebugTimeDelta;
 
+#if USE_SHMEM
+extern SHM *sgSHM;
+#endif
+
 static int time_switch = 0;
 static int date_switch = 0;
 
@@ -81,6 +87,7 @@
 %token DOMAIN USER USERLIST IP NL
 %token PASS REDIRECT LOGDIR SUBST CHAR WEEKLY DATE
 %token WITHIN OUTSIDE ELSE LOGFILE ANONYMOUS
+%token SETSTATE WITHSTATE
 
 %type <string> WORD 
 %type <string> WEEKDAY
@@ -145,6 +152,7 @@
             | OUTSIDE WORD { sgDestTime($2,OUTSIDE); }
             | LOGFILE ANONYMOUS WORD { sgLogFile(SG_BLOCK_DESTINATION,1,$3); }
             | LOGFILE WORD { sgLogFile(SG_BLOCK_DESTINATION,0,$2); }
+                       | SETSTATE WORD { sgDestState($2); }
             ;
 
 source:      SOURCE WORD { sgSource($2); }
@@ -167,6 +175,7 @@
                     | OUTSIDE WORD { sgSourceTime($2,OUTSIDE); }
                     | LOGFILE ANONYMOUS WORD {sgLogFile(SG_BLOCK_SOURCE,1,$3);}
                     | LOGFILE WORD { sgLogFile(SG_BLOCK_SOURCE,0,$2); }
+                                       | WITHSTATE WORD { sgSourceState($2); }
                     ;
 
 
@@ -469,6 +478,7 @@
   sp->time = NULL;
   sp->next=NULL;
   sp->logfile = NULL;
+  sp->withstate = NULL;
   sp->name = (char  *) sgCalloc(1,strlen(source) + 1);
   strcpy(sp->name,source);
 
@@ -485,7 +495,7 @@
 {
  struct Source *s;
  s = lastSource;
- if(s->ip == NULL && s->domainDb == NULL && s->userDb == NULL){
+ if(s->ip == NULL && s->domainDb == NULL && s->userDb == NULL && s->withstate == 
+NULL){
    sgLogError("sourceblock %s missing active content, set inactive",s->name);
    s->time = NULL;
    s->active = 0;
@@ -699,11 +709,18 @@
   fclose(fd);
 }
 
+/* stateful redirector */
+void sgSourceState(char *value){
+  struct Source *sp;
+  sp = lastSource;
+  sp->withstate = (char *) sgCalloc(1,strlen(value) + 1);
+  strcpy(sp->withstate,value);
+}
 /*
   
 
  */
-
+/* // Stateful redirector
 #if __STDC__
 struct Source *sgFindSource (char *net, char *ident, char *domain)
 #else
@@ -712,20 +729,43 @@
      char *ident;
      char *domain;
 #endif
-{
+*/
+#if __STDC__
+struct Source *sgFindSource (char *net, struct SquidInfo *squidInfo)
+#else
+struct Source *sgFindSource (net,squidInfo)
+       char *net;
+       struct SquidInfo *squidInfo;
+#endif
+{ 
+/* Stateful Redirector */
+  char *state;
+  char *ident = squidInfo->ident;
+  char *domain = squidInfo->srcDomain;
+/* End Stateful Redirector */
   struct Source *s;
   struct Ip *ip;
-  int foundip, founduser, founddomain;
+  int foundip, founduser, founddomain, foundstate; //stateful redirector
   unsigned long i, octet = 0, *op;
+
   if(net != NULL){
     op = sgConvDot(net);
     if(op != NULL)
       octet = *op;
   }
   for(s=Source; s != NULL; s = s->next){
-    foundip = founduser = founddomain = 0;
+    foundip = founduser = founddomain = foundstate = 0;
     if(s->active == 0)
       continue;
+       /* stateful redirector */
+       if(s->withstate != NULL) {
+               state = s->withstate;
+               state = sgParseRedirect(state,squidInfo,NULL,NULL);
+               foundstate = (sgFindState(state) != NULL);
+       }
+       else {foundstate = 1;}
+       /* end stateful redirector */
+       
     if(s->ip != NULL){
       if(net == NULL)
        foundip = 0;
@@ -766,7 +806,7 @@
       }
     } else
       founddomain = 1;
-    if(founduser == 1 && foundip == 1 && founddomain == 1){
+    if(founduser == 1 && foundip == 1 && founddomain == 1 && foundstate){  // 
+stateful redirector
       return s;
     }
   }
@@ -801,6 +841,7 @@
   sp->within = 0;
   sp->logfile = NULL;
   sp->next=NULL;
+  sp->setstate=NULL;
   sp->name = (char  *) sgCalloc(1,strlen(dest) + 1);
   strcpy(sp->name,dest);
 
@@ -818,7 +859,7 @@
  struct Destination *d;
  d = lastDest;
  if(d->domainlist == NULL && d->urllist == NULL && d->expressionlist == NULL
-    && d->redirect == NULL && d->rewrite == NULL){
+    && d->redirect == NULL && d->rewrite == NULL && d->setstate == NULL){
    sgLogError("destblock %s missing active content, set inactive",d->name);
    d->time = NULL;
    d->active = 0;
@@ -1004,6 +1045,14 @@
   sp->rewrite = rewrite;
 }
 
+/* stateful redirector */
+void sgDestState(char *value){
+  struct Destination *sp;
+  sp = lastDest;
+  sp->setstate = (char *) sgCalloc(1,strlen(value) + 1);
+  strcpy(sp->setstate,value);
+}
+
 #if __STDC__
 int sgRegExpMatch(struct sgRegExp *regexp, char *str)
 #else
@@ -1663,7 +1712,14 @@
          d->active = 0;
        else
          d->active = 1;
+
+       /* Stateful redirector */
+       if (d->active == 1 && d->setstate != NULL) 
+               if (!strcmp(d->setstate,"reset"))
+                       sgResetState();
+       
     }
+       /* Stateful redirector */
   }
   for(s = Source; s != NULL; s = s->next){
     if(s->time != NULL){
@@ -2023,7 +2079,7 @@
 #endif
 {
   int access = 1,result;
-  char *redirect = NULL, *dbdata = NULL, *p;
+  char *redirect = NULL, *setstate = NULL, *dbdata = NULL, *p;
   struct sgRewrite *rewrite = NULL;
   struct AclDest *aclpass = NULL;
   if(acl == NULL)
@@ -2100,6 +2156,13 @@
       else
        redirect = acl->redirect;
     }
+       /* Stateful Redirector */
+       else if(aclpass->dest !=NULL && aclpass->dest->setstate != NULL) {
+               setstate = aclpass->dest->setstate;
+               setstate = sgParseRedirect(setstate, req, acl, aclpass);
+               sgSetState(setstate);   
+       }
+       /* End Stateful Redirector */
   } else {  /* acl->pass == NULL, probably defaultAcl->pass == NULL */
     access=0;
     redirect = defaultAcl->redirect;
@@ -2130,6 +2193,80 @@
       sgLogRequest(globalLogFile,req,acl,aclpass,NULL);
   }
   return redirect;
+}
+
+#if __STDC__
+void sgSetState(char *state)
+#else
+void sgSetState(state)
+       char *state;
+#endif
+{
+       struct States *S;
+       
+       if (sgFindState(state) == NULL) {
+#ifdef USE_SHMEM
+               if (State == NULL) {
+                       S = (struct States *) shmMalloc(sgSHM,sizeof(struct States));
+                       State = S;
+               }
+               else {
+                       for(S=State;S->next!=NULL;S=S->next);
+                       S->next = (struct States *) shmMalloc(sgSHM,sizeof(struct 
+States));
+                       S=S->next;
+               }
+               S->name = (char *) shmMalloc(sgSHM,strlen(state) + 1);
+#else
+               S = (struct States *) sgCalloc(1,sizeof(struct States));
+               S->name = (char *) sgCalloc(1,strlen(state) + 1);
+               S->next = State;
+               State = S;
+#endif
+               strcpy(S->name,state);
+               S->next = NULL;
+       }
+}
+
+#if __STDC__
+char *sgFindState(char *state)
+#else
+char *sgFindState(state)
+       char *state;
+#endif
+{
+       struct States *s;
+
+       if (State != NULL) {
+               for(s=State;s != NULL;s=s->next) {
+                       if (!strcmp(s->name,state)) 
+                               return state;
+               }
+       }
+       return NULL;
+}
+
+#if __STDC__
+void sgResetState()
+#else
+void sgResetState()
+#endif
+{
+       struct States *s;
+       struct States *l = NULL;
+
+       sgLogError("Reset states");
+#if USE_SHMEM
+       shmClear(sgSHM);
+#else
+       for(s=State;s != NULL;s=s->next) {
+               if (l != NULL) {
+                       sgFree(l->name);
+                       sgFree(l);
+               }
+               l=s;
+       }
+#endif
+       State = NULL;
 }
 
 #if __STDC__
diff -Naur squidGuard-1.1.4/src/sgDiv.c squidGuard-states/src/sgDiv.c
--- squidGuard-1.1.4/src/sgDiv.c        Wed Mar 29 04:03:45 2000
+++ squidGuard-states/src/sgDiv.c       Thu Oct 11 10:36:56 2001
@@ -604,7 +604,7 @@
       p++;
       break;
     case 's': /* Source Class Matched */
-      if(acl->source == NULL || acl->source->name == NULL)
+      if(acl == NULL || acl->source == NULL || acl->source->name == NULL) // stateful 
+redirector
        strcat(buf, "default");
       else
        strcat(buf, acl->source->name);

Reply via email to