#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Squid redirector with RBL support * Redirects to other url if (part of) url is found in list. * Looks up hosts and IP adresses in DNS based blacklists. * * Copyright (C) 2000 - 2021 R.J. van der Putten, Leiden, Holland, * rob at sput dot nl. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * 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. */ #define RSD_VERSION "2021-07-19 19:10:51 UTC" /* * Uncomment the line below for RBL TXT lookups. * You need to compile with -lresolv for this to work; * cc -O2 -Wall -lresolv -o rblsredir rblsredir.c */ //#define RSD_TXT_LKP 1 /* Uncomment for old ACL style files */ //#define USE_OLD_ACL 1 /* Config stuff */ #define RSD_CONFILE "/etc/sredir/sredir.conf" #define RSD_HOSTS_ALLOW "/etc/sredir/hosts.allow" #define RSD_HOSTS_DENY "/etc/sredir/hosts.deny" #define RSD_URLS_ALLOW "/etc/sredir/urls.allow" #define RSD_URLS_DENY "/etc/sredir/urls.deny" /* Log file */ #define RSD_LOGFILE "/var/local/log/squid/sredir.log" struct timeval tv; //char logstr[256]; char tologstr[2048]; FILE *logfil; /* * RBL stuff. * Increase the number below if you want more then 8 DNS based blacklists. * Keep in mind that each _configured_ RBL causes at least one DNS lookup per * (web)site. */ #define RSD_MAXLISTS 8 struct rblist { int mode; char name[252]; } rblists[RSD_MAXLISTS]; int lstcnt; /* Lookup stuff */ int ipvers; /* 1 when IPv6 address */ int isname; /* 1 when hostname */ char adrs46[256]; /* IPv4 or IPv6 address */ char revadrs[256]; /* Reverse address or host name */ char lookup[512]; struct hostent *hp; /* Host lookup stuff */ int adrs[4]; struct addrinfo hints; struct addrinfo *result, *rp; struct sockaddr_in *addr; struct sockaddr_in6 *addr6; struct in_addr ipaddr; struct in6_addr ipaddr6; uint32_t addr4; /* TXT lookup stuff */ #ifdef RSD_TXT_LKP /* struct res_state *statep; */ unsigned char buff[4096]; ns_msg mesg; ns_rr rr; char logbuf[512]; extern int h_errno; #endif /* main */ int af; /* Check address */ int df; /* Debug */ int hlen; char host[256]; /* Hostname */ wchar_t whost[256]; /* Wide char hostname */ int tlf; /* Hostname to lower */ int sslf; int urlen; int umf; char url[4096]; pid_t pid; /* Allow and deny lists */ struct permlist { uint8_t *start; /* Avoid sign confusion */ int cols; int lines; } allowhosts, denyhosts, allowurls, denyurls; void hlp(void) { fprintf(stderr, "RBL Squid redirector version: %s\n", RSD_VERSION); fprintf(stderr, "Config files:\n\n"); fprintf(stderr, "%s\n", RSD_CONFILE); fprintf(stderr, "redirurl Method://host[:Port][/Path]/File\n"); fprintf(stderr, " EG: "); fprintf(stderr, "redirurl http://www.example.org/icons/blank.gif\n"); fprintf(stderr, "checkaddr on\n"); fprintf(stderr, "debug on\n"); fprintf(stderr, "tolow on\n"); fprintf(stderr, "dnsbl mode rbl_name\n"); fprintf(stderr, " mode is an OR of;\n"); fprintf(stderr, " 1: Check hostname\n"); fprintf(stderr, " 2: If alias, check CNAME\n"); fprintf(stderr, " 4: Check IP address(es)\n"); #ifdef RSD_TXT_LKP fprintf(stderr, " 8: Lookup and log TXT record\n"); #endif fprintf(stderr, " EG: "); fprintf(stderr, "dnsbl 4 xbl.spamhaus.org\n"); fprintf(stderr, " You can configure a maximum of %d RBLs.\n\n", RSD_MAXLISTS); fprintf(stderr, "%s and\n%s\n", RSD_HOSTS_ALLOW, RSD_HOSTS_DENY); fprintf(stderr, "Host name, IP address or range\n"); fprintf(stderr, " Examples;\n"); fprintf(stderr, " ad.doubleclick.net\n"); fprintf(stderr, " *.doubleclick.net\n"); fprintf(stderr, " *.doubleclick.*\n"); fprintf(stderr, " 192.168.2.1\n"); fprintf(stderr, " 192.168.2.*\n"); fprintf(stderr, " 2001:db8:2::1\n"); fprintf(stderr, " 2001:db8:2:*\n\n"); fprintf(stderr, "%s and\n%s\n", RSD_URLS_ALLOW, RSD_URLS_DENY); fprintf(stderr, "[Method://][host][:Port][/Path][/File]\n"); fprintf(stderr, " Examples;\n"); fprintf(stderr, " http://ad.doubleclick.net/*\n"); fprintf(stderr, " *count.gif\n"); fprintf(stderr, " *doubleclick*\n"); fprintf(stderr, "Logs to %s\n", RSD_LOGFILE); #ifdef RSD_TXT_LKP fprintf(stderr, \ " Format: epoch.ms pid blocked_host_or_ip blacklist A_record lookup TXT_record\n"); #else fprintf(stderr, \ " Format: epoch.ms pid blocked_host_or_ip blacklist A_record lookup\n"); #endif fprintf(stderr, " The directory has to be writable by the Squid process owner.\n\n"); fprintf(stderr, "Copyright (c) 2000 - 2021, R.J. van der Putten, Leiden, "); fprintf(stderr, "Holland, rob at sput dot nl\n"); fprintf(stderr, "GNU GPL applies.\n\n"); } int prtolog(char *s1, char *s2) { if(s1 == NULL) return(1); gettimeofday(&tv, NULL); if(s2 == NULL) snprintf(tologstr, 2047, "%ld.%03d %5d %s\n", \ tv.tv_sec, (int) (tv.tv_usec / 1000), pid, s1); else snprintf(tologstr, 2047, "%ld.%03d %5d %s %s\n", \ tv.tv_sec, (int) (tv.tv_usec / 1000), pid, s1, s2); fprintf(logfil, "%s", tologstr); fflush(logfil); return(0); } #ifdef RSD_TXT_LKP /* Lookup TXT record */ int rbltxtlkp(char *lookup) { int cnt, i, len; cnt = 0; i = 0; len = 0; logbuf[0] = 0; /* int res_query(const char *dname, int class, int type, unsigned char *answer, int anslen); */ /* if ((len = res_query(lookup, C_IN, T_TXT, buff, 4096)) < 0) { */ if ((len = res_query(lookup, ns_c_in, ns_t_txt, buff, 4096)) < 0) { if(df != 0) prtolog("Debug: rbltxtlkp(): res_query() failed:", \ (char *) hstrerror(h_errno)); return(1); } /* int ns_initparse(const u_char *msg, int msglen, ns_msg *handle) */ if (ns_initparse(buff, len, &mesg) < 0) { if(df != 0) prtolog("Debug: rbltxtlkp(): ns_initparse() failed:", \ strerror(errno)); return(1); } /* u_int16_t ns_msg_count(ns_msg handle, ns_sect section) */ if ((cnt = ns_msg_count(mesg, ns_s_an)) < 1) { if(df != 0) prtolog("Debug: rbltxtlkp(): ns_msg_count() failed:", \ strerror(errno)); return(1); } for (i = 0; i < cnt; i++) { /* int ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr) */ if (ns_parserr(&mesg, ns_s_an, i, &rr) < 0) { if(df != 0) prtolog("Debug: rbltxtlkp(): ns_parserr() failed:", \ strerror(errno)); return(1); } if (rr.rdlength < 256) { strcat(logbuf, " "); /* 1st byte is stringlength */ strncat(logbuf, \ (char *) rr.rdata + 1, rr.rdlength - 1); } if(strlen(logbuf) > 254) break; } return(0); } #endif /* RBL lookup */ int rblookup(char *list, int mode) { int j; j = 0; snprintf(lookup, 511, "%s%s.", revadrs, list); if (df != 0) prtolog("Debug: rblookup(): Lookup:", lookup); hp = gethostbyname(lookup); if (hp == NULL) { if (df != 0) prtolog("Debug: rblookup(): Not listed", NULL); } else { umf = 1; j = 0; while (hp->h_addr_list[j] != NULL) { gettimeofday(&tv, NULL); #ifdef RSD_TXT_LKP if ((mode & 8) != 0 && rbltxtlkp(lookup) == 0 && logbuf[0] != 0) { /* epoch.ms pid blocked_host_or_ip blacklist A lookup TXT */ snprintf(tologstr, 2047, "%ld.%03d %5d %s %s %s %s%s\n", \ tv.tv_sec, (int) (tv.tv_usec / 1000), pid, host, list, \ inet_ntoa( *(struct in_addr*) (hp->h_addr_list[j])), \ lookup, logbuf); } else { /* epoch.ms pid blocked_host_or_ip blacklist A lookup */ snprintf(tologstr, 2047, "%ld.%03d %5d %s %s %s %s\n", \ tv.tv_sec, (int) (tv.tv_usec / 1000), pid, host, list, \ inet_ntoa( *(struct in_addr*) (hp->h_addr_list[j])), \ lookup); } #else /* epoch.ms pid blocked_host_or_ip blacklist A lookup */ snprintf(tologstr, 2047, "%ld.%03d %5d %s %s %s %s\n", \ tv.tv_sec, (int) (tv.tv_usec / 1000), pid, host, list, \ inet_ntoa( *(struct in_addr*) (hp->h_addr_list[j])), lookup); #endif fprintf(logfil, "%s", tologstr); fflush(logfil); j++; if(df == 0) return(1); } } return(umf); } /* Finds host in URL */ int fndhost(void) { int hostptr, i; hlen = 0; hostptr = 0; i = 0; ipvers = 0; isname = 0; host[0] = 0; if (sslf != 0) { /* Reqmet is CONNECT; host:port */ if (url[0] == '[') { /* IPv6 address */ hostptr = 1; i = 1; ipvers = 1; while (i < urlen) { if(url[i] == ']') break; i++; } } else { while (i < urlen) { if(url[i] > '@') /* Is name */ isname = 1; else if(url[i] == ':') break; i++; } } if (i > 255) { if (df != 0) prtolog("Debug: fndhost(): Hostname too long", NULL); return(1); } } else { while (i < urlen) { if (url[i] == '/' && url[i + 1] == '/') { if (url[i + 2] == '[') { /* IPv6 address */ ipvers = 1; i++; } i+=2; hostptr = i; break; } i++; } if (i > 255) { if (df != 0) prtolog("Debug: fndhost(): Hostname too long", NULL); return(1); } if (url[i] == 0) { if (df != 0) prtolog("Debug: fndhost(): Malformed URL", NULL); return(1); } while (i < urlen) { if (ipvers == 0) { if(url[i] > '@') /* Is hostname */ isname = 1; else if(url[i] == '/' || url[i] == ':') break; } else { /* IPv6 address */ if (url[i] == ']') { break; } } i++; } if (i > 255) { if (df != 0) prtolog("Debug: fndhost(): Hostname too long", NULL); return(1); } } hlen = i - hostptr; if (hlen != 0) { /* Check hlen before copy */ strncpy(host, url + hostptr, hlen); } host[hlen] = 0; if (hlen == 0 || host[0] == 0) { if (df != 0) prtolog("Debug: fndhost(): Hostname too short", NULL); return(1); } if (df != 0) prtolog("Debug: fndhost(): Found host:", host); /* Test */ /* if (isname == 0 && ipvers == 0) { for (i = 0; i < hlen; i++) { if (host[i] > '@') { isname = 1; break; } } } */ return(0); } /* Looks up Host or Url in ACL */ int proclist(struct permlist acl, int houlen, char *houstr) { int i, len, lincnt; char *aclstr; uint8_t *readptr; if(acl.start == NULL) return(umf); /* * Uchar * +-----+--------------+ * | Int | chrcnt Chars | * +-----+--------------+ * Uint Char * 16 */ i = 0; len = 0; lincnt = 0; aclstr = NULL; readptr = acl.start; /* Start at begin of list */ while (lincnt < acl.lines) { len = (int) (*((uint16_t *) readptr)); /* readptr[0] and readptr[1] */ aclstr = (char *) (readptr + 2); /* readptr[2] */ if(len == 0 || aclstr[0] == 0) break; if (aclstr[0] == '*' && len <= houlen) { if (aclstr[len - 1] == '*') { for (i = 0; i < houlen - len + 3; i++) { if (strncmp(houstr + i, aclstr + 1, len - 2) == 0) { /* example.net/foobar and *foo* */ umf = 1; break; } } if (umf != 0) { if(df != 0) prtolog("Debug: proclist(): Substr-match:", aclstr); break; } } else if (strncmp(houstr + houlen - len + 1, aclstr + 1, len - 1) == 0) { /* example.net/foo and *foo */ if(df != 0) prtolog("Debug: proclist(): End-match:", aclstr); umf = 1; break; } #ifdef USE_OLD_ACL } else if (strncmp(houstr, aclstr, len) == 0) { /* example.net/foo and example.net */ if(df != 0) prtolog("Debug: proclist(): Begin-match:", aclstr); umf = 1; break; #else } else if (aclstr[len - 1] == '*' && len <= houlen && \ strncmp(houstr, aclstr, len - 1) == 0) { /* example.net/foo */ if(df != 0) prtolog("Debug: proclist(): Begin-match:", aclstr); umf = 1; break; } else if (strcmp(houstr, aclstr) == 0) { /* example.net */ if(df != 0) prtolog("Debug: proclist(): Match:", aclstr); umf = 1; break; #endif } /* Advance ptr by record size */ lincnt++; readptr = readptr + acl.cols; } return(umf); } /* Looks up host in RBLs */ int hostlookup(void) { int i, j, len, ret; i = 0; j = 0; len = 0; ret = 0; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; if (df != 0) prtolog("Debug: hostlookup(): Host:", host); if ((ret = getaddrinfo(host, 0, &hints, &result)) != 0) { if (df != 0) prtolog("Debug: hostlookup(): getaddrinfo():", (char *) gai_strerror(ret)); return(0); } if (ipvers == 0 && isname != 0) { /* Host lookup */ strncpy(revadrs, host, 254); revadrs[254] = 0; strcat(revadrs, "."); for (i = 0; i < lstcnt; i++) { if ((rblists[i].mode & 1) != 0) { if (rblookup(rblists[i].name, rblists[i].mode) != 0) { umf = 1; if (df == 0) { freeaddrinfo(result); return(1); } } } } } for (rp = result; rp != NULL; rp = rp->ai_next) { if (rp->ai_canonname != NULL && strcasecmp(host, rp->ai_canonname) != 0) { strncpy(revadrs, rp->ai_canonname, 254); revadrs[254] = 0; if (df != 0) prtolog("Debug: hostlookup(): Cname:", revadrs); /* CNAME check */ len = strlen(revadrs); if (len != 0 && denyhosts.start != NULL) { /* Lookup CNAME in deny file */ if (df != 0) prtolog("Debug: hostlookup(): Check deny list", NULL); if (proclist(denyhosts, len, revadrs) != 0 && df == 0) { freeaddrinfo(result); return(1); } } strcat(revadrs, "."); for (i = 0; i < lstcnt; i++) { if ((rblists[i].mode & 2) != 0) { if (rblookup(rblists[i].name, rblists[i].mode) != 0) { umf = 1; if (df == 0) { freeaddrinfo(result); return(1); } } } } } if (rp->ai_family == AF_INET) { addr = (struct sockaddr_in *) rp->ai_addr; ipaddr = addr->sin_addr; addr4 = ntohl(ipaddr.s_addr); for (i = 3; i > -1; i--) { adrs[i] = 0xff & addr4; addr4 = addr4 >> 8; } /* Address */ if (inet_ntop(AF_INET, &ipaddr, adrs46, 255) == NULL) adrs46[0] = 0; /* Reverse */ sprintf(revadrs, "%d.%d.%d.%d.", adrs[3], adrs[2], adrs[1], adrs[0]); } else if (rp->ai_family == AF_INET6) { addr6 = (struct sockaddr_in6 *) rp->ai_addr; ipaddr6 = addr6->sin6_addr; /* Address */ if (inet_ntop(AF_INET6, &ipaddr6, adrs46, 255) == NULL) adrs46[0] = 0; /* Reverse */ j = 0; for (i = 15; i > -1; i--) { sprintf(revadrs + j, "%x.", (0xf & ipaddr6.s6_addr[i])); j+=2; sprintf(revadrs + j, "%x.", (ipaddr6.s6_addr[i] >> 4)); j+=2; } revadrs[j] = 0; } else { adrs46[0] = 0; revadrs[0] = 0; } if (adrs46[0] != 0) { if (df != 0) prtolog("Debug: hostlookup(): IP:", adrs46); len = strlen(adrs46); if (af != 0 && len != 0 && denyhosts.start != NULL) { /* Lookup IP address in deny file */ if (df != 0) prtolog("Debug: hostlookup(): Check deny list", NULL); if (proclist(denyhosts, len, adrs46) != 0 && df == 0) { freeaddrinfo(result); return(1); } } } if (revadrs[0] != 0) { /* Lookup IP address in RBLs */ for (i = 0; i < lstcnt; i++) { if ((rblists[i].mode & 4) != 0) { if (rblookup(rblists[i].name, rblists[i].mode) != 0) { umf = 1; if (df == 0) { freeaddrinfo(result); return(1); } } } } } } freeaddrinfo(result); return(umf); } void closemyfiles(void) { if(allowhosts.start != NULL) free(allowhosts.start); if(denyhosts.start != NULL) free(denyhosts.start); if(allowurls.start != NULL) free(allowurls.start); if(denyurls.start != NULL) free(denyurls.start); fflush(logfil); fclose(logfil); } /* Reads allow and deny lists */ uint8_t * readlist(char *lstfilnam, int *charcnt, int *linecnt) { FILE *lstfil; char lgstr[512], line[4096], *str; uint8_t *lstptr, *wrtptr; uint16_t *slen; int chrcnt, lstsiz, i, len, lincnt, llf, recsiz; chrcnt = 0; lstfil = NULL; lstptr = NULL; lstsiz = 0; i = 0; len = 0; lincnt = 0; llf = 0; /* Long Line Flag */ recsiz = 0; slen = NULL; str = NULL; memset(lgstr, 0, 512); memset(line, 0, 4096); wrtptr = NULL; if ((lstfil = fopen(lstfilnam, "r")) == NULL) { prtolog("readlist(): Can't open file", lstfilnam); return(NULL); } /* Measure size of allow/deny file entries */ while (fgets(line, 4096, lstfil)) { len = strlen(line); /* Basic checks */ if (line[len - 1] != 10) { llf = 1; continue; } if (llf != 0) { if(line[len - 1] == 10) llf = 0; continue; } if(line[0] == '#') continue; if(len < 3) continue; if(len > chrcnt) chrcnt = len; /* Max nr of chars per line */ lincnt++; /* Number of lines */ } /* Add size of 16 bit int plus one to be safe */ recsiz = chrcnt + 3; lstsiz = (lincnt + 1) * recsiz; lstptr = malloc(lstsiz); if (lstptr == NULL) { fprintf(stderr, "readlist(): Could not allocate memory for list %s\n", \ lstfilnam); /* Failed malloc is bad news */ fclose(lstfil); closemyfiles(); exit(1); } memset(lstptr, 0, lstsiz); if (df != 0) { snprintf(lgstr, 511, \ "Debug: readlist(): %s: Allocated %d bytes for %d lines of max %d bytes", \ lstfilnam, lstsiz, lincnt, chrcnt - 1); prtolog(lgstr, NULL); } /* * Now actually read the file * * Uchar * lstptr, wrtptr * +-----+--------------+ * | Int | chrcnt Chars | * +-----+--------------+ * Uint Char * 16 str * slen */ /* rewind(lstfil); */ if (fseek(lstfil, 0L, SEEK_SET) != 0) { fprintf(stderr, "readlist(): fseek() failed\n"); fclose(lstfil); closemyfiles(); exit(1); } /* lstptr points to 1st, wrtptr to current */ wrtptr = lstptr; slen = (uint16_t *) wrtptr; /* String length */ str = (char *) (wrtptr + 2); /* String */ *charcnt = recsiz; *linecnt = lincnt; lincnt = 0; llf = 0; while (fgets(line, 4096, lstfil)) { len = strlen(line); /* Basic checks */ if (line[len - 1] != 10) { /* No newline */ llf = 1; snprintf(lgstr, 511, "readlist(): Line too long: %.200s Truncated", line); prtolog(lgstr, NULL); continue; } if (llf != 0) { if(line[len - 1] == 10) llf = 0; continue; } if(line[0] == '#') /* Remark */ continue; if(len < 3) /* Too short */ continue; /* Remove all non printable */ i = 0; while (i < len) { if (line[i] < '!' && line[i] > 0) { line[i] = 0; break; } i++; } if (i < 2) { /* Too short */ continue; } /* Valid data. */ *slen = (uint16_t) (0x7fff & i); /* i should be <= 0x3fff anyway */ strncpy(str, line, chrcnt); /* Prepare for next record */ lincnt++; if(lincnt > *linecnt) break; wrtptr = wrtptr + recsiz; slen = (uint16_t *) wrtptr; str = (char *) (wrtptr + 2); } fclose(lstfil); if (df != 0) { snprintf(lgstr, 511, \ "Debug: readlist(): %s: Read %d lines", lstfilnam, lincnt); prtolog(lgstr, NULL); } return(lstptr); } /* Reads config file */ int readcnf(char *errurl) { FILE *confil; char confstr[256]; int confint; char line[4096]; confil = NULL; confint = 0; af = 0; /* Address Flag */ df = 0; /* Debug Flag */ memset(confstr, 0, 256); memset(line, 0, 4096); if ((confil = fopen(RSD_CONFILE, "r")) == NULL) { fprintf(stderr, "readcnf(): Can't open sredir conf file\n"); return(1); } /* Read config file */ while (fgets(line, 4096, confil)) { if (strncmp(line, "redirurl", 8) == 0) { sscanf(line, "%*s %255s", errurl); if(df != 0) prtolog("Debug: readcnf(): Redir URL:", errurl); } else if (strncmp(line, "checkaddr", 9) == 0) { sscanf(line, "%*s %255s", confstr); if (strcmp(confstr, "on") == 0) af = 1; else if (strcmp(confstr, "off") == 0) af = 0; } else if (strncmp(line, "debug", 5) == 0) { sscanf(line, "%*s %255s", confstr); if (strcmp(confstr, "on") == 0) df = 1; else if (strcmp(confstr, "off") == 0) df = 0; } else if (strncmp(line, "tolow", 5) == 0) { sscanf(line, "%*s %255s", confstr); if (strcmp(confstr, "on") == 0) tlf = 1; else if (strcmp(confstr, "off") == 0) tlf = 0; } else if (strncmp(line, "dnsbl", 5) == 0) { if (sscanf(line, "%*s %d %251s", &confint, confstr) == 2 && \ confint > 0 && confint < 16) { if (lstcnt < RSD_MAXLISTS) { rblists[lstcnt].mode = confint; strcpy(rblists[lstcnt].name, confstr); if(df != 0) prtolog("Debug: readcnf(): Blacklist:", confstr); lstcnt++; } else { fprintf(stderr, "readcnf(): Too many rbl entries\n"); fclose(confil); return(1); } } } } fclose(confil); if (errurl[0] == 0) { fprintf(stderr, "readcnf(): No redirurl in config file\n"); return(1); } return(0); } /* Log local denylists */ void logloc(char *houstr, char *file) { gettimeofday(&tv, NULL); /* epoch.ms pid blocked_host_or_ip blacklist A lookup (TXT) */ #ifdef RSD_TXT_LKP snprintf(tologstr, 2047, "%ld.%03d %5d %s local - %s -\n", \ tv.tv_sec, (int) (tv.tv_usec / 1000), pid, houstr, file); #else snprintf(tologstr, 2047, "%ld.%03d %5d %s local - %s\n", \ tv.tv_sec, (int) (tv.tv_usec / 1000), pid, houstr, file); #endif fprintf(logfil, "%s", tologstr); fflush(logfil); } /* Lists ACLs */ int listlist(struct permlist list) { int i, len; uint8_t *readptr; char liststr[256]; if(list.start == NULL) return(0); i = 0; len = 0; readptr = NULL; memset(liststr, 0, 256); /* * Uchar * +-----+--------------+ * | Int | chrcnt Chars | * +-----+--------------+ * Uint Char * 16 */ prtolog("Debug: listlist(): Nr Len String", NULL); for (i = 0; i < list.lines; i++) { readptr = list.start + (i * list.cols); len = (int) (*((uint16_t *) readptr)); if (len < 200) { snprintf(liststr, 255, "Debug: listlist(): %3d %3d %s", \ i + 1, len, (char *) (readptr + 2)); } else { snprintf(liststr, 255, \ "Debug: listlist(): %3d %3d %.200s Truncated", \ i + 1, len, (char *) (readptr + 2)); } prtolog(liststr, NULL); } return(0); } /* Writes URL to Squid */ int wrturl(char *tsqurl, int tsqulen) { int i, len; i = 0; len = 0; while (len < tsqulen) { i = write(1, tsqurl + len, tsqulen - len); if (i < 1) /* Can't write to Squid */ return(1); len = len + i; } return(0); } /* Converts hostname to lower case */ int hostolow(void) { int i; if (mbstowcs(whost, host, 255) < 0) { prtolog("hostolow(): mbstowcs() failed\n", NULL); return(1); } for(i = 0; i < hlen; i++) whost[i] = towlower(whost[i]); if (wcstombs(host, whost, 255) < 0) { prtolog("hostolow(): wcstombs() failed\n", NULL); return(1); } return(0); } int main(int argc, char **argv) { char *errurl, redirurl[264], req[4096], reqmet[16]; int i, redirlen, reqlen; if (argc > 1) { hlp(); exit(1); } /* Lots of zeros */ af = 0; /* Address Flag */ df = 0; /* Debug Flag */ hlen = 0; i = 0; lstcnt = 0; redirlen = 0; reqlen = 0; sslf = 0; tlf = 0; urlen = 0; umf = 0; /* URL Match Flag. 1 if match */ addr = NULL; addr4 = 0; addr6 = NULL; memset(adrs, 0, sizeof(adrs)); memset(adrs46, 0, sizeof(adrs46)); memset(&allowhosts, 0, sizeof(allowhosts)); /* Allow list. Used as string array */ memset(&denyhosts, 0, sizeof(denyhosts)); /* Deny list. Used as string array */ memset(&allowurls, 0, sizeof(allowurls)); memset(&denyurls, 0, sizeof(denyurls)); memset(host, 0, sizeof(host)); hp = NULL; memset(&ipaddr, 0, sizeof(ipaddr)); memset(&ipaddr6, 0, sizeof(ipaddr6)); //memset(logstr, 0, sizeof(logstr)); /* String to log */ memset(rblists, 0, sizeof(rblists)); /* Array of RBL structs */ memset(redirurl, 0, sizeof(redirurl)); /* Replacement URL */ memset(req, 0, sizeof(req)); /* Request from Squid */ memset(reqmet, 0, sizeof(reqmet)); /* Request method */ memset(tologstr, 0, sizeof(tologstr)); /* String to write to log */ memset(&tv, 0, sizeof(tv)); memset(url, 0, sizeof(url)); /* URL from request */ memset(whost, 0, sizeof(whost)); logfil = NULL; #ifdef RSD_TXT_LKP memset(buff, 0, 4096); memset(&mesg, 0, sizeof(mesg)); memset(&rr, 0, sizeof(rr)); memset(logbuf, 0, 512); #endif /* Set the locale to UTF-8 */ setlocale(LC_ALL, "C.UTF-8"); /* Prepare redirurl */ strcpy(redirurl, "302:"); errurl = redirurl + 4; /* Get my pid */ pid = getpid(); /* Logging */ if ((logfil = fopen(RSD_LOGFILE, "a")) == NULL) { fprintf(stderr, "main(): Can't open sredir log file\n"); exit(1); } prtolog("main(): RBL Squid redirector version:", RSD_VERSION); /* Read config file */ if (readcnf(errurl) != 0) { fprintf(stderr, "main(): Error reading config file\n"); fflush(logfil); fclose(logfil); exit(1); } strcat(redirurl, "\n"); redirlen = strlen(redirurl); /* Read host allow file */ if((allowhosts.start = readlist(RSD_HOSTS_ALLOW, &(allowhosts.cols), &(allowhosts.lines))) == NULL) prtolog("main(): Failed reading allow list file:", RSD_HOSTS_ALLOW); else if (df != 0) listlist(allowhosts); /* Read host deny file */ if((denyhosts.start = readlist(RSD_HOSTS_DENY, &(denyhosts.cols), &(denyhosts.lines))) == NULL) prtolog("main(): Failed reading deny list file:", RSD_HOSTS_DENY); else if (df != 0) listlist(denyhosts); /* Read URL allow file */ if((allowurls.start = readlist(RSD_URLS_ALLOW, &(allowurls.cols), &(allowurls.lines))) == NULL) prtolog("main(): Failed reading allow list file:", RSD_URLS_ALLOW); else if (df != 0) listlist(allowurls); /* Read URL deny file */ if((denyurls.start = readlist(RSD_URLS_DENY, &(denyurls.cols), &(denyurls.lines))) == NULL) prtolog("main(): Failed reading deny list file:", RSD_URLS_DENY); else if (df != 0) listlist(denyurls); /* The actual redirector */ while(1) { /* Read stdin till LF */ i = 0; req[0] = 0; reqlen = 0; reqmet[0] = 0; sslf = 0; umf = 0; urlen = 0; while (reqlen < 4088) { i = read(0, req + reqlen, 4088 - reqlen - 1); if (i < 1) { /* Can't read from Squid */ closemyfiles(); exit(1); } reqlen = reqlen + i; if(req[reqlen - 1] == 10) break; } /* Check request */ if (reqlen < 1 || req[reqlen - 1] != 10) { /* Too long or empty */ if (wrturl(redirurl, redirlen) != 0) { /* Can't write to Squid */ closemyfiles(); exit(1); } continue; /* No more checks */ } req[reqlen - 1] = 0; /* Remove newline */ /* URL client_ip "/" fqdn user method [ kvpairs] */ sscanf(req, "%s %*s %*s %15s %*s", url, reqmet); urlen = strlen(url); if(urlen == 0) continue; if(df != 0) prtolog("Debug: main(): Req:", req); if(df != 0) prtolog("Debug: main(): Reqmet:", reqmet); if(df != 0) prtolog("Debug: main(): URL:", url); if(strcmp(reqmet, "CONNECT") == 0) /* HTTPS */ sslf = 1; if (fndhost() != 0) continue; if(tlf != 0 && hostolow() != 0) continue; if (allowhosts.start != NULL) { if(df != 0) prtolog("Debug: main(): Allow hosts check", NULL); proclist(allowhosts, hlen, host); if (umf != 0) { strcat(url, "\n"); urlen++; if (wrturl(url, urlen) != 0) { closemyfiles(); exit(1); } continue; /* No more checks */ } } if (denyhosts.start != NULL) { if(df != 0) prtolog("Debug: main(): Deny hosts check", NULL); proclist(denyhosts, hlen, host); if (umf != 0) { strcat(url, "\n"); urlen++; if (wrturl(redirurl, redirlen) != 0) { closemyfiles(); exit(1); } logloc(host, "hosts.deny"); continue; /* No more checks */ } } if (sslf == 0) { /* Not HTTPS */ if (allowurls.start != NULL) { /* Allow list lookup */ if(df != 0) prtolog("Debug: main(): Allow urls check", NULL); proclist(allowurls, urlen, url); if (umf != 0) { /* Whitelist match: Write URL now and stop evaluation */ strcat(url, "\n"); urlen++; if (wrturl(url, urlen) != 0) { /* Can't write to Squid */ closemyfiles(); exit(1); } continue; /* No more checks */ } } if (denyurls.start != NULL) { /* Deny list lookup */ if(df != 0) prtolog("Debug: main(): Deny urls check", NULL); proclist(denyurls, urlen, url); if (umf != 0) { /* Do a redirect */ if (wrturl(redirurl, redirlen) != 0) { /* Can't write to Squid */ closemyfiles(); exit(1); } logloc(url, "urls.deny"); continue; /* No more checks */ } } } if (lstcnt != 0) { /* RBL Lookup */ hostlookup(); if (umf != 0) { /* On error redirurl */ if (wrturl(redirurl, redirlen) != 0) { /* Can't write to Squid */ closemyfiles(); exit(1); } continue; /* No more checks */ } } /* Passed all checks: write URL to stdout */ strcat(url, "\n"); urlen++; if (wrturl(url, urlen) != 0) { /* Can't write to Squid */ closemyfiles(); exit(1); } } /* Close files */ closemyfiles(); return(0); }