2 * SARG Squid Analysis Report Generator http://sarg.sourceforge.net
6 * please look at http://sarg.sourceforge.net/donations.php
8 * http://sourceforge.net/projects/sarg/forums/forum/363374
9 * ---------------------------------------------------------------------
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
27 #include "include/conf.h"
28 #include "include/defs.h"
29 #include "include/stringbuffer.h"
30 #include "include/alias.h"
36 //! Longest alias name length including the terminating zero.
37 #define MAX_ALIAS_LEN 256
40 A host name and the name to report.
44 //! The minimum length of a candidate user name.
46 //! Number of wildcards in the mask.
48 //! The mask of the user name.
53 An IPv4 address and the name to report.
59 //! The number of bits in the prefix.
64 An IPv6 address and the name to report.
70 //! The number of bits in the prefix.
78 struct aliasitem_regex
80 //! The regular expression to match against the name.
82 //! \c True if this regular expression contains at least one subpattern
87 //! Type of grouping criterion.
96 //! \brief One item to group.
97 struct AliasItemStruct
99 //! The next item in the list or NULL for the last item.
100 struct AliasItemStruct
*Next
;
101 //! What criterion to use to group the item.
102 enum aliasitem_type Type
;
105 //! The alias of a name.
106 struct aliasitem_name Name
;
107 //! The alias of an IPv4 address.
108 struct aliasitem_ipv4 Ipv4
;
109 //! The alias of an IPv6 address.
110 struct aliasitem_ipv6 Ipv6
;
112 //! The alias of regular expression.
113 struct aliasitem_regex Regex
;
116 //! The replacement name.
120 //! Object to group items together.
123 //! First item in the list.
124 struct AliasItemStruct
*First
;
125 //! Buffer to store the strings.
126 StringBufferObject StringBuffer
;
130 Create an object to alias items.
132 \return A pointer to the object or NULL if the
135 The returned pointer must be freed by Alias_Destroy().
137 AliasObject
Alias_Create(void)
139 struct AliasStruct
*Alias
;
141 Alias
=calloc(1,sizeof(struct AliasStruct
));
142 if (!Alias
) return(NULL
);
144 Alias
->StringBuffer
=StringBuffer_Create();
145 if (!Alias
->StringBuffer
)
155 Destroy the object created by Alias_Create().
157 \param AliasPtr Pointer to the variable containing
158 the alias. It is reset to NULL to prevent subsequent
161 void Alias_Destroy(AliasObject
*AliasPtr
)
163 struct AliasStruct
*Alias
;
164 struct AliasItemStruct
*Item
;
166 if (!AliasPtr
|| !*AliasPtr
) return;
170 for (Item
=Alias
->First
; Item
; Item
=Item
->Next
)
181 pcre_free(Item
->Regex
.Re
);
187 StringBuffer_Destroy(&Alias
->StringBuffer
);
193 Store a name to alias.
195 \param name The name to match including the wildcard.
196 \param next A pointer to the first character after the name.
198 \retval 1 Alias added.
199 \retval 0 Ignore the line.
202 static int Alias_StoreName(struct AliasStruct
*AliasData
,const char *name
,const char *next
)
204 char Name
[MAX_ALIAS_LEN
];
206 const char *ReplaceE
;
208 struct AliasItemStruct
*alias
;
209 struct AliasItemStruct
*new_alias
;
210 struct AliasItemStruct
*prev_alias
;
214 bool in_wildcard
=false;
216 // get user name and count the wildcards
218 for (str
=name
; len
<sizeof(Name
)-1 && str
<next
; str
++)
231 Name
[len
++]=tolower(*str
);
236 if (len
==0) return(0);
240 while (*str
==' ' || *str
=='\t') str
++;
242 while ((unsigned char)*str
>=' ') str
++;
244 if (Replace
==ReplaceE
) return(0);
248 for (alias
=AliasData
->First
; alias
; alias
=alias
->Next
) {
249 if (alias
->Type
==ALIASTYPE_Name
&& !strcmp(Name
,alias
->Name
.Mask
)) {
250 debuga(__FILE__
,__LINE__
,_("Duplicate aliasing directive for name %s\n"),Name
);
256 // insert into the list
257 new_alias
=malloc(sizeof(*new_alias
));
259 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the user name aliasing directives\n"));
262 new_alias
->Type
=ALIASTYPE_Name
;
263 new_alias
->Name
.MinLen
=minlen
;
264 new_alias
->Name
.Wildcards
=wildcards
;
265 new_alias
->Name
.Mask
=StringBuffer_Store(AliasData
->StringBuffer
,Name
);
266 if (!new_alias
->Name
.Mask
) {
267 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the user name aliasing directives\n"));
272 len
=(int)(ReplaceE
-Replace
);
273 new_alias
->Alias
=StringBuffer_StoreLength(AliasData
->StringBuffer
,Replace
,len
);
274 if (!new_alias
->Alias
) {
275 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the user name aliasing directives\n"));
280 new_alias
->Next
=NULL
;
282 prev_alias
->Next
=new_alias
;
284 AliasData
->First
=new_alias
;
289 Store a IPv4 to alias.
291 \param ipv4 The IPv4 to match.
292 \param nbits The number of bits in the prefix
293 \param next A pointer to the first character after the address.
295 \retval 1 Alias added.
296 \retval 0 Ignore the line.
299 static int Alias_StoreIpv4(struct AliasStruct
*AliasData
,unsigned char *ipv4
,int nbits
,const char *next
)
302 const char *ReplaceE
;
304 struct AliasItemStruct
*alias
;
305 struct AliasItemStruct
*new_alias
;
306 struct AliasItemStruct
*prev_alias
;
311 while (*Replace
==' ' || *Replace
=='\t') Replace
++;
312 if ((unsigned char)*Replace
<' ') {
315 for (str
=Replace
; *str
&& (unsigned char)*str
>=' ' ; str
++);
319 // check for duplicate or broader range
321 for (alias
=AliasData
->First
; alias
; alias
=alias
->Next
) {
322 if (alias
->Type
==ALIASTYPE_Ipv4
&& nbits
>=alias
->Ipv4
.NBits
) {
323 int byte
=alias
->Ipv4
.NBits
/8;
324 int bit
=alias
->Ipv4
.NBits
%8;
325 if ((byte
<1 || memcmp(ipv4
,alias
->Ipv4
.Ip
,byte
)==0) && (bit
==0 || (ipv4
[byte
] ^ alias
->Ipv4
.Ip
[byte
]) & (0xFFU
<<(8-bit
)))==0) {
326 if (nbits
==alias
->Ipv4
.NBits
)
327 debuga(__FILE__
,__LINE__
,_("Duplicate aliasing directive for IPv4 address %d.%d.%d.%d/%d\n"),
328 ipv4
[0],ipv4
[1],ipv4
[2],ipv4
[3],nbits
);
330 debuga(__FILE__
,__LINE__
,_("IPv4 aliasing directive ignored for IPv4 address %d.%d.%d.%d/%d as it is"
331 " narrower than a previous one (%d.%d.%d.%d/%d\n"),
332 ipv4
[0],ipv4
[1],ipv4
[2],ipv4
[3],nbits
,
333 alias
->Ipv4
.Ip
[0],alias
->Ipv4
.Ip
[1],alias
->Ipv4
.Ip
[2],alias
->Ipv4
.Ip
[3],
341 // insert into the list
342 new_alias
=malloc(sizeof(*new_alias
));
344 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the host name aliasing directives\n"));
347 new_alias
->Type
=ALIASTYPE_Ipv4
;
348 memcpy(new_alias
->Ipv4
.Ip
,ipv4
,4);
349 new_alias
->Ipv4
.NBits
=nbits
;
353 len
=(int)(ReplaceE
-Replace
);
356 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the host name aliasing directives\n"));
361 memcpy(tmp
+1,Replace
,len
);
363 new_alias
->Alias
=StringBuffer_Store(AliasData
->StringBuffer
,tmp
);
367 sprintf(tmp
,"%c%d.%d.%d.%d/%d",ALIAS_PREFIX
,ipv4
[0],ipv4
[1],ipv4
[2],ipv4
[3],nbits
);
368 new_alias
->Alias
=StringBuffer_Store(AliasData
->StringBuffer
,tmp
);
370 if (!new_alias
->Alias
) {
371 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the IPv4 aliasing directives\n"));
376 new_alias
->Next
=NULL
;
378 prev_alias
->Next
=new_alias
;
380 AliasData
->First
=new_alias
;
385 Store a IPv6 to alias.
387 \param ipv6 The IPv6 to match.
388 \param nbits The number of bits in the prefix
389 \param next A pointer to the first character after the address.
391 \retval 1 Alias added.
392 \retval 0 Ignore the line.
395 static int Alias_StoreIpv6(struct AliasStruct
*AliasData
,unsigned short *ipv6
,int nbits
,const char *next
)
398 const char *ReplaceE
;
400 struct AliasItemStruct
*alias
;
401 struct AliasItemStruct
*new_alias
;
402 struct AliasItemStruct
*prev_alias
;
407 while (*Replace
==' ' || *Replace
=='\t') Replace
++;
408 if ((unsigned char)*Replace
<' ') {
411 for (str
=Replace
; *str
&& (unsigned char)*str
>=' ' ; str
++);
415 // check for duplicate or broader range
417 for (alias
=AliasData
->First
; alias
; alias
=alias
->Next
) {
418 if (alias
->Type
==ALIASTYPE_Ipv6
&& nbits
>=alias
->Ipv6
.NBits
) {
419 int word
=alias
->Ipv6
.NBits
/16;
420 int bit
=alias
->Ipv6
.NBits
%16;
421 if ((word
<1 || memcmp(ipv6
,alias
->Ipv6
.Ip
,word
*2)==0) && (bit
==0 || (ipv6
[word
] ^ alias
->Ipv6
.Ip
[word
]) & (0xFFFFU
<<(16-bit
)))==0) {
422 if (nbits
==alias
->Ipv6
.NBits
)
423 debuga(__FILE__
,__LINE__
,_("Duplicate aliasing directive for IPv6 address %x:%x:%x:%x:%x:%x:%x:%x/%d\n"),
424 ipv6
[0],ipv6
[1],ipv6
[2],ipv6
[3],ipv6
[4],ipv6
[5],ipv6
[6],ipv6
[7],nbits
);
426 debuga(__FILE__
,__LINE__
,_("IPv6 aliasing directive ignored for IPv6 address %x:%x:%x:%x:%x:%x:%x:%x/%d as it is"
427 " narrower than a previous one (%x:%x:%x:%x:%x:%x:%x:%x/%d\n"),
428 ipv6
[0],ipv6
[1],ipv6
[2],ipv6
[3],ipv6
[4],ipv6
[5],ipv6
[6],ipv6
[7],nbits
,
429 alias
->Ipv6
.Ip
[0],alias
->Ipv6
.Ip
[1],alias
->Ipv6
.Ip
[2],alias
->Ipv6
.Ip
[3],alias
->Ipv6
.Ip
[4],
430 alias
->Ipv6
.Ip
[5],alias
->Ipv6
.Ip
[6],alias
->Ipv6
.Ip
[7],alias
->Ipv6
.NBits
);
437 // insert into the list
438 new_alias
=malloc(sizeof(*new_alias
));
440 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the host name aliasing directives\n"));
443 new_alias
->Type
=ALIASTYPE_Ipv6
;
444 memcpy(new_alias
->Ipv6
.Ip
,ipv6
,8*sizeof(unsigned short int));
445 new_alias
->Ipv6
.NBits
=nbits
;
448 len
=ReplaceE
-Replace
;
451 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the host name aliasing directives\n"));
456 memcpy(tmp
+1,Replace
,len
);
458 new_alias
->Alias
=StringBuffer_Store(AliasData
->StringBuffer
,tmp
);
462 sprintf(tmp
,"%c%x:%x:%x:%x:%x:%x:%x:%x/%d",ALIAS_PREFIX
,ipv6
[0],ipv6
[1],ipv6
[2],ipv6
[3],ipv6
[4],ipv6
[5],ipv6
[6],ipv6
[7],nbits
);
463 new_alias
->Alias
=StringBuffer_Store(AliasData
->StringBuffer
,tmp
);
465 if (!new_alias
->Alias
) {
466 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the IPv6 aliasing directives\n"));
471 new_alias
->Next
=NULL
;
473 prev_alias
->Next
=new_alias
;
475 AliasData
->First
=new_alias
;
481 Store a regular expression to match the alias.
483 \retval 1 Alias added.
484 \retval 0 Ignore the line.
487 static int Alias_StoreRegexp(struct AliasStruct
*AliasData
,char *buf
)
491 struct AliasItemStruct
*alias
;
492 struct AliasItemStruct
*new_alias
;
493 struct AliasItemStruct
**prev_alias
;
494 const char *PcreError
;
504 for (End
=buf
; *End
&& *End
!=Delimiter
; End
++) {
507 debuga(__FILE__
,__LINE__
,_("Invalid NUL character found in regular expression\n"));
510 End
++; //ignore the escaped character
513 if (*End
!=Delimiter
) {
514 debuga(__FILE__
,__LINE__
,_("Unterminated regular expression\n"));
519 // get option: currently supported: i=case insensitive
520 while (*End
&& isalpha(*End
)) {
522 ReOption
|=PCRE_CASELESS
;
524 debuga(__FILE__
,__LINE__
,_("Invalid option character %c found after regular expression\n"),*End
);
531 for (Replace
=End
; *Replace
==' ' || *Replace
=='\t' ; Replace
++);
532 for (End
=Replace
; *End
&& (unsigned char)*End
>' ' ; End
++);
536 new_alias
=malloc(sizeof(*new_alias
));
538 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the host name aliasing directives\n"));
541 new_alias
->Type
=ALIASTYPE_Pcre
;
542 new_alias
->Next
=NULL
;
543 new_alias
->Regex
.Re
=pcre_compile(buf
,ReOption
,&PcreError
,&ErrorOffset
,NULL
);
544 if (new_alias
->Regex
.Re
==NULL
) {
545 debuga(__FILE__
,__LINE__
,_("Failed to compile the regular expression \"%s\": %s\n"),buf
,PcreError
);
552 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the host name aliasing directives\n"));
553 pcre_free(new_alias
->Regex
.Re
);
557 memcpy(tmp
+1,Replace
,len
);
559 new_alias
->Alias
=StringBuffer_Store(AliasData
->StringBuffer
,tmp
);
561 if (!new_alias
->Alias
) {
562 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the regex aliasing directives\n"));
567 new_alias
->Regex
.SubPartern
=false;
568 for (i
=0 ; Replace
[i
] ; i
++)
569 // both the sed \1 and the perl $1 replacement operators are accepted
570 if ((Replace
[i
]=='\\' || Replace
[i
]=='$') && isdigit(Replace
[i
+1])) {
571 new_alias
->Regex
.SubPartern
=true;
576 prev_alias
=&AliasData
->First
;
577 for (alias
=AliasData
->First
; alias
; alias
=alias
->Next
)
578 prev_alias
=&alias
->Next
;
579 *prev_alias
=new_alias
;
586 Store an alias in the corresponding list.
588 \param String The string to parse and store.
591 \retval -1 Error in file.
592 \retval -2 Unknown string type to store.
594 int Alias_Store(struct AliasStruct
*AliasData
,char *String
)
598 unsigned char ipv4
[4];
599 unsigned short int ipv6
[8];
604 if (*String
=='#' || *String
==';') return(0);
606 if (strncasecmp(String
,"re:",3)==0) {
608 if (Alias_StoreRegexp(AliasData
,String
+3)<0)
611 debuga(__FILE__
,__LINE__
,_("PCRE not compiled in therefore the regular expressions are not available to alias items\n"));
617 type
=extract_address_mask(String
,&name
,ipv4
,ipv6
,&nbits
,&next
);
619 Error
=Alias_StoreName(AliasData
,name
,next
);
620 } else if (type
==2) {
621 Error
=Alias_StoreIpv4(AliasData
,ipv4
,nbits
,next
);
622 } else if (type
==3) {
623 Error
=Alias_StoreIpv6(AliasData
,ipv6
,nbits
,next
);
627 if (Error
<0) return(-1);
633 Print the list of the aliases stored in the object.
635 \param AliasData Object created by Alias_Create() and
636 containing the aliases stored by Alias_Store().
638 void Alias_PrintList(struct AliasStruct
*AliasData
)
640 struct AliasItemStruct
*alias
;
642 for (alias
=AliasData
->First
; alias
; alias
=alias
->Next
) {
646 debuga(__FILE__
,__LINE__
,_(" %s => %s\n"),alias
->Name
.Mask
,alias
->Alias
);
649 debuga(__FILE__
,__LINE__
,_(" %d.%d.%d.%d/%d => %s\n"),alias
->Ipv4
.Ip
[0],alias
->Ipv4
.Ip
[1],alias
->Ipv4
.Ip
[2],
650 alias
->Ipv4
.Ip
[3],alias
->Ipv4
.NBits
,alias
->Alias
);
653 debuga(__FILE__
,__LINE__
,_(" %x:%x:%x:%x:%x:%x:%x:%x/%d => %s\n"),alias
->Ipv6
.Ip
[0],alias
->Ipv6
.Ip
[1],alias
->Ipv6
.Ip
[2],
654 alias
->Ipv6
.Ip
[3],alias
->Ipv6
.Ip
[4],alias
->Ipv6
.Ip
[5],alias
->Ipv6
.Ip
[6],alias
->Ipv6
.Ip
[7],
655 alias
->Ipv6
.NBits
,alias
->Alias
);
658 debuga(__FILE__
,__LINE__
,_(" Re => %s\n"),alias
->Alias
);
665 Replace the name by its alias if it is in our list.
667 \param name The name to find in the list.
669 \return The pointer to the name or its alias.
671 static bool Alias_MatchName(struct AliasItemStruct
*alias
,const char *name
,int len
)
674 const char *Searched
;
675 const char *Candidate
;
677 if (!alias
->Name
.Wildcards
)
679 if (len
!=alias
->Name
.MinLen
) return(false);
680 return(strcmp(name
,alias
->Name
.Mask
)==0);
682 if (len
<alias
->Name
.MinLen
) return(false);
684 Searched
=alias
->Name
.Mask
;
685 if (Searched
[0]!='*')
687 while (*Searched
&& *Candidate
&& *Searched
!='*')
689 if (Searched
[0]!=Candidate
[0]) return(false);
694 if (Searched
[0]=='*') Searched
++;
695 while (Searched
[0] && Candidate
[0])
697 while (Candidate
[0] && Candidate
[0]!=Searched
[0]) Candidate
++;
698 for (k
=0 ; Candidate
[k
] && Searched
[k
] && Searched
[k
]!='*' && Searched
[k
]==Candidate
[k
] ; k
++);
699 if (Candidate
[k
]=='\0')
701 return(Searched
[k
]=='\0' || (Searched
[k
]=='*' && Searched
[k
+1]=='\0'));
703 if (Searched
[k
]=='\0') return(false);
704 if (Searched
[k
]=='*')
712 return(Searched
[0]=='\0');
716 Replace the IPv4 address by its alias if it is in our list.
718 \param url The host name.
719 \param ipv4 The address.
721 \return The pointer to the host name or its alias.
723 static bool Alias_MatchIpv4(struct AliasItemStruct
*alias
,unsigned char *ipv4
)
728 len
=alias
->Ipv4
.NBits
;
731 if (n
>0 && memcmp(ipv4
,alias
->Ipv4
.Ip
,n
)!=0) return(false);
732 if (m
!=0 && ((ipv4
[n
] ^ alias
->Ipv4
.Ip
[n
]) & (0xFFU
<<(8-m
)))!=0) return(false);
737 Replace the IPv6 address by its alias if it is in our list.
739 \param url The host name.
740 \param ipv6 The address.
742 \return The pointer to the host name or its alias.
744 static bool Alias_MatchIpv6(struct AliasItemStruct
*alias
,unsigned short int *ipv6
)
749 len
=alias
->Ipv6
.NBits
;
750 for (i
=len
/16-1 ; i
>=0 && ipv6
[i
]==alias
->Ipv6
.Ip
[i
] ; i
--);
753 if (i
>=8 || len
%16==0 || ((ipv6
[i
] ^ alias
->Ipv6
.Ip
[i
]) & (0xFFFF<<(len
-i
*16)))==0) {
762 Replace the host name by its alias if it is in our list.
764 \param url_ptr A pointer to the host name to match. It is replaced
765 by a pointer to the alias if a match is found.
767 \return A pointer to the replacement string or NULL if the regex
770 \warning The function is not thread safe as it may return a static
773 static const char *Alias_MatchRegex(struct AliasItemStruct
*alias
,const char *name
)
777 int ovector
[30];//size must be a multiple of 3
778 static char Replacement
[1024];
785 nmatches
=pcre_exec(alias
->Regex
.Re
,NULL
,name
,len
,0,0,ovector
,sizeof(ovector
)/sizeof(ovector
[0]));
786 if (nmatches
<0) return(NULL
);
788 if (nmatches
==0) nmatches
=(int)(sizeof(ovector
)/sizeof(ovector
[0]))/3*2; //only 2/3 of the vector is used by pcre_exec
789 if (nmatches
==1 || !alias
->Regex
.SubPartern
) { //no subpattern to replace
790 return(alias
->Alias
);
795 for (i
=0 ; str
[i
] ; i
++) {
796 // both the sed \1 and the perl $1 replacement operators are accepted
797 if ((str
[i
]=='\\' || str
[i
]=='$') && isdigit(str
[i
+1])) {
799 if (sub
>=1 && sub
<=nmatches
) {
801 * ovector[sub] is the start position of the match.
802 * ovector[sub+1] is the end position of the match.
805 if (repl_idx
+ovector
[sub
+1]-ovector
[sub
]>=sizeof(Replacement
)-1) break;
806 memcpy(Replacement
+repl_idx
,name
+ovector
[sub
],ovector
[sub
+1]-ovector
[sub
]);
807 repl_idx
+=ovector
[sub
+1]-ovector
[sub
];
811 if (repl_idx
>=sizeof(Replacement
)-1) break;
812 Replacement
[repl_idx
++]=str
[i
];
814 Replacement
[repl_idx
]='\0';
819 const char *Alias_Replace(struct AliasStruct
*AliasData
,const char *Name
)
821 struct AliasItemStruct
*alias
;
823 unsigned char ipv4
[4];
824 unsigned short int ipv6
[8];
826 char lname
[MAX_ALIAS_LEN
];
828 if (!AliasData
) return(Name
);
829 for (len
=0 ; len
<sizeof(lname
)-1 && Name
[len
] ; len
++) lname
[len
]=tolower(Name
[len
]);
832 type
=extract_address_mask(Name
,NULL
,ipv4
,ipv6
,NULL
,NULL
);
834 for (alias
=AliasData
->First
; alias
; alias
=alias
->Next
) {
836 if (alias
->Type
==ALIASTYPE_Ipv4
&& Alias_MatchIpv4(alias
,ipv4
))
837 return(alias
->Alias
);
840 if (alias
->Type
==ALIASTYPE_Ipv6
&& Alias_MatchIpv6(alias
,ipv6
))
841 return(alias
->Alias
);
844 if (alias
->Type
==ALIASTYPE_Pcre
) {
845 const char *Result
=Alias_MatchRegex(alias
,Name
);
846 if (Result
) return(Result
);
849 if (alias
->Type
==ALIASTYPE_Name
&& Alias_MatchName(alias
,lname
,len
))
850 return(alias
->Alias
);