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
;
503 for (End
=buf
; *End
&& *End
!=Delimiter
; End
++) {
506 debuga(__FILE__
,__LINE__
,_("Invalid NUL character found in regular expression\n"));
509 End
++; //ignore the escaped character
512 if (*End
!=Delimiter
) {
513 debuga(__FILE__
,__LINE__
,_("Unterminated regular expression\n"));
519 for (Replace
=End
; *Replace
==' ' || *Replace
=='\t' ; Replace
++);
520 for (End
=Replace
; *End
&& (unsigned char)*End
>' ' ; End
++);
524 new_alias
=malloc(sizeof(*new_alias
));
526 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the host name aliasing directives\n"));
529 new_alias
->Type
=ALIASTYPE_Pcre
;
530 new_alias
->Next
=NULL
;
531 new_alias
->Regex
.Re
=pcre_compile(buf
,0,&PcreError
,&ErrorOffset
,NULL
);
532 if (new_alias
->Regex
.Re
==NULL
) {
533 debuga(__FILE__
,__LINE__
,_("Failed to compile the regular expression \"%s\": %s\n"),buf
,PcreError
);
540 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the host name aliasing directives\n"));
541 pcre_free(new_alias
->Regex
.Re
);
545 memcpy(tmp
+1,Replace
,len
);
547 new_alias
->Alias
=StringBuffer_Store(AliasData
->StringBuffer
,tmp
);
549 if (!new_alias
->Alias
) {
550 debuga(__FILE__
,__LINE__
,_("Not enough memory to store the regex aliasing directives\n"));
555 new_alias
->Regex
.SubPartern
=false;
556 for (i
=1 ; tmp
[i
] ; i
++)
557 // both the sed \1 and the perl $1 replacement operators are accepted
558 if ((tmp
[i
]=='\\' || tmp
[i
]=='$') && isdigit(tmp
[i
+1])) {
559 new_alias
->Regex
.SubPartern
=true;
564 prev_alias
=&AliasData
->First
;
565 for (alias
=AliasData
->First
; alias
; alias
=alias
->Next
)
566 prev_alias
=&alias
->Next
;
567 *prev_alias
=new_alias
;
574 Store an alias in the corresponding list.
576 \param String The string to parse and store.
579 \retval -1 Error in file.
580 \retval -2 Unknown string type to store.
582 int Alias_Store(struct AliasStruct
*AliasData
,char *String
)
586 unsigned char ipv4
[4];
587 unsigned short int ipv6
[8];
592 if (*String
=='#' || *String
==';') return(0);
594 if (strncasecmp(String
,"re:",3)==0) {
596 if (Alias_StoreRegexp(AliasData
,String
+3)<0)
599 debuga(__FILE__
,__LINE__
,_("PCRE not compiled in therefore the regular expressions are not available to alias items\n"));
605 type
=extract_address_mask(String
,&name
,ipv4
,ipv6
,&nbits
,&next
);
607 Error
=Alias_StoreName(AliasData
,name
,next
);
608 } else if (type
==2) {
609 Error
=Alias_StoreIpv4(AliasData
,ipv4
,nbits
,next
);
610 } else if (type
==3) {
611 Error
=Alias_StoreIpv6(AliasData
,ipv6
,nbits
,next
);
615 if (Error
<0) return(-1);
621 Print the list of the aliases stored in the object.
623 \param AliasData Object created by Alias_Create() and
624 containing the aliases stored by Alias_Store().
626 void Alias_PrintList(struct AliasStruct
*AliasData
)
628 struct AliasItemStruct
*alias
;
630 for (alias
=AliasData
->First
; alias
; alias
=alias
->Next
) {
634 debuga(__FILE__
,__LINE__
,_(" %s => %s\n"),alias
->Name
.Mask
,alias
->Alias
);
637 debuga(__FILE__
,__LINE__
,_(" %d.%d.%d.%d/%d => %s\n"),alias
->Ipv4
.Ip
[0],alias
->Ipv4
.Ip
[1],alias
->Ipv4
.Ip
[2],
638 alias
->Ipv4
.Ip
[3],alias
->Ipv4
.NBits
,alias
->Alias
);
641 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],
642 alias
->Ipv6
.Ip
[3],alias
->Ipv6
.Ip
[4],alias
->Ipv6
.Ip
[5],alias
->Ipv6
.Ip
[6],alias
->Ipv6
.Ip
[7],
643 alias
->Ipv6
.NBits
,alias
->Alias
);
646 debuga(__FILE__
,__LINE__
,_(" Re => %s\n"),alias
->Alias
);
653 Replace the name by its alias if it is in our list.
655 \param name The name to find in the list.
657 \return The pointer to the name or its alias.
659 static bool Alias_MatchName(struct AliasItemStruct
*alias
,const char *name
,int len
)
662 const char *Searched
;
663 const char *Candidate
;
665 if (!alias
->Name
.Wildcards
)
667 if (len
!=alias
->Name
.MinLen
) return(false);
668 return(strcmp(name
,alias
->Name
.Mask
)==0);
670 if (len
<alias
->Name
.MinLen
) return(false);
672 Searched
=alias
->Name
.Mask
;
673 if (Searched
[0]!='*')
675 while (*Searched
&& *Candidate
&& *Searched
!='*')
677 if (Searched
[0]!=Candidate
[0]) return(false);
682 if (Searched
[0]=='*') Searched
++;
683 while (Searched
[0] && Candidate
[0])
685 while (Candidate
[0] && Candidate
[0]!=Searched
[0]) Candidate
++;
686 for (k
=0 ; Candidate
[k
] && Searched
[k
] && Searched
[k
]!='*' && Searched
[k
]==Candidate
[k
] ; k
++);
687 if (Candidate
[k
]=='\0')
689 return(Searched
[k
]=='\0' || (Searched
[k
]=='*' && Searched
[k
+1]=='\0'));
691 if (Searched
[k
]=='\0') return(false);
692 if (Searched
[k
]=='*')
700 return(Searched
[0]=='\0');
704 Replace the IPv4 address by its alias if it is in our list.
706 \param url The host name.
707 \param ipv4 The address.
709 \return The pointer to the host name or its alias.
711 static bool Alias_MatchIpv4(struct AliasItemStruct
*alias
,unsigned char *ipv4
)
716 len
=alias
->Ipv4
.NBits
;
719 if (n
>0 && memcmp(ipv4
,alias
->Ipv4
.Ip
,n
)!=0) return(false);
720 if (m
!=0 && ((ipv4
[n
] ^ alias
->Ipv4
.Ip
[n
]) & (0xFFU
<<(8-m
)))!=0) return(false);
725 Replace the IPv6 address by its alias if it is in our list.
727 \param url The host name.
728 \param ipv6 The address.
730 \return The pointer to the host name or its alias.
732 static bool Alias_MatchIpv6(struct AliasItemStruct
*alias
,unsigned short int *ipv6
)
737 len
=alias
->Ipv6
.NBits
;
738 for (i
=len
/16-1 ; i
>=0 && ipv6
[i
]==alias
->Ipv6
.Ip
[i
] ; i
--);
741 if (i
>=8 || len
%16==0 || ((ipv6
[i
] ^ alias
->Ipv6
.Ip
[i
]) & (0xFFFF<<(len
-i
*16)))==0) {
750 Replace the host name by its alias if it is in our list.
752 \param url_ptr A pointer to the host name to match. It is replaced
753 by a pointer to the alias if a match is found.
755 \return A pointer to the replacement string or NULL if the regex
758 \warning The function is not thread safe as it may return a static
761 static const char *Alias_MatchRegex(struct AliasItemStruct
*alias
,const char *name
)
765 int ovector
[30];//size must be a multiple of 3
766 static char Replacement
[1024];
773 nmatches
=pcre_exec(alias
->Regex
.Re
,NULL
,name
,len
,0,0,ovector
,sizeof(ovector
)/sizeof(ovector
[0]));
774 if (nmatches
<0) return(NULL
);
776 if (nmatches
==0) nmatches
=(int)(sizeof(ovector
)/sizeof(ovector
[0]))/3*2; //only 2/3 of the vector is used by pcre_exec
777 if (nmatches
==1 || !alias
->Regex
.SubPartern
) { //no subpattern to replace
778 return(alias
->Alias
);
783 for (i
=0 ; str
[i
] ; i
++) {
784 // both the sed \1 and the perl $1 replacement operators are accepted
785 if ((str
[i
]=='\\' || str
[i
]=='$') && isdigit(str
[i
+1])) {
787 if (sub
>=1 && sub
<=nmatches
) {
789 * ovector[sub] is the start position of the match.
790 * ovector[sub+1] is the end position of the match.
793 if (repl_idx
+ovector
[sub
+1]-ovector
[sub
]>=sizeof(Replacement
)-1) break;
794 memcpy(Replacement
+repl_idx
,name
+ovector
[sub
],ovector
[sub
+1]-ovector
[sub
]);
795 repl_idx
+=ovector
[sub
+1]-ovector
[sub
];
799 if (repl_idx
>=sizeof(Replacement
)-1) break;
800 Replacement
[repl_idx
++]=str
[i
];
802 Replacement
[repl_idx
]='\0';
807 const char *Alias_Replace(struct AliasStruct
*AliasData
,const char *Name
)
809 struct AliasItemStruct
*alias
;
811 unsigned char ipv4
[4];
812 unsigned short int ipv6
[8];
814 char lname
[MAX_ALIAS_LEN
];
816 if (!AliasData
) return(Name
);
817 for (len
=0 ; len
<sizeof(lname
)-1 && Name
[len
] ; len
++) lname
[len
]=tolower(Name
[len
]);
820 type
=extract_address_mask(Name
,NULL
,ipv4
,ipv6
,NULL
,NULL
);
822 for (alias
=AliasData
->First
; alias
; alias
=alias
->Next
) {
824 if (alias
->Type
==ALIASTYPE_Ipv4
&& Alias_MatchIpv4(alias
,ipv4
))
825 return(alias
->Alias
);
828 if (alias
->Type
==ALIASTYPE_Ipv6
&& Alias_MatchIpv6(alias
,ipv6
))
829 return(alias
->Alias
);
832 if (alias
->Type
==ALIASTYPE_Pcre
) {
833 const char *Result
=Alias_MatchRegex(alias
,Name
);
834 if (Result
) return(Result
);
837 if (alias
->Type
==ALIASTYPE_Name
&& Alias_MatchName(alias
,lname
,len
))
838 return(alias
->Alias
);