diff -Naur dansguardian-2.8.0.4/ClamAV.cpp dansguardian-2.8.0.4.oden/ClamAV.cpp --- dansguardian-2.8.0.4/ClamAV.cpp 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/ClamAV.cpp 2005-06-17 12:30:35.300932239 +0200 @@ -0,0 +1,88 @@ +// Written by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (www.harvest.com.br) +// 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-1307 USA + +#include "ClamAV.hpp" +#include "OptionContainer.hpp" +#include <syslog.h> +#include <time.h> + +extern OptionContainer o; +extern struct cl_limits limits; +extern struct cl_stat dbstat; + +ClamAV::ClamAV() +:virname(0),root(NULL) {} + +int ClamAV::scanFile(char* filename) { + + int isinfected = 0; + + isinfected = cl_scanfile(filename, &virname, NULL, root, &limits, CL_ARCHIVE| CL_MAIL | CL_OLE2 | CL_SCAN_PE | CL_SCAN_BLOCKBROKEN | CL_SCAN_HTML); + return isinfected; +} + +int ClamAV::loadDB() { + + int ret = 0; + unsigned int no = 0; + const char* dbdir = cl_retdbdir(); + +#ifdef DGDEBUG +system("date"); +std::cout << "Start ClamAV DB load." << std::endl; +#endif + + if (root != NULL) { + cl_free(root); + root = NULL; + } + if ((ret = cl_loaddbdir(dbdir, &root, &no))) { + syslog(LOG_ERR, "Error loading ClamAV DB: %s", cl_perror(ret)); + return ret; + } + else { + syslog(LOG_INFO, "ClamAV DB loaded: %i signatures known", no); + cl_build(root); + cl_statfree(&dbstat); + cl_statinidir(dbdir, &dbstat); + } + +#ifdef DGDEBUG +std::cout << "End of ClamAV DB load." << std::endl; +system("date"); +#endif + + limits.maxfiles = o.cl_max_files; + limits.maxfilesize = o.cl_max_file_size; + limits.maxreclevel = o.cl_max_rec_level; + + return 0; +} + +int ClamAV::checkDB() { + + int ret = 0; + + if (cl_statchkdir(&dbstat) == 1) { + +#ifdef DGDEBUG +std::cout << "ClamAV virus database has changed. Reloading..." << std::endl; +#endif + + ret = loadDB(); + } + + return ret; +} diff -Naur dansguardian-2.8.0.4/ClamAV.hpp dansguardian-2.8.0.4.oden/ClamAV.hpp --- dansguardian-2.8.0.4/ClamAV.hpp 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/ClamAV.hpp 2005-06-17 12:30:35.300932239 +0200 @@ -0,0 +1,36 @@ +// Written by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (www.harvest.com.br) +// 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-1307 USA + +#ifndef __HPP_CLAMAV +#define __HPP_CLAMAV +//#include "OptionContainer.hpp" +#include <clamav.h> + +class ClamAV { + +public: + const char* virname; + struct cl_node* root; + + ClamAV(); +// ~ClamAV(); + + int scanFile(char* filename); + int loadDB(); + int checkDB(); +}; + +#endif + diff -Naur dansguardian-2.8.0.4/ClamDScan.cpp dansguardian-2.8.0.4.oden/ClamDScan.cpp --- dansguardian-2.8.0.4/ClamDScan.cpp 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/ClamDScan.cpp 2005-06-17 12:30:35.300932239 +0200 @@ -0,0 +1,125 @@ + +#include "ClamDScan.hpp" +#include "OptionContainer.hpp" +#include <stdio.h> +#include <unistd.h> +#include <netinet/in.h> +#include <sys/un.h> + +extern OptionContainer o; + +// Constructor +ClamDScan::ClamDScan() +:virname(NULL),errorMsg(NULL) {}; + +// Deconstructor +ClamDScan::~ClamDScan() {}; + +int ClamDScan::dsfile(int sockd, const char *filename) { + int infected = 0; + char buff[4096], scancmd[256]; + FILE *fd; +#ifdef DGDEBUG + std::cout << "Entering dsfile" << std::endl; +#endif + + sprintf(scancmd, "CONTSCAN %s", filename); + +#ifdef DGDEBUG + std::cout << "Before sending clamd command: " << scancmd << std::endl; +#endif + + if(write(sockd, scancmd, strlen(scancmd)) <= 0) { + // Error writing to clamd socket + infected = 2; + } + + // reading response from clamd + if((fd = fdopen(dup(sockd), "r")) == NULL) { + // Error opening socket for reading +#ifdef DGDEBUG + std::cout << "Error opening clamd socket for reading" << std::endl; +#endif + infected = 2; + return infected; + } + + while(fgets(buff, sizeof(buff), fd)) { + if(strstr(buff, "FOUND\n")) { + infected = 1; + } + if (strstr(buff, "ERROR\n")) { + infected = 2; + } + } +#ifdef DGDEBUG + std::cout << "Got reply from clamd: '" << buff << "'" << std::endl; +#endif + + if (infected == 1) { + virname = new char[256]; + strcpy(virname, buff); + virname += (strlen(filename) + 2); // clamd adds " :" to the returning string + virname[strlen(virname) - 7] = '\0'; // Removing " FOUND\n" from returning string +#ifdef DGDEBUG + std::cout << "Virus found: '" << virname << "'" << std::endl; +#endif + } + else if (infected == 2) { + errorMsg = new char[256]; + strcpy(errorMsg, buff); + errorMsg += (strlen(filename) + 2); // clamd adds " :" to the returning text + errorMsg[strlen(errorMsg) - 7] = '\0'; // Removing " ERROR\n" from returning string +#ifdef DGDEBUG + std::cout << "Error found: '" << errorMsg << "'" << std::endl; +#endif + } + fclose(fd); + + return infected; +} + +int ClamDScan::dconnect(void) { + + struct sockaddr_un server; + int sockd; + const char *LocalSocket = o.localsocket.c_str(); + + server.sun_family = AF_UNIX; + strncpy(server.sun_path, LocalSocket, sizeof(server.sun_path)); + + if((sockd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + return -1; + } + + if(connect(sockd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) { + return -1; + } + + return sockd; +} + +int ClamDScan::scanFile(const char *filename) { + int sockd = 0; + int infected = 0; + +#ifdef DGDEBUG + std::cout << "Entering ClamDScan scanFile" << std::endl; + std::cout << "Before calling dconnect" << std::endl; +#endif + if((sockd = dconnect()) < 0) { +#ifdef DGDEBUG + std::cout << "Error connecting to clamd daemon" << std::endl; +#endif + infected = 2; + } + else { +#ifdef DGDEBUG + std::cout << "Before calling dsfile" << std::endl; +#endif + infected = dsfile(sockd, filename); + } + close(sockd); + + return infected; +} diff -Naur dansguardian-2.8.0.4/ClamDScan.hpp dansguardian-2.8.0.4.oden/ClamDScan.hpp --- dansguardian-2.8.0.4/ClamDScan.hpp 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/ClamDScan.hpp 2005-06-17 12:30:35.301932260 +0200 @@ -0,0 +1,36 @@ +// Written by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (www.harvest.com.br) +// 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-1307 USA + +#ifndef __HPP_CLAMDSCAN +#define __HPP_CLAMDSCAN + +#define CL_CLEAN 0 // virus not found +#define CL_VIRUS 1 // virus found + +class ClamDScan { + +public: + char *virname; + char *errorMsg; + + ClamDScan(); + ~ClamDScan(); + + int dsfile(int sockd, const char *filename); + int dconnect(void); + int scanFile(const char *filename); +}; + +#endif diff -Naur dansguardian-2.8.0.4/ConnectionHandler.cpp dansguardian-2.8.0.4.oden/ConnectionHandler.cpp --- dansguardian-2.8.0.4/ConnectionHandler.cpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/ConnectionHandler.cpp 2005-06-17 12:30:35.304932326 +0200 @@ -22,6 +22,7 @@ #include "ImageContainer.hpp" #include "ConnectionHandler.hpp" #include "DataBuffer.hpp" +#include "VirusScanner.hpp" #include "Socket.hpp" #include "UDSocket.hpp" #include "Ident.hpp" @@ -52,555 +53,758 @@ extern OptionContainer o; void ConnectionHandler::handleConnection(int peerfd, String ip, int port) { + + Socket proxysock; // to hold connection to proxy + Socket peerconn; peerconn.close(); peerconn.setFD(peerfd); - - struct timeval thestart; - struct timezone notused; - gettimeofday(&thestart, ¬used); - peerconn.setTimeout(10); HTTPHeader header; // to hold the incoming client request header - header.setTimeout(10); // set a timeout as we don't want blocking 4 eva HTTPHeader docheader; // to hold the returned page header from proxy - - docheader.setTimeout(20); + docheader.setTimeout(120); DataBuffer docbody; // to hold the returned page - docbody.setTimeout(120); + VirusScanner virusscan; + + Ident ident; // for holding + bool waschecked = false; // flags bool wasrequested = false; bool isexception = false; + bool novirusscan = false; bool isourwebserver = false; bool wasclean = false; bool cachehit = false; bool forceauthrequest = false; bool isbypass = false; bool iscookiebypass = false; - int bypasstimestamp = 0; bool ispostblock = false; bool pausedtoobig = false; + int docsize = 0; // to store the size of the returned document for loggin + int bypasstimestamp = 0; + int contentlength = 0; + int rc = 0; + if (o.preemptive_banning == 0) { - forceauthrequest = true; + forceauthrequest = true; } + std::string exceptionreason; // to hold the reason for not blocking + std::string clientip = ip.toCharArray(); // hold the clients ip std::string mimetype = "-"; String url; String urld; String urldomain; - std::string exceptionreason; // to hold the reason for not blocking - - int docsize = 0; // to store the size of the returned document for loggin - - Ident ident; // for holding - - std::string clientip = ip.toCharArray(); // hold the clients ip + struct timeval thestart; + struct timezone notused; + gettimeofday(&thestart, ¬used); #ifdef DGDEBUG // debug stuff surprisingly enough std::cout << "got connection" << std::endl; std::cout << clientip << std::endl; #endif - Socket proxysock; // to hold connection to proxy - try { - // connect to proxy - int rc = proxysock.connect(o.proxy_ip, o.proxy_port); - - if (rc) { - #ifdef DGDEBUG - std::cerr << "Error connecting to proxy" << std::endl; - #endif - syslog(LOG_ERR, "%s","Error connecting to proxy"); - return; // if we can't connect to the proxy, there is no point - // in continuing - } - - header.in(&peerconn); // get header from client - url = header.url(); - urld = header.decode(url); - if (url.after("://").contains("/")) { - urldomain = url.after("//").before("/"); - } - else { - urldomain = url.after("//"); - } - - if (header.malformedURL(url)) { - // checks for bad URLs to prevent security hole - try { // writestring throws exception on error/timeout - peerconn.writeString("HTTP/1.0 400 Bad Request\n"); - peerconn.writeString("Content-Type: text/html\n\n"); - peerconn.writeString("<HTML><HEAD><TITLE>DansGuardian - 400 Bad Request</TITLE></HEAD><BODY><H1>DansGuardian - 400 Bad Request</H1> "); - peerconn.writeString(o.language_list.getTranslation(200)); - // The requested URL is malformed. - peerconn.writeString("</BODY></HTML>\n"); - } catch (exception& e) {} - try { - proxysock.close(); // close connection to proxy - } catch (exception& e) {} - return; - } - - if (o.use_xforwardedfor == 1) { - std::string xforwardip = header.getXForwardedForIP(); - if (xforwardip.length() > 6) { - clientip = xforwardip; - } - #ifdef DGDEBUG - std::cout << "using x-forwardedfor:" << clientip << std::endl; - #endif - } - - std::string clientuser = ident.getUsername(&header, &clientip, port); - // extract username - - #ifdef DGDEBUG - std::cout << "About to determine group" << std::endl; - #endif - - int filtergroup = determineGroup(&clientuser); - if (filtergroup < 0) { - filtergroup = determineGroup(&clientip); - } - if (filtergroup < 0) { - filtergroup = 0; //default group - one day configurable? - } - - #ifdef DGDEBUG - std::cout << "filtergroup:" << filtergroup << std::endl; - #endif - - if (o.forwarded_for == 1) { - header.addXForwardedFor(clientip); // add squid-like entry - } - if ((*o.fg[filtergroup]).bypass_mode > 0) { - #ifdef DGDEBUG - std::cout << "About to check for bypass..." << std::endl; - #endif - bypasstimestamp = header.isBypassURL(&url, (*o.fg[filtergroup]).magic.c_str(), clientip.c_str()); - if (bypasstimestamp > 0) { - #ifdef DGDEBUG - std::cout << "Bypass URL match" << std::endl; - #endif - header.chopBypass(url); - url = header.url(); - urld = header.decode(url); - if (bypasstimestamp > 1) { // not expired - isbypass = true; - exceptionreason = o.language_list.getTranslation(606); - } - } - else if (header.isBypassCookie(&urldomain, (*o.fg[filtergroup]).cookie_magic.c_str(), clientip.c_str())) { - #ifdef DGDEBUG - std::cout << "Bypass cookie match" << std::endl; - #endif - iscookiebypass = true; - isbypass = true; - exceptionreason = o.language_list.getTranslation(607); - } - #ifdef DGDEBUG - std::cout << "Finished bypass checks." << std::endl; - #endif - } - if (isbypass) { - #ifdef DGDEBUG - std::cout << "Bypass activated!" << std::endl; - #endif - } - else if (o.inipexceptions(&clientip)) { // admin pc - isexception = true; - exceptionreason = o.language_list.getTranslation(600); - // Exception client IP match. - } - else if (o.inuserexceptions(&clientuser)) { // admin user - isexception = true; - exceptionreason = o.language_list.getTranslation(601); - // Exception client user match. - } - else if ((*o.fg[filtergroup]).inexceptions(urld)) { // allowed site - if ((*o.fg[0]).iswebserver(url)) { - isourwebserver = true; - } - else { - isexception = true; - exceptionreason = o.language_list.getTranslation(602); - // Exception site match. - } - } - else if ((*o.fg[filtergroup]).inurlexceptions(urld)) { // allowed url - isexception = true; - exceptionreason = o.language_list.getTranslation(603); - // Exception url match. - } - - - - #ifdef DGDEBUG - std::cout << "extracted url:" << urld << std::endl; - #endif - - - if ( (isourwebserver || isexception || iscookiebypass) - // don't filter exception and local web server - // Cookie bypass so don't need to add cookie so just CONNECT - && !o.inBannedIPList(&clientip) // bad users pc - && !o.inBannedUserList(&clientuser) ) { // bad user - - proxysock.readyForOutput(10); // exception on timeout or error - header.out(&proxysock); // send proxy the request - try { - FDTunnel fdt; // make a tunnel object - // tunnel from client to proxy and back - fdt.tunnel(proxysock.getFD(), peerconn.getFD()); // not expected to exception - docsize = fdt.throughput; - if (!isourwebserver) { // don't log requests to the web server - decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, o.log_exception_hits, false, &thestart, cachehit, 200, mimetype); - } - - } catch (exception& e) {} - try { - proxysock.close(); // close connection to proxy - } catch (exception& e) {} - return; // connection dealt with so exit - } - - - NaughtyFilter checkme; // our filter object - checkme.filtergroup = filtergroup; - - char* i; - - // Improved IF structure as suggested by AFN - - if ((!forceauthrequest || header.requesttype().startsWith("CONNECT")) && !isbypass) { - // if its a connect and we don't do filtering on it now then - // it will get tunneled and not filtered. We can't tunnel later - // as its ssl so we can't see the return header etc - // So preemptive banning is forced on with ssl unfortunately. - // It is unlikely to cause many problems though. - requestChecks(&header, &checkme, &urld, &clientip, &clientuser, filtergroup, &ispostblock); - } - - - if (!checkme.isItNaughty && header.requesttype().startsWith("CONNECT")) { - // can't filter content of CONNECT - proxysock.readyForOutput(10); // exception on timeout or error - header.out(&proxysock); // send proxy the request - try { - FDTunnel fdt; // make a tunnel object - // tunnel from client to proxy and back - fdt.tunnel(proxysock.getFD(), peerconn.getFD()); // not expected to exception - docsize = fdt.throughput; - decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, o.log_exception_hits, false, &thestart, cachehit, 200, mimetype); - } catch (exception& e) {} - try { - proxysock.close(); // close connection to proxy - } catch (exception& e) {} - return; // connection dealt with so exit - } - - if (!checkme.isItNaughty) { - proxysock.readyForOutput(10); - header.out(&proxysock); // send header to proxy - proxysock.checkForInput(120); - docheader.in(&proxysock); // get header from proxy - #ifdef DGDEBUG - std::cout << "got header from proxy" << std::endl; - #endif - wasrequested = true; // so we know where we are later - - if (isbypass) { - docheader.setCookie("GBYPASS", hashedCookie(&urldomain, filtergroup, &clientip, bypasstimestamp).toCharArray()); - } - - mimetype = docheader.getcontenttype().toCharArray(); - unsigned int p = (*o.fg[filtergroup]).banned_mimetype_list; -// i = o.banned_mimetype_list.findInList((char*)mimetype.c_str()); - if ((i = (*o.lm.l[p]).findInList((char*)mimetype.c_str())) != NULL) { - checkme.whatIsNaughty = o.language_list.getTranslation(800); - // Banned MIME Type: - checkme.whatIsNaughty += i; - checkme.whatIsNaughtyLog = checkme.whatIsNaughty; - checkme.isItNaughty = true; - } - #ifdef DGDEBUG - std::cout << mimetype.length() << std::endl; - std::cout << ":" << mimetype; - std::cout << ":" << std::endl; - #endif - - if (!checkme.isItNaughty && !docheader.isRedirection()) { - // Can't ban file extensions of URLs that just redirect - String tempurl = urld; - String tempdispos = docheader.disposition(); - if (tempdispos.length() > 1) { - // dispos filename must take presidense - #ifdef DGDEBUG - std::cout << "Disposition filename:" << tempdispos << ":" << std::endl; - #endif - // The function expects a url so we have to - // generate a psudo one. - tempdispos = "http://foo.bar/" + tempdispos; - if ((i = (*o.fg[filtergroup]).inBannedExtensionList(tempdispos)) != NULL) { - checkme.whatIsNaughty = o.language_list.getTranslation(900); - // Banned extension: - checkme.whatIsNaughty += i; - checkme.whatIsNaughtyLog = checkme.whatIsNaughty; - checkme.isItNaughty = true; - } - } - else { - if (!tempurl.contains("?")) { -// i = o.inBannedExtensionList(tempurl); - if ((i = (*o.fg[filtergroup]).inBannedExtensionList(tempurl)) != NULL) { - checkme.whatIsNaughty = o.language_list.getTranslation(900); - // Banned extension: - checkme.whatIsNaughty += i; - checkme.whatIsNaughtyLog = checkme.whatIsNaughty; - checkme.isItNaughty = true; - } - } - if (String(mimetype.c_str()).contains("application/")) { - while (tempurl.endsWith("?")) { - tempurl.chop(); - } - while(tempurl.contains("/")) { // no slash no url - if ((i = (*o.fg[filtergroup]).inBannedExtensionList(tempurl)) != NULL) { - checkme.whatIsNaughty = o.language_list.getTranslation(900); - // Banned extension: - checkme.whatIsNaughty += i; - checkme.whatIsNaughtyLog = checkme.whatIsNaughty; - checkme.isItNaughty = true; - break; - } - while (tempurl.contains("/") && !tempurl.endsWith("?")) { - tempurl.chop(); - } - tempurl.chop(); // get rid of the ? - - } - } - } - } - - if (!checkme.isItNaughty && forceauthrequest && !docheader.authRequired()) { - requestChecks(&header, &checkme, &urld, &clientip, &clientuser, filtergroup, &ispostblock); - } - - if (docheader.iscontenttype("text") && !checkme.isItNaughty) { - // here we check if its a known good one - // if so we skip content checking - waschecked = true; - proxysock.checkForInput(120); - if (docheader.isCompressed()) { - docbody.setDecompress(docheader.contentEncoding()); - } - #ifdef DGDEBUG - std::cout << docheader.contentEncoding() << std::endl; - std::cout << "about to get body from proxy" << std::endl; - #endif - pausedtoobig = docbody.in(&proxysock); // get body from proxy - #ifdef DGDEBUG - if (pausedtoobig) { - std::cout << "got PARTIAL body from proxy" << std::endl; - } - else { - std::cout << "got body from proxy" << std::endl; - } - #endif - - int dblen = docbody.length(); - docsize = dblen; - - if (o.url_cache_number > 0) { - if (wasClean(urld)) { - wasclean = true; - cachehit = true; - #ifdef DGDEBUG - std::cout << "url was clean skipping content checking" << std::endl; - #endif - } - // was not clean - else { - #ifdef DGDEBUG - system("date"); - #endif - if (dblen <= o.max_content_filter_size - || o.max_content_filter_size == 0) { - checkme.checkme(&docbody); // content filtering - } - #ifdef DGDEBUG - else { - std::cout << "content length large so skipping content filtering" << std::endl; - } - system("date"); - #endif - } - } - // urlcache is not used - else { - #ifdef DGDEBUG - system("date"); - #endif - if (dblen <= o.max_content_filter_size - || o.max_content_filter_size == 0) { - checkme.checkme(&docbody); // content filtering - } - #ifdef DGDEBUG - else { - std::cout << "content length large so skipping content filtering" << std::endl; - } - system("date"); - #endif - } - bool contentmodified = false; - if (dblen <= o.max_content_filter_size || o.max_content_filter_size == 0) { - contentmodified = docbody.contentRegExp(filtergroup); // content modifying - // uses global variable - } - #ifdef DGDEBUG - else { - std::cout << "content length large so skipping content modifying" << std::endl; - } - system("date"); - #endif - - if (contentmodified) { - #ifdef DGDEBUG - std::cout << "content modification made" << std::endl; - #endif - if (docheader.isCompressed()) { - docheader.removeEncoding(dblen); - // need to modify header to mark as not compressed - // it also modifies Content-Length as well - } - else { - docheader.setContentLength(docbody.buffer_length); - } - } - else { - docbody.swapbacktocompressed(); - // if we've not modified it might as well go back to - // the original compressed version (if there) and send - // that to the browser - } - // here if its OK then request addition to good url database - } - } - - if (checkme.isException) { - isexception = true; - exceptionreason = checkme.whatIsNaughtyLog; - } - - if (docheader.isRedirection()) { - checkme.isItNaughty = false; - } - - if (o.url_cache_number > 0) { - if (!wasclean && !checkme.isItNaughty && docheader.iscontenttype("text") && header.requesttype() == "GET") { + header.in(&peerconn); // get header from client + url = header.url(); + urld = header.decode(url); + if (url.after("://").contains("/")) { + urldomain = url.after("//").before("/"); + } + else { + urldomain = url.after("//"); + } + + if (header.malformedURL(url)) { + // checks for bad URLs to prevent security hole + try { // writestring throws exception on error/timeout + peerconn.writeString("HTTP/1.0 400 Bad Request\n"); + peerconn.writeString("Content-Type: text/html\n\n"); + peerconn.writeString("<HTML><HEAD><TITLE>DansGuardian - 400 Bad Request</TITLE></HEAD><BODY><H1>DansGuardian - 400 Bad Request</H1> "); + peerconn.writeString(o.language_list.getTranslation(200)); + // The requested URL is malformed. + peerconn.writeString("</BODY></HTML>\n"); + } catch (exception& e) {} + return; + } + + if (o.use_xforwardedfor == 1) { + std::string xforwardip = header.getXForwardedForIP(); + if (xforwardip.length() > 6) { + clientip = xforwardip; + } + #ifdef DGDEBUG + std::cout << "using x-forwardedfor:" << clientip << std::endl; + #endif + } + + std::string clientuser = ident.getUsername(&header, &clientip, port); + // extract username + + #ifdef DGDEBUG + std::cout << "About to determine group" << std::endl; + #endif + + int filtergroup = determineGroup(&clientuser); + if (filtergroup < 0) { + filtergroup = determineGroup(&clientip); + } + if (filtergroup < 0) { + filtergroup = 0; //default group - one day configurable? + } + + #ifdef DGDEBUG + std::cout << "filtergroup:" << filtergroup << std::endl; + #endif + + if (!(o.virus_scan && o.fg[filtergroup]->virusscan)) { + novirusscan = true; + #ifdef DGDEBUG + std::cout << "Virus scan is disabled." << std::endl; + #endif + } + + if (o.forwarded_for == 1) { + header.addXForwardedFor(clientip); // add squid-like entry + } + + if ((*o.fg[filtergroup]).bypass_mode > 0) { + #ifdef DGDEBUG + std::cout << "About to check for bypass..." << std::endl; + #endif + bypasstimestamp = header.isBypassURL(&url, (*o.fg[filtergroup]).magic.c_str(), clientip.c_str()); + if (bypasstimestamp > 0) { + #ifdef DGDEBUG + std::cout << "Bypass URL match" << std::endl; + #endif + header.chopBypass(url); + url = header.url(); + urld = header.decode(url); + if (bypasstimestamp > 1) { // not expired + isbypass = true; + exceptionreason = o.language_list.getTranslation(606); + } + } + else if (header.isBypassCookie(&urldomain, (*o.fg[filtergroup]).cookie_magic.c_str(), clientip.c_str())) { + #ifdef DGDEBUG + std::cout << "Bypass cookie match" << std::endl; + #endif + iscookiebypass = true; + isbypass = true; + exceptionreason = o.language_list.getTranslation(607); + } + #ifdef DGDEBUG + std::cout << "Finished bypass checks." << std::endl; + #endif + } + if (isbypass) { + #ifdef DGDEBUG + std::cout << "Bypass activated!" << std::endl; + #endif + } + else if (o.inipexceptions(&clientip)) { // admin pc + isexception = true; + exceptionreason = o.language_list.getTranslation(600); + // Exception client IP match. + } + else if (o.inuserexceptions(&clientuser)) { // admin user + isexception = true; + exceptionreason = o.language_list.getTranslation(601); + // Exception client user match. + } + else if ((*o.fg[filtergroup]).inexceptions(urld)) { // allowed site + if ((*o.fg[0]).iswebserver(url)) { + isourwebserver = true; + } + else { + isexception = true; + exceptionreason = o.language_list.getTranslation(602); + // Exception site match. + } + } + else if ((*o.fg[filtergroup]).inurlexceptions(urld)) { // allowed url + isexception = true; + exceptionreason = o.language_list.getTranslation(603); + // Exception url match. + } + + #ifdef DGDEBUG + std::cout << "extracted url:" << urld << std::endl; + #endif + + // Speed improvement to take advantage of dgav url_cache_clean_only + // Aecio F. Neto - dgav 6.3.7 + if (!novirusscan && o.url_cache_clean_only && (o.url_cache_number > 0)) { + if (wasClean(urld)) { + novirusscan = true; + #ifdef DGDEBUG + std::cout << "url was found in cache. Scanning disabled" << std::endl; + #endif + wasclean = true; + cachehit = true; + } + } + + // connect to proxy + if ((rc = proxysock.connect(o.proxy_ip, o.proxy_port)) > 0) { + #ifdef DGDEBUG + std::cerr << "Error connecting to proxy" << std::endl; + #endif + syslog(LOG_ERR, "%s","Error connecting to proxy"); + return; // if we can't connect to the proxy, there is no point + // in continuing + } + + if ( (isourwebserver || isexception || iscookiebypass) + // don't filter exception and local web server + // Cookie bypass so don't need to add cookie so just CONNECT + && !o.inBannedIPList(&clientip) // bad users pc + && !o.inBannedUserList(&clientuser) ) { // bad user + + proxysock.readyForOutput(10); // exception on timeout or error + header.out(&proxysock); // send proxy the request + + #ifdef DGDEBUG + std::cout << "sent request header to proxy" << std::endl; + #endif + + proxysock.checkForInput(120); + docheader.in(&proxysock); // get body header from proxy + contentlength = docheader.contentlength(); + + #ifdef DGDEBUG + std::cout << "got body header from proxy" << std::endl; + std::cout << "contentlength = " << contentlength << std::endl; + std::cout << "sending reply header to client" << std::endl; + #endif + + peerconn.readyForOutput(10); // exceptions on error/timeout + docheader.out(&peerconn); // send body header to client + + #ifdef DGDEBUG + std::cout << "sent body header to client" << std::endl; + #endif + + if (!novirusscan) { // see if we do not need to virus scan the content. + novirusscan = noVirusScanCheck(urld, filtergroup, &header, &docheader); + } + + if (novirusscan) { + makeTunnel(&docsize, &proxysock, &peerconn); + try { + proxysock.close(); + } catch (exception& e) {} + } + else { + #ifdef DGDEBUG + std::cout << "calling 1st scanning routine" << std::endl; + std::cout << "docheader.contentlength = " << contentlength << std::endl; + #endif + try { + virusscan.handleScanning(&proxysock, &peerconn, url, 0, contentlength, clientuser, clientip, filtergroup, o.access_denied_address, o.reporting_level, 0); + } catch (exception& e) { + #ifdef DGDEBUG + std::cout << "exception caught - scanning/sending url" << std::endl; + #endif + exceptionreason = "*ERROR* - exception caught scanning/sending data" + exceptionreason; + decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, true, 0, o.log_exception_hits, false, &thestart, cachehit, 200, mimetype); + try { + proxysock.close(); + } catch (exception& e2) {} + return; + } + docsize = virusscan.file_length; + #ifdef DGDEBUG + std::cout << "handlescanning properly ran" << std::endl; + std::cout << "docsize: " << docsize << std::endl; + #endif + } + + if (!novirusscan && (virusscan.isInfected == 1)) { + exceptionreason = virusscan.virname; + } + + if (!isourwebserver) { // don't log requests to the web server + decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, novirusscan, virusscan.isInfected, o.log_exception_hits, false, &thestart, cachehit, 200, mimetype); + } + return; // connection dealt with so exit + } + + NaughtyFilter checkme; // our filter object + checkme.filtergroup = filtergroup; + + char* i; + + // Improved IF structure as suggested by AFN + + if ((!forceauthrequest || header.requesttype().startsWith("CONNECT")) && !isbypass) { + // if its a connect and we don't do filtering on it now then + // it will get tunneled and not filtered. We can't tunnel later + // as its ssl so we can't see the return header etc + // So preemptive banning is forced on with ssl unfortunately. + // It is unlikely to cause many problems though. + requestChecks(&header, &checkme, &urld, &clientip, &clientuser, filtergroup, &ispostblock); + } + + if (!checkme.isItNaughty && header.requesttype().startsWith("CONNECT")) { + // can't filter content of CONNECT + proxysock.readyForOutput(10); // exception on timeout or error + header.out(&proxysock); // send proxy the request + makeTunnel(&docsize, &proxysock, &peerconn); + decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, true, 0, o.log_exception_hits, false, &thestart, cachehit, 200, mimetype); + try { + proxysock.close(); // close connection to proxy + } catch (exception& e) {} + return; // connection dealt with so exit + } + + if (!checkme.isItNaughty && !wasclean) { + proxysock.readyForOutput(10); + header.out(&proxysock); // send header to proxy + proxysock.checkForInput(120); + docheader.in(&proxysock); // get header from proxy + contentlength = docheader.contentlength(); + + #ifdef DGDEBUG + std::cout << "got header from proxy" << std::endl; + #endif + + wasrequested = true; // so we know where we are later + + if (isbypass) { + docheader.setCookie("GBYPASS", hashedCookie(&urldomain, filtergroup, &clientip, bypasstimestamp).toCharArray()); + } + + mimetype = docheader.getcontenttype().toCharArray(); + unsigned int p = (*o.fg[filtergroup]).banned_mimetype_list; +// i = o.banned_mimetype_list.findInList((char*)mimetype.c_str()); + if ((i = (*o.lm.l[p]).findInList((char*)mimetype.c_str())) != NULL) { + checkme.whatIsNaughty = o.language_list.getTranslation(800); + // Banned MIME Type: + checkme.whatIsNaughty += i; + checkme.whatIsNaughtyLog = checkme.whatIsNaughty; + checkme.isItNaughty = true; + } + #ifdef DGDEBUG + std::cout << mimetype.length() << std::endl; + std::cout << ":" << mimetype; + std::cout << ":" << std::endl; + #endif + + if (!checkme.isItNaughty && !docheader.isRedirection()) { + // Can't ban file extensions of URLs that just redirect + String tempurl = urld; + String tempdispos = docheader.disposition(); + if (tempdispos.length() > 1) { + // dispos filename must take presidense + #ifdef DGDEBUG + std::cout << "Disposition filename:" << tempdispos << ":" << std::endl; + #endif + // The function expects a url so we have to + // generate a psudo one. + tempdispos = "http://foo.bar/" + tempdispos; + if ((i = (*o.fg[filtergroup]).inBannedExtensionList(tempdispos)) != NULL) { + checkme.whatIsNaughty = o.language_list.getTranslation(900); + // Banned extension: + checkme.whatIsNaughty += i; + checkme.whatIsNaughtyLog = checkme.whatIsNaughty; + checkme.isItNaughty = true; + } + } + else { + if (!tempurl.contains("?")) { +// i = o.inBannedExtensionList(tempurl); + if ((i = (*o.fg[filtergroup]).inBannedExtensionList(tempurl)) != NULL) { + checkme.whatIsNaughty = o.language_list.getTranslation(900); + // Banned extension: + checkme.whatIsNaughty += i; + checkme.whatIsNaughtyLog = checkme.whatIsNaughty; + checkme.isItNaughty = true; + } + } + if (String(mimetype.c_str()).contains("application/")) { + while (tempurl.endsWith("?")) { + tempurl.chop(); + } + while(tempurl.contains("/")) { // no slash no url + if ((i = (*o.fg[filtergroup]).inBannedExtensionList(tempurl)) != NULL) { + checkme.whatIsNaughty = o.language_list.getTranslation(900); + // Banned extension: + checkme.whatIsNaughty += i; + checkme.whatIsNaughtyLog = checkme.whatIsNaughty; + checkme.isItNaughty = true; + break; + } + while (tempurl.contains("/") && !tempurl.endsWith("?")) { + tempurl.chop(); + } + tempurl.chop(); // get rid of the ? + } + } + } + } + + if (!checkme.isItNaughty && forceauthrequest && !docheader.authRequired()) { + requestChecks(&header, &checkme, &urld, &clientip, &clientuser, filtergroup, &ispostblock); + } + + if (docheader.iscontenttype("text") && !checkme.isItNaughty) { + // here we check if its a known good one + // if so we skip content checking + waschecked = true; + proxysock.checkForInput(120); + if (docheader.isCompressed()) { + docbody.setDecompress(docheader.contentEncoding()); + } + #ifdef DGDEBUG + std::cout << docheader.contentEncoding() << std::endl; + std::cout << "about to get body from proxy" << std::endl; + #endif + pausedtoobig = docbody.in(&proxysock); // get body from proxy + #ifdef DGDEBUG + if (pausedtoobig) { + std::cout << "got PARTIAL body from proxy" << std::endl; + } + else { + std::cout << "got body from proxy" << std::endl; + } + #endif + + int dblen = docbody.length(); + docsize = dblen; + + if (o.url_cache_number > 0) { + if (wasClean(urld)) { + wasclean = true; + cachehit = true; + #ifdef DGDEBUG + std::cout << "url was clean skipping content checking" << std::endl; + #endif + } + // was not clean + else { + #ifdef DGDEBUG + system("date"); + #endif + if (dblen <= o.max_content_filter_size + || o.max_content_filter_size == 0) { + checkme.checkme(&docbody); // content filtering + } + #ifdef DGDEBUG + else { + std::cout << "content length large so skipping content filtering" << std::endl; + } + system("date"); + #endif + } + } + // urlcache is not used + else { + #ifdef DGDEBUG + system("date"); + #endif + if (dblen <= o.max_content_filter_size + || o.max_content_filter_size == 0) { + checkme.checkme(&docbody); // content filtering + } + #ifdef DGDEBUG + else { + std::cout << "content length large so skipping content filtering" << std::endl; + } + system("date"); + #endif + } + bool contentmodified = false; + if (dblen <= o.max_content_filter_size || o.max_content_filter_size == 0) { + contentmodified = docbody.contentRegExp(filtergroup); // content modifying + // uses global variable + } + #ifdef DGDEBUG + else { + std::cout << "content length large so skipping content modifying" << std::endl; + } + system("date"); + #endif + + if (contentmodified) { + #ifdef DGDEBUG + std::cout << "content modification made" << std::endl; + #endif + if (docheader.isCompressed()) { + docheader.removeEncoding(dblen); + // need to modify header to mark as not compressed + // it also modifies Content-Length as well + } + else { + docheader.setContentLength(docbody.buffer_length); + } + } + else { + docbody.swapbacktocompressed(); + // if we've not modified it might as well go back to + // the original compressed version (if there) and send + // that to the browser + } + } + } + + if (checkme.isException) { + isexception = true; + exceptionreason = checkme.whatIsNaughtyLog; + } + + if (docheader.isRedirection()) { + checkme.isItNaughty = false; + } + + if (checkme.isItNaughty && !isbypass) { // then we deny, unless we were told to bypass the block + decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, checkme.whatIsNaughtyLog, header.requesttype().toCharArray(), docsize, o.ll, true, false, true, 0, false, false, &thestart, cachehit, 403, mimetype); + if (denyAccess(&peerconn, &proxysock, &header, &docheader, &url, &checkme, &clientuser, &clientip, filtergroup, ispostblock)) { + return; // not stealth mode + } + // if get here in stealth mode + } + + if (!wasrequested) { + proxysock.readyForOutput(10); + header.out(&proxysock); // send header to proxy + proxysock.checkForInput(120); + docheader.in(&proxysock); // get reply header from proxy + contentlength = docheader.contentlength(); + } + + #ifdef DGDEBUG + std::cout << "sending header to client" << std::endl; + #endif + peerconn.readyForOutput(10); + docheader.out(&peerconn); // send body header to client + #ifdef DGDEBUG + std::cout << "sent header to client" << std::endl; + #endif + + if (!novirusscan) { // see if we do not need to virus scan the content. + novirusscan = noVirusScanCheck(urld, filtergroup, &header, &docheader); + } + + if (waschecked) { + if (novirusscan) { + // Original Dansguardian method + #ifdef DGDEBUG + std::cout << "sending body to client" << std::endl; + #endif + peerconn.readyForOutput(10); + docbody.out(&peerconn); // send doc body to client + if (pausedtoobig) { + #ifdef DGDEBUG + std::cout << "sent PARTIAL body to client" << std::endl; + std::cout << "about to start tunnel to send the rest" << std::endl; + #endif + + makeTunnel(&docsize, &proxysock, &peerconn); + } + #ifdef DGDEBUG + else { + std::cout << "sent body to client" << std::endl; + } + #endif + } + else { + #ifdef DGDEBUG + std::cout << "creating tempfile for docbody" << std::endl; + #endif + try { + virusscan.createTempFile(); + docbody.writeFile(virusscan.tempFileName); + } catch (exception& e) { + #ifdef DGDEBUG + std::cout << "exception caught - writing file" << std::endl; + #endif + exceptionreason = "*ERROR* - exception caught writing file" + exceptionreason; + decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, true, 0, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype); + try { + proxysock.close(); + } catch (exception& e2) {} + return; + } + #ifdef DGDEBUG + std::cout << "tempfile for docbody created" << std::endl; + std::cout << "calling 2nd scanning routine" << std::endl; + std::cout << "docheader.contentlength = " << contentlength << std::endl; + #endif + try { + virusscan.handleScanning(&proxysock, &peerconn, url, docsize, contentlength, clientuser, clientip, filtergroup, o.access_denied_address, o.reporting_level, 1); + } catch (exception& e) { + #ifdef DGDEBUG + std::cout << "exception caught - scanning/sending url" << std::endl; + #endif + docsize = virusscan.file_length; + exceptionreason = "*ERROR* - exception caught scanning/sending url" + exceptionreason; + decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, novirusscan, virusscan.isInfected, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype); + try { + proxysock.close(); + } catch (exception& e2) {} + return; + } + docsize = virusscan.file_length; + if (virusscan.isInfected == 1) { + exceptionreason = virusscan.virname; + } + #ifdef DGDEBUG + std::cout << "handlescanning properly ran" << std::endl; + std::cout << "docsize: " << docsize << std::endl; + #endif + } + } + else { // was not supposed to be checked + if (novirusscan) { + #ifdef DGDEBUG + std::cout << "non-text content" << std::endl; + std::cout << "before checkforinput" << std::endl; + #endif + makeTunnel(&docsize, &proxysock, &peerconn); + } + else { + #ifdef DGDEBUG + std::cout << "calling 3rd scanning routine" << std::endl; + std::cout << "docheader.contentlength = " << contentlength << std::endl; + #endif + try { + virusscan.handleScanning(&proxysock, &peerconn, url, 0, contentlength, clientuser, clientip, filtergroup, o.access_denied_address, o.reporting_level, 0); + } catch (exception& e) { + #ifdef DGDEBUG + std::cout << "exception caught - scanning/sending url" << std::endl; + #endif + docsize = virusscan.file_length; + exceptionreason = "*ERROR* - exception caught scanning/sending url" + exceptionreason; + decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, novirusscan, virusscan.isInfected, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype); + try { + proxysock.close(); + } catch (exception& e2) {} + return; + } + docsize = virusscan.file_length; + if (virusscan.isInfected == 1) { + exceptionreason = virusscan.virname; + } + #ifdef DGDEBUG + std::cout << "handlescanning properly ran" << std::endl; + std::cout << "docsize: " << docsize << std::endl; + #endif + } + } + decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, novirusscan, virusscan.isInfected, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype); + + if ((o.url_cache_number > 0) && (header.requesttype() == "GET")) { + if (!checkme.isItNaughty && !wasclean) { + if (!novirusscan && o.url_cache_clean_only && !virusscan.isInfected) { + // Both content AND virus scanning is necessary to insert + // an entry into urlcache. + + addToClean(urld); + // insert new entry in urlcache + } + else if (docheader.iscontenttype("text")) { + addToClean(urld); + // insert new entry in urlcache + } + } + } + } catch (exception& e) { + #ifdef DGDEBUG + std::cout << "connection handler caught an exception" << std::endl; + #endif + try { + proxysock.close(); // close connection to proxy + } catch (exception& e) {} + return; + } + + try { + proxysock.close(); // close conection to squid + } catch (exception& e) {} + try { + peerconn.readyForOutput(10); + } + catch (exception& e) { + return; + } + return; +} - addToClean(urld); - } - } +void ConnectionHandler::makeTunnel(int *docsize, Socket *proxysock, Socket *peerconn) { + FDTunnel fdt; // make a tunnel object + + try { + // tunnel from client to proxy and back + fdt.tunnel((*proxysock).getFD(), (*peerconn).getFD()); // not expected to exception + } catch (exception& e) {} - if (checkme.isItNaughty && !isbypass) { // then we deny, unless we were told to bypass the block - decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, checkme.whatIsNaughtyLog, header.requesttype().toCharArray(), docsize, o.ll, true, false, false, false, &thestart, cachehit, 403, mimetype); - if (denyAccess(&peerconn, &proxysock, &header, &docheader, &url, &checkme, &clientuser, &clientip, filtergroup, ispostblock)) { - return; // not stealth mode - } - // if get here in stealth mode - } + *docsize = fdt.throughput; +} - if (wasrequested == false) { - proxysock.readyForOutput(10); // exceptions on error/timeout - header.out(&proxysock); // exceptions on error/timeout - proxysock.checkForInput(120); // exceptions on error/timeout - docheader.in(&proxysock); // get reply header from proxy - } +bool ConnectionHandler::noVirusScanCheck(String urld, int filtergroup, HTTPHeader *header, HTTPHeader *docheader) { - #ifdef DGDEBUG - std::cout << "sending header to client" << std::endl; - #endif - peerconn.readyForOutput(10); // exceptions on error/timeout - docheader.out(&peerconn); // send header to client - #ifdef DGDEBUG - std::cout << "sent header to client" << std::endl; - #endif - if (waschecked) { - if(!docheader.authRequired() && !pausedtoobig) { - decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype); - } + int contentlength = (*docheader).contentlength(); + String contenttype = (*docheader).getcontenttype(); - #ifdef DGDEBUG - std::cout << "sending body to client" << std::endl; - #endif - peerconn.readyForOutput(10); // check for error/timeout needed - docbody.out(&peerconn); // send doc body to client - #ifdef DGDEBUG - if (pausedtoobig) { - std::cout << "sent PARTIAL body to client" << std::endl; - } - else { - std::cout << "sent body to client" << std::endl; - } - #endif - if (pausedtoobig) { - #ifdef DGDEBUG - std::cout << "about to start tunnel to send the rest" << std::endl; - #endif + if (o.virus_scan && o.fg[filtergroup]->virusscan) { // see if we do not need to virus scan the content. + #ifdef DGDEBUG + std::cout << "checking to see if we don't have to virus scan - case 2" << std::endl; + std::cout << "url:" << urld << std::endl; + #endif + + if ((*header).requesttype().startsWith("CONNECT")) { + #ifdef DGDEBUG + std::cout << "http method is CONNECT - virus scan skipped." << std::endl; + #endif + return true; + } + else if ((*o.fg[filtergroup]).invirussiteexceptions(urld)) { + #ifdef DGDEBUG + std::cout << "address found in virus scan exception site" << std::endl; + #endif + return true; + } + else if ((*o.fg[filtergroup]).invirusurlexceptions(urld)) { + #ifdef DGDEBUG + std::cout << "address found in virus scan exception url" << std::endl; + #endif + return true; + } + else if ((o.max_content_scan_size > 0) && (contentlength > o.max_content_scan_size)) { + #ifdef DGDEBUG + std::cout << "contentlength is bigger than content size to be scanned" << std::endl; + #endif + return true; + } + else if (o.exception_virus_mimetype_list.AVfindInList(contenttype.toCharArray()) >= 0) { + #ifdef DGDEBUG + std::cout << "mimetype '" << contenttype.toCharArray() << "' in exception virus mime-type list" << std::endl; + #endif + return true; + } + else if (o.inVirusExceptionExtensionList(urld) >= 0) { + #ifdef DGDEBUG + std::cout << "file extension in exception virus extension list" << std::endl; + #endif + return true; + } + } + else { + return true; + } - FDTunnel fdt; - #ifdef DGDEBUG - std::cout << "tunnel activated" << std::endl; - #endif - fdt.tunnel(proxysock.getFD(), peerconn.getFD()); - docsize += fdt.throughput; - decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype); - } - } - else { // was not supposed to be checked - FDTunnel fdt; - #ifdef DGDEBUG - std::cout << "tunnel activated" << std::endl; - #endif - fdt.tunnel(proxysock.getFD(), peerconn.getFD()); - docsize = fdt.throughput; - - decideHowToLog(clientuser, clientip, url.toCharArray(), header.port, exceptionreason, header.requesttype().toCharArray(), docsize, o.ll, false, isexception, o.log_exception_hits, docheader.iscontenttype("text"), &thestart, cachehit, 200, mimetype); - - } - } catch (exception& e) { - #ifdef DGDEBUG - std::cout << "connection handler caught an exception" << std::endl; - #endif - try { - proxysock.close(); // close connection to proxy - } catch (exception& e) {} - return; - } - - try { - proxysock.close(); // close conection to squid - } catch (exception& e) {} - try { - peerconn.readyForOutput(10); - } - catch (exception& e) { - return; - } - return; + return false; } // if we don't do this the browsers complain @@ -710,7 +914,7 @@ -void ConnectionHandler::decideHowToLog(std::string who, std::string from, std::string where, unsigned int port, std::string what, std::string how, int size, int loglevel, bool isnaughty, bool isexception, int logexceptions, bool istext, struct timeval *thestart, bool cachehit, int code, std::string mimetype) { +void ConnectionHandler::decideHowToLog(std::string who, std::string from, std::string where, unsigned int port, std::string what, std::string how, int size, int loglevel, bool isnaughty, bool isexception, bool novirusscan, int infected, int logexceptions, bool istext, struct timeval *thestart, bool cachehit, int code, std::string mimetype) { if (loglevel == 0) { return; @@ -742,16 +946,23 @@ // make it stand out in the logs and also // more easily findable with a search } + if (isexception) { if (logexceptions == 1) { what = "*EXCEPTION* " + what; } - else { - what = ""; - } } + + if (infected == 1) { + what = "*INFECTED* FOUND:" + what; + } + else if (!novirusscan) { + what = "*SCANNED* " + what; + } + if ((isexception && logexceptions == 1) || isnaughty + || (infected == 1) || loglevel == 3 || (loglevel == 2 && istext)) { doTheLogMan(who, from, where, what, how, size, thestart, cachehit, code, mimetype); diff -Naur dansguardian-2.8.0.4/ConnectionHandler.hpp dansguardian-2.8.0.4.oden/ConnectionHandler.hpp --- dansguardian-2.8.0.4/ConnectionHandler.hpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/ConnectionHandler.hpp 2005-06-17 12:30:35.304932326 +0200 @@ -32,11 +32,13 @@ void handleConnection(int peerfd, String ip, int port); private: + void makeTunnel(int *docsize, Socket *proxysock, Socket *peerconn); + bool noVirusScanCheck(String urld, int filtergroup, HTTPHeader *header, HTTPHeader *docheader); + std::string miniURLEncode(std::string s); void doTheLogMan(std::string who, std::string from, std::string where, std::string what, std::string how, int size, struct timeval *thestart, bool cachehit, int code, std::string mimetype); - std::string miniURLEncode(std::string s); - void decideHowToLog(std::string who, std::string from, std::string where, unsigned int port, std::string what, std::string how, int size, int loglevel, bool isnaughtly, bool isexception, int logexceptions, bool istext, struct timeval *thestart, bool cachehit, int code, std::string mimetype); - bool wasClean(String url); + void decideHowToLog(std::string who, std::string from, std::string where, unsigned int port, std::string what, std::string how, int size, int loglevel, bool isnaughtly, bool isexception, bool novirusscan, int infected, int logexceptions, bool istext, struct timeval *thestart, bool cachehit, int code, std::string mimetype); void addToClean(String url); + bool wasClean(String url); void requestChecks(HTTPHeader *header, NaughtyFilter *checkme, String *urld, std::string *clientip, std::string *clientuser, int filtergroup, bool *ispostblock); bool isIPHostnameStrip(String url); int determineGroup(std::string *user); diff -Naur dansguardian-2.8.0.4/DataBuffer.cpp dansguardian-2.8.0.4.oden/DataBuffer.cpp --- dansguardian-2.8.0.4/DataBuffer.cpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/DataBuffer.cpp 2005-06-17 12:30:35.305932348 +0200 @@ -234,7 +234,7 @@ } while (true) { #ifdef DGDEBUG - std::cerr << "inflate loop" << std::endl; + std::cout << "inflate loop" << std::endl; #endif err = inflate(&d_stream, Z_NO_FLUSH); bout = newsize - bytesgot - d_stream.avail_out; @@ -340,3 +340,25 @@ compressed_buffer_length = 0; } // drop the decompressed version if there } + +// Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) +void DataBuffer::writeFile(char* tempFileName) throw(exception) { + int i = 0; + fstream tmpFile; + + tmpFile.open(tempFileName, ios::out); + + #ifdef DGDEBUG + std::cout << "DataBuffer - filename: " << tempFileName << std::endl; + std::cout << "DataBuffer - writing to file " << tempFileName << ", length = '" << buffer_length << "'" << std::endl; + #endif + + for (i = 0; i < buffer_length; i++) + tmpFile.put(data[i]); + + #ifdef DGDEBUG + std::cout << "DataBuffer - wrote to " << tempFileName << ", length = '" << i << "'" << std::endl; + #endif + tmpFile.close(); +} + // End of Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) diff -Naur dansguardian-2.8.0.4/DataBuffer.hpp dansguardian-2.8.0.4.oden/DataBuffer.hpp --- dansguardian-2.8.0.4/DataBuffer.hpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/DataBuffer.hpp 2005-06-17 12:30:35.305932348 +0200 @@ -42,7 +42,10 @@ void setDecompress(String d); bool contentRegExp(int filtergroup); void swapbacktocompressed(); - + // Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) + void writeFile(char* tempFileName) throw(exception); + // End of Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) + private: int timeout; String decompress; diff -Naur dansguardian-2.8.0.4/FOptionContainer.cpp dansguardian-2.8.0.4.oden/FOptionContainer.cpp --- dansguardian-2.8.0.4/FOptionContainer.cpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/FOptionContainer.cpp 2005-06-17 12:30:35.306932369 +0200 @@ -47,6 +47,8 @@ o.lm.deRefList(grey_url_list); o.lm.deRefList(banned_regexpurl_list); o.lm.deRefList(content_regexp_list); + o.lm.deRefList(exception_virus_site_list); + o.lm.deRefList(exception_virus_url_list); } void FOptionContainer::reset() { @@ -61,6 +63,10 @@ o.lm.deRefList(grey_url_list); o.lm.deRefList(banned_regexpurl_list); o.lm.deRefList(content_regexp_list); + + o.lm.deRefList(exception_virus_site_list); + o.lm.deRefList(exception_virus_url_list); + banned_phrase_list_index.clear(); conffile.clear(); banned_regexpurl_list_comp.clear(); @@ -148,6 +154,20 @@ enable_PICS = 1; } + // per filtergroup virusscan option + if (findoptionS("virusscan") == "off") { + #ifdef DGDEBUG + std::cout << "virusscan for group is: off" << std::endl; + #endif + virusscan = 0; + } + else { + #ifdef DGDEBUG + std::cout << "virusscan for group is: on" << std::endl; + #endif + virusscan = 1; + } + naughtyness_limit = findoptionI("naughtynesslimit"); if (!realitycheck(String(naughtyness_limit), 1, 4, "naughtynesslimit")) { return false; } @@ -165,6 +185,9 @@ exceptions_site_list_location = findoptionS("exceptionsitelist"); exceptions_url_list_location = findoptionS("exceptionurllist"); + exceptions_virus_site_list_location = findoptionS("exceptionvirussitelist"); + exceptions_virus_url_list_location = findoptionS("exceptionvirusurllist"); + pics_rsac_nudity = findoptionI("RSACnudity"); pics_rsac_language = findoptionI("RSAClanguage"); pics_rsac_sex = findoptionI("RSACsex"); @@ -274,7 +297,6 @@ if (!readeslfile(exceptions_site_list_location.c_str())) { return false; } // site exceptions - if (!readeurllfile(exceptions_url_list_location.c_str())) { return false; } // url exceptions @@ -304,6 +326,14 @@ if (!readcrelfile(content_regexp_list_location.c_str())) { return false; } // content replacement regular expressions + + if (!readevslfile(exceptions_virus_site_list_location.c_str())) { + return false; + } // site exceptions + + if (!readevurllfile(exceptions_virus_url_list_location.c_str())) { + return false; + } // url exceptions #ifdef DGDEBUG std::cout << "lists into memory" << filename << std::endl; #endif @@ -1127,3 +1157,113 @@ return false; } +bool FOptionContainer::readevslfile(const char* filename) { + int result = o.lm.newItemList(filename, false, 0, true); + if (result < 0) { + if (!isDaemonised) { + std::cerr << "Error opening exceptionvirussitelist" << std::endl; + } + syslog(LOG_ERR, "%s","Error opening exceptionvirussitelist"); + return false; + } + exception_virus_site_list = (unsigned)result; + if (!(*o.lm.l[exception_virus_site_list]).used) { + (*o.lm.l[exception_virus_site_list]).endsWithSort(); + if (createlistcachefiles == 1) { + if (!(*o.lm.l[exception_virus_site_list]).createCacheFile()) { + return false; + } + } + (*o.lm.l[exception_virus_site_list]).used = true; + } // idea is that if the list has already been used it is already + // compiled, sorted, etc so no point doing it again + return true; +} + +bool FOptionContainer::readevurllfile(const char* filename) { + int result = o.lm.newItemList(filename, false, 0, true); + if (result < 0) { + if (!isDaemonised) { + std::cerr << "Error opening exceptionvirusurllist" << std::endl; + } + syslog(LOG_ERR, "%s","Error opening exceptionvirusurllist"); + return false; + } + exception_virus_url_list = (unsigned)result; + if (!(*o.lm.l[exception_virus_url_list]).used) { + (*o.lm.l[exception_virus_url_list]).startsWithSort(); + if (createlistcachefiles == 1) { + if (!(*o.lm.l[exception_virus_url_list]).createCacheFile()) { + return false; + } + } + (*o.lm.l[exception_virus_url_list]).used = true; + } + return true; +} + +bool FOptionContainer::invirussiteexceptions(String url) { + if (iswebserver(url)) { // don't filter our web server + return true; + } + url.toLower(); + url.removePTP(); // chop off the ht(f)tp(s):// + if (url.contains("/")) { + url = url.before("/"); // chop off any path after the domain + } + char *i; + while (url.contains(".")) { + i = (*o.lm.l[exception_virus_site_list]).findInList(url.toCharArray()); + if (i != NULL) { + return true; // exact match + } + url = url.after("."); // check for being in higher level domains + } + if (url.length() > 1) { // allows matching of .tld + url = "." + url; + i = (*o.lm.l[exception_virus_site_list]).findInList(url.toCharArray()); + if (i != NULL) { + return i; // exact match + } + } + return false; // and our survey said "UUHH UURRGHH" +} + + +bool FOptionContainer::invirusurlexceptions(String url) { + int fl; + char *i; + String foundurl; + url.removeWhiteSpace(); // just in case of weird browser crap + url.toLower(); + url.removePTP(); // chop off the ht(f)tp(s):// + if (url.contains("/")) { + String tpath = "/"; + tpath += url.after("/"); + url = url.before("/"); + tpath.hexDecode(); + tpath.realPath(); + url += tpath; // will resolve ../ and %2e2e/ and // etc + } + if (url.endsWith("/")) { + url.chop(); // chop off trailing / if any + } + while (url.before("/").contains(".")) { + i = (*o.lm.l[exception_virus_url_list]).findStartsWith(url.toCharArray()); + if (i != NULL) { + foundurl = i; + fl = foundurl.length(); + if (url.length() > fl) { + unsigned char c = url[fl]; + if (c == '/' || c == '?' || c == '&' || c == '=') { + return true; // matches /blah/ or /blah/foo but not /blahfoo + } + } + else { + return true; // exact match + } + } + url = url.after("."); // check for being in higher level domains + } + return false; +} diff -Naur dansguardian-2.8.0.4/FOptionContainer.hpp dansguardian-2.8.0.4.oden/FOptionContainer.hpp --- dansguardian-2.8.0.4/FOptionContainer.hpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/FOptionContainer.hpp 2005-06-17 12:30:35.307932391 +0200 @@ -32,20 +32,25 @@ public: ~FOptionContainer(); - bool read(std::string filename); void reset(); + bool read(std::string filename); bool inexceptions(String url); bool iswebserver(String url); bool inurlexceptions(String url); - char* inBannedSiteList(String url); - char* inBannedURLList(String url); bool inGreySiteList(String url); bool inGreyURLList(String url); - int inBannedRegExpURLList(String url); - char* inBannedExtensionList(String url); bool isIPHostname(String url); + + bool invirussiteexceptions(String url); + bool invirusurlexceptions(String url); + + char* inBannedSiteList(String url); + char* inBannedURLList(String url); + char* inBannedExtensionList(String url); + int inBannedRegExpURLList(String url); int weighted_phrase_mode; int naughtyness_limit; + int virusscan; int createlistcachefiles; int enable_PICS; int blanketblock; @@ -138,6 +143,10 @@ std::string banned_mimetype_list_location; std::string exceptions_site_list_location; std::string exceptions_url_list_location; + + std::string exceptions_virus_site_list_location; + std::string exceptions_virus_url_list_location; + unsigned int banned_phrase_list; unsigned int exception_site_list; unsigned int exception_url_list; @@ -149,6 +158,10 @@ unsigned int grey_url_list; unsigned int banned_regexpurl_list; unsigned int content_regexp_list; + + unsigned int exception_virus_site_list; + unsigned int exception_virus_url_list; + std::deque<int> banned_phrase_list_index; std::deque<RegExp> banned_regexpurl_list_comp; std::deque<String> banned_regexpurl_list_source; @@ -178,13 +191,15 @@ bool readbreulfile(const char* filename); bool compilebreulfile(unsigned int list); bool readcrelfile(const char* filename); + + bool readevslfile(const char* filename); + bool readevurllfile(const char* filename); + void bwlfilehelper(String line, int index); void bwlfilehelperhelper(String line, int index); int findoptionI(const char* option); std::string findoptionS(const char* option); bool realitycheck(String s, int minl, int maxl, char* emessage); - - }; #endif diff -Naur dansguardian-2.8.0.4/ListContainer.cpp dansguardian-2.8.0.4.oden/ListContainer.cpp --- dansguardian-2.8.0.4/ListContainer.cpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/ListContainer.cpp 2005-06-17 12:30:35.308932413 +0200 @@ -1314,3 +1314,106 @@ } return true; } + +// Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) +int ListContainer::AVfindEndsWith(char* string) { + if (items < 1) { + return -1; + } + return searchREW(0, items-1, string); +} + +int ListContainer::AVfindInList(char* string) { + if (items < 1) { + return -1; + } + if (isSW) { + return searchRSWF(0, items-1, string); + } + return searchREWF(0, items-1, string); +} + +bool ListContainer::AVreadItemList(const char* filename, bool startswith, int filters) { + std::string linebuffer; + RegExp re; + re.comp("^.*\\:[0-9]+\\/.*"); + #ifdef DGDEBUG + std::cout << filename << std::endl; + #endif + if (isCacheFileNewer(filename)) { // see if cached .process file + linebuffer = filename; // is available and up to date + linebuffer += ".processed"; + + if (getFileLength(linebuffer.c_str()) >= 4000) { + // don't bother with small files + if (!readProcessedItemList(linebuffer.c_str(), startswith, filters)) { // read cached + return false; + } + issorted = true; // don't bother sorting cached file + return true; + } + } + int len = getFileLength(filename); + if (len < 0) { + if (!isDaemonised) { + std::cerr << "Error reading file: " << filename << std::endl; + } + syslog(LOG_ERR, "%s","Error reading file:"); + syslog(LOG_ERR, "%s",filename); + return false; + } + if (len < 4) { + return true; // its blank - perhaps due to webmin editing + // just return + } + increaseMemoryBy(len); // Allocate some memory to hold file + ifstream listfile(filename, ios::in); + if (!listfile.good()) { + if (!isDaemonised) { + std::cerr << "Error opening :"<< filename << std::endl; + } + syslog(LOG_ERR, "%s","Error opening :"); + syslog(LOG_ERR, "%s", filename); + return false; + } + String temp, inc, hostname, url; + while (!listfile.eof()) { + getline(listfile, linebuffer); + if (linebuffer.length() < 2) continue; // its jibberish + if (linebuffer[0] == '#') continue; // its a comment + temp = (char*)linebuffer.c_str(); + if (temp.contains("#")) { + temp = temp.before("#"); // tidy up + } + temp.removeWhiteSpace(); // tidy up and make it handle CRLF files + if (temp.startsWith(".Include<")) { // see if we have another list + inc = temp.after(".Include<"); // to include + inc = inc.before(">"); + if (!readItemList(inc.toCharArray(), startswith, filters)) { // read it + listfile.close(); + return false; + } + continue; + } + if (temp.endsWith("/")) { + temp.chop(); // tidy up + } + if (temp.startsWith("ftp://")) { + temp = temp.after("ftp://"); // tidy up + } + if (filters == 1) { // remove port addresses + if (temp.contains(":")) { // quicker than full regexp + if (re.match(temp.toCharArray())) { + hostname = temp.before(":"); + url = temp.after("/"); + temp = hostname + "/" + url; + } + } + } + temp.toLower(); // tidy up + addToItemList(temp.toCharArray(), temp.length()); // add to unsorted + // list + } + listfile.close(); + return true; // sucessful read +} diff -Naur dansguardian-2.8.0.4/ListContainer.hpp dansguardian-2.8.0.4.oden/ListContainer.hpp --- dansguardian-2.8.0.4/ListContainer.hpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/ListContainer.hpp 2005-06-17 12:30:35.309932435 +0200 @@ -78,6 +78,11 @@ std::deque<unsigned int> morelists; // has to be non private as reg // exp compiler needs to access these + // Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) + int AVfindInList(char* string); + int AVfindEndsWith(char* string); + bool AVreadItemList(const char* filename, bool startswith, int filters); + private: bool sourceisexception; diff -Naur dansguardian-2.8.0.4/Makefile.in dansguardian-2.8.0.4.oden/Makefile.in --- dansguardian-2.8.0.4/Makefile.in 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/Makefile.in 2005-06-17 12:30:35.309932435 +0200 @@ -3,4 +3,3 @@ #Please email me (daniel@/jadeb .com) if you have modified this file for any #particular distribution or if I have got anything wrong. #Don't forget the trailing / - diff -Naur dansguardian-2.8.0.4/OptionContainer.cpp dansguardian-2.8.0.4.oden/OptionContainer.cpp --- dansguardian-2.8.0.4/OptionContainer.cpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/OptionContainer.cpp 2005-06-17 12:30:35.310932456 +0200 @@ -45,6 +45,8 @@ deleteFilterGroups(); exception_user_list.reset(); exception_ip_list.reset(); + exception_virus_mimetype_list.reset(); + exception_virus_extension_list.reset(); banned_ip_list.reset(); banned_user_list.reset(); html_template.reset(); @@ -317,6 +319,101 @@ reverse_client_ip_lookups = 0; } + // Virus scanning options + if (findoptionS("virusscan") == "on") { + virus_scan = 1; + } + else { + virus_scan = 0; + } + + std::string enginefound; + if ((enginefound = findoptionS("virusengine")) == "") { + virus_scan = 0; + } + else if (enginefound == "clamav") + virus_engine = 1; + else if (enginefound == "kav") + virus_engine = 2; + else if (enginefound == "clamdscan") + virus_engine = 3; + else + virus_scan = 0; + + trickle_length = findoptionI("tricklelength"); + if (!realitycheck(String(trickle_length), 1, 6, "tricklelength")) + { return false; } + if (trickle_length <= 0) + trickle_length = -1; + + first_trickle_delay = findoptionI("firsttrickledelay"); + if (!realitycheck(String(first_trickle_delay), 1, 3, "firsttrickledelay")) + { return false; } + if (first_trickle_delay < 1) + first_trickle_delay = 60; + + following_trickle_delay = findoptionI("followingtrickledelay"); + if (!realitycheck(String(following_trickle_delay), 1, 3, "followingtrickledelay")) + { return false; } + if (following_trickle_delay < 1) + following_trickle_delay = 30; + + max_content_scan_size = findoptionI("maxcontentscansize") * 1024; + if (!realitycheck(String(max_content_filter_size), 1, 8, "maxcontentscansize")) + { return false; } + if (max_content_scan_size < 1) + max_content_scan_size = 0; // assumes default: no limit + + if (findoptionS("virusscanexceptions") == "on") { + virus_scan_exceptions = 1; + } + else { + virus_scan_exceptions = 0; + } + + if (findoptionS("urlcachecleanonly") == "on") { + url_cache_clean_only = 1; + } + else { + url_cache_clean_only = 0; + } + + if ((download_dir = findoptionS("downloaddir")) == "") + download_dir = "/tmp/dgvirus"; + + cl_max_files = findoptionI("clmaxfiles"); + if (!realitycheck(String(cl_max_files), 1, 5, "clmaxfiles")) + { return false; } + if (cl_max_files < 1) + cl_max_files = 1500; // assumes default: 1500 files + + cl_max_rec_level = findoptionI("clmaxreclevel"); + if (!realitycheck(String(cl_max_rec_level), 1, 1, "clmaxreclevel")) + { return false; } + if (cl_max_rec_level < 1) + cl_max_rec_level = 3; // assumes default: 3 levels + + cl_max_file_size = findoptionI("clmaxfilesize"); + if (!realitycheck(String(cl_max_file_size), 1, 10, "cl_maxfilesize")) + { return false; } + if (cl_max_file_size < 1) + cl_max_file_size = 10485760; // assumes default: 10 megabytes + + virus_scanner_timeout = findoptionI("virusscannertimeout"); + + notify = findoptionI("notify"); + if (!realitycheck(notify, 1, 1, "notify")) { + notify = 0; // disabled + } + postmaster = findoptionS("postmaster"); + email_domain = findoptionS("emaildomain"); + email_server = findoptionS("emailserver"); + + + if ((localsocket = findoptionS("localsocket")) == "") { + localsocket = "/tmp/clamd"; + } + // End of Virus scanning options if (findoptionS("usexforwardedfor") == "on") { use_xforwardedfor = 1; @@ -342,6 +439,10 @@ banned_user_list_location = findoptionS("banneduserlist"); exceptions_user_list_location = findoptionS("exceptionuserlist"); exceptions_ip_list_location = findoptionS("exceptioniplist"); + + exceptions_virus_mimetype_list_location = findoptionS("exceptionvirusmimetypelist"); + exceptions_virus_extension_list_location = findoptionS("exceptionvirusextensionlist"); + language_list_location = findoptionS("languagedir") + "/" + findoptionS("language") + "/messages"; access_denied_address = findoptionS("accessdeniedaddress"); ada = access_denied_address.c_str(); @@ -382,6 +483,16 @@ return false; } // site exceptions + if (virus_scan) { + if (!readevmlfile(exceptions_virus_mimetype_list_location.c_str())) { + return false; + } // virus mimetype exceptions + + if (!readevelfile(exceptions_virus_extension_list_location.c_str())) { + return false; + } // virus extensions exceptions + } + if (!language_list.readLanguageList(language_list_location.c_str())) { return false; } // messages language file @@ -679,4 +790,45 @@ return true; } +// Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) +int OptionContainer::inVirusExceptionExtensionList(String url) { + url.removeWhiteSpace(); // just in case of weird browser crap + url.toLower(); + url.removePTP(); // chop off the ht(f)tp(s):// + url = url.after("/"); // chop off any domain before the path + if (url.length() < 2) { // will never match + return -1; + } + return exception_virus_extension_list.AVfindEndsWith(url.toCharArray()); +} + +bool OptionContainer::readevmlfile(const char* filename) { + + bool result = exception_virus_mimetype_list.AVreadItemList(filename, false, 0); + if (!result) { + if (!isDaemonised) { + std::cerr << "Error opening exceptionvirusmimetypelist" << std::endl; + } + syslog(LOG_ERR, "%s","Error opening exceptionvirusmimetypelist"); + return false; + } + exception_virus_mimetype_list.endsWithSort(); + return true; +} + +bool OptionContainer::readevelfile(const char* filename) { + + bool result = exception_virus_extension_list.AVreadItemList(filename, false, 0); + if (!result) { + if (!isDaemonised) { + std::cerr << "Error opening exceptionvirusextensionlist" << std::endl; + } + syslog(LOG_ERR, "%s","Error opening exceptionvirusextensionlist"); + return false; + } + exception_virus_extension_list.endsWithSort(); + return true; +} + + diff -Naur dansguardian-2.8.0.4/OptionContainer.hpp dansguardian-2.8.0.4.oden/OptionContainer.hpp --- dansguardian-2.8.0.4/OptionContainer.hpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/OptionContainer.hpp 2005-06-17 12:30:35.310932456 +0200 @@ -44,6 +44,7 @@ bool inBannedUserList(const std::string *user); bool readFilterGroupConf(); bool readfgfile(const char* filename); + int inVirusExceptionExtensionList(String url); int filter_groups; int log_exception_hits; int non_standard_delimiter; @@ -87,12 +88,33 @@ int proxy_group; int root_user; + int virus_scan; // signals if we are using the Virus Scanner Code. + int virus_engine; + int first_trickle_delay; + int following_trickle_delay; + int max_content_scan_size; + int trickle_length; + int virus_scan_exceptions; + int url_cache_clean_only; + std::string download_dir; + int cl_max_files; + int cl_max_rec_level; + unsigned long int cl_max_file_size; + int virus_scanner_timeout; + int notify; + std::string postmaster; + std::string email_domain; + std::string email_server; + std::string localsocket; + std::string filter_groups_list_location; std::string html_template_location; std::string banned_ip_list_location; std::string banned_user_list_location; std::string exceptions_user_list_location; std::string exceptions_ip_list_location; + std::string exceptions_virus_mimetype_list_location; + std::string exceptions_virus_extension_list_location; std::string language_list_location; std::string access_denied_address; std::string log_location; @@ -110,6 +132,8 @@ ListContainer filter_groups_list; ListContainer exception_user_list; ListContainer exception_ip_list; + ListContainer exception_virus_mimetype_list; + ListContainer exception_virus_extension_list; ListContainer banned_ip_list; ListContainer banned_user_list; LanguageContainer language_list; @@ -132,6 +156,8 @@ bool realitycheck(String s, int minl, int maxl, char* emessage); bool readAnotherFilterGroupConf(const char *filename); + bool readevmlfile(const char* filename); // exception virus mimetype list + bool readevelfile(const char* filename); // exception virus extension list }; #endif diff -Naur dansguardian-2.8.0.4/README.AV dansguardian-2.8.0.4.oden/README.AV --- dansguardian-2.8.0.4/README.AV 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/README.AV 2005-06-17 12:30:35.311932478 +0200 @@ -0,0 +1,306 @@ +For all support, instructions and copyright go to: +http://www.sourceforge.net/projects/dgav + +DansGuardian is a content filtering proxy that works in conjunction with +another caching proxy such as Squid or Oops. + +DansGuardian Anti-Virus Plugin is an add-on to provide real-time virus scanning +capabilities for content access through DansGuardian. + +Original code was written by +James A. Pattie (james@pcxperience.com) +(c) 2002 by Xperience, Inc. (http://www.pcxperience.com) +Licensed under GPL. + + 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-1307 USA + +This add-on is a modified version, developed and maintained by: +Aecio F. Neto (afn@harvest.com.br) +Harvest Consultoria (http://www.harvest.com.br) +----- +Nerijus Baliunas (nerijus@users.sourceforge.net) +http://www.sat.lt +----- +Alexander Marx (mad-ml@madness.at) +http://www.madness.at + + +CONTENTS +----------------------------------------- +Instructions +References +Configuration options +Configuration options for CLAMDSCAN +Configuration options for CLAMAV +Known issues +Enabling CLAMD daemon +Enabling KASPERSKY +Information for developers + + +Instructions +----------------------------------------- +To be able to compile this program it is necessary to have ClamAV or Kaspersky +antivirus software installed. No other engine is supported up to now. +Because this program uses library calls to one of these engines, it is +necessary to have also their respective development libraries and +include files available before compiling. + + +References +----------------------------------------- +1. Anti-Virus plugin +Sourceforge project +http://www.sourceforge.net/projects/dgav + +Web content filtering portal at Harvest Consultoria +http://www.harvest.com.br/asp/afn/wcfp.nsf + +Antivirus plugin at pcxperience +http://www.pcxperience.com/dgvirus/index.html + + +2. ClamAV and ClamDScan +Official web site: +http://www.clamav.net + +Debian package: +apt-get install libclamav1-dev + +(also recommended) +apt-get install clamav-freshclam + +RPM based systems: +Install clamav from source or check available versions at +http://www.rpmfind.net + + +3. Kaspersky +Official web site: +http://www.kasperskylabs.com + +KAV Client +http://kavclient.sourceforge.net + + +4. libesmtp +Official web site: +http://www.stafford.uklinux.net/libesmtp + +Debian package: +apt-get install libesmtp5 libesmtp-dev + +RPM based systems: +ftp://chbm.nu/pub/ +or install libesmtp from source or check available versions at +http://www.rpmfind.net + + +Configuration options +----------------------------------------- +1. virusscan +If on, we scan all downloaded content using embedded virus engine. +Only supported engines of this version are ClamAV or Kaspersky. +If off, we don't scan any downloaded content. + +Default value: on + +2. tricklelength +If off (value = -1), the scanner will send 1 byte per delay period +to the client to keep a download connection alive. +When the whole file is downloaded and scanned, the client will receive +all remaining bytes, if the file is clean. +Set to a positive integer value to enable immediate delivery to the client. +Value set means minimum number of bytes of the downloaded file +that will be held and delivered after virus scan. +If clean, the remaining bytes will be sent to the client. +If infected, file downloaded will be incomplete and a warning message +will be sent to the postmaster and possibly the user. + +Default value: 32768 + +3. firsttrickledelay +Delay in seconds to deliver the first byte to the client. +This option only applies if tricklelength is set to -1 (off). + +Default value: 30 + +4. follwingtrickledelay +Delay in seconds to deliver subsequent bytes to the client. +This option only applies if tricklelength is set to -1 (off). + +Defaul value: 60 + +5. exceptionvirusmimetypelist +Full path to file that defines mime types that will *not* be virus scanned. + +Default value: /<config dir>/exceptionvirusmimetypelist + +6. exceptionvirusextensionlist +File that defines file extensions that will *not* be virus scanned. + +Default value: /<config dir>/exceptionvirusextensionlist + +7. downloaddir +Set where the files are downloaded to before they are scanned. + +Default value: /tmp/dgvirus + +8. virusscanexceptions +If off, antivirus scanner will ignore exception sites and urls. + +Default value: on + +9. urlcachecleanonly +If off, url cache will contain entries of text only urls. +Keeping it off, preserves original Dansguardian feature and +downloaded content will be always scanned by antivirus. +When turned on, urlcache will be loaded only with content +found to be good and that is virus free. +Thus, content of urls will not be scanned for each request while it is found +in urlcache. + +Default value: on + +10. virusscannertimeout +The maximum length of time the virus scanner is allowed to run (in seconds). + +Default value: 60 + +11. notify +Sets who receives email notification when a virus is found. Users must be +authenticated to be able to receive messages. Email address for users will be +formed by the authentication name received by DG plus @emaildomain +(see option below) +0 = disabled +1 = user only +2 = postmaster only +3 = postmaster and users (default) + +Default value: 3 + +12. emaildomain +Set email domain to use when notifying users of an infected file. +This is just the domain name part, after @. +It is *highly* recommended to set this option before running dansguardian. + +Default value: your.domain.com + +13. postmaster +Set email address of who to notify about any infections found. +Should put your full domain name here too. +It is *highly* recommended to set this option before running dansguardian. + +Default value: postmaster@your.domain.com + +14. emailserver +Set the address and port of the mail server to send notifications through. +It is *highly* recommended to set this option before running dansguardian. + +Default value: 127.0.0.1:25 + +virusengine +Set the embedded virus scan engine to be used (clamav or kaspersky). + +Default value: clamav + +Configuration options for CLAMDSCAN +----------------------------------------- +1. localsocket +Defines local socket file path that will be used to communicate with clamd +daemon. + +Default value: '/tmp/clamd' + + +Configuration options for CLAMAV +----------------------------------------- +1. maxfiles +Indicates maximum number of files inside a downloaded compressed file. + +Default value: 1500 + +2. maxreclevel +Indicates maximum recursion level that will be performed when it finds a +compressed file inside downloaded compressed file. + +Default value: 3 + +3. maxfilesize +Indicates maximum size, in bytes, of each file inside a downloaded compressed +file. + +Default value: 10485760 +(10 Mbytes) + + +Known issues +----------------------------------------- +1. If a big file is downloaded and virus scanning takes longer than browser +timeout value, such file will not be downloaded properly. +The only workaround to this issue is: + 1. Insert file URL in exception list. + 2. Change virusscanexceptions to off. + 3. Gently restart Dansguardian. + 4. Download file and perform virus scanning locally. + 5. Remove file URL from exception list. + 6. Change virusscanexceptions to your previous value. + 7. Gently restart Dansguardian. + +2. Enabling Kasperksy instead of clamAV and vice-versa requires changes of +libraries and define statements in Makefile (or .in files for all systems). +This situation is already planned to be solved in some later release. + +Enabling CLAMD daemon +----------------------------------------- +To enable clamd daemon, you must apply the patch before configuring: + +patch -p1 < clamdscan.patch + +Also, the local socket file path used by clamd must be properly setup in +dansguardian.conf. + +Dansguardian must be allowed to communicate with clamd, so clamd must run +with same user and group that Dansguardian does (usually nobody). + + +Enabling KASPERSKY +----------------------------------------- +Install kavclient (http://kavclient.sourceforge.net/) and apply the patch: + +patch -p1 < kav.patch + +If you get "libesmtp.so: undefined reference to `pthread_getspecific'" +error while linking, add -pthread at the end of LIBS line in autoconf/linux.in. + + +Information for developers +----------------------------------------- +How to update to a new DG version: +"diff -urN dansguardian-2.8.0.2 dansguardian-2.8.0.3 > dg.diff" +"cd dgav" +"patch -p1 < ../dg.diff" +Update failed hunks manually. +It would be better to import newer version with cvs. + +What to do before the release: +Update version information in DGVirus.spec, dansguardian.cpp and +configure files. Commit all uncommitted changes. Tag a new release +in cvs tree: "cvs tag DGAV_6_3". + +How to create antivirus patch which can be released: +"diff -urN -x CVS -x .cvsignore -x phraselists -X dgav/.cvsignore dansguardian-2.8.0.3 dgav > dgav.patch" + +<EOF> \ No newline at end of file diff -Naur dansguardian-2.8.0.4/String.cpp dansguardian-2.8.0.4.oden/String.cpp --- dansguardian-2.8.0.4/String.cpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/String.cpp 2005-06-17 12:30:35.312932500 +0200 @@ -102,6 +102,29 @@ } #endif +// Bodge to handle GCC3.x - improvements very welcome!!! +// Sun 1st December 2002 - daniel@ //jadeb.com +#ifdef __GCCVER3 + String::String(const long unsigned num) { + std::ostringstream buf; + buf << num << std::ends; + std::string s = buf.str(); + char* bs = (char*)s.c_str(); + int l = strlen(bs); + data = new char[l + 1]; + memcpy(data, bs, l); + sl = l; + data[sl] = '\0'; + } +#else + String::String(const long unsigned num) { + std::ostrstream buf; + buf << num << std::ends; + data = buf.str(); // with side effect: it calls buf.freeze() + sl = buf.pcount() - 1; + } +#endif + String::String(const char* bs, int len) { data = new char[len + 1]; memcpy(data, bs, len); diff -Naur dansguardian-2.8.0.4/String.hpp dansguardian-2.8.0.4.oden/String.hpp --- dansguardian-2.8.0.4/String.hpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/String.hpp 2005-06-17 12:30:35.312932500 +0200 @@ -38,6 +38,7 @@ String(const String& s); String(const int num); String(const long num); + String(const long unsigned num); String(const char bs[], int len); String(const char bs[], int start, int len); friend ostream & operator << (ostream & out, const String& s); diff -Naur dansguardian-2.8.0.4/VirusEngine.cpp dansguardian-2.8.0.4.oden/VirusEngine.cpp --- dansguardian-2.8.0.4/VirusEngine.cpp 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/VirusEngine.cpp 2005-06-17 12:30:35.312932500 +0200 @@ -0,0 +1,103 @@ +// Written by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) +// Additions by Nerijus Baliunas (nerijus@users.sourceforge.net) + +// 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-1307 USA + +#include "autoconf/platform.h" +#include "VirusEngine.hpp" +#include "OptionContainer.hpp" +#include <unistd.h> +#include <fstream> + +#ifdef __CLAMDSCAN +#include "ClamDScan.hpp" +#endif + +#ifdef __CLAMAV +#include "ClamAV.hpp" +#include <clamav.h> +extern ClamAV clamav; +#endif + +#ifdef __KAV +#include "KAV.hpp" +extern KAV kav; +#endif + +extern OptionContainer o; + +VirusEngine::VirusEngine() +:virname(0), strError(0) {} +// virname - holds the virus name, if found +// strError - holds the error message text from the engine library + +VirusEngine::~VirusEngine() { +// delete statements removed according to Alexander Marx mem leak patch +} + +int VirusEngine::scan(char* tempFileName) { + + int ret = 0; + + if (o.virus_engine == 1) { // ClamAV +#ifdef __CLAMAV + ret = clamav.scanFile(tempFileName); + virname = clamav.virname; + + if (ret == CL_CLEAN) + ret = AV_CLEAN; + else if (ret == CL_VIRUS) + ret = AV_VIRUS; + else { + strError = cl_strerror(ret); + } +#endif + } + else if (o.virus_engine == 2) { // KAV +#ifdef __KAV + ret = kav.scanFile(tempFileName); + virname = kav.virname; + + if (ret == KAV_STATUS_CLEAN || ret == KAV_STATUS_CORRUPTED_FOUND) + ret = AV_CLEAN; + else { + //if (ret == KAV_STATUS_VIRUSES_FOUND || ret == KAV_STATUS_SUSPICIOUS_FOUND + //|| ret == KAV_STATUS_CORRUPTED_VIRUSES_FOUND) + ret = AV_VIRUS; + } +#endif + } + else if (o.virus_engine == 3) { // ClamDScan +#ifdef __CLAMDSCAN + ClamDScan clamdscan; + ret = clamdscan.scanFile(tempFileName); + virname = clamdscan.virname; + + if (ret == CL_CLEAN) + ret = AV_CLEAN; + else if (ret == CL_VIRUS) + ret = AV_VIRUS; + else { + strError = clamdscan.errorMsg; + } +#endif + } + else { + virname = ""; + ret = AV_CLEAN; + } + + return ret; +} diff -Naur dansguardian-2.8.0.4/VirusEngine.hpp dansguardian-2.8.0.4.oden/VirusEngine.hpp --- dansguardian-2.8.0.4/VirusEngine.hpp 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/VirusEngine.hpp 2005-06-17 12:30:35.313932522 +0200 @@ -0,0 +1,35 @@ +// Written by Aecio F. Neto(afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) + +// 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-1307 USA + +#ifndef __HPP_VIRUSENGINE +#define __HPP_VIRUSENGINE + +#define AV_CLEAN 0 /* virus not found */ +#define AV_VIRUS 1 /* virus found */ + +class VirusEngine { + +public: + const char* virname; + const char* strError; + + VirusEngine(); + ~VirusEngine(); + + int scan(char* tempFileName); +}; + +#endif diff -Naur dansguardian-2.8.0.4/VirusScanner.cpp dansguardian-2.8.0.4.oden/VirusScanner.cpp --- dansguardian-2.8.0.4/VirusScanner.cpp 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/VirusScanner.cpp 2005-06-17 12:30:35.313932522 +0200 @@ -0,0 +1,482 @@ +// Written by James A. Pattie (james@pcxperience.com) Xperience, Inc. (www.pcxperience.com) +// Additions by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) +// Additions by Nerijus Baliunas (nerijus@users.sourceforge.net) + +// 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-1307 USA + +#include "autoconf/platform.h" +#include "VirusEngine.hpp" +#include "VirusScanner.hpp" +#include "String.hpp" +#include <libesmtp.h> +#include <syslog.h> +#include <unistd.h> +#include <errno.h> +#define MAX_BUF 262144 // 256k +#define SIZE 32768 // 32k + +extern OptionContainer o; + +VirusScanner::VirusScanner() +:file_length(0),isInfected(0),virname(0),tempFileName(new char[0]),tempfileFD(0),timeout(60),trickle_pos(0) {} + +VirusScanner::~VirusScanner() { + unlink(tempFileName); + + delete[] tempFileName; + // delete temporary file when class is destroyed + // basically this is for when an exception is caught +} + +void VirusScanner::handleScanning(Socket *proxysock, Socket *peerconn, String url, int docsize, int contentLength, + std::string username, std::string clientip, int filtergroup, + std::string location, int reporting_level, int scantype) throw(exception) { + if (scantype == 0) { + // scantype 0 indicates that a temporary file has to be created and + // its content will be retrieved by this class + createTempFile(); + } + + if (contentLength >= docsize) { + // this means that some bytes were already written to disk + // but not all of them. So, continue receiving them from proxy + try { + in(proxysock, peerconn, url, docsize, contentLength, username, clientip, scantype); + } catch (exception& e) { + #ifdef DGDEBUG + std::cout << "***exception on Viruscanner::in***" << std::endl; + #endif + throw e; + } + } + + // scantype indicates that a temporary file already exists and + // its content has a FD in this class + scan(peerconn, url, username, clientip, filtergroup, location, reporting_level, scantype); +} + +void VirusScanner::createTempFile(void) throw (exception) { + fstream tmpFile; + + tempFileName = new char[256]; + strcpy(tempFileName, o.download_dir.c_str()); + strcat(tempFileName, "/tfXXXXXX"); + + + if ((tempfileFD = mkstemp(tempFileName)) < 0) { + #ifdef DGDEBUG + std::cout << "VirusScanner could not create temp file " << tempFileName << std::endl; + #endif + + if (mkdir(o.download_dir.c_str(), S_IRWXU) == -1) { + if (errno != EEXIST) { + #ifdef DGDEBUG + std::cout << "VirusScanner could not re-create temp dir" << std::endl; + #endif + throw exception(); + } + } + + strcpy(tempFileName, o.download_dir.c_str()); + strcat(tempFileName, "/tfXXXXXX"); + if ((tempfileFD = mkstemp(tempFileName)) < 0) { + #ifdef DGDEBUG + std::cout << "VirusScanner could not create temp file " << tempFileName << std::endl; + #endif + throw exception(); + } + } +} + +bool VirusScanner::out(Socket *sock, char *block, int size) throw(exception) { + + // exceptions on timeout or error + (*sock).readyForOutput(timeout); + + // need exception or something for a bad write + if (!(*sock).writeToSocket(block, size, 0, timeout)) { + #ifdef DGDEBUG + std::cout << "Error sending data to socket" << endl; + #endif + throw exception(); + return false; + } // write the data block out to the stream + + return true; +} + +void VirusScanner::in(Socket *proxysock, Socket *peerconn, String url, int docsize, int contentLength, + std::string username, std::string clientip, int scantype) throw(exception) { + + // ConnectionHandler had received all data. Bye... we will only scan it. + if ((contentLength > 0) && (docsize == contentLength)) { + #ifdef DGDEBUG + std::cout << "ConnectionHandler had received all data. returning..." << std::endl; + #endif + return; + } + + time_t lastWrite; + time_t current; + + char input[MAX_BUF]; // buffer for storing a grabbed block from the input stream + char output[SIZE]; + + int tricklelength = o.trickle_length; + int followingtrickledelay = o.following_trickle_delay; + int delay = o.first_trickle_delay; + int size = 1; + int bufferlength = 0; + int lastsize = 0; + int flush = 0; + int rci = 0; + int rco = 0; + + bool flushed = false; + + if (contentLength > 0) { + file_length = contentLength; + // lastsize is only used for positive tricklelength + lastsize = contentLength - tricklelength; + } + else { + // bad web server don't provide file sizes. + // switches back to 1 byte trickle method + // because we don't know when to stop sending data in trickle mode + tricklelength = 1; + lastsize = 0; + } + + if (scantype == 1) { + lseek(tempfileFD, 0, SEEK_SET); + + while(true) { + rci = read(tempfileFD, input, tricklelength); + + if (rci < 1) { + break; + } + + if (rci > 0) { + bufferlength += rci; + + if (out(peerconn, input, rci)) { + trickle_pos += rci; + } + else { + #ifdef DGDEBUG + std::cout << "Could not write block to socket." << std::endl; + #endif + throw exception(); + } + } + + if ((tricklelength > 0) && (trickle_pos + tricklelength) >= lastsize) { + break; + } + } + } + + // position file pointer to append more data + lseek(tempfileFD, 0, SEEK_END); + + lastWrite = time(NULL); + current = lastWrite; + while(true) { + flushed = false; + try { + // grab a fixed amount of input + (*proxysock).checkForInput(timeout); + rci = (*proxysock).readFromSocket(&input[flush], SIZE, 0, timeout); + } catch (exception& e) { + break; + } + + if (rci < 1) { + break; // nothing received or an error ocurred + } + + if ((flush + rci + SIZE) >= MAX_BUF) { + // write direct to block instead of looping to insert 1 byte each time + // based on code from Alexander Marx + lseek(tempfileFD, 0, SEEK_END); + write(tempfileFD, input, flush + rci); + bufferlength += (flush + rci); // update data size counter + flushed = true; + #ifdef DGDEBUG + std::cout << "flushed data to disk due to a full buffer" << std::endl; + #endif + } + + if (tricklelength > 0) { + if ((trickle_pos + SIZE + tricklelength) < lastsize) { + if (out(peerconn, &input[flush], rci)) { + trickle_pos += rci; + } + else { + break; + } + } + } + else if (bufferlength > trickle_pos) { + if (trickle_pos > 0) { + delay = followingtrickledelay; + } + + current = time(NULL); + if ((current - lastWrite) >= delay) { + #ifdef DGDEBUG + std::cout << "It is time to send some data to client" << std::endl; + #endif + + lseek(tempfileFD, trickle_pos, SEEK_SET); + rco = read(tempfileFD, output, size); + + // write the data block out to the stream + if (out(peerconn, output, rco)) { + lastWrite = current; + trickle_pos += rco; + } + #ifdef DGDEBUG + else { + std::cout << "Could not trickle byte " << trickle_pos << " - filename: " << tempFileName << "'" << std::endl; + } + #endif + } + } + + #ifdef DGDEBUG + std::cout << "flush: " << flush << std::endl; + std::cout << "rci: " << rci << std::endl; + std::cout << "trickle_pos: " << trickle_pos << std::endl; + std::cout << "bufferlength: " << bufferlength << std::endl; + std::cout << "tricklelength: " << tricklelength << std::endl; + std::cout << "size: " << size << std::endl; + #endif + + if (flushed) { + flush = 0; + } + else { + flush += rci; + } + } + + // There is data left in memory block + if (!flushed && (flush > 0)) { + #ifdef DGDEBUG + std::cout << "Writing data left in memory to disk" << std::endl; + #endif + lseek(tempfileFD, 0, SEEK_END); + write(tempfileFD, input, flush); + } +} + +void VirusScanner::scan(Socket *peerconn, String url, std::string username, std::string clientip, int filtergroup, std::string location, int reporting_level, int scantype) throw(exception) { + int rc = 0; + char block[MAX_BUF]; // buffer for storing a grabbed block from the input stream + + VirusEngine avEngine; + + #ifdef DGDEBUG + std::cout << "Scanning file: '" << tempFileName << "' for user '" << username << "'..." << std::endl; + #endif + + if (file_length == 0) { + lseek(tempfileFD, 0, SEEK_SET); + file_length = lseek(tempfileFD, 0, SEEK_END); + } + lseek(tempfileFD, 0, SEEK_SET); + + #ifdef DGDEBUG + std::cout << "Starting file scanning..." << endl; + system("date"); + #endif + isInfected = avEngine.scan(tempFileName); + #ifdef DGDEBUG + system("date"); + std::cout << "End of file scanning" << endl; + std::cout << "VirusScanner - isInfected = " << isInfected << std::endl; + #endif + + if (isInfected == AV_CLEAN) { + #ifdef DGDEBUG + std::cout << "sending data from trickle_pos = '" << trickle_pos << "', length = '" << file_length << std::endl; + #endif + + // position ourselves. + lseek(tempfileFD, trickle_pos, SEEK_SET); + while(true) { + rc = read(tempfileFD, block, MAX_BUF); + #ifdef DGDEBUG + std::cout << "rc = " << rc << std::endl; + #endif + + if (rc < 1) { + break; // an error occured so end the while() + // or none received so pipe is closed + } + + if (rc > 0) { + if (out(peerconn, block, rc)) { + #ifdef DGDEBUG + std::cout << "Wrote block to client socket." << std::endl; + #endif + } + #ifdef DGDEBUG + else { + std::cout << "Could not write block to client socket." << std::endl; + } + #endif + } + } + } + else if (isInfected == AV_VIRUS) { + virname = avEngine.virname; + // send the browser an error html document stating that the file was infected with virus. + // Added feature to use dansguardian.pl + try { + // HTTP header was already sent to client + // Sending only HTTP body to client + if (reporting_level == 3) { + String msg; + msg = "Virus "; + msg += virname; + msg += " found"; + o.html_template.display(peerconn, + url.toCharArray(), + msg, + o.language_list.getTranslation(1100), + username.c_str(), + clientip.c_str(), + String (filtergroup + 1), + "" + ); + } + else { + (*peerconn).writeString("<html><head>"); + (*peerconn).writeString("<script language=JavaScript>"); + (*peerconn).writeString("location.href="); + (*peerconn).writeString(location.c_str()); + (*peerconn).writeString("?USER=="); + (*peerconn).writeString(username.c_str()); + (*peerconn).writeString("::REASON==Virus%20'"); + (*peerconn).writeString(virname); + (*peerconn).writeString("'%20found"); + (*peerconn).writeString("::URL=="); + (*peerconn).writeString(url.toCharArray()); + (*peerconn).writeString("</script>"); + (*peerconn).writeString("</head></html>"); + } + + if (o.notify) { + #ifdef DGDEBUG + std::cout << "Sending email..." << endl; + #endif + sendmail(username, url, clientip); + } + } catch (exception& e) { + #ifdef DGDEBUG + std::cout << "sending infected message failed!" << std::endl; + #endif + throw e; + } + #ifdef DGDEBUG + std::cout << "VirusName: " << virname << endl; + #endif + } + else { + try { + (*peerconn).writeString("<html><head><title>DansGuardian - Antivirus Error</title></head>"); + (*peerconn).writeString("<body><center><h1>DansGuardian Antivirus Patch - Error during scanning</h1>"); + (*peerconn).writeString("Error message: '"); + (*peerconn).writeString(avEngine.strError); + (*peerconn).writeString("'</center></body></html>"); + } catch (exception& e) { + throw e; + } + } + + close(tempfileFD); + unlink(tempFileName); +} + +void VirusScanner::sendmail (std::string username, String url, std::string clientip) { + smtp_session_t session; + smtp_message_t message; + smtp_recipient_t recipient; + char *host = (char *)o.email_server.c_str(); + std::string from = "\"DansGuardian Anti-Virus\" <dgvirus@"; + from += o.email_domain; + from += ">"; + std::string return_path = "dgvirus@"; + return_path += o.email_domain; + char *subject = "Virus Found by DansGuardian Anti-Virus"; + + session = smtp_create_session (); + message = smtp_add_message (session); + + // Set the host running the SMTP server. LibESMTP has a default port + // number of 587, however this is not widely deployed so the port + // is specified as 25 along with the default MTA host. + smtp_set_server (session, host); + + // Set the reverse path for the mail envelope. (NULL is ok) + smtp_set_reverse_path (message, return_path.c_str()); + + // RFC 2822 doesn't require recipient headers but a To: header would + // be nice to have if not present. + smtp_set_header (message, "To", NULL, NULL); + + // Set the Subject: header. For no reason, we want the supplied subject + // to override any subject line in the message headers. + if (subject != NULL) { + smtp_set_header (message, "Subject", subject); + smtp_set_header_option (message, "Subject", Hdr_OVERRIDE, 1); + } + + std::string str = "MIME-Version: 1.0\r\n"; + str += "Content-Type: text/plain; charset=iso-8859-1\r\n"; + str += "Content-Transfer-Encoding: 7bit\r\n"; + str += "From: "; + str += from; + str += "\r\n\r\n"; + str += "Virus : "; + str += virname; + str += "\r\n"; + str += "User : "; + str += username; + str += "\r\nURL : "; + str += url.toCharArray(); + str += "\r\nUser IP : "; + str += clientip; + smtp_set_message_str(message, (char *)str.c_str()); + + if (o.notify > 1) + recipient = smtp_add_recipient (message, o.postmaster.c_str()); + + if (o.notify != 2) + if (username != "-") + recipient = smtp_add_recipient (message, (username + "@" + o.email_domain).c_str()); + + // Initiate a connection to the SMTP server and transfer the message. + if (!smtp_start_session (session)) { + char buf[128]; + syslog(LOG_ERR, "SMTP server problem: %s", + smtp_strerror (smtp_errno (), buf, sizeof buf)); + } + + smtp_destroy_session (session); +} diff -Naur dansguardian-2.8.0.4/VirusScanner.hpp dansguardian-2.8.0.4.oden/VirusScanner.hpp --- dansguardian-2.8.0.4/VirusScanner.hpp 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/VirusScanner.hpp 2005-06-17 12:30:35.314932544 +0200 @@ -0,0 +1,51 @@ +// Written by James A. Pattie (james@pcxperience.com) Xperience, Inc. (www.pcxperience.com) + +// 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-1307 USA + +#ifndef __HPP_VIRUSSCANNER +#define __HPP_VIRUSSCANNER +#include "OptionContainer.hpp" +#include "Socket.hpp" +#include "String.hpp" +#include <exception> +#include <fstream> +#include <sys/stat.h> + +class VirusScanner { + +public: + long unsigned int file_length; + int isInfected; + const char *virname; + char *tempFileName; + int tempfileFD; + + VirusScanner(); + ~VirusScanner(); + + void handleScanning(Socket *proxysock, Socket *peerconn, String url, int docsize, int contentLength, std::string username, std::string clientip, int filtergroup, std::string location, int reporting_level, int scantype) throw(exception); + void setTimeout(int t); + void createTempFile(void) throw(exception); + bool out(Socket *sock, char *block, int size) throw(exception); + void in(Socket* sock, Socket* outSock, String url, int docsize, int contentLength, std::string username, std::string clientip, int scantype) throw(exception); + void scan(Socket *peerconn, String url, std::string username, std::string clientip, int filtergroup, std::string location, int reporting_level, int scantype) throw(exception); + void sendmail (std::string username, String url, std::string clientip); + +private: + int timeout; + int trickle_pos; +}; + +#endif diff -Naur dansguardian-2.8.0.4/autoconf/linux.in dansguardian-2.8.0.4.oden/autoconf/linux.in --- dansguardian-2.8.0.4/autoconf/linux.in 2005-06-17 12:32:42.648709149 +0200 +++ dansguardian-2.8.0.4.oden/autoconf/linux.in 2005-06-17 12:32:23.725296145 +0200 @@ -12,15 +12,19 @@ DataBuffer.o HTTPHeader.o NaughtyFilter.o RegExp.o Socket.o \ FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \ HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \ - FOptionContainer.o ListManager.o md5.o + FOptionContainer.o ListManager.o md5.o \ + ClamAV.o VirusEngine.o VirusScanner.o # Why so? Already linked against zlib dynamically, -lz hereunder in DGCFLAGS #LIBS = -Wl,-Bstatic -lz -Wl,-Bdynamic +LIBS = -lclamav -lesmtp PROG = dansguardian INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \ bannedphraselist exceptionsitelist dansguardian.pl \ bannedextensionlist bannedmimetypelist pics exceptioniplist \ - exceptionuserlist exceptionurllist contentregexplist + exceptionuserlist exceptionurllist contentregexplist \ + exceptionvirusmimetypelist exceptionvirusextensionlist \ + exceptionvirussitelist exceptionvirusurllist CONFIGUREFILES = autoconf/platform.h confdefs.h config.log conftest.subs \ dansguardian.conf config.cache Makefile logrotation weightedphraselist \ bannedphraselist exceptionphraselist dansguardian.sysv \ @@ -79,6 +83,10 @@ cp -f ./exceptionurllist $I$(CONFFILELOCATION)exceptionurllist cp -f ./exceptionuserlist $I$(CONFFILELOCATION)exceptionuserlist cp -f ./exceptioniplist $I$(CONFFILELOCATION)exceptioniplist + cp -f ./exceptionvirusmimetypelist $I$(CONFFILELOCATION)exceptionvirusmimetypelist + cp -f ./exceptionvirusextensionlist $I$(CONFFILELOCATION)exceptionvirusextensionlist + cp -f ./exceptionvirussitelist $I$(CONFFILELOCATION)exceptionvirussitelist + cp -f ./exceptionvirusurllist $I$(CONFFILELOCATION)exceptionvirusurllist cp -f ./pics $I$(CONFFILELOCATION)pics cp -f ./transparent1x1.gif $I$(CONFFILELOCATION)transparent1x1.gif cp -f ./logrotation $I$(CONFFILELOCATION)logrotation diff -Naur dansguardian-2.8.0.4/clamdscan.patch dansguardian-2.8.0.4.oden/clamdscan.patch --- dansguardian-2.8.0.4/clamdscan.patch 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/clamdscan.patch 2005-06-17 12:30:35.314932544 +0200 @@ -0,0 +1,126 @@ +diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/fbsd.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/fbsd.in +--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/fbsd.in 2004-10-23 16:19:25.000000000 -0300 ++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/fbsd.in 2005-02-06 22:20:01.000000000 -0200 +@@ -9,9 +9,9 @@ + FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \ + HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \ + FOptionContainer.o ListManager.o md5.o \ +- ClamAV.o VirusEngine.o VirusScanner.o ++ ClamDScan.o VirusEngine.o VirusScanner.o + +-LIBS = $(libdir)/libz.a -lclamav -lesmtp ++LIBS = $(libdir)/libz.a -lesmtp + PROG = dansguardian + INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \ + bannedphraselist exceptionsitelist dansguardian.pl \ +diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/hpux.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/hpux.in +--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/hpux.in 2004-10-23 16:20:20.000000000 -0300 ++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/hpux.in 2005-02-06 22:20:01.000000000 -0200 +@@ -13,9 +13,9 @@ + FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \ + HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \ + FOptionContainer.o ListManager.o md5.o \ +- ClamAV.o VirusEngine.o VirusScanner.o ++ ClamDScan.o VirusEngine.o VirusScanner.o + +-LIBS = $(libdir)/libz.a -lclamav -lesmtp ++LIBS = $(libdir)/libz.a -lesmtp + PROG = dansguardian + INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \ + bannedphraselist exceptionsitelist dansguardian.pl \ +diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/linux.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/linux.in +--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/linux.in 2004-10-23 15:16:26.000000000 -0300 ++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/linux.in 2005-02-06 22:20:01.000000000 -0200 +@@ -13,9 +13,9 @@ + FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \ + HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \ + FOptionContainer.o ListManager.o md5.o \ +- ClamAV.o VirusEngine.o VirusScanner.o ++ ClamDScan.o VirusEngine.o VirusScanner.o + +-LIBS = $(libdir)/libz.a -lclamav -lesmtp ++LIBS = $(libdir)/libz.a -lesmtp + PROG = dansguardian + INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \ + bannedphraselist exceptionsitelist dansguardian.pl \ +diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/nbsd.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/nbsd.in +--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/nbsd.in 2004-10-23 16:21:25.000000000 -0300 ++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/nbsd.in 2005-02-06 22:20:01.000000000 -0200 +@@ -9,9 +9,9 @@ + FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \ + HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \ + FOptionContainer.o ListManager.o md5.o \ +- ClamAV.o VirusEngine.o VirusScanner.o ++ ClamDScan.o VirusEngine.o VirusScanner.o + +-LIBS = $(libdir)/libz.a -lclamav -lesmtp ++LIBS = $(libdir)/libz.a -lesmtp + PROG = dansguardian + INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \ + bannedphraselist exceptionsitelist dansguardian.pl \ +diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/obsd.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/obsd.in +--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/obsd.in 2004-10-23 16:22:21.000000000 -0300 ++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/obsd.in 2005-02-06 22:20:01.000000000 -0200 +@@ -9,9 +9,9 @@ + FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \ + HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \ + FOptionContainer.o ListManager.o md5.o \ +- ClamAV.o VirusEngine.o VirusScanner.o ++ ClamDScan.o VirusEngine.o VirusScanner.o + +-LIBS = $(libdir)/libz.a -lclamav -lesmtp ++LIBS = $(libdir)/libz.a -lesmtp + PROG = dansguardian + INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \ + bannedphraselist exceptionsitelist dansguardian.pl \ +diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/osx.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/osx.in +--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/osx.in 2004-10-23 16:23:02.000000000 -0300 ++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/osx.in 2005-02-06 22:20:01.000000000 -0200 +@@ -9,9 +9,9 @@ + FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \ + HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \ + FOptionContainer.o ListManager.o md5.o \ +- ClamAV.o VirusEngine.o VirusScanner.o ++ ClamDScan.o VirusEngine.o VirusScanner.o + +-LIBS = $(libdir)/libgcc.a -lclamav -lesmtp ++LIBS = $(libdir)/libgcc.a -lesmtp + PROG = dansguardian + INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \ + bannedphraselist exceptionsitelist dansguardian.pl \ +diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/solaris.in dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/solaris.in +--- dansguardian-2.8.0.3-antivirus-6.3.7/autoconf/solaris.in 2004-10-23 16:23:55.000000000 -0300 ++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/autoconf/solaris.in 2005-02-06 22:20:01.000000000 -0200 +@@ -9,9 +9,9 @@ + FatController.o UDSocket.o SysV.o ListContainer.o Ident.o \ + HTMLTemplate.o LanguageContainer.o DynamicURLList.o ImageContainer.o \ + FOptionContainer.o ListManager.o md5.o \ +- ClamAV.o VirusEngine.o VirusScanner.o ++ ClamDScan.o VirusEngine.o VirusScanner.o + +-LIBS = -lstdc++ -lsocket -lnsl -lz -lresolv -lclamav -lesmtp ++LIBS = -lstdc++ -lsocket -lnsl -lz -lresolv -lesmtp + PROG = dansguardian + INSTALLFILES = dansguardian dansguardian.conf dansguardian.sysv \ + bannedphraselist exceptionsitelist dansguardian.pl \ +diff -urN dansguardian-2.8.0.3-antivirus-6.3.7/configure dansguardian-2.8.0.3-antivirus-6.3.7-clamd/configure +--- dansguardian-2.8.0.3-antivirus-6.3.7/configure 2004-10-23 14:13:16.000000000 -0300 ++++ dansguardian-2.8.0.3-antivirus-6.3.7-clamd/configure 2005-02-06 22:20:01.000000000 -0200 +@@ -2618,7 +2618,7 @@ + ;; + esac + # Added by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) +-echo "#define __CLAMAV 1" >> autoconf/platform.h ++echo "#define __CLAMDSCAN 1" >> autoconf/platform.h + + + +@@ -3197,7 +3197,7 @@ + + echo "# OPTION: virusengine">>dansguardian.conf + echo "# Set the embedded virus scan engine to be used (clamdscan, clamav or kav).">>dansguardian.conf +-echo "virusengine = 'clamav'">>dansguardian.conf ++echo "virusengine = 'clamdscan'">>dansguardian.conf + echo "" >>dansguardian.conf + + echo "# OPTION: tricklelength" >>dansguardian.conf diff -Naur dansguardian-2.8.0.4/configure dansguardian-2.8.0.4.oden/configure --- dansguardian-2.8.0.4/configure 2005-06-17 12:32:42.654709280 +0200 +++ dansguardian-2.8.0.4.oden/configure 2005-06-17 12:30:35.317932609 +0200 @@ -2417,6 +2417,7 @@ echo "Using IPv6 support" fi +###AFN trap '' 1 2 15 cat > confcache <<\EOF @@ -2616,7 +2617,8 @@ $extendedecho "#define __ENDIANH \"$endianh\"" >>autoconf/platform.h ;; esac - +# Added by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) +echo "#define __CLAMAV 1" >> autoconf/platform.h @@ -2786,7 +2788,7 @@ rm -f dansguardian.conf fi -echo "# DansGuardian config file for version 2.8.0" >dansguardian.conf +echo "# DansGuardian config file for version 2.8.0 with Anti-Virus plug-in 6.3.6" >dansguardian.conf echo "" >>dansguardian.conf echo "# **NOTE** as of version 2.7.5 most of the list files are now in dansguardianf1.conf" >>dansguardian.conf echo "" >>dansguardian.conf @@ -2925,9 +2927,9 @@ echo "# Positive result caching for text URLs" >>dansguardian.conf echo "# Caches good pages so they don't need to be scanned again" >>dansguardian.conf echo "# 0 = off (recommended for ISPs with users with disimilar browsing)" >>dansguardian.conf -echo "# 1000 = recommended for most users" >>dansguardian.conf +echo "# 2000 = recommended for most users" >>dansguardian.conf echo "# 5000 = suggested max upper limit" >>dansguardian.conf -echo "urlcachenumber = 1000" >>dansguardian.conf +echo "urlcachenumber = 2000" >>dansguardian.conf echo "#" >>dansguardian.conf echo "# Age before they are stale and should be ignored in seconds" >>dansguardian.conf echo "# 0 = never" >>dansguardian.conf @@ -3177,11 +3179,159 @@ echo "# on|off ( defaults to off )" >>dansguardian.conf echo "softrestart = off" >>dansguardian.conf echo "" >>dansguardian.conf +echo "" >>dansguardian.conf +echo "" >>dansguardian.conf +# Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) +echo "# ANTIVIRUS SETTINGS" >>dansguardian.conf +echo "# --------------------" >>dansguardian.conf +echo "" >>dansguardian.conf +echo "# OPTION: virusscan" >>dansguardian.conf +echo "# If on, we scan all downloaded content using embedded virus engine." >>dansguardian.conf +echo "# Supported engines of this version are ClamAV, KAV or ClamDScan." >>dansguardian.conf +echo "# If off, we don't scan any downloaded content." >>dansguardian.conf +echo "# See http://www.pcxperience.org/dgvirus/ for more details." >>dansguardian.conf +echo "#" >>dansguardian.conf +echo "virusscan = on" >>dansguardian.conf +echo "" >>dansguardian.conf +echo "# OPTION: virusengine">>dansguardian.conf +echo "# Set the embedded virus scan engine to be used (clamdscan, clamav or kav).">>dansguardian.conf +echo "virusengine = 'clamav'">>dansguardian.conf +echo "" >>dansguardian.conf + +echo "# OPTION: tricklelength" >>dansguardian.conf +echo "# If off (value = -1), the scanner will send 1 byte per delay period" >>dansguardian.conf +echo "# to the client to keep a download connection alive." >>dansguardian.conf +echo "# When the whole file is downloaded and scanned, the client will receive " >>dansguardian.conf +echo "# all remaining bytes, if the file is clean." >>dansguardian.conf +echo "# Set to a positive integer value to enable immediate delivery to the client. " >>dansguardian.conf +echo "# Value set means minimum number of bytes of the downloaded file " >>dansguardian.conf +echo "# that will be held and delivered after virus scan." >>dansguardian.conf +echo "# If clean, the remaining bytes will be sent to the client." >>dansguardian.conf +echo "# If infected, file downloaded will be incomplete and a warning message " >>dansguardian.conf +echo "# will be sent to the postmaster and possibly the user." >>dansguardian.conf +echo "# Recommended minimum positive value: 32768 (32 kbytes)" >>dansguardian.conf +echo "#" >>dansguardian.conf +echo "tricklelength = 32768" >>dansguardian.conf +echo "" >>dansguardian.conf +echo "# OPTION: firsttrickledelay" >>dansguardian.conf +echo "# Delay in seconds to deliver the first byte to the client." >>dansguardian.conf +echo "# This option only applies if tricklelength is set to -1 (off)." >>dansguardian.conf +echo "#" >>dansguardian.conf +echo "firsttrickledelay = 30" >>dansguardian.conf +echo "" >>dansguardian.conf +echo "# OPTION: follwingtrickledelay" >>dansguardian.conf +echo "# Delay in seconds to deliver subsequent bytes to the client." >>dansguardian.conf +echo "# This option only applies if tricklelength is set to -1 (off)." >>dansguardian.conf +echo "#" >>dansguardian.conf +echo "followingtrickledelay = 60" >>dansguardian.conf +echo "" >>dansguardian.conf +echo "# OPTION: exceptionvirusmimetypelist" >>dansguardian.conf +echo "# The following file allow you to define mime types" >>dansguardian.conf +echo "# that should not be virus scanned." >>dansguardian.conf +$extendedecho "exceptionvirusmimetypelist = '$prefixdir$sysconfdir\c" >>dansguardian.conf +echo "exceptionvirusmimetypelist'" >>dansguardian.conf +echo "" >>dansguardian.conf +echo "# OPTION: maxcontentscansize" >>dansguardian.conf +echo "# Set the maximum size of a content to be virus scanned." >>dansguardian.conf +echo "# Content size above this value will not be scanned against viruses." >>dansguardian.conf +echo "# To have no limit, use 0 (zero)." >>dansguardian.conf +echo "maxcontentscansize = 0" >>dansguardian.conf +echo "" >>dansguardian.conf + +echo "# OPTION: exceptionvirusextensionlist" >>dansguardian.conf +echo "# The following file allow you to define file extensions" >>dansguardian.conf +echo "# that should not be virus scanned." >>dansguardian.conf +$extendedecho "exceptionvirusextensionlist = '$prefixdir$sysconfdir\c" >>dansguardian.conf +echo "exceptionvirusextensionlist'" >>dansguardian.conf +echo "" >>dansguardian.conf + +echo "# OPTION: downloaddir">>dansguardian.conf +echo "# Set where the files are downloaded to before they are scanned." >>dansguardian.conf +echo "downloaddir = '/tmp/dgvirus'" >>dansguardian.conf +echo "" >>dansguardian.conf + +echo "# OPTION: virusscanexceptions" >>dansguardian.conf +echo "# If off, antivirus scanner will ignore exception sites and urls." >>dansguardian.conf +echo "#" >>dansguardian.conf +echo "virusscanexceptions = on" >>dansguardian.conf +echo "" >>dansguardian.conf + +echo "# OPTION: urlcachecleanonly" >>dansguardian.conf +echo "# If off, url cache will contain entries of text only urls." >>dansguardian.conf +echo "# Keeping it off, preserves original Dansguardian feature and" >>dansguardian.conf +echo "# downloaded content will be always scanned by antivirus." >>dansguardian.conf +echo "# When turned on, urlcache will be loaded only with content" >>dansguardian.conf +echo "# found to be good and that is virus free." >>dansguardian.conf +echo "# Thus, content of urls found in urlcache WILL NOT BE SCANNED AGAIN." >>dansguardian.conf +echo "#" >>dansguardian.conf +echo "urlcachecleanonly = on" >>dansguardian.conf +echo "" >>dansguardian.conf + +echo "# OPTION: virusscannertimeout">>dansguardian.conf +echo "# The maximum length of time the commercial virus scanner is allowed to run">>dansguardian.conf +echo "# for 1 batch of messages (in seconds).">>dansguardian.conf +echo "virusscannertimeout = 60">>dansguardian.conf +echo "" >>dansguardian.conf + +echo "# OPTION: notify">>dansguardian.conf +echo "# Sets who receives email notification when a virus is found.">>dansguardian.conf +echo "# Users must be authenticated to be able to receive messages.">>dansguardian.conf +echo "# Email address for users will be formed by the authentication name received by DG">>dansguardian.conf +echo "# plus @emaildomain (see option below)">>dansguardian.conf +echo "# 0 = disabled">>dansguardian.conf +echo "# 1 = user only">>dansguardian.conf +echo "# 2 = postmaster only">>dansguardian.conf +echo "# 3 = postmaster and users (default)">>dansguardian.conf +echo "notify = 0">>dansguardian.conf +echo "" >>dansguardian.conf +echo "# OPTION: emaildomain">>dansguardian.conf +echo "# Set email domain to use when notifying users of an infected file.">>dansguardian.conf +echo "# This is just the domain name part, after the @">>dansguardian.conf +echo "emaildomain = 'your.domain.com'">>dansguardian.conf +echo "" >>dansguardian.conf +echo "# OPTION: postmaster">>dansguardian.conf +echo "# Set email address of who to notify about any infections found.">>dansguardian.conf +echo "# Should put your full domain name here too.">>dansguardian.conf +echo "postmaster = 'postmaster@your.domain.com'">>dansguardian.conf +echo "" >>dansguardian.conf +echo "# OPTION: emailserver">>dansguardian.conf +echo "# Set the address and port of the Mail Server to send notifications through.">>dansguardian.conf +echo "emailserver = '127.0.0.1:25'">>dansguardian.conf +echo "" >>dansguardian.conf +echo "" >>dansguardian.conf + +echo "# CLAMDSCAN SETTINGS">>dansguardian.conf +echo "# --------------------" >>dansguardian.conf +echo "# OPTION: localsocket">>dansguardian.conf +echo "# Set name of the local socket file">>dansguardian.conf +echo "# default: '/tmp/clamd'">>dansguardian.conf +echo "localsocket = '/tmp/clamd'">>dansguardian.conf +echo "" >>dansguardian.conf +echo "" >>dansguardian.conf + +echo "# CLAMAV SETTINGS">>dansguardian.conf +echo "# --------------------" >>dansguardian.conf +echo "# OPTION: maxfiles">>dansguardian.conf +echo "# Set maximum number of files inside a compressed file">>dansguardian.conf +echo "# default: 1500 files">>dansguardian.conf +echo "clmaxfiles = 1500">>dansguardian.conf +echo "" >>dansguardian.conf +echo "# OPTION: maxreclevel">>dansguardian.conf +echo "# Set maximum recursion level to perform scan on a compressed file">>dansguardian.conf +echo "# that is inside a compressed file">>dansguardian.conf +echo "# default: 3 levels">>dansguardian.conf +echo "clmaxreclevel = 3">>dansguardian.conf +echo "" >>dansguardian.conf +echo "# OPTION: maxfilesize">>dansguardian.conf +echo "# Set maximum file size of a file inside a compressed file">>dansguardian.conf +echo "# default: 10485760 = 10 Mbytes">>dansguardian.conf +echo "clmaxfilesize = 10485760">>dansguardian.conf +echo "" >>dansguardian.conf echo "Generating platform specific dansguardianf1.conf..." if [ -f dansguardianf1.conf ]; then @@ -3263,6 +3413,24 @@ echo "# '76b42abc1cd0fdcaf6e943dcbc93b826' = an example" >>dansguardianf1.conf echo "bypasskey = ''" >>dansguardianf1.conf echo "" >>dansguardianf1.conf +echo "" >>dansguardianf1.conf + +# Included by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) +echo "# ANTIVIRUS SETTINGS for this group" >>dansguardianf1.conf +echo "# --------------------" >>dansguardianf1.conf +echo "" >>dansguardianf1.conf +echo "# OPTION: virusscan" >>dansguardianf1.conf +echo "# If on, content will be scanned using virus engine setup in dansguardian.conf." >>dansguardianf1.conf +echo "# More options are available in dansguardian.conf." >>dansguardianf1.conf +echo "#" >>dansguardianf1.conf +echo "virusscan = on" >>dansguardianf1.conf +echo "" >>dansguardianf1.conf + +$extendedecho "exceptionvirussitelist = '$prefixdir$sysconfdir\c" >>dansguardianf1.conf +echo "exceptionvirussitelist'" >>dansguardianf1.conf +$extendedecho "exceptionvirusurllist = '$prefixdir$sysconfdir\c" >>dansguardianf1.conf +echo "exceptionvirusurllist'" >>dansguardianf1.conf + echo "Generating platform specific weightedphraselist..." if [ -f weightedphraselist ]; then diff -Naur dansguardian-2.8.0.4/dansguardian.cpp dansguardian-2.8.0.4.oden/dansguardian.cpp --- dansguardian-2.8.0.4/dansguardian.cpp 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/dansguardian.cpp 2005-06-17 12:30:35.318932631 +0200 @@ -37,6 +37,8 @@ #include "SysV.hpp" #include <sys/time.h> #include <sys/resource.h> +#include <sys/stat.h> +#include <cerrno> #ifdef __BSD #include <sys/wait.h> #else @@ -48,6 +50,19 @@ #define FD_SETSIZE 256 #endif +#ifdef __CLAMAV +#include "ClamAV.hpp" +#include <clamav.h> +struct cl_node *root = NULL; +struct cl_limits limits; +struct cl_stat dbstat; +ClamAV clamav; +#endif + +#ifdef __KAV +#include "KAV.hpp" +KAV kav; +#endif OptionContainer o; @@ -77,12 +92,13 @@ std::string configfile = __CONFFILE; srand(time(NULL)); SysV sysv; - int rc; + int rc,no,ret; for (int i=1; i<argc; i++) { int option = (argv[i][0] == '-')? argv[i][1] : 0; // 0 will never be a valid option switch (option) { case 'P': + std::cout << "Anti-Virus plugin version 6.3.8" << std::endl; return 0; case 'q': ReadConfig(configfile.c_str(), 0); @@ -204,8 +220,27 @@ } logfiletest.close(); } - - + + + if (o.virus_scan) { + if (mkdir(o.download_dir.c_str(), S_IRWXU) == -1) + if (errno != EEXIST) { + std::cerr << std::endl << "Error creating temporary folder. " << std::endl; + return 1; + } + +#ifdef __CLAMAV + if (o.virus_engine == 1) { + int ret = 0; + + if ((ret = clamav.loadDB()) != 0) { + syslog(LOG_INFO, "loadDB ret: %i", ret); + return ret; + } + } +#endif + } + FatController f; // Thomas The Tank Engine @@ -261,6 +296,15 @@ std::cout << "conf file read." << std::endl; #endif +#ifdef __CLAMAV + if (o.virus_scan && o.virus_engine == 1) { + if ((ret = clamav.checkDB()) != 0) { + syslog(LOG_INFO, "checkDB ret: %i", ret); + return ret; + } + } +#endif + if (nodaemon) { o.no_daemon = 1; } diff -Naur dansguardian-2.8.0.4/exceptionvirusextensionlist dansguardian-2.8.0.4.oden/exceptionvirusextensionlist --- dansguardian-2.8.0.4/exceptionvirusextensionlist 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/exceptionvirusextensionlist 2005-06-17 12:30:35.319932652 +0200 @@ -0,0 +1,31 @@ +#Exception Virus extension list +# The Virus scanning code will ignore files with these extensions. + +# File extensions with executable code + + +# Files which one normally things as non-executable but +# can contain harmful macros and viruses + + +# Other files which may contain files with executable code + + +# Time/bandwidth wasting files + +.mp3 # Music file +.mpeg # Movie file +.mpg # Movie file +.avi # Movie file +.ra # Real Audio +.ram # " +.rm # " + +# Image files not to scan +.gif +.png +.ico +# http://www.kb.cert.org/vuls/id/297462 +#.jpg +#.jpeg + diff -Naur dansguardian-2.8.0.4/exceptionvirusmimetypelist dansguardian-2.8.0.4.oden/exceptionvirusmimetypelist --- dansguardian-2.8.0.4/exceptionvirusmimetypelist 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/exceptionvirusmimetypelist 2005-06-17 12:30:35.319932652 +0200 @@ -0,0 +1,22 @@ +# MIME types the virus scanning code ignores. + +audio/mpeg +audio/x-mpeg +audio/x-pn-realaudio +audio/x-wav +audio/x-realaudio +audio/x-pn-realaudio +audio/vnd.rn-realaudio +video/mpeg +video/x-mpeg2 +video/acorn-replay +video/quicktime +video/x-msvideo +video/msvideo +video/vnd.rn-realvideo + +image/png +image/gif +image/tiff +# http://www.kb.cert.org/vuls/id/297462 +# image/jpeg diff -Naur dansguardian-2.8.0.4/exceptionvirussitelist dansguardian-2.8.0.4.oden/exceptionvirussitelist --- dansguardian-2.8.0.4/exceptionvirussitelist 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/exceptionvirussitelist 2005-06-17 12:30:35.319932652 +0200 @@ -0,0 +1,14 @@ +#Sites in virus exception list will not be virus scanned +#Don't bother with the www. or +#the http:// +# +#These are specifically domains and are not URLs. +#For example 'foo.bar/porn/' is no good, you need +#to just have 'foo.bar'. +# +#You can also match IPs here too. +# +#As of DansGuardian 2.7.3 you can now include +#.tld so for example you can match .gov for example + +dansguardian.org diff -Naur dansguardian-2.8.0.4/exceptionvirusurllist dansguardian-2.8.0.4.oden/exceptionvirusurllist --- dansguardian-2.8.0.4/exceptionvirusurllist 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/exceptionvirusurllist 2005-06-17 12:30:35.320932674 +0200 @@ -0,0 +1,15 @@ +#URLs in exception virus list will not be virus scanned +#Don't bother with the www. or +#the http:// +# +#These are parts of sites that filtering should +#be switched off for. +# +#These should not be domains, i.e. entire sites, +#they should be a domain with a path. +# +#For example 'foo.bar' is no good, you need +#to just have 'foo.bar/porn/'. +# +#Another example: +#generallybadsite.tld/partthatsok/ diff -Naur dansguardian-2.8.0.4/languages/lithuanian/messages dansguardian-2.8.0.4.oden/languages/lithuanian/messages --- dansguardian-2.8.0.4/languages/lithuanian/messages 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/languages/lithuanian/messages 2005-06-17 12:30:35.320932674 +0200 @@ -42,3 +42,5 @@ "900","Uþdraustas praplëtimas: " "1000","Virðytas ðio tinklalapio PICS vertinimo kriterijus." + +"1100","Turinys uþkrëstas virusu." diff -Naur dansguardian-2.8.0.4/languages/lithuanian/messages.orig dansguardian-2.8.0.4.oden/languages/lithuanian/messages.orig --- dansguardian-2.8.0.4/languages/lithuanian/messages.orig 1970-01-01 01:00:00.000000000 +0100 +++ dansguardian-2.8.0.4.oden/languages/lithuanian/messages.orig 2005-06-17 12:30:35.320932674 +0200 @@ -0,0 +1,44 @@ +# DansGuardian 2.8 messages file +# Lithuanian version by Nerijus Baliûnas +# charset=windows-1257 + +"1","Tinklalapis uþdraustas" + +"100","Ið jûsø IP adreso negalima pasiekti tinklalapio: " +"101","Draudþiama narðyti ið jûsø IP adreso." +"102","Ðiam vartotojui negalima pasiekti tinklalapio: " + +"200","Neteisingas URL." + +"300","Rasta uþdrausta frazë: " +"301","Rasta uþdrausta frazë." + +"400","Rasta uþdrausta fraziø kombinacija: " +"401","Rasta uþdrausta fraziø kombinacija." +"402","Sumuojamø fraziø limitas " +"403","Virðytas sumuojamø fraziø limitas." + +"500","Uþdraustas adresas (site): " +"501","Uþdraustas URL: " +"502","Leidþiamas priëjimas tik prie tinklalapiø, esanèiø 'baltame' sàraðe, bet ðio tinklalapio jame nëra." +"503","Draudþiamas URL regexp: " +"504","Rastas draudþiamas URL regexp." +"505","Priëjimas prie IP adresø uþdraustas, o ðis adresas yra tik IP adresas." + +"600","Kliento IP yra iðimèiø sàraðe." +"601","Kliento vardas yra iðimèiø sàraðe." +"602","Adresas yra iðimèiø sàraðe." +"603","URL yra iðimèiø sàraðe." +"604","Rasta frazë ið iðimèiø sàraðo: " +"605","Rasta fraziø kombinacija ið iðimèiø sàraðo: " +"606","URL apëjimo iðimtis." +"607","Slapuko apëjimo iðimtis." + +"700","Nusiuntimas (upload) yra uþdraustas." +"701","Virðytas nusiuntimo (upload) limitas." + +"800","Uþdraustas MIME tipas: " + +"900","Uþdraustas praplëtimas: " + +"1000","Virðytas ðio tinklalapio PICS vertinimo kriterijus." diff -Naur dansguardian-2.8.0.4/languages/portuguese/messages dansguardian-2.8.0.4.oden/languages/portuguese/messages --- dansguardian-2.8.0.4/languages/portuguese/messages 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/languages/portuguese/messages 2005-06-17 12:30:35.321932696 +0200 @@ -39,5 +39,6 @@ "900","Extensão proibida: " "1000","Nível de rotulagem PICS excedido no sítio acima." +"1100","Conteudo infectado por vírus." # 1,1000 by Andson Gomes - html internet diff -Naur dansguardian-2.8.0.4/languages/portuguese/template.html dansguardian-2.8.0.4.oden/languages/portuguese/template.html --- dansguardian-2.8.0.4/languages/portuguese/template.html 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/languages/portuguese/template.html 2005-06-17 12:30:35.321932696 +0200 @@ -1,35 +1,77 @@ -<HTML><HEAD><TITLE>DansGuardian - Acesso negado</TITLE></HEAD> -<BODY> -<CENTER><H2>O ACESSO FOI NEGADO AO USUÁRIO '-USER-'</H2> -<br>O acesso à página:<P> -<strong><a href="-URL-">-URL-</a></strong> -<P>... foi negado pela seguinte razão:<P> -<strong><font color="#ff0000">-REASONGIVEN-</font></strong> -<p><table border=1 bgcolor="#FFEE00"><tr><td>Você está vendo este erro porque a página que você tentou acessar<br> -contém - ou é rotulada como contendo - material considerado<br> -impróprio.</td></tr></table> -<P><table border=1 bgcolor="#44dd44"><tr><td>Se você tem alguma dúvida, contacte o Coordenador de IT ou o Administrador da Rede.</td></tr></table> - -<P><font size=-3>Turbinado por <a href="http://dansguardian.org" target="_blank">DansGuardian</a></font> -</center> -</BODY> -</HTML> +<html> + +<head> +<title>DansGuardian - Acesso negado</title> +</head> + +<body bgcolor=#FFFFFF> + +<center> +<table border=0 cellspacing=0 cellpadding=2 height=540 width=700> +<tr> + <td colspan=2 bgcolor=#FEA700 height=100 align=center> + <font face=arial,helvetica size=6> + <b>O acesso foi negado!</b> + </td> +</tr> +<tr> + <td colspan=2 bgcolor=#FFFACD height=30 align=right> + <font face=arial,helvetica size=3 color=black> + <b>-USER- </b> + </td> +</tr> +<tr> + <td align=center valign=bottom width=150 bgcolor=#B0C4DE> + <font face=arial,helvetica size=1 color=black> + YOUR ORG NAME + </td> + <td width=550 bgcolor=#FFFFFF align=center valign=center> + <font face=arial,helvetica color=black> + <font size=4> + O acesso à página: + <br><br> + <a href="-URL-" target="_blank">-URL-</a> + <br><br> + <font size=3> + ... foi negado pela seguinte razã: + <br><br> + <font color=red> + <b>-REASONGIVEN-</b> + <font color=black> + <br><br><br><br> + Você está vendo este erro porque o conteú que você tentou + acessar parece ter, ou indica que contém, material classificado como impróprio. + <br><br> + Se você necessitar de auxí para este acesso, entre em contato com administrador da rede. + <br><br><br><br> + <font size=1> + Powered by <a href="http://www.dansguardian.org" target="_blank">DansGuardian</a> + </td> +</tr> +</table> + +</body> + +</html> + <!-- -As variáveis disponíveis são as seguintes: -- URL- informa a URL que o usuário estava tentando acessar. -- REASONGIVEN- informa o motivo do bloqueio gentilmente, isto é, não mostra a frase proibida. -- REASONLOGGED- informa o motivo que foi registrado, incluindo todos os detalhes. -- USER- informa o nome de usuário, se conhecido. -- IP- informa o IP de origem. +The available variables are as follows: +- URL- gives the URL the user was trying to get to. +- REASONGIVEN- gives the nice reason (i.e. not quoting the banned phrase). +- REASONLOGGED- gives the reason that gets logged including full details. +- USER- gives the username if known. +- IP- gives the originating IP. - FILTERGROUP- gives the group number. - BYPASS- gives URL which allows temporary bypass of denied page +You need to remove the space between the - and the variable to use them +in your HTML. They are there above so extra processing is not required. -Você precisa remover o espaço entre o hífen (-) e a variável para usá-la em seu HTML. Elas estão na definição da página acima, portanto processamento extra não é requerido. - -Mais moldes de exemplo serão provavalmente encontrados no sítio de DansGuardian, na página de Extras. +More example templates are likely to be found on the DansGuardian web site +on the Extras page. -Daniel Barron 2002-03-27 +This page was designed by Paul Richards. (http://www.ridney.com/) -Traduzido/translated por/by Henrique Araujo (henrique@colegiosaogoncalo.g12.br) +Daniel Barron 2003-09-01 --!> + diff -Naur dansguardian-2.8.0.4/languages/ukenglish/messages dansguardian-2.8.0.4.oden/languages/ukenglish/messages --- dansguardian-2.8.0.4/languages/ukenglish/messages 2005-02-20 16:25:10.000000000 +0100 +++ dansguardian-2.8.0.4.oden/languages/ukenglish/messages 2005-06-17 12:30:35.322932718 +0200 @@ -40,3 +40,5 @@ "900","Banned extension: " "1000","PICS labeling level exceeded on the above site." + +"1100","Virus infected content found."