From: Frederic Marchal Date: Fri, 8 Mar 2013 18:08:17 +0000 (+0100) Subject: Add a user alias X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c4f0ea8f64bcad0d22bdc1e5504f87bd7255c40e;p=thirdparty%2Fsarg.git Add a user alias It is similar to the hostalias file but is applied to the user ID. --- diff --git a/Makefile.in b/Makefile.in index bd121d4..0ff7e09 100644 --- a/Makefile.in +++ b/Makefile.in @@ -35,18 +35,19 @@ EXEEXT = @EXEEXT@ INSTALL_PROGRAM = $(INSTALL) SRCS = util.c log.c report.c topuser.c email.c sort.c html.c \ - index.c getconf.c usage.c decomp.c ip2name.c ip2name_dns.c \ - useragent.c exclude.c convlog.c totday.c repday.c datafile.c\ - indexonly.c splitlog.c lastlog.c topsites.c siteuser.c css.c \ - smartfilter.c denied.c authfail.c charset.c dichotomic.c \ - redirector.c auth.c download.c grepday.c ip2name_exec.c \ - dansguardian_log.c dansguardian_report.c realtime.c btree_cache.c \ - usertab.c userinfo.c longline.c url.c fnmatch.c stringbuffer.c \ - filelist.c readlog.c \ - readlog_squid.c readlog_sarg.c readlog_extlog.c readlog_common.c + index.c getconf.c usage.c decomp.c ip2name.c ip2name_dns.c \ + useragent.c exclude.c convlog.c totday.c repday.c datafile.c\ + indexonly.c splitlog.c lastlog.c topsites.c siteuser.c css.c \ + smartfilter.c denied.c authfail.c charset.c dichotomic.c \ + redirector.c auth.c download.c grepday.c ip2name_exec.c \ + dansguardian_log.c dansguardian_report.c realtime.c btree_cache.c \ + usertab.c userinfo.c longline.c url.c fnmatch.c stringbuffer.c \ + filelist.c readlog.c alias.c \ + readlog_squid.c readlog_sarg.c readlog_extlog.c readlog_common.c *.o: include/conf.h include/info.h include/defs.h +alias.o: include/alias.h include/stringbuffer.h authfail.o: include/readlog.h denied.o: include/readlog.h download.o: include/readlog.h @@ -58,7 +59,7 @@ readlog_extlog.o: include/readlog.h readlog_sarg.o: include/readlog.h readlog_squid.o: include/readlog.h stringbuffer.o: include/stringbuffer.h -userinfo.o: include/stringbuffer.h +userinfo.o: include/stringbuffer.h include/alias.h OBJS = $(SRCS:.c=.o) diff --git a/alias.c b/alias.c new file mode 100644 index 0000000..8be7ca7 --- /dev/null +++ b/alias.c @@ -0,0 +1,841 @@ +/* + * SARG Squid Analysis Report Generator http://sarg.sourceforge.net + * 1998, 2013 + * + * SARG donations: + * please look at http://sarg.sourceforge.net/donations.php + * Support: + * http://sourceforge.net/projects/sarg/forums/forum/363374 + * --------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#define HAVE_PCRE_H 1 //!< \bug Remove this line! + +#include "include/conf.h" +#include "include/defs.h" +#include "include/stringbuffer.h" +#include "include/alias.h" +#ifdef HAVE_PCRE_H +#include +#define USE_PCRE 1 +#endif + +//! Longest alias name length including the terminating zero. +#define MAX_ALIAS_LEN 256 + +/*! +A host name and the name to report. +*/ +struct aliasitem_name +{ + //! The minimum length of a candidate user name. + int MinLen; + //! Number of wildcards in the mask. + int Wildcards; + //! The mask of the user name. + const char *Mask; +}; + +/*! +An IPv4 address and the name to report. +*/ +struct aliasitem_ipv4 +{ + //! The IP address. + unsigned char Ip[4]; + //! The number of bits in the prefix. + int NBits; +}; + +/*! +An IPv6 address and the name to report. +*/ +struct aliasitem_ipv6 +{ + //! The IP address. + unsigned short Ip[8]; + //! The number of bits in the prefix. + int NBits; +}; + +#ifdef USE_PCRE +/*! +A regular expression. +*/ +struct aliasitem_regex +{ + //! The regular expression to match against the name. + pcre *Re; + //! \c True if this regular expression contains at least one subpattern + bool SubPartern; +}; +#endif + +//! Type of grouping criterion. +enum aliasitem_type +{ + ALIASTYPE_Name, + ALIASTYPE_Ipv4, + ALIASTYPE_Ipv6, + ALIASTYPE_Pcre +}; + +//! \brief One item to group. +struct AliasItemStruct +{ + //! The next item in the list or NULL for the last item. + struct AliasItemStruct *Next; + //! What criterion to use to group the item. + enum aliasitem_type Type; + union + { + //! The alias of a name. + struct aliasitem_name Name; + //! The alias of an IPv4 address. + struct aliasitem_ipv4 Ipv4; + //! The alias of an IPv6 address. + struct aliasitem_ipv6 Ipv6; +#ifdef USE_PCRE + //! The alias of regular expression. + struct aliasitem_regex Regex; +#endif + }; + //! The replacement name. + const char *Alias; +}; + +//! Object to group items together. +struct AliasStruct +{ + //! First item in the list. + struct AliasItemStruct *First; + //! Buffer to store the strings. + StringBufferObject StringBuffer; +}; + +/*! + Create an object to alias items. + + \return A pointer to the object or NULL if the + creation failed. + + The returned pointer must be freed by Alias_Destroy(). + */ +AliasObject Alias_Create(void) +{ + struct AliasStruct *Alias; + + Alias=calloc(1,sizeof(struct AliasStruct)); + if (!Alias) return(NULL); + + Alias->StringBuffer=StringBuffer_Create(); + if (!Alias->StringBuffer) + { + free(Alias); + return(NULL); + } + + return(Alias); +} + +/*! + Destroy the object created by Alias_Create(). + + \param AliasPtr Pointer to the variable containing + the alias. It is reset to NULL to prevent subsequent + use of the pointer. + */ +void Alias_Destroy(AliasObject *AliasPtr) +{ + struct AliasStruct *Alias; + struct AliasItemStruct *Item; + + if (!AliasPtr || !*AliasPtr) return; + Alias=*AliasPtr; + *AliasPtr=NULL; + + for (Item=Alias->First ; Item ; Item=Item->Next) + { + switch (Item->Type) + { + case ALIASTYPE_Name: + case ALIASTYPE_Ipv4: + case ALIASTYPE_Ipv6: + break; + + case ALIASTYPE_Pcre: +#ifdef USE_PCRE + pcre_free(Item->Regex.Re); +#endif + break; + } + } + + StringBuffer_Destroy(&Alias->StringBuffer); + free(Alias); +} + + +/*! + Store a name to alias. + + \param name The name to match including the wildcard. + \param next A pointer to the first character after the name. + + \retval 1 Alias added. + \retval 0 Ignore the line. + \retval -1 Error. + */ +static int Alias_StoreName(struct AliasStruct *AliasData,const char *name,const char *next) +{ + char Name[MAX_ALIAS_LEN]; + const char *Replace; + const char *ReplaceE; + const char *str; + struct AliasItemStruct *alias; + struct AliasItemStruct *new_alias; + struct AliasItemStruct *prev_alias; + int len; + int minlen=0; + int wildcards=0; + bool in_wildcard=false; + + // get user name and count the wildcards + len=0; + for (str=name ; len=' ') str++; + ReplaceE=str; + if (Replace==ReplaceE) return(0); + + // ignore duplicates + prev_alias=NULL; + for (alias=AliasData->First ; alias ; alias=alias->Next) { + if (alias->Type==ALIASTYPE_Name && !strcmp(Name,alias->Name.Mask)) { + debuga(_("Duplicate aliasing directive for name %s\n"),Name); + return(0); + } + prev_alias=alias; + } + + // insert into the list + new_alias=malloc(sizeof(*new_alias)); + if (!new_alias) { + debuga(_("Not enough memory to store the user name aliasing directives\n")); + return(-1); + } + new_alias->Type=ALIASTYPE_Name; + new_alias->Name.MinLen=minlen; + new_alias->Name.Wildcards=wildcards; + new_alias->Name.Mask=StringBuffer_Store(AliasData->StringBuffer,Name); + if (!new_alias->Name.Mask) { + debuga(_("Not enough memory to store the user name aliasing directives\n")); + free(new_alias); + return(-1); + } + + len=(int)(ReplaceE-Replace); + new_alias->Alias=StringBuffer_StoreLength(AliasData->StringBuffer,Replace,len); + if (!new_alias->Alias) { + debuga(_("Not enough memory to store the user name aliasing directives\n")); + free(new_alias); + return(-1); + } + + new_alias->Next=NULL; + if (prev_alias) + prev_alias->Next=new_alias; + else + AliasData->First=new_alias; + return(1); +} + +/*! + Store a IPv4 to alias. + + \param ipv4 The IPv4 to match. + \param nbits The number of bits in the prefix + \param next A pointer to the first character after the address. + + \retval 1 Alias added. + \retval 0 Ignore the line. + \retval -1 Error. + */ +static int Alias_StoreIpv4(struct AliasStruct *AliasData,unsigned char *ipv4,int nbits,const char *next) +{ + const char *Replace; + const char *ReplaceE; + const char *str; + struct AliasItemStruct *alias; + struct AliasItemStruct *new_alias; + struct AliasItemStruct *prev_alias; + int len; + + // get the alias + Replace=next; + while (*Replace==' ' || *Replace=='\t') Replace++; + if ((unsigned char)*Replace<' ') { + Replace=NULL; + } else { + for (str=Replace ; *str && (unsigned char)*str>=' ' ; str++); + ReplaceE=str; + } + + // check for duplicate or broader range + prev_alias=NULL; + for (alias=AliasData->First ; alias ; alias=alias->Next) { + if (alias->Type==ALIASTYPE_Ipv4 && nbits>=alias->Ipv4.NBits) { + int byte=alias->Ipv4.NBits/8; + int bit=alias->Ipv4.NBits%8; + if ((byte<1 || memcmp(ipv4,alias->Ipv4.Ip,byte)==0) && (bit==0 || (ipv4[byte] ^ alias->Ipv4.Ip[byte]) & (0xFFU<<(8-bit)))==0) { + if (nbits==alias->Ipv4.NBits) + debuga(_("Duplicate aliasing directive for IPv4 address %d.%d.%d.%d/%d\n"), + ipv4[0],ipv4[1],ipv4[2],ipv4[3],nbits); + else + debuga(_("IPv4 aliasing directive ignored for IPv4 address %d.%d.%d.%d/%d as it is" + " narrower than a previous one (%d.%d.%d.%d/%d\n"), + ipv4[0],ipv4[1],ipv4[2],ipv4[3],nbits, + alias->Ipv4.Ip[0],alias->Ipv4.Ip[1],alias->Ipv4.Ip[2],alias->Ipv4.Ip[3], + alias->Ipv4.NBits); + return(0); + } + } + prev_alias=alias; + } + + // insert into the list + new_alias=malloc(sizeof(*new_alias)); + if (!new_alias) { + debuga(_("Not enough memory to store the host name aliasing directives\n")); + return(-1); + } + new_alias->Type=ALIASTYPE_Ipv4; + memcpy(new_alias->Ipv4.Ip,ipv4,4); + new_alias->Ipv4.NBits=nbits; + if (Replace) { + char *tmp; + + len=(int)(ReplaceE-Replace); + tmp=malloc(len+2); + if (!tmp) { + debuga(_("Not enough memory to store the host name aliasing directives\n")); + free(new_alias); + return(-1); + } + tmp[0]=ALIAS_PREFIX; + memcpy(tmp+1,Replace,len); + tmp[len+1]='\0'; + new_alias->Alias=StringBuffer_Store(AliasData->StringBuffer,tmp); + free(tmp); + } else { + char tmp[5*4+1]; + sprintf(tmp,"%c%d.%d.%d.%d/%d",ALIAS_PREFIX,ipv4[0],ipv4[1],ipv4[2],ipv4[3],nbits); + new_alias->Alias=StringBuffer_Store(AliasData->StringBuffer,tmp); + } + if (!new_alias->Alias) { + debuga(_("Not enough memory to store the IPv4 aliasing directives\n")); + free(new_alias); + return(-1); + } + + new_alias->Next=NULL; + if (prev_alias) + prev_alias->Next=new_alias; + else + AliasData->First=new_alias; + return(1); +} + +/*! + Store a IPv6 to alias. + + \param ipv6 The IPv6 to match. + \param nbits The number of bits in the prefix + \param next A pointer to the first character after the address. + + \retval 1 Alias added. + \retval 0 Ignore the line. + \retval -1 Error. + */ +static int Alias_StoreIpv6(struct AliasStruct *AliasData,unsigned short *ipv6,int nbits,const char *next) +{ + const char *Replace; + const char *ReplaceE; + const char *str; + struct AliasItemStruct *alias; + struct AliasItemStruct *new_alias; + struct AliasItemStruct *prev_alias; + int len; + + // get the alias + Replace=next; + while (*Replace==' ' || *Replace=='\t') Replace++; + if ((unsigned char)*Replace<' ') { + Replace=NULL; + } else { + for (str=Replace ; *str && (unsigned char)*str>=' ' ; str++); + ReplaceE=str; + } + + // check for duplicate or broader range + prev_alias=NULL; + for (alias=AliasData->First ; alias ; alias=alias->Next) { + if (alias->Type==ALIASTYPE_Ipv6 && nbits>=alias->Ipv6.NBits) { + int word=alias->Ipv6.NBits/16; + int bit=alias->Ipv6.NBits%16; + if ((word<1 || memcmp(ipv6,alias->Ipv6.Ip,word*2)==0) && (bit==0 || (ipv6[word] ^ alias->Ipv6.Ip[word]) & (0xFFFFU<<(16-bit)))==0) { + if (nbits==alias->Ipv6.NBits) + debuga(_("Duplicate aliasing directive for IPv6 address %x:%x:%x:%x:%x:%x:%x:%x/%d\n"), + ipv6[0],ipv6[1],ipv6[2],ipv6[3],ipv6[4],ipv6[5],ipv6[6],ipv6[7],nbits); + else + debuga(_("IPv6 aliasing directive ignored for IPv6 address %x:%x:%x:%x:%x:%x:%x:%x/%d as it is" + " narrower than a previous one (%x:%x:%x:%x:%x:%x:%x:%x/%d\n"), + ipv6[0],ipv6[1],ipv6[2],ipv6[3],ipv6[4],ipv6[5],ipv6[6],ipv6[7],nbits, + alias->Ipv6.Ip[0],alias->Ipv6.Ip[1],alias->Ipv6.Ip[2],alias->Ipv6.Ip[3],alias->Ipv6.Ip[4], + alias->Ipv6.Ip[5],alias->Ipv6.Ip[6],alias->Ipv6.Ip[7],alias->Ipv6.NBits); + return(0); + } + } + prev_alias=alias; + } + + // insert into the list + new_alias=malloc(sizeof(*new_alias)); + if (!new_alias) { + debuga(_("Not enough memory to store the host name aliasing directives\n")); + return(-1); + } + new_alias->Type=ALIASTYPE_Ipv6; + memcpy(new_alias->Ipv6.Ip,ipv6,8*sizeof(unsigned short int)); + new_alias->Ipv6.NBits=nbits; + if (Replace) { + char *tmp; + len=ReplaceE-Replace; + tmp=malloc(len+2); + if (!tmp) { + debuga(_("Not enough memory to store the host name aliasing directives\n")); + free(new_alias); + return(-1); + } + tmp[0]=ALIAS_PREFIX; + memcpy(tmp+1,Replace,len); + tmp[len+1]='\0'; + new_alias->Alias=StringBuffer_Store(AliasData->StringBuffer,tmp); + free(tmp); + } else { + char tmp[5*8+5]; + 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); + new_alias->Alias=StringBuffer_Store(AliasData->StringBuffer,tmp); + } + if (!new_alias->Alias) { + debuga(_("Not enough memory to store the IPv6 aliasing directives\n")); + free(new_alias); + return(-1); + } + + new_alias->Next=NULL; + if (prev_alias) + prev_alias->Next=new_alias; + else + AliasData->First=new_alias; + return(1); +} + +#ifdef USE_PCRE +/*! +Store a regular expression to match the alias. + +\retval 1 Alias added. +\retval 0 Ignore the line. +\retval -1 Error. +*/ +static int Alias_StoreRegexp(struct AliasStruct *AliasData,char *buf) +{ + char Delimiter; + char *End; + struct AliasItemStruct *alias; + struct AliasItemStruct *new_alias; + struct AliasItemStruct **prev_alias; + const char *PcreError; + int ErrorOffset; + char *Replace; + int len; + char *tmp; + int i; + + // find the pattern + Delimiter=*buf++; + for (End=buf ; *End && *End!=Delimiter ; End++) { + if (*End=='\\') { + if (End[1]=='\0') { + debuga(_("Invalid NUL character found in regular expression\n")); + return(-1); + } + End++; //ignore the escaped character + } + } + if (*End!=Delimiter) { + debuga(_("Unterminated regular expression\n")); + return(-1); + } + *End++='\0'; + + // find the alias + for (Replace=End ; *Replace==' ' || *Replace=='\t' ; Replace++); + for (End=Replace ; *End && (unsigned char)*End>' ' ; End++); + *End='\0'; + + // store it + new_alias=malloc(sizeof(*new_alias)); + if (!new_alias) { + debuga(_("Not enough memory to store the host name aliasing directives\n")); + return(-1); + } + new_alias->Type=ALIASTYPE_Pcre; + new_alias->Next=NULL; + new_alias->Regex.Re=pcre_compile(buf,0,&PcreError,&ErrorOffset,NULL); + if (new_alias->Regex.Re==NULL) { + debuga(_("Failed to compile the regular expression \"%s\": %s\n"),buf,PcreError); + free(new_alias); + return(-1); + } + len=strlen(Replace); + tmp=malloc(len+2); + if (!tmp) { + debuga(_("Not enough memory to store the host name aliasing directives\n")); + pcre_free(new_alias->Regex.Re); + return(-1); + } + tmp[0]=ALIAS_PREFIX; + memcpy(tmp+1,Replace,len); + tmp[len+1]='\0'; + new_alias->Alias=StringBuffer_Store(AliasData->StringBuffer,tmp); + free(tmp); + if (!new_alias->Alias) { + debuga(_("Not enough memory to store the regex aliasing directives\n")); + free(new_alias); + return(-1); + } + + new_alias->Regex.SubPartern=false; + for (i=1 ; tmp[i] ; i++) + // both the sed \1 and the perl $1 replacement operators are accepted + if ((tmp[i]=='\\' || tmp[i]=='$') && isdigit(tmp[i+1])) { + new_alias->Regex.SubPartern=true; + break; + } + + // chain it + prev_alias=&AliasData->First; + for (alias=AliasData->First ; alias ; alias=alias->Next) + prev_alias=&alias->Next; + *prev_alias=new_alias; + + return(1); +} +#endif + +/*! +Store an alias in the corresponding list. + +\param String The string to parse and store. + +\retval 0 No error. +\retval -1 Error in file. +\retval -2 Unknown string type to store. +*/ +int Alias_Store(struct AliasStruct *AliasData,char *String) +{ + int type; + const char *name; + unsigned char ipv4[4]; + unsigned short int ipv6[8]; + int nbits; + const char *next; + int Error=-2; + + if (*String=='#' || *String==';') return(0); + + if (strncasecmp(String,"re:",3)==0) { +#ifdef USE_PCRE + if (Alias_StoreRegexp(AliasData,String+3)<0) + return(-1); +#else + debuga(_("PCRE not compiled in therefore the regular expressions are not available to alias items\n")); + return(-1); +#endif + } + else + { + type=extract_address_mask(String,&name,ipv4,ipv6,&nbits,&next); + if (type==1) { + Error=Alias_StoreName(AliasData,name,next); + } else if (type==2) { + Error=Alias_StoreIpv4(AliasData,ipv4,nbits,next); + } else if (type==3) { + Error=Alias_StoreIpv6(AliasData,ipv6,nbits,next); + } else { + return(-1); + } + if (Error<0) return(-1); + } + return(0); +} + +/*! + Print the list of the aliases stored in the object. + + \param AliasData Object created by Alias_Create() and + containing the aliases stored by Alias_Store(). + */ +void Alias_PrintList(struct AliasStruct *AliasData) +{ + struct AliasItemStruct *alias; + + for (alias=AliasData->First ; alias ; alias=alias->Next) { + switch (alias->Type) + { + case ALIASTYPE_Name: + debuga(_(" %s => %s\n"),alias->Name.Mask,alias->Alias); + break; + case ALIASTYPE_Ipv4: + debuga(_(" %d.%d.%d.%d/%d => %s\n"),alias->Ipv4.Ip[0],alias->Ipv4.Ip[1],alias->Ipv4.Ip[2], + alias->Ipv4.Ip[3],alias->Ipv4.NBits,alias->Alias); + break; + case ALIASTYPE_Ipv6: + debuga(_(" %x:%x:%x:%x:%x:%x:%x:%x/%d => %s\n"),alias->Ipv6.Ip[0],alias->Ipv6.Ip[1],alias->Ipv6.Ip[2], + alias->Ipv6.Ip[3],alias->Ipv6.Ip[4],alias->Ipv6.Ip[5],alias->Ipv6.Ip[6],alias->Ipv6.Ip[7], + alias->Ipv6.NBits,alias->Alias); + break; + case ALIASTYPE_Pcre: + debuga(_(" Re => %s\n"),alias->Alias); + break; + } + } +} + +/*! +Replace the name by its alias if it is in our list. + +\param name The name to find in the list. + +\return The pointer to the name or its alias. +*/ +static bool Alias_MatchName(struct AliasItemStruct *alias,const char *name,int len) +{ + int k; + const char *Searched; + const char *Candidate; + + if (!alias->Name.Wildcards) + { + if (len!=alias->Name.MinLen) return(false); + return(strcmp(name,alias->Name.Mask)==0); + } + if (lenName.MinLen) return(false); + Candidate=name; + Searched=alias->Name.Mask; + if (Searched[0]!='*') + { + while (*Searched && *Candidate && *Searched!='*') + { + if (Searched[0]!=Candidate[0]) return(false); + Searched++; + Candidate++; + } + } + if (Searched[0]=='*') Searched++; + while (Searched[0] && Candidate[0]) + { + while (Candidate[0] && Candidate[0]!=Searched[0]) Candidate++; + for (k=0 ; Candidate[k] && Searched[k] && Searched[k]!='*' && Searched[k]==Candidate[k] ; k++); + if (Candidate[k]=='\0') + { + return(Searched[k]=='\0' || (Searched[k]=='*' && Searched[k+1]=='\0')); + } + if (Searched[k]=='\0') return(false); + if (Searched[k]=='*') + { + Searched+=k+1; + Candidate+=k; + } + else + Candidate++; + } + return(Searched[0]=='\0'); +} + +/*! +Replace the IPv4 address by its alias if it is in our list. + +\param url The host name. +\param ipv4 The address. + +\return The pointer to the host name or its alias. +*/ +static bool Alias_MatchIpv4(struct AliasItemStruct *alias,unsigned char *ipv4) +{ + int len; + + len=alias->Ipv4.NBits; + if ((len<8 || memcmp(ipv4,alias->Ipv4.Ip,len/8)==0) && ((len%8)==0 || (ipv4[len/8] ^ alias->Ipv4.Ip[len/8]) & (0xFFU<<(8-len%8)))==0) { + return(true); + } + return(false); +} + +/*! +Replace the IPv6 address by its alias if it is in our list. + +\param url The host name. +\param ipv6 The address. + +\return The pointer to the host name or its alias. +*/ +static bool Alias_MatchIpv6(struct AliasItemStruct *alias,unsigned short int *ipv6) +{ + int len; + int i; + + len=alias->Ipv6.NBits; + for (i=len/16-1 ; i>=0 && ipv6[i]==alias->Ipv6.Ip[i] ; i--); + if (i<0) { + i=len/16; + if (i>=8 || len%16==0 || ((ipv6[i] ^ alias->Ipv6.Ip[i]) & (0xFFFF<<(len-i*16)))==0) { + return(true); + } + } + return(false); +} + +#ifdef USE_PCRE +/*! +Replace the host name by its alias if it is in our list. + +\param url_ptr A pointer to the host name to match. It is replaced +by a pointer to the alias if a match is found. + +\return A pointer to the replacement string or NULL if the regex +doesn't match. + +\warning The function is not thread safe as it may return a static +internal buffer. +*/ +static const char *Alias_MatchRegex(struct AliasItemStruct *alias,const char *name) +{ + int nmatches; + int len; + int ovector[30];//size must be a multiple of 3 + static char Replacement[1024]; + const char *str; + int i; + int sub; + int repl_idx; + + len=strlen(name); + nmatches=pcre_exec(alias->Regex.Re,NULL,name,len,0,0,ovector,sizeof(ovector)/sizeof(ovector[0])); + if (nmatches<0) return(NULL); + + if (nmatches==0) nmatches=(int)(sizeof(ovector)/sizeof(ovector[0]))/3*2; //only 2/3 of the vector is used by pcre_exec + if (nmatches==1 || !alias->Regex.SubPartern) { //no subpattern to replace + return(alias->Alias); + } + + repl_idx=0; + str=alias->Alias; + for (i=0 ; str[i] ; i++) { + // both the sed \1 and the perl $1 replacement operators are accepted + if ((str[i]=='\\' || str[i]=='$') && isdigit(str[i+1])) { + sub=str[++i]-'0'; + if (sub>=1 && sub<=nmatches) { + /* + * ovector[sub] is the start position of the match. + * ovector[sub+1] is the end position of the match. + */ + sub<<=1; + if (repl_idx+ovector[sub+1]-ovector[sub]>=sizeof(Replacement)-1) break; + memcpy(Replacement+repl_idx,name+ovector[sub],ovector[sub+1]-ovector[sub]); + repl_idx+=ovector[sub+1]-ovector[sub]; + continue; + } + } + if (repl_idx>=sizeof(Replacement)-1) break; + Replacement[repl_idx++]=str[i]; + } + Replacement[repl_idx]='\0'; + return(Replacement); +} +#endif + +const char *Alias_Replace(struct AliasStruct *AliasData,const char *Name) +{ + struct AliasItemStruct *alias; + int type; + unsigned char ipv4[4]; + unsigned short int ipv6[8]; + int len; + char lname[MAX_ALIAS_LEN]; + + if (!AliasData) return(Name); + for (len=0 ; lenFirst ; alias ; alias=alias->Next) { + if (type==2) { + if (alias->Type==ALIASTYPE_Ipv4 && Alias_MatchIpv4(alias,ipv4)) + return(alias->Alias); + } + if (type==3) { + if (alias->Type==ALIASTYPE_Ipv6 && Alias_MatchIpv6(alias,ipv6)) + return(alias->Alias); + } +#ifdef USE_PCRE + if (alias->Type==ALIASTYPE_Pcre) { + const char *Result=Alias_MatchRegex(alias,Name); + if (Result) return(Result); + } +#endif + if (alias->Type==ALIASTYPE_Name && Alias_MatchName(alias,lname,len)) + return(alias->Alias); + } + return(Name); +} diff --git a/getconf.c b/getconf.c index 43f0657..f034e7c 100644 --- a/getconf.c +++ b/getconf.c @@ -779,6 +779,8 @@ static void parmtest(char *buf) if (getparam_string("hostalias",buf,HostAliasFile,sizeof(HostAliasFile))>0) return; + if (getparam_string("useralias",buf,UserAliasFile,sizeof(UserAliasFile))>0) return; + if (getparam_bool("keep_temp_log",buf,&KeepTempLog)>0) return; if (getparam_int("max_successive_log_errors",buf,&NumLogSuccessiveErrors)>0) return; diff --git a/include/alias.h b/include/alias.h new file mode 100644 index 0000000..f9b445d --- /dev/null +++ b/include/alias.h @@ -0,0 +1,14 @@ +#ifndef ALIAS_HEADER +#define ALIAS_HEADER + +//! Object to group items. +typedef struct AliasStruct *AliasObject; + +AliasObject Alias_Create(void); +void Alias_Destroy(AliasObject *AliasPtr); + +int Alias_Store(AliasObject AliasData,char *String); +void Alias_PrintList(struct AliasStruct *AliasData); +const char *Alias_Replace(struct AliasStruct *AliasData,const char *Name); + +#endif // ALIAS_HEADER diff --git a/include/conf.h b/include/conf.h index 9080e5d..3de0631 100755 --- a/include/conf.h +++ b/include/conf.h @@ -441,6 +441,8 @@ char GraphFont[MAXLEN]; char SortTableJs[256]; //! The name of the file containing the host names to replace by an alias in the report. char HostAliasFile[512]; +//! The name of the file containing the user names to replace by an alias in the report. +char UserAliasFile[512]; //! The number of consecutive errors allowed in an input log file before the process is interrupted. int NumLogSuccessiveErrors; /*! diff --git a/include/defs.h b/include/defs.h index d9453a3..6a14051 100755 --- a/include/defs.h +++ b/include/defs.h @@ -279,6 +279,9 @@ userscan userinfo_startscan(void); void userinfo_stopscan(userscan uscan); struct userinfostruct *userinfo_advancescan(userscan uscan); void userinfo_clearflag(void); +void read_useralias(const char *Filename); +void free_useralias(void); +const char *process_user(const char *user); // usertab.c void init_usertab(const char *UserTabFile); diff --git a/log.c b/log.c index 2277276..92e10a1 100644 --- a/log.c +++ b/log.c @@ -237,6 +237,7 @@ int main(int argc,char *argv[]) DansGuardianConf[0]='\0'; hm_str[0]='\0'; HostAliasFile[0]='\0'; + UserAliasFile[0]='\0'; dansguardian_count=0; redirector_count=0; @@ -531,6 +532,8 @@ int main(int argc,char *argv[]) } if (HostAliasFile[0] != '\0') read_hostalias(HostAliasFile); + if (UserAliasFile[0] != '\0') + read_useralias(UserAliasFile); indexonly=false; if(ReadFilter.UserFilter) { @@ -743,6 +746,7 @@ int main(int argc,char *argv[]) ip2name_cleanup(); free_hostalias(); + free_useralias(); userinfo_free(); if(userfile) free(userfile); diff --git a/readlog.c b/readlog.c index 7f04113..f47fd73 100644 --- a/readlog.c +++ b/readlog.c @@ -527,6 +527,7 @@ static void ReadOneLogFile(struct ReadLogDataStruct *Filter,const char *arq) } } + log_entry.User=process_user(log_entry.User); if (log_entry.User[0]=='\0' || (log_entry.User[1]=='\0' && (log_entry.User[0]=='-' || log_entry.User[0]==' ' || log_entry.User[0]==':'))) { excluded_count[ER_NoUser]++; diff --git a/sarg.conf b/sarg.conf index bc17892..881fda8 100644 --- a/sarg.conf +++ b/sarg.conf @@ -808,6 +808,24 @@ # re:/([\w-]+)\.(\w*[a-zA-Z]\w*)(?::\d+)?$/\1.\2 #hostalias /usr/local/sarg/hostalias +# TAG: useralias +# The name of a text file containing the user names one per line and the +# optional alias to use in the report instead of that user name. +# User names may contain wildcards denoted by a *. +# The user name may be followed by an optional alias but if no alias is +# provided, the user name, including the wildcard, replaces any matching +# user name found in the log. +# User names replaced by identical aliases are grouped together in the +# reports. +# IP addresses are supported and accept the CIDR notation both for IPv4 and +# IPv6 addresses. +# Regular expressions can also be used if sarg was compiled with libpcre. +# A regular expression is formated as re:/regexp/ alias +# The regexp is a perl regular expression (see man perlre). +# Subpatterns are allowed in the alias. Sarg recognizes sed (\1) or perl ($1) +# subpatterns. Only 9 subpatterns are allowed in the replacement string. +#useralias /usr/local/sarg/useralias + # TAG: keep_temp_log yes|no # Keep temporary files created by sarg to produce its reports. The normal # operation mode is to delete those files when they are not necessary any more. diff --git a/userinfo.c b/userinfo.c index f40c3fe..c1b5dc0 100644 --- a/userinfo.c +++ b/userinfo.c @@ -27,6 +27,7 @@ #include "include/conf.h" #include "include/defs.h" #include "include/stringbuffer.h" +#include "include/alias.h" //! The number of users to group in one unit. #define USERS_PER_GROUP 50 @@ -61,6 +62,8 @@ static struct usergroupstruct *first_user_group=NULL; static int AnonymousCounter=0; //! String buffer to store the user's related constants. static StringBufferObject UserStrings=NULL; +//! User aliases. +static AliasObject UserAliases=NULL; struct userinfostruct *userinfo_create(const char *userid) { @@ -274,3 +277,67 @@ void userinfo_clearflag(void) } } +/*! +Read the file containing the user names to alias in the report. + +\param Filename The name of the file. +*/ +void read_useralias(const char *Filename) +{ + FILE *fi; + longline line; + char *buf; + + if (debug) debuga(_("Reading user alias file \"%s\"\n"),Filename); + + UserAliases=Alias_Create(); + if (!UserAliases) { + debuga(_("Cannot store user's aliases\n")); + exit(EXIT_FAILURE); + } + + fi=fopen(Filename,"rt"); + if (!fi) { + debuga(_("Cannot read user name alias file \"%s\" - %s\n"),Filename,strerror(errno)); + exit(EXIT_FAILURE); + } + + if ((line=longline_create())==NULL) { + debuga(_("Not enough memory to read the user name aliases\n")); + exit(EXIT_FAILURE); + } + + while ((buf=longline_read(fi,line)) != NULL) { + if (Alias_Store(UserAliases,buf)<0) { + debuga(_("While reading \"%s\"\n"),Filename); + exit(EXIT_FAILURE); + } + } + + longline_destroy(&line); + fclose(fi); + + if (debug) { + debuga(_("List of user names to alias:\n")); + Alias_PrintList(UserAliases); + } +} + +/*! +Free the memory allocated by read_useralias(). +*/ +void free_useralias(void) +{ + Alias_Destroy(&UserAliases); +} + +/*! +Replace the user's name or ID by an alias if one is defined. + +\param user The user's name or ID as extracted from the report. +*/ +const char *process_user(const char *user) +{ + user=Alias_Replace(UserAliases,user); + return(user); +}