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);