pax_global_header00006660000000000000000000000064147737236050014530gustar00rootroot0000000000000052 comment=f4cad4a7959a1ca3cf8172d88e554977c34cd5b4 e2guardian-5.5.8r/000077500000000000000000000000001477372360500137525ustar00rootroot00000000000000e2guardian-5.5.8r/.gitattributes000066400000000000000000000000431477372360500166420ustar00rootroot00000000000000data/languages/* linguist-vendored e2guardian-5.5.8r/.gitignore000066400000000000000000000001711477372360500157410ustar00rootroot00000000000000*.[oa] *.Po *.Tpo *~ Makefile autom4te.cache README.md .vscode/ **/Makefile.in **/.dirstamp e2guardian.code-workspace e2guardian-5.5.8r/.gitlab-ci.yml000066400000000000000000000014241477372360500164070ustar00rootroot00000000000000workflow: rules: - if: $CI_COMMIT_BRANCH variables: CONTAINER_CLIENT_IMAGE: amd64/debian:bookworm-slim stages: - quality - build-debian - create-package-debian - test-publish-debian - build - create-package - test-publish - Docker-hub-build - Docker-hub-test - Docker-hub-pushtag - Docker-hub-build-arm - Docker-hub-test-arm - Docker-hub-pushtag-arm - Chatgtp hadolint: image: hadolint/hadolint:latest-debian stage: quality before_script: - cd $CI_PROJECT_DIR script: - hadolint --ignore DL3008 gitlabci/docker-ci/Dockerfile include: - 'gitlabci/debianlatest.yml' - 'gitlabci/docker-hub-arm.yml' - 'gitlabci/docker-hub.yml' - 'gitlabci/ubuntulatest.yml' - 'gitlabci/armdebian.yml' - project: 'fredbcode/chatgpt-ci' file: 'chatgpte2.yml'e2guardian-5.5.8r/AUTHORS000077500000000000000000000001171477372360500150240ustar00rootroot00000000000000For authors go to http://e2guardian.org/ This file required by the autotools. e2guardian-5.5.8r/CMakeLists.txt000066400000000000000000000057551477372360500165260ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) project(v5_5) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS -DHAVE_CONFIG_H -D__LOGLOCATION -D__PIDDIR -D__PROXYUSER -D__PROXYGROUP -D__CONFDIR) include_directories(.) include_directories(src) add_executable(v5_5 src/authplugins/dnsauth.cpp src/authplugins/header.cpp src/authplugins/ident.cpp src/authplugins/ip.cpp src/authplugins/port.cpp src/authplugins/ProxyFirstBasic.cpp src/authplugins/BearerBasic.cpp src/contentscanners/avastdscan.cpp src/contentscanners/clamdscan.cpp src/contentscanners/commandlinescan.cpp src/contentscanners/icapscan.cpp src/contentscanners/kavdscan.cpp src/downloadmanagers/default.cpp src/downloadmanagers/fancy.cpp src/downloadmanagers/trickle.cpp src/Auth.cpp src/Auth.hpp src/BackedStore.cpp src/BackedStore.hpp src/BaseSocket.cpp src/BaseSocket.hpp src/CertificateAuthority.cpp src/CertificateAuthority.hpp src/ConfigVar.cpp src/ConfigVar.hpp src/ConnectionHandler.cpp src/ConnectionHandler.hpp src/ContentScanner.cpp src/ContentScanner.hpp src/DataBuffer.cpp src/DataBuffer.hpp src/DownloadManager.cpp src/DownloadManager.hpp src/DynamicIPList.cpp src/DynamicIPList.hpp src/DynamicURLList.cpp src/DynamicURLList.hpp src/e2guardian.cpp src/FatController.cpp src/FatController.hpp src/FDTunnel.cpp src/FDTunnel.hpp src/FOptionContainer.cpp src/FOptionContainer.hpp src/HTMLTemplate.cpp src/HTMLTemplate.hpp src/HTTPHeader.cpp src/HTTPHeader.hpp src/ICAPHeader.cpp src/ICAPHeader.hpp src/ImageContainer.cpp src/ImageContainer.hpp src/IPList.cpp src/IPList.hpp src/LanguageContainer.cpp src/LanguageContainer.hpp src/ListContainer.cpp src/ListContainer.hpp src/ListManager.cpp src/ListManager.hpp src/ListMeta.cpp src/ListMeta.hpp src/Logger.cpp src/Logger.hpp src/LOptionContainer.cpp src/LOptionContainer.hpp src/Makefile src/md5.cpp src/md5.hpp src/NaughtyFilter.cpp src/NaughtyFilter.hpp src/OptionContainer.cpp src/OptionContainer.hpp src/Plugin.hpp src/Queue.hpp src/RegExp.cpp src/RegExp.hpp src/SBFunction.cpp src/SBFunction.hpp src/SNI.cpp src/Socket.cpp src/Socket.hpp src/SocketArray.cpp src/SocketArray.hpp src/StoryBoard.cpp src/StoryBoard.hpp src/String.cpp src/String.hpp src/SysV.cpp src/SysV.hpp src/UdpSocket.cpp src/UdpSocket.hpp src/UDSocket.cpp src/UDSocket.hpp src/UrlRec.hpp e2config.h) e2guardian-5.5.8r/COPYING000077500000000000000000000443161477372360500150200ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. In addition, as a special exception, the copyright holders of this work, gives permission to link the code of its release of E2Guardian with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "OpenSSL". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. e2guardian-5.5.8r/ChangeLog000077500000000000000000000403021477372360500155260ustar00rootroot00000000000000About "Bug" tags show "https://github.com/e2guardian/e2guardian/issues?q=is%3Aissue+is%3Aclosed" version 5.5.8r December 2024 to April 2025 Fix bug #841 - generated cert dates validaition produces 1970 end date with CA root notAfterDate greater than 2k38. Fix dive arm Fix dive x86 Add corrected 100 continue logic Fix issue #838 - proxyport listed twice in e2guardian.conf.in version 5.5.7r July 2024 to December 2024 Auto dates for cert generation feature added Add chatgpt ChatGPT Analysis to v5.5 Fix bug #831 Minimum SNI record length is 7 (not 8) Remove compilation cache Further work on #827 - oversize domain names now ignored Further bound checks added (relates to #827) Possible fix for #827 - Intermitant crash withj transparent https Fix ssl memleak and out of range access detected by valgrind Fix access to unintialised memory found by valgrind memcheck Fix arm compilation version 5.5.6r May 2024 to June 2024 Add addECHtoFlags option for monitoring purposes Rewrite of SNI extraction code and addition of checkme.hasECH variable Fix for #810 - intermitant garbled SNI extraction with THTTPS Fix #816 - unused variable in trickle.cpp Fix #817 RegExp::match() issue Fix #819 ICAP send time-out but does not close connection Impliment #818 multiple domains/ips in tlsproxycn Update expire artifacts ttl Update armdebian.yml Update debianlatest.yml Update ubuntujammy.yml Fix #815 - Change to invoke pthreads_sigmask before creating any threads to fix occational issue of logging stopping Fix #814 adjustments for LibreSSL and bug fix DEBUG_debug call. Add -funsigned-char to CXXFLAGS to ensure char is unsigned If this is not set causes stack and other issues in some platforms. More robust regular expressions in bannedregexpurl list version 5.5.5r September 2023 to April 2024 Fix #809 Reading files not respecting abortiflistmissing = off settina Also improve list error reporting to show list files and configuration files that have failing files under their tree. Fix #807 __CONFFILE defined twice Change default generatedcertstart to 1st Apr 2024 Change method of generating Certificate serial numbers #631 When generating Serial numbers from host names a hash of the rootCA, start_date and end_date is added to the CN and hashed to produce a unique serial number. This means that the serial number for a host will change if the rootCA or start/end date is changed. This will force a re-generation of the certificate. The generated cert store should be cleared to remove the now stale certificates previously generated. Fix openssl dependency in configure - issue #806 Fix for bug #804 - Logger build error Merge pull request #796 from sunweaver/pr/typo-fixes-5.5 NEWIN_{v4,v5},configs/e2guardian*.conf.in}: Various typo fixes. Fix issue #799 - write PID file in parent process rather than deomon child Merge pull request #795 from sunweaver/pr/maxcontentramcachesize-regression-v5.4-v5.5 {configs/e2guardian.conf.in,src/OptionContainer.cpp}: Make sure values of maxcontentfiltersize and maxcontentramcachesize obey to the requirements in the (inline) documentation. Fix for #794 - Bad headers in whatsapp requestsi cause crash Upgrade to Debian12 in CI/CD Fix #792 - dstat output buffer may be too small - buffer size increased to 200 Fix #790 - error in lists/common/Makefile.am Add ICAP client notes Update example config files documentation Fix #788 Rest of header not being output when keep-alive triggered Fix #786 - request log not writing Move debuglevel to be read earlier - so that log settings can be debuged Fix #785 BYPASS not working in v5.5 also bypass log messages Fix #783 - command line option -N being ignored Add Spanish translation for message: 122 version 5.5.4r August 2023 Fix possible XSS in bypass url - issue #782 Correct INSTALL file - issue #781 Fix bug #780 - e2g crashes if invalid group name is provided via BearerBasic auth plugin Update Italian messages - pull request #778 from albanobattistella Update Italian block templalte.html version 5.5.3r March to May 2023 Fix #773 - when blocking match is on an individual IP, match is returned blank. Add binary IP search to rooms feature Merge pull request #772 from Arjow/v5.5 fixing error with uint32_t Convert iprange and iprangemap lists to binary sort/search, remove ip mask lists, convert IP subnet and CIDR to internal rangelist. Fix #770 - only write blocked url to alert.log Fix #768 - findInList() (and other search functions) may return illegal address Add rsync to docker image Amend template config file to add missing set_storytrace and cover issues raised in #761 Comment out debug lines and tidy Logger code Based on pull request #763 by KDGundermann Add missing E2LOGGER_warning macro Tidy storyboardtrace logic in OptionContainer Fix #761 - dockermode settings for access.log overwritten when set_accesslog and loglocation missing from config file Minor changes to e2guardian.cpp to clarify DEBUG_LOW usage Fix #762 Inconsistent references to downloadmanagers February 2023 Fixes #759 Segmentation fault - Corrupted TEMPLATE returns Possible Fix for #760 version 5.5.2 July 2022 to February 2023 Fixes Amend Spanish translations Fix bug #756 extension checking requires removal of cgi from url Correct HOWTO_Logger.md Re-add google translate site/url checking (translate.goog) Merge pull request #748 from meyertime/v5.5buildfix Fix build error AM_INIT_AUTOMAKE expanded multiple times List definition option anonlog now only blanks URL Fix bug #743 - logfileformat defaulting to 1 instead of 8 as documented Comment out fancy and trickle download managers as these are not supported in v5.5 Obsolete define terms removed from code and clean-up and autoupdate of configure.ac Partial work towards issue #731 Fix obscure bug with some lists - replace sort() with stable_sort() and improve memory handling for lists Fix bug #730 - large downloads in chunked format not completing New features UDP logging option added (based on code/idea by KDG) Add SEMI-TRUSTED flag to logs Add kiddle search terms extractor #739 refers Add storyboarding, list definitions, message for TLD allowed as suggested by Dalacor #733. Note I have used allowedtldlist rather than exceptiontld. Exceptions generally override everything but this list is used in blanketblock and so will be overridden by exception site urls etc. version 5.5.1 September 2021 to June 2022 Fixes Update default values for connecttimeout & maxheaderslines Fix #713 - raise upper limit to 2500 for maxheaderlines Remove bionic (18.04) add jammy (22.04) Fix bug #727 also added list test function self_check() Fix bug in set_accesslog Fix bug #720 - upper case search terms not blocked Correct spelling in message 160 Fix bug #716 - proxyip being ignored Major changes to socket and tunnel code to fix bug - #714 - extra high CPU usage under high load Fix bug #712 - message wrong when upstream connection fails New features Add regexpreplacelist to available lists for refererin state (SB) Mime type stop feature - to prevent scanning of non-relevant mime types. Category lists - category can be checked against category list - new list type categorylist added - new storyboard state categoryin Response log option - when set logs all responses in separate log Alert log - can be used by external process to email alerts/reports etc - new storyboard flag alert - when set log in alertlog as well as accesslog - new storyboard action setalert - storyboard modified so that categories that match alertcategorylist are logged in atert.log. December 2020 to August 2021 Fixes - fix #686 icap default filtergroup is not set. - fix #679 ICAP protocol error - fix #678 -N reloads instead of stops with -q - fix #646 - data maplist of more than 16 lines crash e2g - fix #649 - set got_orig_ip in get_origianl_ip_port (by Raifeg) - Fix #660 - revert to use of iostream for log files - fixes delay due to buffer issue in logger - Fix #670 - request log not being written - Fix #672 - Cert error not giving status page - Fix #677 - exceptionfile test in wrong place - Fix #683 - ports ignored in authplugin conf files - Fix #684 - crash when only one entry in a maplist - Fix #685 - uppercase domain in username never matches - Fix #687 - slowness on browsing some sites - issue with new duplex tunnelling New features Add extracheckports option to allow loop checking when cache if front. Add new semi exception lists and flag - allows reverse logic for selected sites i.e. Trust a site - but block some urls within site. Note: semiexceptionsitelist and blockurllist must be of the same type to work so if site is in localsemiexceptionsitelist then block of url will only work if in localblockurllist or if in semiexceptionsitelist the block of url will only work if in blockurllist Add timestamp to debug logs, but not to syslog entries Refactoring OptionContainer variables reorganised (by KDGundermann) version 5.5.0 October to November 2020 Bug-fixes to new IO Secure TLS proxy added March to September 2020 New features Log rotation New Logger/Debug integrated from coding by KDGundermann IO (normal and MITM ssl) rewritten - timeouts now honored when in MITM mode Removal of support for pre v1.1 OpenSSL versions Much code tidying version 5.4.2 April to August 2020 Fixes - Fix #619 When using x-forwarded-IP behind proxy MITM requests show wrong IP - Fix #616 IP auth issue in ICAP mode - Fix #609 .Include not working in e2guardian.conf - Fix #607 Merge pull request #608 from KDGundermann wrong file path for example - Remove example .Includes for phraselists that are no longer in distribution - Fix #602 - log timestamp records time log written - Fix #520 - by-pass cookie generation and check does not match New features Add 'hook' calls and placeholder functions to common.story Add pf-basic auth plugin - use when squid in front of e2g #620 Reorganize lists and example config files (#618) Feature .Define LISTDIR <> and __LISTDIR__ insertion added #610 version 5.4.1 August 2019 to April 2020 Fixes - Fix #469 - remove punctuation from within phraselists when read in - Fix #493 - refererexception not working - Fix #549 - wrong url in CGI block and bypass - Fix #555 - improved embeded url detection - Fix #565 - segfault when no write permission on generated certs directory - Fix #585 - Bypass not working with mitm https - Fix #590 - Storyboard parsing failing with trailing comments - Fix #595 - MITM - block page not delivered when connection to site fails New features - Add Server Name to Block Page #560 - Auth list files moved into storyboard system - fixes #458 - Improve auth plugin logic - add per-plugin default group options - On single list reading failure do not abort but check rest of config - Tidy up request log output - New usedashforblank option for logs - Extended logs added (type 7 & 8) and -EXTFLAGS- added to block page params - Add searchterms field to log types 7,8 - new logclientnameandip config flag - Make consistant punctuation removal in NaughtyFilter - Time based list and storyboard functions added - #529 - SB: Add timed blanket block - SB: Add support for log-only function (logcategory flag) - SB: Response HTTP header modification added & listenportin state added - SB: Add #568 feature - give warning when defined list is not used - New useoriginalip option - solves issues with some apps who use non-stqndard SNI. - nomitm lists added for sites which refuse to be mitm. - nolog lists added and actioned via new SB entry point - for clearer logs - searchexception list added to override searchregexplist - so search complete calls are not treated as a user search and give misleading denies on logs Config changes - Remove safelabel from bannedphraselist - does not appears to have been adopted on web - Revised Phraselists added - #264 refers - Phraselist tree now has language as top level - Switch dstats on by default in config - Update httpworkers comments re 32-bit systems - All auth config files have changed - check sample configs - Revised/new flags in e2guardian.conf Definitions/Variables - DG replaced by E2 in all directive and configure variables e.g. DGDEBUG is now E2DEBUG etc. version 5.3.4 January 2020 - Increase example maxcontentcachesize to make filtering youtube work - Fix #565 segfault when no write permission on generated certs directory - Fix #493 referexception not working - Fix #549 - Url in CGI and bypass wrong in MITM - Bug fix sigwait code for OpenBSD - Amend example bannedregexpulrlist - Fix #554 Override Search Terms not overriding weighted search term check - Add request log option for diagnostics - see notes/LogRequests and more ... Version 5.3.3 July 2019 - Memory not released when startSslServer returns error #542 June 2019 - IE10/11 on Win7 reports 408/9 error on some sites #538 May 2019 - Fix segfault when corrupt SNI presented April 2019 - Fix bug #532 - reverse IP lookup give random chars in log and segfaults - Update comments in list files - as per issue #530 - Add support for reading openssl config files - new optional e2guardian.conf params useopensslconf and opensslconffile - Fix bug #527 - memory leaking when complied with openssl v1.1 - Loop detect code added - enhancement #523 - Note that to activate loop detect 'checkip' lines need to be added to e2guardian.conf, one for each ip the e2guardian system is listening on, including loopback and any VIP used. March 2019 - Fix #512 - Fix segfault bug #509 Version 5.3.2 March 2019 - SSLMITM source code clean-up - no logic or call changes - Fix bug #514 - useragentin - Fix ICAP error (with SSL denied) introduced in 5.3.1 Version 5.3.1 January 2019 - Fix bug with Firefox and SSL denied web sites (connection still opened, massive performance issue) - Update ICAP client (tested with drweb AV) - Add stealth mode (reporting without block) to StoryBoard mode - Add new secure bypass mode (experimental) - Better handling for non-tls and non-sni calls on transparent https - Fix bug #490 modified URL not shown in log - Fix bug #489 - exception file ext/mime type not working correctly - Fix bug #486 - bypass cookie not being set in proxy mode December 2018 - Fix for #485 related to #481 wrong upstream site called in direct mode - Fix bug #481 auth exception being denied - Fix bug #480 Ignore http 100 when no expect: 100-continue - Fix bug #478 check searchterms always being called - Fix bug #476 - only check when potential url is longer than 3 chars and contains'.' - Fix bug #464 proxy auth issues - Fix bug #475 Client Hostname blank in template - Fix bug #473 ICAP mode: Wrong group in respmod - Fix bug #465 Incorrect wildcard certificate validation and more ... Version 5.2.2 September 2018 - Reenable content regexp option - Allow the ip authplugin to use the X-Client-IP header when using ICAP - Fix bug #432 Block html page gets shown twice - Fix bug #436 compilation bug with avast and kavscan - Fix some lags with debugmanager - Allow the ip authplugin to use the X-Client-IP header when using ICAP - Update default template page (denied access) and more ... August 2018 - Add new per cent option of weighted phrase lists - Global code review (remove gcc warnings) July 2018 - Fix ICAP client - tested with f-secure and Kav4proxy - - Fix bug #417 urlredirectregexplist doesn't work - Fix bug #418 NTLM auth is not working - Fix bug #410 segfault if "neterrtemplate=" doesn't exist in config - Fix bug #414 compiler error caused by extra brace Summary of changes in this release (v5.2) can be found in e2guardian.release and notes/NEWIN_v5 Changes to E2guardian 4.x.x can be found in ChangeLog4.x Changes to E2guardian 3.x.x can be found in ChangeLog3.x e2guardian-5.5.8r/ChangeLog3.x000066400000000000000000000151461477372360500160640ustar00rootroot00000000000000 - Fix bug #181 ERR_RESPONSE_HEADERS with bypass - Fix bug #183 whitelist and identification - Fix bug #182 e2guardian systemd service does not support reload - Fix bug #178 (NTLM Messages) February 2016 - Version 3.5.0 Summary of changes in this release (v3.5.0) can be found in e2guardian.release January 2016 Many Improvements and bug fixes, big thanks to Chris Nighswonger for his help - Remove unused code - Fix Bug #169 Mismached use of free() - Fix Bug #162 Check missing dependency openssl - Fix Bug #163 e2guardian -g causes segfault December 2016 - Fix Bug #157 (Code 400 log) - Fix Bug #130 filtergroupslist not working with NTLM authentication (HTTPS) November 2016 - Fix Bug #135 SSL Bypass not working - Fix Bug #156 Crash with url beyond 2048 characters (in list) October 2016 - New blockedflash.swf version + source September 2016 - Add Squid Log File Format August 2016 - Fix Bug #119 readFromSocket incorrect error path - Fix Bug #136 (Ignoring the body of 304 replies causes corrupted messages) - New auth plugin - identification by header - July 2016 - New logheadervalue option added June 2016 - Russian translation - Update french translation March 2016 - Version 3.4.0.3 Summary of changes in this release (v3.4.0.3) can be found in e2guardian.release - Fix segfault when weightedphrasemode not defined - naughtynesslimit now defaults to 50 (previously undefined) February 2016 - Fix ICAP with c-icap server, new option previewforce - Version 3.4.0.2 - Fix bug #109 (bannedphraselist not working) - Version 3.4.0.1 - Hotfixes bugs #103 #105 (segfault) January 2016 - Version 3.4.0 - weightedphrasemode now optional - defaults to 1 - allowemptyhostcert option added (optional) - createlistcachefiles now defaults to off December 2015 - new sslsiteregexplist added (optional) - search term logic changed so banned_search_override works with both search block list and with weighted search term. - list_flags checked on lookups to avoid segv when lists not present - Fix SEGV when SSL connect fails - add logsslerrors option - Fix certificate generation with hostnames longer than 64 bytes (bug #96) November 2015 - Fix bug #96 Certificate Issues - requires X509_V_FLAG_TRUSTED_FIRST support in openssl - Add SNI support to MITM - note only works with openssh 1.0.1e or higher - Fix bux #94 new option textmimetypes - Reports error on ssl_connect failure - extra message (160) added - SSLMITM Upgrade sha1 to sha256 September 2015 - originalip off by default (complain about 400 Bad request URL is malformed) - Added, "namesuffix" option - one log file by instance (syslog) - August 2015 Summary of changes in this release (v3.2.0) can be found in e2guardian.release July 2015 - Fix bug #76 sslaccessdeniedaddress and reporting level < 3 - Remove old & unused values - Remove max_upload option from e2guardian.conf June 2015 - Fix Bug #75 contentscanners commandlinescan broken and potential problem with the others - Fix bug #30 compilation issue with --program-prefix - Security Fix: AV and empty access_denied_domain value break scan silently - Fix bug #72 (wrong config file) - Add new option: "xforwardedforfilterip" - Fix weighted phrases bug #15 - Add brazilian translation - Code cleaning - Add Czech translation - New ssllegacylogic option (SSL lists greysslsitelist and bannedsslsitelist in separate files) default off - Add SEARCHWORDS as default - Add SSL_EXTRA_LIST as default - Add LOCAL_LIST as default new option enablelocallists = on/off - Add TOTAL_BLOCK_LIST as default - Fix e2 now can start with --enable-sslmitm=yes without ca certificat January 2015 - MITM cert checking re-enabled - MITM cert checking can be turned on/off with mitmcheckcert option - Optional nocheckcertsitelist introduced December 2014 - Version 3.1.2 - Several bug fixes - MITM cert checking disabled to enable cross-platform working to address Google retirement of nosslsearch feature. December 2014 - Version 3.1.1 MITM now working - See notes/ssl_mitm for details - Fix Segfault with filter_ports < auth plugins bug #44 - Fix e2guardianf1.conf and groupmode = 0 breaks identification bug #38 November 2014 - Fix blanket block not working bug May/June 2014 - CIDR format support added in IP lists - Rooms facility updated - now includes room specific overide white lists - New perroomdirectory option in e2guardian.conf added - Backward compatible with perroombanneddirectory option. - Hard coded room user/log messages removed and replaced with new items in language messages file. - Reading of lists functions amended to accomodate reading of multiple types of lists from a single file. - Fixes from 3.0.4 merged - Fixes from 3.0.3 merged - Start of development version 3.1 - Fix Compilation problem with --enable-dnsauth=yes June/July 2014 - e2guardian 3.0.3 - Fix issue with urls ending in '//' - Fix Compilation problem with --enable-dnsauth=yes June 2014 - e2guardian 3.0.3 - Fix site truncation when total_block_list in use - Error message now given when maxchildren is reached - Fix gentle restart - A '-g' gentle restart does not kill current connections but filter group config is re-read. - Fix ugly check about "open room definitions" - Information message should be given when e2guardian is reloaded Mai 2014 - Release 3.0.2 - It fixes some compile errors reported in v3.0.1 April 2014 - Release 3.0.1 - see e2guardian.release for details February 2014 - Maxlogitemlength code moved into ConnectionHandler.cpp so as to prevent very large URLs corrupting log messages and to lower load on inter-process communication. - URL cache - Cache is now only checked (or added to) if URL is less than 2000 bytes and method is GET. - Emtpy list set issue fixed - was causes failure of logging and URLcache processes when filtergroup was set to block all. November 2013 - e2guardian 1.0.0.1 - mapportstoips conf option added - when off listens on every filterports on every IP address, - when on maps filterports to filterip's, default on First alpha version with E2BN Protex features added - See e2guardian.release for details. September 2013 - e2guardian 1.0.0.0 - URLs with # no longer truncated when reading lists - mapauthtoports conf option added - when off scans all auth plugs on every listening addr/port, when on maps auth plugins to addr/ports, default on - Warning message about reporting level by Frederic Bourgeois - Added, full banned URL, including parameters, for sslaccesdenied By frederic Bourgeois - Added, nonstandarddelimiter per filtergroupe By frederic Bourgeois - Fix Libpcre crash by Russell coker from Debian - Fix BSD crash (process forking out of control) By Philip Pearce and Martin Coco For historical changes to DansGuardian see DGChangeLog e2guardian-5.5.8r/ChangeLog4.x000077500000000000000000000043641477372360500160700ustar00rootroot00000000000000About "Bug" tags show "https://github.com/e2guardian/e2guardian/issues?q=is%3Aissue+is%3Aclosed" Version 4.1.5 April 2018 - Fix bug #268 and bug #344 accept libssl1.0 & 1.1 - Fix bug #369 Segfault on some systems Mars 2018 - Fix some problems with log (Bug #354) January 2018 - Fix random crashes with Header (update) - Fix incorrect timeout in DataBuffer - Fix regression with sslaccessdeniedaddress Version 4.1.4 November 2017 - Fix bug #288 307 Answer is not CRLF just LF - Fix bug #302 Client address is missing with squid format logs - Fix random crashes with: Header without size - Fix bug #300 restrict log permissions with systemd Version 4.1.3 September 2017 - Fix segfault with SSLMITM - Fix many bugs with BYPASS - Fix bug #277 add option "disablecontentscanerror" - Fix bug #276 give better informations about header size filtering August 2017 - Fix bug #270 add logexceptionhits level 3 - Fix issue with sslaccessdeniedaddress and reporting level < 3 July 2017 - Fix bug #262 unable to compile with Kavdscan and avastdscan - Security update, user with GBYPASS must pass through deny page (reportinglevel !3) - Force log for BYPASS requests Version 4.1.2 - Fix segfault in HTTPHeader::returnCode - Fix bug #229 Segmentation fault with -HOST- placeholder - Fix fail with "Make dist" Version 4.1.1 Many improvements and changes are done at this version - Fix bug #241 SSLMITM and XForwarded_for - Fix several bugs with GYBYPASS - Add new banned list bannedsitelistwithbypass (block definitively a domain) - Fix bug #224 part2 clamdscan - Fix potential segfaut with wrong DNS request - Fix segfault when not able to open cert file May 2017 - Fix segfault "Illegal instruction" on FreeBSD - Fix bug #224 part1 segfault with commandlinescan - Fix bug #221 (dnsauth) error:cast from pointer - Fix bug #213 sudden segfault - Fix bug #212 fix bug with IP plugin, log with wrong IP - Fix bug #212 fix numerous bugs with NTLM (chrome and IE) and log with wrong IP - Fix bug #217 Segfault with wrong htmtemplate - Fix bug #216 Chrome rejects generated certificate - Fix bug #215 Filtergroupslist not being reloaded Summary of changes in this release (v4.1) can be found in e2guardian.release and notes/NEWIN_v4 Changes to E2guardian 3.x.x can be found in ChangeLog3.x e2guardian-5.5.8r/DGChangeLog000077500000000000000000002126301477372360500157460ustar00rootroot00000000000000 June 2013 - DansGuardian 2.12.0.7 - Added, DansGuardian now supports epoll (partial and Linux only), bringing large increases in performance and scalability to Linux System - By Philip Pearce (e2bn.org) This feature must be used with --with-filedescriptors=N compile option - Fixed security bug, Dansguardian uses -USER- and -REASONGIVEN- that are replaced with what is necessary. In the authentication if we input html code or javascript it will include it on the error page, Thanks to Alfredo Sylva for report. - by frederic Bourgeois May 2013 - DansGuardian 2.12.0.6 - Fixed Reportinglevel per filtegroup By frederic Bourgeois - Added sslaccessdeniedaddress (fix HTTPS denied blanck page with reporting level 3 - only with firefox -) By frederic Bourgeois - Added ssldeniedrewrite (fix HTTPS denied blanck page with reporting level 3, need an HTTPS website - By frederic Bourgeois - Added zero value (optional) for some options, like maxcontentramcachescansize - By frederic Bourgeois - Some minor efficiency improvements (remove GCC warning, etc) - By Frederic Bourgeois April 2013 - DansGuardian 2.12.0.5 - alpha - Increase max children to support large networks by Frederic Bourgeois This new option is used to define the number of file descriptors for DansGuardian on large system Need to recompile DansGuardian with the --with-filedescriptors=N compile option configure option --with-filedescriptors=N N=MaxChildren Max January 2013 - Dansguardian 2.12.0.4 - alpha - Set persistent cnx timeout in e2guardian.conf by Frederic Bourgeois - Multiple auth fixed - checked only with one port - (crash fixed before patch #9) - by Frédéric Bourgeois - French translation update (accents problem) - by Frédéric Bourgeois - Added allow regex header filtering with greylist - Frédéric Bourgeois - MITM issues null ceriticate end date value (fixed) - by Marcelloc - Concat values resulting in /// on cert path (fixed) - by Marcelloc - Mitm_magic check code does not work, even if you do not set any mitm key (disabled in code)(ConnectionHandler.cpp) - by Marcelloc - Re-include general max_upload_size general option - use max_upload_size general value if not set in filtergrouplist - by Marcelloc - Fixed maxuploadsize silently ignored - all posts blocked - (patch #12) by Frederic Bourgeois - Applied patch 1 (allow regex filtering with greylist) - by Frederic Bourgeois - Fixed minor compilation error - by Frederic Bourgeois - Applied patch 13 (CONNECT broken due to attempted persistency) - by Nils Goroll September 2012 - Dansguardian 2.12.0.3 - alpha - Fixed memory leaks reported by analysis from coverity - Improved persistent connection for a better RFC compliant implementation, but not yet fully HTTP 1.1 - Minor improvement for debug output - Applied patch #11 (Maxuploadsize per filtergroup) by Frederic Bourgeois September 2012 - Dansguardian 2.12.0.2 - alpha - Applied patch #9 (Crash when more than one authplugin are selected) by Frederic Bourgeois - Added feature to allow Facebook mock ajax (request #6) by Jason Spiro - Added contrib dir - Added a new html & css validated html template in contrib (request #3). By Chris Peschke - Converted iso-8859 message files to utf-8 (bug #86). Suggested by Fred Ulisses Maranhao - Fixed Error reading Content-Length (bug #84). By Carlos Soto - Fixed compilation error BSD due lack of string.h when using memcpy() (bug #75). By Alexander Hornung - Fixed exceptioniplist case sensitivity (bug #11). By Mark J Hewitt - Fixed accept-encoding support for new tokens (bug #13). By userquin May 2012 - Dansguardian 2.12.0.1 - alpha - UNRELEASED - Applied patch 3438750 (GCC 4.4 and 4.6 compatibility) by Mathieu PARENT - Applied patch 3438749 (French translation update) by Mathieu PARENT - Applied patch 3418297 (Set proxy timeout in e2guardian.conf) by Frederic Bourgeois - Applied patch 3419088 (login/password in URL is dropped) by Mathieu PARENT - Applied patch 3419089 ("Expect" header should be dropped) by Mathieu PARENT - Applied patch 3438751 (Fix queue handling in OptionContainer) by Mathieu PARENT - Applied patch 3515167 (Fix digest identication) by Frederic Bourgeois - Fixed GCC warnings - LFS review in String.cpp (requires different arch review yet) August 2011 - DansGuardian 2.12.0.0 - alpha - Search term filtering - POST data scanning - ClamAV scanner removed (direct library usage, not ClamD) - "KavAV" scanner removed (direct library usage, not KavD) - Fix crash on logging very long URLs - Per-group PICS settings - Option to use a specific blocked response for Flash (".swf" URLs; "application/x-shockwave-flash" MIME type) - HTTPHeader bug fixes re. persistent connection detection, crash in some (unknown) circumstances when trying to tunnel POST data - Added per-room-blocking. - Fixed a very old problem with gentle restarts where DG would fail to release the RAM for the first set of config loaded. This was very noticeable on systems with many groups. - Added the ability to have DG listen on more than one port and to use a different authentication plugin on each port. However, some combinations are just not physically possible - for example basic auth (proxy auth) won't mix with other authentication methods. - Added transparent NTLM authentication, however to be useful needs an authentication daemon and daemon-aware, NTLM-enabled web server. (read source code for how to use it) - Added experimental SSL MITM. (read source code for how to use it) - Added experimental SSL certificate checking. (read source code for how to use it) - Added patch by Massimiliano Hofer to add Avast! support. - Tidied up licensing notices and removed some email addresses. - Support individual log items up to 32KB in length, which may require multiple calls to getLine to read in. 2010 - DansGuardian 2.11.*.* - Unreleased. Fri 5th June 2009 - DansGuardian 2.10.1.1 - stable - Add "originalip" option to e2guardian.conf, for determining the original destination IP in transparent proxy set-ups, and ensuring that the destination domain of the request resolves to that IP. This can help to address a particular transparent proxy security vulnerability (US-CERT VU#435052), but because of certain limitations - only implemented on Linux/Netfilter; potential breakage of websites using round-robin DNS - the code is not enabled by default. Enable by passing "--enable-orig-ip" to the configure script. - Fix a crash which could occur when dealing with simultaneous incoming connections in configurations using more than one listening socket. - Fix a crash when checking time limits on item lists. - Fix potential usage of uninitialised memory during phrase filtering. Wed 21st January 2009 - DansGuardian 2.10.0.3 - stable - uClibc++ compilation patch from Natanael Copa. - Fix crash on exit when running out of memory during phrase tree preparation, from Victor Stinner. - Clean up destructors for various objects, removing code duplication with reset() methods. - Compilation fixes from Jeffrey A. Young. - Better handling of whitespace (tab characters) in configuration files. - Fix HTTPS access for unauthenticated users when using basic or NTLM authentication plugins. - Reload list files on soft restart if cached (".processed") files have been updated directly, from Harry Mason. - Chop carriage return off useragent strings when "loguseragent" is enabled. - Don't force contents of e2guardianf*.conf files to lower-case on loading, so as not to destroy the case of group names. - Make temporary bypass cookies valid for subdomains of the original bypassed domain, including stripping "www.". Wed 26th November 2008 - DansGuardian 2.10.0.2 - stable - Fix persistent connection detection to resolve issues with HTTP 1.1 browsers (Firefox), NTLM authentication and HTTPS websites. - Change supported syntax for blocking HTTPS site access by IP to match that documented in the default bannedsitelist (use "*ips", as documented, NOT "**ips"). Tue 21st October 2008 - DansGuardian 2.10.0.1 - stable - Improve malformed URL detection (dc2008.de no longer incorrectly classed as malformed). - Improve persistent connection detection, correcting some situations in which DG would return a blank page to browsers. - Updated "proxies" weighted phrase list. - Updated Chinese Big-5 messages file from Vicente Chua. Wed 8th October 2008 - DansGuardian 2.10 - STABLE! - Fixed handling of content with no MIME type: it will be phrase filtered, but no Content-Type header will be inserted into the response, so a browser's own automatic type detection doesn't get interfered with - Fixed a performance issue with CONNECT requests being incorrectly marked as persistent, identified by Jason Deasi - Updated the man page (Jens Wilke) and French messages file (Jeanuel) - Clarifications to some of the included documents (INSTALL, UPGRADING) - Considered stable (future planned changes are fairly wide reaching, so work will continue in a new series of beta releases) Thu 11th September 2008 - DansGuardian 2.9.9.8 - Assume that content with no Content-Type header is HTML, so that it doesn't bypass the phrase filter. - Fix some incorrect usage of integer types in ListManager and ListContainer which can lead to crashes in some rare cases. - Escape certain characters in URLs when displaying the HTML template to prevent XSS. - Don't add responses other than "200 OK" to the clean URL cache. Mon 18th August 2008 - DansGuardian 2.9.9.7 - stable-surely - Fixed problem with PID file creation when pidfilename is not explicitly given in the config. Wed 13th August 2008 - DansGuardian 2.9.9.6 - stable-surely Build system clean-ups: - Use pkg-config to detect PCRE and ClamAV, not pcre-config and clamav-config. Please note that this means DG now depends on pkg-config. - Remove platform.h.in and use preprocessor symbols from dgconfig.h directly - Define preprocessor symbols corresponding to directory names using CXXFLAGS in the makefile, not platform.h, as the latter is no longer processed by the autotools - Remove checks in configure script for functions not actually called by the code - Remove a lot of obsolescent tests from the configure script, and get rid of platform.h completely as a result Improvements/bugfixes: - Fix an off-by-one error in NaughtyFilter which could result in phrase matching code looking at an uninitialised byte during filtering - Allow regular expression comments with PCRE - Fix a child process handling error which would cause DG to become unresponsive if restarted then soft-restarted in quick succession Mon 9th June 2008 - DansGuardian 2.9.9.5 - do-we-really-still-call-it-beta - IP range & subnet support in banned & exception IP lists. - Honour "--with-sysconfsubdir" setting when installing config. files. - Code clean-ups: remove some unused function arguments, and eliminate compiler warnings from checks enabled by default in recent versions of GCC. - New contributed Polish pornography and "good" weighted phrases. Tue 29th April 2008 - DansGuardian 2.9.9.4 - beta - Replaced quicksort with std::sort when loading in site & URL lists - should behave better with pre-sorted input. - Switch back to original compressed data before sending content to clients, if the decompressed data is found to be zero length (i.e. just compression headers). - Change file blocking logic; exceptionextensionlist and exceptionmimetypelist are now always loaded, and can override the banned lists (much more similar to URL/domain blocking). - ClamAV plugin updated to work with 0.93-style unpacking limits (only; no support for 0.92.1 or earlier). Wed 27th February 2008 - DansGuardian 2.9.9.3 - beta - Large file (2GB+) download & scanning support - Updated German block page template from Peter Vollmar - Small fix to phrase matching to allow it to match the full 0-255 range for each byte, improving foreign language filtering - Fix for incorrect interpretation of URLs containing colons in list files (long standing but rare bug; could cause memory corruption and match failures) - More documentation added to the installation (not new content, but docs that were previously only in the source tarball now get installed) Thu 13th December 2007 - DansGuardian 2.9.9.2 - beta - Fixed a few memory leaks, including a fairly large one in the String class. - Attempt to allocate less memory when reading in phrase lists (start small, and resize the block if needed). - Check for out-of-memory conditions when loading phrase lists, as this was a common cause of segfaults on soft restart on systems with low free memory (daemon will still die, but a bit more gracefully). - Re-worked phrase filtering: fix occurrence counting for combination phrases, and possible performance improvements in weightedphrasemode 2. - Digest auth support (based on contributions by Darryl Sutherland). - uclibc compatibility patch from Gentoo Linux. - Example of headerregexplist usage: force filtering on Windows Live Search by cookie modification. - Contributed updates to some of the language templates. - Miscellaneous other bug fixes, applications of const correctness, etc. Fri 24th August 2007 - DansGuardian 2.9.9.1 - beta - Fix for transparent proxying. - Fixed missing list and language files omitted from previous release. Mon 13th August 2007 - DansGuardian 2.9.9.0 - beta - Fix settings validation to allow maxcontentfiltersize to be set to 0. - Tidy up option data types (hexdecodecontent, forcequicksearch and usecustombannedimage are now "on"/"off" instead of 1/0). - Tweak exception logging (logexceptionhits now has 3 supported values). - Improved pre-emptive blocking for HTTPS requests. - Altered blanket block implementation to also allow blanket exception & grey domain/URL list matching (eg you can have allow everything). - Added exceptionfileurllist in addition to exceptionfilesitelist. - Added outgoing HTTP header blocking & modification. - Added optional domain and URL lists for categorisation without blocking. - Enabled TCP_NODELAY on all TCP sockets to work around a performance hit with persistent connections. - Improved phrase loading code to allow null bytes in phrases. Should allow for support of phrases in encodings such as UTF-16. Tue 27th March 2007 - DansGuardian 2.9.8.5 - beta - Fix behaviour of maxcontent* settings when set to zero ("cascading" values). - Don't allow daemon to start with maxcontent* settings all zero. - Tweak config. validation to better check for allowed values. - Fix memory corruption/leaks in PICS and ClamAV (not ClamD) code. - Protect against more types of IP obfuscation. - Minor cleanups in string class. - Add contributed Japanese messages & template.html. - Add contributed YaST-specific info to supplied init script. - Compatible with ClamAV 0.90. - Fix pre-emptive blocking for unrecognised users when the auth plugin(s) in use don't rely on the parent proxy (ident, ip). - Fixed suggested file paths for using squidGuard/URLBlacklist list sets. DansGuardian 2.9.8.3 and 2.9.8.4 - beta - Never fully released Mon 22nd January 2007 - DansGuardian 2.9.8.2 - beta - "managedmimetypelist" and "managedextensionlist" made into an either/or match, instead of both. - Handle single dots on the end of domain names (e.g. "http://www.cnn.com./"). - Typo fixes to a few error messages and config files. - Allow WebDAV/OWA to work. - Fix a bug in the phrase filtering that was introduced along with the enhanced character encoding support. - Add a little more info to error messages generated by failure to connect to the URL cache, as some people seem to be having trouble with it. Fri 8th December 2006 - DansGuardian 2.9.8.1 - beta - Pre-emptive blocking re-introduced, but for authed users only (don't retrieve anything, even headers, from banned sites/URLs; helps defeat trackers). - Build option to record a backtrace on segmentation fault ("--enable-segv-backtrace"). - Rudimentary performance testing options (add -D__BENCHMARK to your CXXFLAGS when compiling to make some extra command-line options available; content to be tested is read in on standard input). - Syslog logging support. - Performance increase for weightedphrasemode 2. - Reduce some ListContainer code duplication. - FAQ and plugin documents added, see doc subdirectory. Mon 25th September 2006 - DansGuardian 2.9.8.0 - beta - Command-line content scanner. - "Trickle" download manager. - New "#noconvert" instruction in phraselists to prevent case conversion (aid support for exotic character encodings). - New valid value for "preservecase" to scan pages once with case preserved, and once without (aid support for exotic character encodings). - OpenBSD fixes, with thanks to Soner Tari. Wed 9th August 2006 - DansGuardian 2.9.7.5 - alpha - Fixes a logic error with persistent authentication, i.e. NTLM (and ident by IP when not obeying x-forwarded-for headers). POST upload blocking didn't work because it is skipped for unauthenticated users, and persistent authentication methods weren't setting the authenticated flag for every request. Tue 8th August 2006 - DansGuardian 2.9.7.4 - alpha - Updated Dutch translations. - Tweaks which may or may not fix cpu load issues. - Added a work around for IE bug randomly dropping file extensions of files that are scanned. Thu 3rd August 2006 - DansGuardian 2.9.7.3 - alpha - Complete URL unescaping during deep URL analysis. - Fixed more minor memory leaks. - Stricter adherence to target throughput in FDTunnel. - "Graceful" socket closing. - Fixed (some?) IE POST problems (hopefully) which were introduced since persistant connection support. Fri 28th July 2006 - DansGuardian 2.9.7.2 - alpha - Handle more simultaneous phraselist character encodings (help alleviate the infamous "more than 60 links from this node" error). - Fixed POST upload problems with NTLM. - Nicer error messages when phrase tree cannot be built (human-readable version of "more than 60 links from this node"). - Correctly parse ICAP URLs without explicit port numbers. Thu 8th June 2006 - DansGuardian 2.9.7.1 - alpha - Don't phrase filter non-text content when AV is enabled. - Free up unused memory after loading phrase lists. - Corrections to persistent connection detection. - Moved looking up of client hostnames for logging back into ConnectionHandler, to remove potential bottleneck introduced in last version. - Don't convert regular expressions to lowercase when reading in from list files! (Big difference in meaning between e.g. \W and \w). - Fixed memory leak in ipToHostname. Mon 8th May 2006 - DansGuardian 2.9.7.0 - alpha - Added -SERVERIP- placeholder in fancy DM template. - icapscan compatibility greatly enhanced (known working with servers from Symantec, Dr. Web, Trend Micro, AVIRA and Kaspersky ICAP {unreleased}). - ListContainer and HTTPHeader performance enhancements. - "pathprefix" option in clamdscan & kavdscan, for compatibility with daemons inside chroot jails. - Auth by IP plugin correctly ignores blank lines in the ipgroups file. - "reportinglevel = -1" is now allowed in filter group config files. - Time-limited regular expression lists (not fully hierarchic, just checks top-level file and file containing the current expression). - Don't log garbage when logclienthostnames is enabled and a client IP has no DNS record. - Read in maxcontentfilecachescansize option correctly. - Delete temp files after sending when a partially downloaded file becomes too big to scan (default DM). - Safe handling of files larger than maxcontentfilecachescansize in the fancy DM (new "maxdownloadsize" option in fancy.conf). - Email notification of blocked pages/viruses (--enable-email compile option, various new configuration options; patch by J. Gauthier). - Direct tunnelling of traffic from exception sites (that are excepted from both filtering and virus scanning). - Various other bugfixes & build tweaks. - Includes the latest updated phrase lists. Mon 3rd April 2006 - DansGuardian 2.9.6.2 - alpha - NTLM username strings are correctly terminated after conversion from UTF-16LE. - Spelling mistake fixed in ukenglish template.html. - Fixed "-c" option (Jason Gauthier). - Slightly tweaked handling of exception CONNECT (HTTPS) requests - should help those with IE & SSL problems. - Tweaked detection of persistent connections (HTTP 1.1 requests are assumed persistent unless marked otherwise, as per standards). - Buffered network input in BaseSocket::getLine (fewer system calls during HTTP header retrieval). - Optional logging of child process handling operations (logchildprocesshandling). - Conditional installation of contentscanners, lists/contentscanners and lists/downloadmanagers directories. - Fancy DM has managedextensionlist enabled by default, not managedmimetypelist. Fri 3rd March 2006 - DansGuardian 2.9.6.1 - alpha - Improvements/fixes to filter group range checking (auth by IP plugin usable again - sorry!). - URL cache sorting & searching fixed. - Workaround for clients that send lowercase "host:" headers. - Workaround for certain types of Squid-unfriendly request (allows more clients to work with DG+Squid used as a transparent proxy). - Updated Turkish language files (Ozgur Karatas). - Confusing "--with-libiconv=yes" configure option removed; iconv check now looks for library in standard search path automatically if a platform-native iconv function is not found. - Iconv library check looks for both "iconv" and "libiconv" functions - should help OS X 10.3 users. - Time limits are applied hierarchically to included list files, as has been the intention for a while now (obviously the feature is not widely used :P). - Case-insensitive handling of HTTP headers (Nerijus Baliunas). Mon 20th February 2006 - DansGuardian 2.9.6.0 - alpha - "ident" auth plugin supports usexforwardedfor when determining client IPs. - Range checking on filtergroup returned from auth plugins (pointed out by Götz Babin-Ebell). - Auth by IP plugin ignores entries in ipgroups which specify an out-of-range filtergroup, and prints warning messages when it encounters them. - Fixed "SafeNetalcoholtobacco" typo in PICS settings file. - Changed handling of POST uploads; large files should no longer cause timeouts. - There are now default managed extension/MIME type lists under. "/etc/e2guardian/lists/downloadmanagers", and the latter is enabled by default in the fancy download manager's config file.Götz Babin-Ebell. - Optional blanket download blocking, with new exception MIME and extension lists for overriding the block (existing banned MIME & extension lists not used in this mode - everything not excepted is banned). - New "exceptionfilesites" list, for defining domains which are not subject to filtering by MIME type or extension, i.e. trusted download sites. - Log format changes: IP and hostname are both logged (instead of using one field for either); group name & number are now separate fields. - Connections are correctly closed after being sent a download link from the fancy download manager. Thu 2nd February 2006 - DansGuardian 2.9.5.0 - alpha - Auth by IP plugin works without PCRE. - When using auth by IP, credentials are cached for the lifetime of a persistent connection, instead of querying the plugin for every request (unless usexforwarded for is enabled). - Added blanket SSL block (**s) and blanket SSL IP block (*ips). - All usernames show up as lowercase in the logs. - Auth plugins get reloaded on -g (allows changes to IP plugin's. ipgroups file to take effect as well as changes to the filtergroupslist). - Added allow per-group override of reportinglevel (hence per-group choice between HTML template & external access denied address). - Fixed a few memory leaks & some minor memory corruption. Wed 25th January 2006 - DansGuardian 2.9.4.0 - alpha - NTLM and proxy auth plugins renamed to "proxy-ntlm" and "proxy-basic" - Fixed child process failure after -g - Changed clamav's memory scanning method (uses more reliable parts of Clam API; both clamav and clamdscan should now detect the same viruses) - Unfortunately the new method does involve saving to file first; however, you can use POSIX shared memory on supported platforms, or specify a ramfs/tmpfs location, with a fallback to using the standard filecachedir. - Also, the temp directory used internally by libclamav can be configured. - zlib can optionally be statically linked - Improved handling of HEAD requests and HTTP redirects - Improved error logging in clamav & clamdscan - clamdscan now issues "SCAN" command instead of "CONTSCAN" (stops scanning after the first error/virus is found) - kavdscan can handle archive files containing multiple viruses (patch submitted by littlecahya) - More verbose output in debug mode - Improved default & fancy download managers handling of files larger than maxcontentfiltersize (or maxcontentfilecachescansize) - Filter groups can be named - Only load filter group 1's settings if no auth plugins are loaded - Changed -FILTERGROUP- placeholder to return group name - Added -RAWFILTERGROUP- placeholder for retrieving group number - Added -SERVERIP- placeholder; returns the IP on which the filter is running (returns correct address if using multiple filterip options; returns 0.0.0.0 if filterip is blank) - URL cache now stores the group(s) for which a URL is clean, in addition to URLs themselves (cannot access a page with a score beyond your naughtiness limit if someone in a group with higher limit/different phrases has previously accessed it) - Fixed child process crashes when using maxips option, and add logging of IP usage statistics (new statlocation option) Wed 21st December 2005 - DansGuardian 2.9.3.2 - alpha - Adds logging of upstream proxy return code & user's filter group number - Fixes/improvements to configure script (FreeBSD users: try '--with-libiconv=/usr/local') Fri 16th December 2005 - DansGuardian 2.9.3.1 - alpha - Fixed x-forwarded-for support in Auth by IP plugin. - NTLM plugin now compiles on FreeBSD. - Can now log in using NTLM when browsing HTTPS sites. - Fixed mis-authentication with downstream proxies. - Fixed log all usernames, even those not in the filtergroupslist. - NTLM plugin handles UTF-16 usernames. - Improved child process responsiveness when using tunnels. Tue 6th December 2005 - DansGuardian 2.9.3.0 - alpha and approaching feature complete - Added NTLM Auth support. - IP Auth plugin now supports X-Forwarded-For headers. - Added Persistent connection support to allow pass through of a number of protocols like NTLM and others that break without persistent connections. It also should improve performance. - Added better support for filter/infection bypass mode "-1" setting. - Added configurable category list thresholding where you can configure it to only show the top phrase categories matched. - Performance enhancements to phrase filtering. - Added infection bypass mode (with optionally only allow on scan error). - Multiple auth plugins now allowed. - Embedded URL (links, images) extraction & weighting to allow sites with lots of links to banned domains to be more likely to be denied. - Now marks content & URL modifications in the logs. - Added Hungarian & Portuguese Brazilian language files. - Fixed URL regular expression search & replace. - Fixed hexdecodecontent handling of a page's last few bytes. - Fix for empty pages with gzip & zlib encoding. - Fixed content-length header for pages with content modifications. Tue 8th November 2005 - DansGuardian 2.9.2.0 - alpha and feature incomplete - Filter groups now define a "filtering mode": banneduserlist and exceptionuserlist support have been removed, and replaced with the concept of a user being a member of a group which is in banned/filtered/unfiltered mode. - Added plugin-based authentication system - proxy and identd auth methods are now provided as plugins. - Added auth by IP plugin - maps individual IPs, IP ranges & subnets to filter groups. - Download managers support optional extension/mimetype inclusion lists, for additional limiting of which requests get handled by a given manager. - Fancy download manager supports HTML templates. - Added client hostname display for banned pages (new placeholder available for HTML templates; new parameter for CGI). - Page weights are now explicitly logged against all filtered requests (not just as part of the "reason" string on weighted phrase banned requests). - Now includes a lot of example content and URL regexps. - Now includes latest phrase lists. - Fixed PICS enable/disable option fixed (PICS now disabled by default). - Fixed filter bypass links cannot bypass the blocking of files virus scanned and found to be infected: you can now only download known infected files by being a member of an exception filter group (if contentscanexceptions is disabled), or by being in a group with disablecontentscan set. - Fixed improved extension detection for downloads from CGI scripts - fewer downloads can now slip through the net. - Fixed exceptionvirusmimetypelist support (i.e. it now works). - Fixed improved behaviour of logclienthostnames option. - Fixed configure script checks that the installed PCRE version is recent enough, if building with PCRE support. Mon 24th October 2005 - DansGuardian 2.9.1.0 - alpha and feature incomplete - Added Deep URL scanning to spot URLs in URLs to for example block images in google images. - Added Advanced advert blocking, including an option not to log advert blocks (DG now includes advert domain/URL lists from squidGuard, with added categorisation, to demonstrate this feature). - Added Korean PICS support. - Added new phrase filtering mode: looks only at contents of title & meta tags. - Added case insensitive detection of headers retrieved from webservers which don't obey standards. - PCRE support is now optional at configure time (--with-pcre; disabled by default). - e2guardian.org removed from exceptionsitelist, and a rule added to bannedregexpurllist, to prevent accidental access to the DG sample virus repository. - Firefox DoS fix in contentregexplist (commented out by default to conserve CPU). - Fixed Grey URL list checking scans greyurllist, not bannedurllist. - Fixed support for matching multiple URLs which differ only by a suffix. - Fixed ClamD/KavD so they now run as both the same user & group as DG and successfully access temp files. Wed 19th October 2005 - DansGuardian 2.9.0.1 - alpha and feature incomplete - Designed from scratch easily pluginable download management system which works with the... - Designed from scratch content scanner system with a simple plugin system which makes it easy to write new content scanners such as the included AV. It can also chain multiple scanners so you could use more than one AV engine for example. Other nice things include in-memory scanning if the plugin supports it saving writing to disk. - Download manager is compatible with WGET and software updates and will switch to a fancier version with percent graph when used with a browser. - Download manager is secure as each download is keyed to the original downloader. - No dependance on libtool or pkg-config unlike the original 2.9 AV version. - Added time/day controls support for lists. - Added category support so when it says banned site it says what category. - General speed and code improvements. - New code style guide produced. - Code is now in the new style. - Ported to automake and autoconf for a more standard and logical code maintanence. - Developer documentation is now provided online to describe the code better. - Regular expression content replacement is improved so back references work so browser vulnerabilites can usually be automagically removed. - Added URL regular expression replacement so you can force safe search in google or redirect people to a different site. - Long log lines are now inteligently truncated. - Port < 1024 can be used and group and user set IDs are properly used and similar clean ups. - Multiple command line options are now possible. - The compile time options are displayed with a -v. - Anonimised logs option for countries with strict privacy laws like Germany. - Added a -Q option which allows a restart and a full config change for example. - Added regular expression exception URLs. - Added the ability to listen on N IPs rather than 1 or all. - The latest phrase lists from Fernand (phrase maintainer). Sun 14th August 2005 - DansGuardian 2.8.0.6 - More phrase and other list mods. - Increased IPC buffers to solve problems with long log lines and URLs. Tue 9th August 2005 - DansGuardian 2.8.0.5 - Fixed not being able to regexp content replace with a blank. - Fixed long standing bug with processed list files which now makes startup several times faster if you are using multiple filter groups. - Added a nocache directive to the HTML template display header. - Added Slovak language files. - Removed german2 language files. - Added option so bypass hash could be enabled but no valid hash presented so external auth mechanisms can be used with the bypass feature. - Removed deleting of log ipc file on -r restart. - Added lots of phraselists thanks to Fernand Jonker. - Added fix so it should compile on Fedore Core 4 and other GCC4 distros. - Corrected TEMPLATE spelling error. - Filter groups now limited to 99 not 9. - Updated proxy testing error message. Sun 20th February 2005 - DansGuardian 2.8.0.4 - Corrections to Brazillian messages in Portuguese. - Updates to Lithuanian messages. - Updates to INSTALL file to stop confusing debian users. - Fixed libz problem in Makefile for OSX. - Fixed long standing bug where when it sigtermed with a -q it killed itself rather than exiting gracefully. - Fix to long standing bug in HTTP header handling causing possible problems with non-browser HTTP clients. - Minor improvement to HTTP header sending. - Updated HTTP header to handle RFC compliant but abnormal requests. - Fixed long standing bug where a -r restart would not cause the users groups file to be re-read correctly so users who were changed group would not always be detected. This bug could also cause users to appear to be in the wrong group sometimes. Fri 10th September 2004 - DansGuardian 2.8.0.3 - Corrected log configuration logic for running with a seperate log daemon. - Corrected hard-coded /usr/lib which causes problems on 64-bit systems. - Corrected mistake introduced by a 3rd party submitted patch which caused the entire set of config files to be loaded and processed even on a soft restart. - Corrected potential unsafe permissions on the log dir. - Updated Big5 language file. Thu 29th July 2004 - DansGuardian 2.8.0.2 - Corrected missing new line in French messages file. - Added include in String.cpp for Debian/FreeBSD compile problems. - Removed eronous openssl part in configure script. - More INSTALL file improvements. - Added some more domains to default exception lists. Wed 28th July 2004 - DansGuardian 2.8.0.1 - Corrected INSTALL file. - Corrected some language files. - Corrected a locale issue. - Corrected issue where banned extensions could be bypassed by hex encoding the file name. - Fix to start script to make it more debian compatible. - Moved to new numbering scheme and lower case source name as requested by package builders. Wed 21st July 2004 - DansGuardian 2.8.0 - Included zlib static building info in INSTALL file. Fri 16th July 2004 (pm) - DansGuardian 2.7.7-11 - Made parameter checking more durable and less messy. Fri 16th July 2004 (4am) - DansGuardian 2.7.7-10 - Fixed a typo in the FreeBSD Makefile. - Added new googlesearches phrase file. - Fixed a terrible DoS bug in the file download buffering so that large binary files with no length header but are marked as text no longer eat up all the RAM. - gzip or zlib compressed pages are now no longer sent to the client uncompressed if they are not modified with content replacement. This is extra good if the clients are remote to the filter as it will save bandwidth. This was a pleasant side effect of fixing the DoS bug. - Updated to use zlib 1.2.1 for gzip in-memory decompression but will fail gracefully if an older version is installed. This change was *required* to fix the DoS bug. - Slightly improved socket handling so it's a tiny bit quicker and takes up less file descriptors. This was a result of the DoS bug fixing. Mon 14th June 2004 - DansGuardian 2.7.7-9 - Improved sysv file for Linux. - Fixed some compile issues on FreeBSD with needing more #includes. - Incorporated FreeBSD Makefile changes at request of port maintainer. - Fixed checking for auth required so it only matches "407 Proxy Authentication Required". - Disabled log entries for "407 Proxy Authentication Required" to prevent log poisoning (user X attempting to access a bad URL posing as user Y). - Fixed issue with very small list files being ignored. - Fixed crash on empty phraselists. - Reinstated case sensitivity in conf files. - Improved socket exception error messages. - Fixed "user@site" URLs not being filtered properly. Wed 28th April 2004 - DansGuardian 2.7.7-8 - Fixed grey lists not overriding regexpurl lists. - Added frenchpornography phrases. Wed 21st April 2004 - DansGuardian 2.7.7-7 - Fixed compile issue on FreeBSD. - Fixed ".Include" files not working for bannedregexpurllist. Wed 14th April 2004 - DansGuardian 2.7.7-6 - Fixed content filter bypass which with a specially crafted request could force an unclean page into the clean page cache. - Fixed a memory allocation error in the list files which lead to 1 byte of memory potentially being overwritten. - Corrected German messages file. - Fixed mistake in the hash parameter sent to the access denied perl script. Fri 26th March 2004 - DansGuardian 2.7.7-5 - Increased some timeouts to more reasonable values. - Client IP now passed to deny script. - Added feature where when a user is not found the IP is checked for in the filter group list. - Added workaround for OS X bypass feature problem. - Slightly improved content replacement so it can handle " characters. - Included updated lists for use with urlblacklist.com. - Added danish language. Wed 25th February 2004 - DansGuardian 2.7.7-4 - Gentle restart now flushes the clean URL cache so changes don't appear to not work. - Fixed bug where non-text URLs were getting added to the URL cache. - Changed cookie bypass to use the same time code as the URL bypass so that users are not able to effectively double the time period. - Fixed mistake in bypass logic which stopped exeptions from working. - Changed configure to try to detect the correct path to endian.h. - Dissabled bypass when post block. - Added check for ..\ Thu 19th February 2004 - DansGuardian 2.7.7-3 - Fixed problem with Darwin using non-standard accept() Wed 18th February 2004 - DansGuardian 2.7.7-2 - Added check for /../ in URLs to stop users bypassing part of the filtering. Wed 4th February 2004 - DansGuardian 2.7.7-1 - Fixed minor problem with IE and denied page. Tue 3rd February 2004 - DansGuardian 2.7.7 - Added a temporal denied page bypass facility. Sat 3rd January 2004 - DansGuardian 2.7.6-7 - Improved detection of when a sub list file has changed for gentle restart. Wed 24th December 2003 - DansGuardian 2.7.6-6 - Made header request line tolerant to multiple spaces. - Fixed preemptive banning not deactivating unless proxy auth was also enabled. - Fixed broken bannedphraselist file included by default. - Fixed segfault on missing config file. - Removed arbitary limit on filter groups. Now 99. - Fixed SSL site banning when preemptive banning is switched off. Sun 14th December 2003 - DansGuardian 2.7.6-5 - Fixed a segfault when using gentle restart in some situations. Thu 11th December 2003 - DansGuardian 2.7.6-4 - Fixed segfault with -N option. - Fixed typo in bannedurllist. - Fixed cross site scripting vuln in e2guardian.pl. - Fixed error in Solaris Makefile. - Fixed '//' detection yet again to use a further different approach due to broken sites. Thu 4th December 2003 pm - DansGuardian 2.7.6-3 - Fixed a URL filtering bypass which worked by appending a period '.' after the domain. Thu 4th December 2003 am - DansGuardian 2.7.6-2 - '//' detection fixed to not give so many false positives. Wed 3rd December 2003 - DansGuardian 2.7.6-1 - Fixed some compile problems with a missing cerrno. - ICRAviolenceobjects was missing and is now added. - Exception phrases now take presidence when there is a dup. - URL matching has been improved to support non-path elements better. - '//' in URL paths properly checked for now. - Tidied some code structure. - Fixed inability to listen on privelaged ports. - Fixed gentle restart segfault when a file is changed twice. - Removed lots of commented out code. Thu 20th November 2003 - DansGuardian 2.7.6 - Sub list (.Include) support fixed. - Added processed file support for grey and exception URL and site lists. - Fixed bug where it would not exit when there was a mistake in the f1 conf file on start up. - Made non-standard redirector delimiting optional. - 'e2guardian -s' now works as non-root. - Generally improved errors with the -s -r -g and -q options. - Ident now uses X-Forwarded-For when DansGuardian is configured to use it. - Bug fixed in exceptionurllist using wrong list file. - './configure --logdir' option being ignored by default fixed. - OSX compiler warnings solved. Sun 16th November 2003 - DansGuardian 2.7.5 - Improved URL encoding for e2guardian.pl. - Added greysitelist and greyurllist to allow sites past the URL filtering but to still have the content filtering applied. - Added chineasegb2312. - Minor code changes. - Fixed underscores in hostnames giving malformed URL. - Fixed an image replacement bug. - Startup speed improved by using seperate lists per each included file. - Fixed '-r' restart problems not killing processes. - Fixed race condition causing problems when restarting. - Added a '-g' gentle restart that does not kill current connections but filter group config is re-read. - Added filter group support so different filtering settings can be used for different groups of users. - Added inteligent list managing so that if different filter groups use the same file they will share one copy of it. It also means it does not need to read in two copies. The list managing also caches the lists between restarts thus reducing restart speed dramatically. - Added a German pornography phrase category. Wed 10th September 2003 - DansGuardian 2.7.3-1 - Added check for '//' in URLs thus stopping the URL filter workaround. Sun 31st August 2003 - DansGuardian 2.7.3 - HTTP port now stored in logs. - Exception/banned site lists can now use .tld - Fixed banned extension not being logged. - Improved whitelist mode so that domains listed in the bannedsitelist are actually allowed, however they have the normal filtering applied. - URL matching fixed so that /blah no longer matches /blahfoo - Fixed broken image replacement due to debug code left in. - Included an upgrading guide. Wed 30th July 2003 - DansGuardian 2.7.2 - AD image replacing improved with mime checking as well as extension checking. - Added option to dissable pre-emptive banning which provides many benifits such as not needing to access a clean site first before your unfiltered user is recognised. - Fixed bug where a missing '/' in the .conf caused everything to be not filtered. - Added options to specify log, pid, and uds so that multiple instances can be run simulaneously. Previously this was only possible with code editing an recompiling. - Added support for HP/UX. - Added support for fully qualified addresses in banned and exception ip lists. - Fixed a memory leak. - Added option to disable forking into the background. - Added option to disable logging process. - Added option to specify the user that it runs as to override the compile default. - Added option to do a 'safe' restart where the process leader does not send a kill to every other process. This makes it possible to run as su safer. - Added mxspanish language files. Sat 12th July 2003 - DansGuardian 2.7.1-4 - Fixed typos in several autoconf files. - Removed redundant fcntl calls which caused problems in FBSD. Mon 7th July 2003 - DansGuardian 2.7.1-3 - Fixed 2 file descriptor leaks in the logging and url cache code. Sun 6th July 2003 - DansGuardian 2.7.1-2 - Fixed bugs with URL cache. - Fixed the exiting on HUP. - Included new Bulgarian language files. - Fixed typo in linux.in file. Fri 13th June 2003 - DansGuardian 2.7.1-1 - Fixed Ident. Mon 12th May 2003 - DansGuardian 2.7.1 - Added image replacement code based on ideas from Aecio F. Neto which guesses if a banned file is an image and if so replaces it with a configurable replacement such as a 1x1 gif. - Much improved the IF structure in the ConnectionHandler.cpp to make it slightly faster and a LOT more organised. Based on ideas from Aecio F. Neto again. - Fixed bug in Makefile where contentregexplist would get deleted by mistake. Sun 20th April 2003 - DansGuardian 2.7.0-1 - Removed some debug code left in by mistake. Fri 18th April 2003 - DansGuardian 2.7.0 - Added option to do raw, smart or both phrase filtering thus optionally reducing cpu usage by half. - Added ICRA vk PICS option support. - Added improved internationalisation language file support. - Removed bodge for UDS truncated file and replaced with a proper fix. - Added WebDAV support (for Outlook Express access to Hotmail). - Added forkpooling to on average half cpu usage and improve scaleability. (big feature) - Descriptors 0-2 are now dup2ed to /dev/null to prevent some shell hanging. - Added improved granulinarity to the content filtering limit. - Added option to switch off DFA searching so full 16-bit char support is regained. - Added more 16-bit char support. - Added support for lower-casing accented characters. - Added support for unescaping HTML content. Wed 14th April 2004 - DansGuardian 2.6.1-13 - Fixed content filter bypass which with a specially crafted request could force an unclean page into the clean page cache. - Fixed a memory allocation error in the list files which lead to 1 byte of memory potentially being overwritten. Wed 25th February 2004 - DansGuardian 2.6.1-12 - Added check for ..\ and ../ in URLs Wed 24th December 2003 - DansGuardian 2.6.1-11 - Made header request line tolerant to multiple spaces. - Fixed broken bannedphraselist file included by default. Thu 11th December 2003 - DansGuardian 2.6.1-10 - Fixed cross site scripting vuln in e2guardian.pl. - Fixed '//' detection yet again to use a further different approach due to broken sites. Thu 4th December 2003 pm - DansGuardian 2.6.1-9 - Fixed a URL filtering bypass which worked by appending a period '.' after the domain. Thu 4th December 2003 am - DansGuardian 2.6.1-8 - '//' detection fixed to not give so many false positives. Wed 3rd December 2003 - DansGuardian 2.6.1-7 - ICRAviolenceobjects was missing and is now added. - '//' in URL paths properly checked for now. Thu 20th November 2003 2003 - DansGuardian 2.6.1-6 - Fixed malformed URL error with a '_' in the hostname. Wed 10th September 2003 - DansGuardian 2.6.1-5 - Added check for '//' in URLs thus stopping the URL filter workaround. Sun 31st August 2003 - DansGuardian 2.6.1-4 - Typo in OBSD Makefile to do with 'install' program fixed. - Fixed banned extension not being logged. Mon 7th July 2003 - DansGuardian 2.6.1-3 - Fixed 2 file descriptor leaks in the logging and url cache code. Sun 6th July 2003 - DansGuardian 2.6.1-2 - Fixed bugs with URL cache. - Included new Bulgarian language files. Fri 13th June 2003 - DansGuardian 2.6.1-1 - Fixed case sensitivity in Ident. Mon 12th May 2003 - DansGuardian 2.6.1 - Improved file extension list. - Removed RLIMIT code that seems to cause problems and is not needed anyway. - Added Italian bad words. Mon 14th April 2003 - DansGuardian 2.6.0 - Fixed URL cache bug that caused it to stop caching when not enough different URLs are used. - Fixed dlopen false requirement in configure. - Increased logrotate.d sleep to cope with slower machines better. - Fixed bug where log ipc socket not closed on error which could cause fd resource problems. - Fixed bug in sysv script so stdin is redirected to /dev/null to prevent some shell hanging. - Fixed bug where when a list file had been modifed but not the main refering file, the processed file would not get recreated and a file descriptor left hanging. - Fixed bug where rlim_cur was set too low causing forking problems in high loads. - Fixed bug where the url passed to the cgi reporting script was not encoded enough. - Fixed bug where it can check a PICS rating against the wrong service if more than one service rating is contained in the label. - Minor HTTP header improvement. Tue 7th January 2003 - DansGuardian-2.5.3-4 - Added a test to get round a mis-feature of squid where it allows hostnames of the form host.domain..tld which bypassed the URL filtering. Sun 1st December 2002 - DansGuardian-2.5.3-3 - Double free, compile warnings and configure typo fixed. Sat 30th November 2002 - DansGuardian-2.5.3 - Fixed a bug with trailing '/' being added on some URLs with Mozilla. - Added feature where you can limit the file size that text documents are under before they are content filtered and content replaced. This is most useful for sites that label 640mb iso images as text. - Added feature where you can now log in the squid log format for use with your favourite log analysers. - Added guessing as to gcc version so should compile easier on gcc 3. - Some typographical errors corrected. - A fix for when an empty phrase list is used. Fri 8th November 2002 - DansGuardian-2.5.2 - Added Mac OS X support. Sat 26th October 2002 - DansGuardian-2.5.1 - Fixed 2 important typos in default configure files and options reading code. (Namely to do with X-Forwarded-For and a phrase list directory). - Added an improved logging support patch for logrotate.d by James A. Pattie. Thu 24th October 2002 - DansGuardian-2.5.0-2 - Fixed a number of spelling mistakes. Wed 23rd October 2002 - DansGuardian-2.5.0-1 - Fixed reload bug where the caching url proccess would not reload when a reload was requested. - Fixed a problem where several sockets were left hanging. Sun 20th October 2002 - DansGuardian-2.5.0 - Added content regular expression replacement. (eg popup removal) - Added foreign language support to the messages. (Comes with Spanish, Chinese, Dutch, French, German, Indonesian, Italian, Polish, Portuguese, Turkish and English). - Added clean-url caching to dramatically improve performance in a classroom environment. - Added support for NetBSD. - Added tab delimitation log format. - Dramatically improved phrase table generation speed by at least 10 times which will reduce daemon start time by half when coupled with the large URL blacklist. - Added 3rd party command-line '-P' plugin option. - Added '-' as the entry for a blank username like squid. Tue 7th January 2003 - DansGuardian-2.4.6-8 - Added a test to get round a mis-feature of squid where it allows hostnames of the form host.domain..tld which bypassed the URL filtering. Sat 30th November 2002 - DansGuardian-2.4.6-7 - Fixed a bug with trailing '/' being added on some URLs with Mozilla. - A fix for when an empty phrase list is used. Sat 26th October 2002 - DansGuardian-2.4.6-6 - Fixed 2 important typos in default configure files and options reading code. (Namely to do with X-Forwarded-For and a phrase list directory). Wed 16th October 2002 - DansGuardian-2.4.6-5 - Fixed bug where a parameter being sent to accept() was not being initialised properly after an error condition. Thu 10th October 2002 - DansGuardian-2.4.6-4 - Fixed over allowing in exceptionsitelist due to matching just part of url. - Fixed bug in socket code which was not initiating a struct properly. - Fixed mistake in accept() code using size_t rather than socklen_t. Mon 23rd September 2002 - DansGuardian-2.4.6-3 - Added x-forwarded-for support for the incomming request. - Slighlty improved malform URL checking. - Included the latest phrase lists from the PMG. - Fixed bug in reg exp code where it did not find all matches. - Fixed bug in url reporting code when in transparent mode. Wed 4th September 2002 - DansGuardian-2.4.5-3 - Fixed bug in URL matching code where it matched on a partial which should have been a complete. Thu 25th July 2002 - DansGuardian-2.4.5-2 - Fixed properly this time the pid file bug fixed last time. - Added a workaround for buggy browsers causing Malformed URL errors. - Included the latest phrase lists from the PMG. Sun 21st July 2002 - DansGuardian-2.4.5-1 - Added a hex decoder to prevent users from bypassing URL checking by hex encodint the URL string. - Fixed a problem with the template display where it was adding additional newlines within the replaced fields. Sun 14th July 2002 - DansGuardian-2.4.5 - Fixed properly this time the logging bug fixed last time. - A bug that might have caused random crashes with some squidGuard lists has been fixed. - Problems with the pid missing a newline and having odd permissions have been solved. - Stealthmode works again. This has been broken for some time. Sun 16th June 2002 - DansGuardian-2.4.4 - Fixed a bug in the code logging the size of a file passing through. - Implemented a work around for template file displaying with HTTPS blocked pages. - Fixed a minor bug in the HTML template displaying code. - Included the very latest phrase lists. Sun 19th May 2002 - DansGuardian-2.4.3-2 - Fixed a mistake in the linux.in file that made the install prefix be ignored for the weightedphraselist. Plus a number of other minor issues to do with the weightedphraselist file and the Makefile and the source distribution. Sat 18th May 2002 - DansGuardian-2.4.3-1 - Fixed a bug in the configure script that caused an incorrect default weightphraselist to be installed. - Fixed an issue where one of the weightedphraselist files was missing. Mon 13th May 2002 - DansGuardian-2.4.3 - Fixed a bug where an unused socket FD was left hanging. - Fixed a bug where Netscape 6.2 users would be blocked with an 'Malformed URL' error. - Fixed a bug with overblocking and file extensions. - Added an installprefix option to configure so that the project can be installed to a different location, but still be coded to work without the prefix. E.g. for package contruction such as .deb. - Implemented a work around to deal with broken browsers that pass invalid URLs which caused the URL filtering to not work. - Slightly improved error checking on the accessdeniedaddress setting. - Fixed a bug where stdin was getting closed too early. - Fixed a bug with certain pages generating a reporting URL too long for the perl script to handle which caused no page at all to display. - Fixed a bug which caused "THIS IS NOT HAPPENING!" messages. - Fixed bug where all combination weighted phrases were given a value of zero instead of their correct value which meant they had no effect at all! How did that go undetected for so long??? - Fixed bug where the combination weighted phrases were not logged as part of the weighting calculation. - Added a feature where negative weighted phrases are logged with a prefix of a minus sign. - Included the latest weighted phrase lists from the PornMasterGeneral. Mon 8th April 2002 - DansGuardian-2.4.2-2 - Added an option so that any weighted phrase in a page only counted once. Sun 31st March 2002 - DansGuardian-2.4.2-1 - Fixed small problem with compiling on GCC 3. - Fixed small issue with exceptionurllist unblocking too much. Thu 28th March 2002 - DansGuardian-2.4.2 - The default included PICS settings are less strict and so more useable. - A bug in the zlib deflation code causing partial display of some pages has been fixed. - A problem with the 'e2guardian -r' feature on FreeBSD and OpenBSD has been fixed. - Added a HTML template feature where DansGuardian displays a HTML file for the access denied page which makes installation easier as no perl script or web server are needed. It also makes modifying the page much simpler. And finally the addition does not use URL encoding to pass the information to the page so no more long nasty URLs. Fri 15th March 2002 - DansGuardian-2.4.1 - Fixed GCC3 compilation problem (hopefully) due to incorrect use of namespace. - Fixed complilation problem on Solaris, OpenBSD and FreeBSD. - Reduced non-C++ library usage in String class. - Fixed an issue where banned IPs and users were unable to view the message saying they were banned because their web access was blocked. - Fixed a bug were it reported the wrong phrases when a banned phrase combination was found. - Recompiled statically linked binaries with updated zlib. The fixes a double free bug in zlib which DansGuardian uses which makes it theoretically possible for a web site to cause denial of service or even run arbitrary code. This only affects those that do not compile from source but users are advised to update their zlib and recompile if needed. - Improved gcc 3 compatability (i.e. fixed some non-standard coding). - Increased use of more appropriate C++ libraries rather than C libraries. - Fixed a problem with overblocking banned file extensions and certain URLs. Most notably with hotmail attachments and also with pages that redirect. - Added exception URL list feature. Sun 10th March 2002 - DansGuardian-2.4.0 - Fixed a single long standing memory leak in the string handling code. - Fixed a problem with URL matching not comparing the final character and so causing overblocking. Fri 1st March 2002 - DansGuardian-2.3.3 - Added -HUP and 'e2guardian -r' support. - Better handling of tabs in list files. - Optional CSV format log file format. - Better banned file extension list. - Improved handling under very heavy load. - Added the username to the information passed to the perl reporting script. - Reworked some of the code for more speed. - Better phrase lists. - Weighted phrase matching can now optionally log and report the phrases found. - PICS filtering can be switched off globally. Sun 17th February 2002 - DansGuardian-2.3.2 - Improved String class speed. - Added basic url filtering for https. - Improved included weightedphraselist. Thu 14th February 2002 - DansGuardian-2.3.1-1 - Fixed a problem causing freezing, segfaults and all sorts of nastyness when top bit set characters appear in a web page. Mon 11th February 2002 - DansGuardian-2.3.1 - Fixed an issue which caused DG to stop responding under very heavy load. - Fixed a security issue that allowed file extension filtering to be bypassed. - Added support for Big5 etc. Sat 2nd February 2002 - DansGuardian-2.3.0-2 - Fixed bug in .Include in phrase lists. - Corrected the included Makefile. Wed 30th January 2002 - DansGuardian-2.3.0-1 - Total rewrite of the phrase searching code. Now it uses an advanced Deterministic Finite Automata Graph Algorithm. This means the searching on large phrase lists is faster by several times. On small lists it makes no difference, but lists over 300 no longer slow it down as much as it used to. - Almost all console errors are now logged in SysLog for easier problem solving. - The exception matching now logs (configurable) exception hits which makes it easier to find out why a certain page is not blocking. - Exception phrases are now seperated into a different file for ease of maintaining and no longer need the '!'. - A new weighted phrase system has been added where phrases can be assigned a good or bad value. If the totals for the page are over a configurable limit then it will block. This allows for much finer control of filtering and will reduce over or under blocking. - Banned, weighted and exception phrases can all use combinations. Previously it was just banned phrases that had this feature. - There is now a banned user list and banned ip list. - Better Debian and RedHat 7.2 support has been added to the configure and Makefiles. - The log format now includes the size of the requested page or file. - Blanket blocking now logs the IP the user was trying to get to. - Overall the code is faster, but has more features. Mon 22nd April 2002 - DansGuardian-2.2.10 - Fixed a bug where an unused socket FD was left hanging. - Fixed a bug where Netscape 6.2 users would be blocked with an 'Malformed URL' error. - Fixed a bug with overblocking and file extensions. - Fixed a bug where stdin was getting closed too early. Tue 9th April 2002 - DansGuardian-2.2.9-1 - Fixed a bug introduced in 2.2.9 which makes DansGuardian think that https is a malformed URL. Mon 8th April 2002 - DansGuardian-2.2.9 - Implemented a work around to deal with broken browsers that pass invalid URLs which caused the URL filtering to not work. - Slightly improved error checking on the accessdeniedaddress setting. Thu 28th March 2002 - DansGuardian-2.2.8 - Fixed a bug in the zlib code which caused certain pages in certain browsers to be half missing. - Added a compile option to disable PICS filtering. - The default included PICS settings are less strict and so more useable. Fri 15th March 2002 - DansGuardian-2.2.7-1 - Recompiled statically linked binaries with updated zlib. The fixes a double free bug in zlib which DansGuardian uses which makes it theoretically possible for a web site to cause denial of service or even run arbitrary code. This only affects those that do not compile from source but users are advised to update their zlib and recompile if needed. Sun 10th March 2002 - DansGuardian-2.2.7 - Fixed 3 long standing memory leaks in the string handling code. - Fixed a problem with URL matching not comparing the final character and so causing overblocking. - Improved gcc 3 compatability (i.e. fixed some non-standard coding). - Increased use of more appropriate C++ libraries rather than C libraries. - Fixed a problem with overblocking banned file extensions and certain URLs. Most notably with hotmail attachments and also with pages that redirect. Sun 24th February 2002 - DansGuardian-2.2.6 - Improved handling under very heavy load. - Added the username to the information passed to the perl reporting script. Wed 1th February 2002 - DansGuardian-2.2.5-1 - Replaced included bannedphraselist file with the correct one. - Improved included bannedextensionlist. Sun 10th February 2002 - DansGuardian-2.2.5 - Fixed an issue which caused DG to stop responding under very heavy load. - Fixed a security issue that allowed file extension filtering to be bypassed. Mon 21st January 2002 - DansGuardian-2.2.4 - Fixed an issue with case sensitivity when filtering file extensions and URLs. Sat 12th January 2002 - DansGuardian-2.2.3 - Fixed an important bug that caused it to report the wrong name of the banned phrase found. This bug was introduced in version 2.2.2. Thu 27th Dec 2001 - DansGuardian-2.2.2 - Fixed small bug in xBSD start/stop script that failed to remove a temporary Unix Domain Socket file upon shutting it down. - Fixed small bug for when the accessdeniedaddress setting contains a port number; it could cause an endless loop. Tue 20th November 2001 - DansGuardian-2.2.1 - Content-Encoding: deflate - finally works. - Fixed bug in file extension checking which caused .com domains to be blocked. - Added Blanket IP Block feature which allows IP based URLs to be blocked. - Added forward dns lookup feature where it looks up the hostname for an IP based URL and checks for it in the bannedsitelist and bannedurllist. This closes a big security loop hole that allowed users to simply type the IP of a banned site instead to bypass the URL filtering. The forward dns lookup checks all the aliases for that IP as well. - Improved the URL filtering so that all variants of web addresses don't need to be listed, eg www.domain.com/blah/ and domain.com/blah/. Now just the highest level domain (domain.com/blah/) is needed. Sat 17th November 2001 - DansGuardian-2.2.0-7 - Improved handling of odd entries in stock squidGuard lists. - Fixed bug in URL extraction code to do with port numbers. - Fixed bug which caused it to not block some domains in the bannedsitelist under certain conditions. Fri 16th November 2001 - DansGuardian-2.2.0-6 - Fixed bug causing url and site matching problems when a port was specified in the URL. - Added blanket blocking feature to bannedsitelist so it can now block all sites except those in the exceptionsitelist. - Made username comparison case insensitive for people used to Windblows machines. - Fixed an issue with --prefix and the configure script. Mon 12th November 2001 - DansGuardian-2.2.0-5 - Added ident support for logging the username. - Fixed small issue with man page for Solaris as it does not support gzipped man pages. - Added regular expression URL support. - Fixed small bug that switched off filtering or caused problems if some of the list files had no entries. - Replaced search algorthm with one based on Quick Search. This gives an 87% speed increase in phrase matching. - Made regular expression code comply with the reportinglevel. - Added option so that DansGuardian can limit the listening to on one IP only. Sat 10th November 2001 - DansGuardian-2.1.2 - Fixed small bug that switched off filtering or caused problems if some of the list files had no entries. Wed 7th November 2001 - DansGuardian-2.1.1 - Improved ability to handle non-unix format text files for the config files and especally the site and URL lists. - Fixed bug which caused an incorrect extraction of the URL from the header of requests from some types of browser. (Should fix problems with exceptionsitelist, bannedurllist). - Fixed another bug in the exceptionsitelist code. - Fixed mistake in Solaris Makefile to now use non gzipped man page. - Added a '--prefix' option to the configure script. Fri 2nd November 2001 - DansGuardian-2.1.0-4 - Removed redundant code that caused a segfault due to being naughty. Thu 1st November 2001 - DansGuardian-2.1.0-3 - Fixed bug in certain list searching which caused some matches to fail incorrectly. This specifically affected exceptionuserlist and exceptioniplist. - Fixed bug where the main parent process lost count of number of child processes due to a race condition. - Fixed mistake in configure file which missed off an option in the e2guardian.conf file. - Slightly more colourful perl script. Mon 29th October 2001 - DansGuardian-2.1.0-2 - Fixed bug that caused problems with a blank domain or url list. Sun 28th October 2001 - DansGuardian-2.1.0-1 - Fixed bug in mimetype blocking. - Added url and domain blocking. A major feature. - Improved all list conf files (except bpl) so they now have little impact on speed if they are large. - Improved overall speed by reduced passing of objects and switching to pointers instead. - Now runs on Solaris 8. - Improved xBSD start/stop/restart scripts. - Improved Linux start/stop/restart scripts. Now not so RedHat dependant. - Fixed bug in configure script which gave an incorrect X-Forwarded-For format option in the conf file. Sat 13th October 2001 - DansGuardian-2.0.0-pre9 - e2guardian -v gives proper message now. - LICENSE file included now. - Missing steath-mode option in conf file reinstated. - Fixed mistake in logrotation script. - Added banned phrase exception feature. Mon 1st October 2001 - DansGuardian-2.0.0-pre8 - Fixed bug with failure to test for fork() failure condition. - Fixed bug with x-forwarded-for option not working. - Added self quitting and status reading from the command line (-q and -s). - Added option to not log accept()-type errors. - URL encoder improved to better handle reporting of phrases with symbols. - Now checks to see if its already running before it starts. - Now supports .pid file. - Fixed bug in OpenBSD start/stop script. I.e. it works now. - rlimit is now specifically set so that maxchildren should always work. - configure script now creates the sysv and bsdv scripts so the paths will be correct. - Improved documentation. - Fixed bug causing PICS options not to be read correctly. - Fixed problem with Makefile not creating whole directory tree when needed. - Re-added log feature missing that was in version 1.1.x. Thu 13th September 2001 - DansGuardian-2.0.0-pre7 - Now supports OpenBSD. - Fixed issue with UDS file name getting truncated. - Added better logging of problems including reasons for exiting. - Fixed bug in deflate compressed HTML - i.e. it works now. But its only a work-around. - Now does not exit as soon as a tcp/ip error occurs as these occur under heavy load in normal conditions. - Now monitors tcp/ip errors and exits if it goes over a threshold of continuous errors. - Vastly improved logging. It has a dedicated process for logging which keeps the file open as well as allowing it to scale to more users better. Hopefully the not logging bug has been fixed by this as well. - Fixed 2 bugs in the xBSD start script. - Some minor efficiency improvements. - Permissions on the configure files is correct now. - Timeouts in the SSL code have been increased to deal with slow sites. - Random exiting problems have been cured. - Problems under heavy load caused by incorrect handling of error conditions have been reduced or cured. - An improved bannedphraselist file is included. - A minor permission problem with logging has been fixed. Sat 1st September 2001 - DansGuardian-2.0.0-pre6 - Now supports FreeBSD. - Fixed bug stopping file extensions from being blocked. (was a missing '!') - Added a .Include feature to the bannedphraselist file so that different groups of additional phrases can be included. - Added better log file error detection. - DG now checks log file access before it starts. - RH RPM puts DG in runlevel 3+5 now. - Runlevel number changed from 89 to 92 to ensure its after squid. Mon 27th August 2001 - DansGuardian-2.0.0-pre5 - Made minor adjustments to improve tunneling code speed. - Fixed bug where a 47 was added to the hostname. - Fixed bug where RPM did not create and permission the /var/log/e2guardian directory. Sun 19th August 2001 - DansGuardian-2.0.0-pre4 - Fixed bug in phrase checking that caused it to overwrite the wrong memory. - Added web upload limiting and blocking feature. - Fixed minor bug with content type checking (typo). - Added process spawning capping to prevent DoS attacks killing the server. - Added stealth mode - logs, but does not block. - e2guardian.conf format modified slightly Sun 5th August 2001 - DansGuardian-2.0.0-pre3 - Fixed bug with compressed HTML support. - Speeded up DataBuffer class by a factor of 2. Sun 5th August 2001 - DansGuardian-2.0.0-pre2 - Added comand-line interface (-h for help on this) (includes version number). - Added content-encoding (gzip, deflate - not compress). - Improved the inteligent HTML scanning code. - Fixed not-reading-last-line-of-conf-file bug. - Issues with ^M in the log file and site exceptions fixed. Sun 22nd July 2001 - DansGuardian-2.0.0-pre1 - First beta test released. e2guardian-5.5.8r/INSTALL000077500000000000000000000311531477372360500150110ustar00rootroot00000000000000 NOTE: For v5 - please read notes/NEWIN_v5 first! ------------------------------------------------ NOTE: A Linux port is recommended - features are limited on FreeBSD. Other ports are not tested! HOW TO BUILD: ------------- The distribution uses GNU autotools for building. Run ./autogen.sh or autoreconf after downloading and unpacking to (re)generate the configure script and Makefile.in files. Most users should then follow the standard "./configure; make; make install" process common to UNIX packages. Please read the sections below for your OS for the most widely used configuration options. *** xBSD users, please ensure you have bash1 installed first *** *** Debian and Ubuntu users, please read the Debian section first. *** 1. Run the ./autogen.sh script to generate the configure script. If an error occurs in running autogen.sh or ./configure then make sure you have an up-to-date version of automake tools and then run autoreconf. 2. Run the configure (./configure --help) script with the help option to see the user selectable settings. Default settings are shown in []. 3. Run the configure (./configure) script with your options, if any. It is HIGHLY LIKELY that you will want to change some options. Please read down for suggested options for your platform. 4. "make"** or "gmake" will now build E2Guardian. 5. "make install" will create the directory structure and install all the files in the chosen paths. For a more efficient install, try "make install-strip" which will strip symbol information to the E2G binary smaller. 6. "make clean" will remove the now un-needed object files etc. 7. See the section later called ADDITIONAL SCRIPTS to add log rotation, optional CGI block page, startup scripts etc. ** You can often use "make -j 2" or "make -j 5" to compile E2G faster, especially on multiple CPU computers. As a general rule of thumb, use the number of CPUs/cores plus one. OPTIONS: -------- `configure' configures e2guardian to adapt to many kinds of systems. Usage: ./configure [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print `checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for `--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or `..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [/usr/local] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, `make install' will install all the files in `/usr/local/bin', `/usr/local/lib' etc. You can specify an installation prefix other than `/usr/local' using `--prefix', for instance `--prefix=$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --datadir=DIR read-only architecture-independent data [PREFIX/share] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --infodir=DIR info documentation [PREFIX/info] --mandir=DIR man documentation [PREFIX/man] Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] Optional Features: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-dependency-tracking speeds up one-time build --enable-dependency-tracking do not reject slow dependency extractors --enable-static-zlib[=no] Enable static linking of zlib --enable-pcre[=yes] Enable support for the PCRE library --enable-lfs[=yes] Enable large file support on 32 bit systems --enable-clamd[=no] Enable support for the ClamD content scanner --enable-avastd[=no] Enable support for the AvastD content scanner --enable-icap[=no] Enable support for ICAP AV server content scanner --enable-kavd[=no] Enable support for the Kaspersky AV daemon content scanner --enable-commandline[=no] Enable support for command-line content scanners --enable-dnsauth[=no] Enable support for the DNS auth plugin --enable-email[=no] Enable support for email reporting functionality Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-zlib[=NONE] non-standard search path for zlib library --with-debug_high[=on] switch on high level debug build mode --with-debug_low[=off] switch on low level debug build mode --with-proxyuser[=nobody] name of proxy user --with-proxygroup[=nobody] name of proxy group --with-piddir[=${localstatedir}/run] path for pid file --with-logdir[=${localstatedir}/log/${PACKAGE_NAME}] path for log files --with-libiconv[=NONE] Specify search path on a system which requires an external iconv library (only used in conjunction with NTLM auth plugin). --with-sysconfsubdir[=e2guardian] subdirectory under sysconfdir in which to place config files Some influential environment variables: CXX C++ compiler command CXXFLAGS C++ compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CC C compiler command CFLAGS C compiler flags CPP C preprocessor CXXCPP C++ preprocessor PKG_CONFIG path to pkg-config utility PCRE_CFLAGS C compiler flags for PCRE, overriding pkg-config PCRE_LIBS linker flags for PCRE, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. See ./configure --help for more details. MACOSX: - Note MACOS is not currently supported in V5 ------- *** Note in order to do any compiling in OS X you need to install *** * the Developer Tools - an additional pkg called BSDSDK.pkg. * A standard configure script that should work, provided you have installed MACOSX and the associated programs in their default locations. ./configure --localstatedir=/var \ --mandir=/usr/share/man/ \ --bindir=/usr/local/sbin/ You might consider changing the location of the log files to '--with-logdir=/usr/local/e2guardian/logs/' and use the provided log rotation script. Alternatively stick with the default (/var/log/e2guardian/) and read newsyslog(8). DEBIAN: ------- Build-Depends: base-files, base-passwd, bash, coreutils, dash, debianutils, diffutils, dpkg, e2fsprogs, findutils, grep, gzip, hostname, ncurses-base, libevent_pthreads, libevent-dev, ncurses-bin, perl-base, sed, login, sysvinit-utils, sysvinit, tar, bsdutils, mount, util-linux, libc6-dev , libc-dev, gcc , g++ , make, dpkg-dev , autotools-dev, debhelper , dh-autoreconf, dpatch , libclamav-dev , libpcre3-dev, zlib1g-dev, pkg-config, libssl-dev, libssl1.1 (can be also compiled with libssl1.0) Other packages related to E2guardian : adduser, perl, libbz2, libc6, libgcc1, libpcre3, libstdc++, libtommath0, zlib1g Other Packages suggests to E2guardian: squid, clamav, clamav-freshclam Autogen is needed ! Eg: ./autogen.sh && ./configure '--prefix=/usr' '--enable-clamd=yes' '--with-proxyuser=e2guardian' '--with-proxygroup=e2guardian' '--sysconfdir=/etc' '--localstatedir=/var' '--enable-icap=yes' '--enable-commandline=yes' '--enable-email=yes' '--enable-ntlm=yes' '--mandir=${prefix}/share/man' '--infodir=${prefix}/share/info' '--enable-pcre=yes' '--enable-sslmitm=yes' 'CPPFLAGS=-mno-sse2 -g -O2' && make NETBSD: ------- A standard configure script that should work, provided you have installed NetBSD and the associated programs in their default locations. ./configure --localstatedir=/var \ --prefix=/usr/pkg --sysconfdir=/usr/pkg/etc \ --bindir=/usr/pkg/sbin/ For NetBSD you might consider changing the location of the log files to '--with-logdir=/usr/local/e2guardian/logs/' and use the provided log rotation script. Alternatively stick with the default (/var/log/e2guardian/) and read newsyslog(8). Be sure that /usr/sbin/ is in your PATH before make install (for chown). FREEBSD: -------- A standard configure script that should work, provided you have installed FreeBSD and the associated programs in their default locations. ./configure --localstatedir=/var For FreeBSD and OpenBSD you might consider changing the location of the log files to '--with-logdir=/usr/local/e2guardian/logs/' and use the provided log rotation script. Alternatively stick with the default (/var/log/e2guardian/) and read newsyslog(8). OPENBSD: -------- A standard configure script that should work, provided you have installed OpenBSD and the associated programs in their default locations. ./configure --localstatedir=/var \ --bindir=/usr/sbin \ --mandir=/usr/share/man \ --sysconfdir=/etc HOW TO CONFIGURE: ----------------- Edit the e2guardian.conf, e2guardianf1.conf and other files. The files are commented well. ADDITIONAL SCRIPTS: ------------------- In /usr/local/share/e2guardian/scripts (or wherever you configured it to be) you will find at least the following files: bsd-init - a BSD style startup script to be put in your rc.d solaris-init - a Solaris style startup script to be put in your rc.d systemv-init - a Linux style startup script to be put in your rc.d e2guardian.service - a Linux style startup script to be put in your systemd directory e2guardian - a logrotate.d file logrotation - a sh script to rotate the logs ../e2guardian.pl - a cgi script for an access denied page The installation of startup scripts is deemed out of the scope of source code and is the playground of packagers and sysadmins. This is also true of log rotation scripts. You can choose to use the example logrotate.d file or you could crontab the logrotation script thus: crontab -e 59 23 * * sat /usr/local/share/e2guardian/scripts/logrotation (now save) I.e. 23:59 every Saturday. Or change to as pleases you. If you wish to use the cgi denied script rather than the template html denied page you will find it in: /usr/local/share/e2guardian/ (or wherever you configured it to be). This is not usually recommended but if you do want to do this copy it to your web servers' cgi-bin directory. E2Guardian. HOW TO RUN: ----------- You can start it by just running the binary. You can stop it by appending a ' -q' to the end, thus: 'e2guardian -q'. Or you can use the SysV(-like) script provided. HOW TO GET HELP: ---------------- http://e2guardian.org/ Here is the first place to start for getting support. There is a mailing list available for those that do not find the answers to their questions from the url above. The mailing list can be found at the url. Please DO NOT email the authors for support as you will be just directed at the mailing list. e2guardian-5.5.8r/LICENSE000077500000000000000000000366241477372360500147750ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS In addition, as a special exception, the copyright holders of e2guardian, give permission to link the code of this release of E2Guardian with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "OpenSSL". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. e2guardian-5.5.8r/Makefile.am000077500000000000000000000001021477372360500160020ustar00rootroot00000000000000SUBDIRS= . doc data configs src EXTRA_DIST = autogen.sh UPGRADING e2guardian-5.5.8r/NEWS000077500000000000000000000001171477372360500144530ustar00rootroot00000000000000For news go to http://e2guardian.org/ This file is required by the autotools. e2guardian-5.5.8r/README.md000066400000000000000000000057641477372360500152450ustar00rootroot00000000000000# [E2Guardian](http://e2guardian.org) This is the stable v5.5 version - v5.5.8r Development of v5.5 is now frozen - just bug-fixes will be applied in v5.5 branch. Note: Some configuration files in this version are not fully backward compatible with v5.4 configuration files. Please read notes/NEWIN_v5 before installing. If upgrading from an earlier version please read notes/Upgrading_to_V5.4 **For copyright go to: http://e2guardian.org** e2guardian is a content filtering proxy that can work in explicit and transparent proxy mode or as a ICAP server mode. More information can be found in the "notes" subdirectory of the distribution, and the comments in the configuration and list files themselves. Read the INSTALL for installation instructions. ## Contributing Github: https://github.com/e2guardian * Bugfixes primarily occurs in the version branch ## Bugs and Feature Requests Github: https://github.com/e2guardian/e2guardian/issues Codacy Badge: [![Codacy Badge](https://api.codacy.com/project/badge/Grade/92742338bce249c6a52739d0343dabfa)](https://www.codacy.com/app/numsys/e2guardian?utm_source=github.com&utm_medium=referral&utm_content=e2guardian/e2guardian&utm_campaign=Badge_Grade) ## Community * Join the [Community Forum](https://groups.google.com/forum/#!forum/e2guardian). ## Wiki * Helpful information can be found in the [Wiki](https://github.com/e2guardian/e2guardian/wiki). ## Packages/Docker Packages for Debian/Ubuntu: https://e2guardian.numsys.eu Official Images on Docker Hub: https://hub.docker.com/r/fredbcode/e2guardian Container image for Squid proxy server, Docker Hub: https://hub.docker.com/r/fredbcode/squid (no support here !) ## License Copyright 2022, [Frederic Bourgeois](http://numsys.eu), [E2BN Protex](http://protex.e2bn.org) Ltd and others. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 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 In addition, as a special exception, the copyright holders of this work, give permission to link the code of its release of e2guardian with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "OpenSSL". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. e2guardian-5.5.8r/autogen.sh000077500000000000000000000003021477372360500157460ustar00rootroot00000000000000#! /bin/sh set -x cp README.md README # remove previous generation rm -f compile config.guess config.sub missing depcomp aclocal -I m4 && autoheader && automake --add-missing --copy && autoconf e2guardian-5.5.8r/configs/000077500000000000000000000000001477372360500154025ustar00rootroot00000000000000e2guardian-5.5.8r/configs/Makefile.am000077500000000000000000000012311477372360500174360ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in \ e2guardian.conf \ e2guardianf1.conf \ examplef1.story SUBDIRS = lists downloadmanagers authplugins . if NEED_CSCONFIGS SUBDIRS += contentscanners endif FLISTS = e2guardian.conf e2guardianf1.conf examplef1.story common.story \ site.story preauth.story EXTRA_DIST = e2guardian.conf.in e2guardianf1.conf.in examplef1.story.in install-data-local: $(mkinstalldirs) $(DESTDIR)$(E2CONFDIR) && \ for l in $(FLISTS) ; do \ echo "$(INSTALL_DATA) $$l $(DESTDIR)$(E2CONFDIR)/$$l"; \ $(INSTALL_DATA) $$l $(DESTDIR)$(E2CONFDIR)/$$l; \ done uninstall-local: for l in $(FLISTS) ; do \ rm -f $(DESTDIR)$(E2CONFDIR)/$$l ; \ done e2guardian-5.5.8r/configs/authplugins/000077500000000000000000000000001477372360500177455ustar00rootroot00000000000000e2guardian-5.5.8r/configs/authplugins/BearerBasic.conf000077500000000000000000000012751477372360500227660ustar00rootroot00000000000000# Bearer-Basic auth plugin # Identifies Bearer tokens in "Proxy-Authorization: Basic" headers # and returns 407 header if token is not present or is invalid plugname = 'bearer-basic' # ports - restrict this plugin to these ports # - default is blank = no restriction - applies to all ports ports = 8089 bearersecret = testsecret realm = "Protex test" # Default group settings for this plug-in # If these are set group determination will always succeed # and auth plug-in scan will stop # even if the user is not found in group list(s) # These settings override the global defaultfiltergroup # settings for this plug-in only. #defaultfiltergroup = 1 # This applies to explict proxy requests e2guardian-5.5.8r/configs/authplugins/Makefile.am000077500000000000000000000011441477372360500220040ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in E2DATADIR = $(E2CONFDIR)/authplugins SUBDIRS = . FLISTS = ident.conf ip.conf \ port.conf pf-basic.conf \ BearerBasic.conf if PRT_DNSAUTH FLISTS += dnsauth.conf endif EXTRA_DIST = ident.conf ip.conf \ port.conf dnsauth.conf pf-basic.conf \ BearerBasic.conf install-data-local: $(mkinstalldirs) $(DESTDIR)$(E2DATADIR) && \ for l in $(FLISTS) ; do \ echo "$(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l"; \ $(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l; \ done uninstall-local: for l in $(FLISTS) ; do \ rm -f $(DESTDIR)$(E2DATADIR)/$$l ; \ done e2guardian-5.5.8r/configs/authplugins/dnsauth.conf000066400000000000000000000011631477372360500222630ustar00rootroot00000000000000# IP/DNS-based auth plugin # # Obtains user and group from domain entry maintained by separate authentication# program. plugname = 'dnsauth' # ports - restrict this plugin to these portsthis applies too i # - default is blank = no restriction - applies to all ports #ports = 8081,8082 # Base DNS domain #basedomain = "my.privatedomain" # Authentication URL #authurl = "http://192.168.1.3/auth/login/login.pl?url" # Prefix for auth URLs #prefix_auth = "http://192.168.1.3/auth/" # Redirect to auth (i.e. log-in) # yes - redirects to authurl to login # no - drops through to next auth plugin redirect_to_auth = "yes" e2guardian-5.5.8r/configs/authplugins/ident.conf000077500000000000000000000016031477372360500217220ustar00rootroot00000000000000# Ident auth plugin # Identifies users via IDENT servers running on client machines plugname = 'ident' story_function = 'auth_ident' # This defines the storybaord function in pre-auth.story # which determines the filter group used. # ports - restrict this plugin to these ports # - default is blank = no restriction - applies to all ports #ports = 8081,8082 # Default group settings for this plug-in # If these are set group determination will always succeed # and auth plug-in scan will stop # even if the user is not found in group list(s) # These settings override the global defaultfiltergroup and # defaulttransparentfiltergroup settings for this plug-in only. #defaultfiltergroup = 1 # This applies to explict proxy and icap requests #defaulttransparentfiltergroup = 2 # This appies to transparent requests (http & https) # Note if not defined does NOT default to defaultfiltergroup e2guardian-5.5.8r/configs/authplugins/ip.conf000066400000000000000000000021141477372360500212220ustar00rootroot00000000000000# IP-based auth plugin # # Maps client IPs to filter groups. # If "usexforwardedfor" is enabled, grabs the IP from the X-Forwarded-For # header, if available. plugname = 'ip' # ipgroups file - is defined in e2guardian.conf from v5.4 # List file assigning IP addresses, subnets and ranges to filter groups story_function = 'auth_ip' # ports - restrict this plugin to these ports # - default is blank = no restriction - applies to all ports #ports = 8081,8082 # This defines the storybaord function in pre-auth.story # which determines the filter group used. # Default group settings for this plug-in # If these are set group determination will always succeed # and auth plug-in scan will stop # even if the user is not found in group list(s) # These settings override the global defaultfiltergroup and # defaulttransparentfiltergroup settings for this plug-in only. #defaultfiltergroup = 1 # This applies to explict proxy and icap requests #defaulttransparentfiltergroup = 2 # This appies to transparent requests (http & https) # Note if not defined does NOT default to defaultfiltergroup e2guardian-5.5.8r/configs/authplugins/pf-basic.conf000077500000000000000000000017251477372360500223100ustar00rootroot00000000000000# Proxy-Basic auth plugin # Identifies usernames in "Proxy-Authorization: Basic" headers; # relies upon the upstream proxy (squid) to perform the actual password check. plugname = 'pf-basic' story_function = 'auth_pf_basic' # This defines the storybaord function in pre-auth.story # which determines the filter group used. # ports - restrict this plugin to these ports # - default is blank = no restriction - applies to all ports #ports = 8081,8082 # Default group settings for this plug-in # If these are set group determination will always succeed # and auth plug-in scan will stop # even if the user is not found in group list(s) # These settings override the global defaultfiltergroup and # defaulttransparentfiltergroup settings for this plug-in only. #defaultfiltergroup = 1 # This applies to explict proxy requests #defaulttransparentfiltergroup = 2 # This appies to transparent requests (http & https) # Note if not defined does NOT default to defaultfiltergroup e2guardian-5.5.8r/configs/authplugins/port.conf000066400000000000000000000015531477372360500216040ustar00rootroot00000000000000# IP-Port-based auth plugin # # Maps IP Ports to filter groups. plugname = 'port' story_function = auth_port # This defines the storybaord function in pre-auth.story # which determines the filter group used. # ports - restrict this plugin to these ports # - default is blank = no restriction - applies to all ports #ports = 8081,8082 # Default group settings for this plug-in # If these are set group determination will always succeed # and auth plug-in scan will stop # even if the user is not found in group list(s) # These settings override the global defaultfiltergroup and # defaulttransparentfiltergroup settings for this plug-in only. #defaultfiltergroup = 1 # This applies to explict proxy and icap requests #defaulttransparentfiltergroup = 2 # This appies to transparent requests (http & https) # Note if not defined does NOT default to defaultfiltergroup e2guardian-5.5.8r/configs/authplugins/proxy-header.conf000066400000000000000000000024521477372360500232260ustar00rootroot00000000000000# Proxy-header auth plugin # FredB August 2016 # Identifies users with header; # relies upon the upstream proxy. # Eg: in groups file # Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0=filter3 # here: # header = 'user-agent' # Firefox 47 + windows is added to group3 without any kind of authentification (! not related with your proxy configuration/identification !) # THIS IS REALLY INSECURE This can be easily compromised by spoofing the Origin header # low case header = '' plugname = 'proxy-header' story_function = auth_proxy_header # This defines the storybaord function in pre-auth.story # which determines the filter group used. # ports - restrict this plugin to these ports # - default is blank = no restriction - applies to all ports #ports = 8081,8082 # Default group settings for this plug-in # If these are set group determination will always succeed # and auth plug-in scan will stop # even if the user is not found in group list(s) # These settings override the global defaultfiltergroup and # defaulttransparentfiltergroup settings for this plug-in only. #defaultfiltergroup = 1 # This applies to explict proxy and icap requests #defaulttransparentfiltergroup = 2 # This appies to transparent requests (http & https) # Note if not defined does NOT default to defaultfiltergroup e2guardian-5.5.8r/configs/common.story000066400000000000000000000267041477372360500200050ustar00rootroot00000000000000# Storyboard library file # For ease of upgrade DO NOT CHANGE THIS library file # Make your function changes by using user 'hook' functions # or by overriding functions # in the site.story file - for site wide changes. # # and in filtergroup specific story file - see examplef1.story # # This library is built to largely duplicate the logic in V4 # # Many e2guardian[f1].conf flags are replaced by overiding # library functions - see site.story and examplef1.story # # Simple functions are defined which control the logic flow and the # lists that are used. See notes/Storyboard for details. # # The entry point in v5 for standard filtering is 'checkrequest' # # Entry function called by standard proxy module to check http, # https proxy and transparent http requests # It is also called within a MITM session function(checkrequest) if(true) returnif hook_checkrequest if(viruscheckset) checknoscanlists if(bypassallowset) checknobypasslists if(exceptionset) return true #if(fullurlin,searchterms) setsearchterm if(true) is_search_term ifnot(greyset) returnif localcheckrequest if(connect) return sslrequestcheck ifnot(greyset) returnif exceptioncheck ifnot(semiexceptionset) greycheck ifnot(greyset) returnif bannedcheck if(semiexceptionset) return setexception if(fullurlin, change) setmodurl if(true) returnif embeddedcheck if(headerin,headermods) setmodheader if(fullurlin, addheader) setaddheader if(searchin,override) setexception if(returnset) return setdone if(searchin,banned) return setblock if(fullurlin,redirect) return setredirect if(true) setgrey # Entry function called by proxy module to check http response function(checkresponse) if(true) returnif hook_checkresponse if(exceptionset) return false if(responseheaderin,responseheadermods) setmodheader if(viruscheckset) checknoscantypes if(urlin,exceptionfile) return false if(true) return checkfiletype # Entry function called by THTTPS module to check https request function(thttps-checkrequest) if(true) returnif hook_thttps-checkrequest if(true) thttps_automitm if(true) returnif checktimesblocked if(true) returnif localsslrequestcheck if(true) returnif sslrequestcheck ifnot(hassniset) checksni # Entry function called by ICAP module to check reqmod function(icap-checkrequest) if(true) returnif hook_icap-checkrequest #unless blocked or redirect or connect - leave logging for RESPMOD if(connect) return icapsslrequestcheck ifnot(greyset) icap-checkrequest2 if(redirectset) return true ifnot(blockset) setnolog function(icap-checkrequest2) if(viruscheckset) checknoscanlists if(bypassallowset) checknobypasslists if(exceptionset) return true #if(fullurlin,searchterms) setsearchterm if(true) is_search_term ifnot(greyset) returnif localcheckrequest ifnot(greyset) returnif exceptioncheck ifnot(greyset) greycheck ifnot(greyset) returnif bannedcheck if(fullurlin, change) setmodurl if(true) returnif embeddedcheck if(headerin,headermods) setmodheader if(fullurlin, addheader) setaddheader if(searchin,override) return setgrey if(searchin,banned) return setblock if(true) setgrey # Entry function called by ICAP module to check respmod function(icap-checkresponse) if(true) returnif hook_icap-checkresponse if(viruscheckset) checknoscanlists if(true) return checkresponse # Checks embeded urls # returns true if blocked, otherwise false function(embeddedcheck) if(embeddedin, localexception) return false if(embeddedin, localgrey) return false if(embeddedin, localbanned) return setblock if(embeddedin, exception) return false if(embeddedin, grey) return false if(embeddedin, banned) return setblock # Local checks # returns true if matches local exception or banned function(localcheckrequest) if(true) returnif hook_localcheckrequest if(connect) return localsslrequestcheck if(true) checktimesblocked if(returnset) return setblock ifnot(greyset) returnif localexceptioncheck ifnot(semiexceptionset) returnif localgreycheck ifnot(greyset) returnif localbannedcheck if(semiexceptionset) return setexception if(searchin,localbanned) return setblock # Local SSL checks # returns true if matches local exception function(localsslrequestcheck) if(true) returnif hook_localsslrequestcheck if(true) returnif sslchecktimesblocked if(sitein, localsemiexception) setsemiexception if(semiexceptionset) returnif sslcheckmitm if(sitein, localexception) return setexception if(sitein, localgreyssl) returnif sslcheckmitm if(sitein, localgrey) returnif sslcheckmitm if(sitein, localbanned) true ifnot(returnset) return false if(true) returnif sslcheckmitm if(true) return setblock # SSL site replace (used instead of dns kulge) # returns true on match and successful replacement function(sslreplace) if(fullurlin,sslreplace) return setconnectsite if(true) return false function(sslchecktimesblocked) if(true) checktimesblocked ifnot(returnset) return false if(true) returnif sslcheckmitm if(true) return setblock # Local grey check # returns true on match function(localgreycheck) if(urlin, localgrey) return setgrey # Local banned check # returns true on match function(localbannedcheck) if(urlin, localbanned) return setblock # Local exception check # returns true on match function(localexceptioncheck) if(sitein, localsemiexception) setsemiexception if(returnset) return false if(urlin, localexception) return setexception # Exception check # returns true on match function(exceptioncheck) if(sitein, semiexception) setsemiexception if(returnset) return false if(urlin, exception) return setexception if(refererin,refererexception) return setexception if(headerin, exceptionheader) return setexception if(useragentin, exceptionuseragent) return setexception ifnot(urlin,embededreferer) return false if(embeddedin,refererexception) return setexception # SSL Exception check # returns true on match function(sslexceptioncheck) if(sitein, semiexception) setsemiexception if(semiexceptionset) returnif sslcheckmitm if(semiexceptionset) unsetsemiexception if(sitein, exception) return setexception if(headerin, exceptionheader) return setexception if(useragentin, exceptionuseragent) return setexception if(true) return false # Greylist check # returns true on match function(greycheck) if(urlin, grey) return setgrey # Banned list check # returns true on match function(bannedcheck) if(true) returnif checkblanketblock if(urlin, banned) return setblock ifnot(urlin,exceptionfile) returnif checkurlextension if(useragentin, banneduseragent) return setblock if(headerin, bannedheader) return setblock # Checks url for banned extensions # returns true on match function(checkurlextension) if(urlin,bannedextension) return setblock # Local SSL list(s) check # returns true on match function(localsslcheckrequest) if(true) returnif hook_localsslcheckrequest if(sitein, localsemiexception) setsemiexception if(semiexceptionset) returnif sslcheckmitm if(semiexceptionset) unsetsemiexception if(sitein, localexception) return setexception #if(sitein, localbanned) return setblock # Check whether to go MITM # returns true if yes, false if no function(sslcheckmitm) # use next line to have general MITM if(true) return sslcheckmitmgeneral # use next line instead of last to limit MITM to greylist #if(true) return sslcheckmitmgreyonly # Always go MITM # returns true if yes, false if no function(sslcheckmitmgeneral) if(true) setgomitm ifnot(returnset) return false if(sitein, nocheckcert) setnocheckcert if(true) sslreplace if(true) return true # Only go MITM when in greyssl list # returns true if yes, false if no function(sslcheckmitmgreyonly) if(sitein, greyssl) setgomitm ifnot(returnset) return false if(sitein, nocheckcert) setnocheckcert if(true) sslreplace if(true) return true # SSL request check # returns true if exception or gomitm function(sslrequestcheck) if(true) returnif hook_sslrequestcheck if(true) returnif sslexceptioncheck if(true) returnif sslcheckmitm if(sitein, banned) return setblock if(true) sslreplace ifnot(returnset) returnif sslcheckblanketblock if(true) setgrey function(checknoscanlists) if(urlin,exceptionvirus) unsetviruscheck function(checknoscantypes) if(mimein,exceptionvirus) return unsetviruscheck if(extensionin,exceptionvirus) return unsetviruscheck function(checknobypasslists) if(urlin,bannedbypass) return unsetbypassallow # ICAP SSL request check # returns true if exception function(icapsslrequestcheck) if(true) returnif hook_icapsslrequestcheck if(true) returnif icapsquidbump if(true) returnif sslexceptioncheck # Squid ignores sslreplace and throws error so comment out for now #if(true) sslreplace if(sitein, banned) return setblock # Blanket block # returns true if to block # Placeholder function - overide in fn.story function(checkblanketblock) # SSL Blanket block # returns true if to block # Placeholder function - overide in fn.story function(sslcheckblanketblock) # ICAP Squid bump # override in site.story to return true if bump is being deployed on squid function(icapsquidbump) # File type blocking # returns true if blocking # Default uses banned lists and allows all others # Overide in site.story or fn.story if only types in exception file type lists # are to be allowed function(checkfiletype) if(mimein, bannedmime) return setblock if(extensionin, bannedextension) return setblock # SNI checking - determines default action when no SNI or TSL is present on a # THTTPS connection # Default blocks all requests with TLS or SNI absent that are not ip site exceptions function(checksni) ifnot(tls,,511) return setblock ifnot(hassniset,,512) return setblock # automitm on transparent https # default enables on transparent https - to allow block/status pages to browsers # override this is fn.story if this causes problems with apps function(thttps_automitm) if(true) setautomitm # Timed global block # returns true if to block # Placeholder function - overide in fn.story function(checktimesblocked) # Entry function to check if to log # returns true if log entry is to be made # This can be overriden in site.story (or fn.story) to log all for testing. function(checklogging) if(true) nologcheck ifnot(returnset) return true function(nologcheck) if(extensionin,nolog) return setnolog if(urlin,nolog) return setnolog # Only set alerts for blocked urls ifnot(blockset) return false if(categoryin,alert) setalert function(is_search_term) if(urlin,searchtermexceptions) return false if(fullurlin,searchterms) setsearchterm if(returnset) return true # Default entry functions for download managers # Returns false is no match # or true for match to attivate download manager #trickleDM experimental function(use_trickle_dm) # not yet functional so just return false for now # to test comment out next line and uncomment the remaining lines in function if(true) return false #ifnot(useragentin,trickleuseragent) return false #if(mimein,trickleallow) return true #if(extensionin,trickleallow) return true #fancyDM function(use_fancy_dm) # not yet functional so just return false for now if(true) return false #ifnot(useragentin,fancyuseragent) return false #if(mimein,fancyallow) return true #if(extensionin,fancyallow) return true # Placeholder functions for user hooks # Allow user code to be actioned at the start of key functions # If hook function returns false (default) processing continues # in parent function # if hook function returns true parent function exits with true # function(hook_checkrequest) function(hook_checkresponse) function(hook_thttps-checkrequest) function(hook_icap-checkrequest) function(hook_icap-checkresponse) function(hook_localcheckrequest) function(hook_localsslrequestcheck) function(hook_localsslcheckrequest) function(hook_sslrequestcheck) function(hook_icapsslrequestcheck) e2guardian-5.5.8r/configs/contentscanners/000077500000000000000000000000001477372360500206115ustar00rootroot00000000000000e2guardian-5.5.8r/configs/contentscanners/Makefile.am000077500000000000000000000015331477372360500226520ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in \ clamdscan.conf icapscan.conf \ kavdscan.conf commandlinescan.conf E2DATADIR = $(E2CONFDIR)/contentscanners SUBDIRS = . FLISTS = if ENABLE_CLAMD FLISTS += clamdscan.conf endif if ENABLE_AVASTD FLISTS += avastdscan.conf endif if ENABLE_ICAP FLISTS += icapscan.conf endif if ENABLE_KAVD FLISTS += kavdscan.conf endif if ENABLE_COMMANDLINE FLISTS += commandlinescan.conf endif EXTRA_DIST = clamdscan.conf.in avastdscan.conf.in icapscan.conf.in \ kavdscan.conf.in commandlinescan.conf.in install-data-local: $(mkinstalldirs) $(DESTDIR)$(E2DATADIR) && \ for l in $(FLISTS) ; do \ echo "$(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l"; \ $(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l; \ done uninstall-local: for l in $(FLISTS) ; do \ rm -f $(DESTDIR)$(E2DATADIR)/$$l ; \ done e2guardian-5.5.8r/configs/contentscanners/avastdscan.conf.in000077500000000000000000000005531477372360500242220ustar00rootroot00000000000000plugname = 'avastdscan' # edit this to match the location of your AvastD UNIX domain socket #avastdudsfile = '/var/run/avast4/local.sock' # edit this to block unscannable files (e.g. encrypted archives) #archivewarn = off #Specify the version of avast protocol. It Must be 'avast4' or 'avast2014' #default is avast4 for compatibility avastprotocol = 'avast4' e2guardian-5.5.8r/configs/contentscanners/clamdscan.conf.in000077500000000000000000000011231477372360500240120ustar00rootroot00000000000000plugname = 'clamdscan' # edit this to match the location of your ClamD UNIX domain socket #clamdudsfile = '/var/run/clamav/clamd.sock' # If this string is set, the text it contains shall be removed from the # beginning of filenames when passing them to ClamD. # Use it to - for example - support a ClamD running inside a chroot jail: # if DG's filecachedir is set to "/var/clamdchroot/downloads/" and pathprefix # is set to "/var/clamdchroot", then file names given to ClamD will be of the # form "/downloads/tf*" instead of "/var/clamdchroot/downloads/tf*". #pathprefix = '/var/clamdchroot' e2guardian-5.5.8r/configs/contentscanners/commandlinescan.conf.in000077500000000000000000000023401477372360500252220ustar00rootroot00000000000000plugname = 'commandlinescan' # Program to run & initial arguments - filename for scanning will be appended #progname = /path/to/scanner # At least one of the following three options must be defined! # They are checked in the following order, with the first match determining # the scan result: # virusregexp - regular expression for extracting virus names from # the scanner's output # cleancodes - program return code(s), as a comma-separated list, for # uninfected files # infectedcodes - program return code(s), as a comma-separated list, for # infected files #virusregexp = (someregexp) # Which submatch of the above contains the virus name? (0 = all matched text) #submatch = 1 # cleancodes = 0 # infectedcodes = 1,2,3 # Default result when none of the other options triggers a match # Valid values are "infected" and "clean" #defaultresult = infected # # Example configuration for clamdscan # ## Path to binary #progname = '/usr/bin/clamdscan' ## Program returns 0 for clean files (for an easy out) #cleancodes = 0 ## Regular expression for virus names #virusregexp = : ([ -/a-zA-Z0-9\.]+) FOUND #submatch = 1 ## Default scan result when the above don't match #defaultresult = infected e2guardian-5.5.8r/configs/contentscanners/icapscan.conf.in000077500000000000000000000002171477372360500236510ustar00rootroot00000000000000plugname = 'icapscan' # ICAP URL # Use hostname rather than IP address # Always specify the port # icapurl = 'icap://icapserver:1344/avscan' e2guardian-5.5.8r/configs/contentscanners/kavdscan.conf.in000077500000000000000000000011011477372360500236530ustar00rootroot00000000000000plugname = 'kavdscan' # edit this to match the location of your KAVD UNIX domain socket #kavdudsfile = '/var/run/aveserver' # If this string is set, the text it contains shall be removed from the # beginning of filenames when passing them to KAVD. # Use it to - for example - support a KAVD running inside a chroot jail: # if DG's filecachedir is set to "/var/kavdchroot/downloads/" and pathprefix # is set to "/var/kavdchroot", then file names given to KAVD will be of the # form "/downloads/tf*" instead of "/var/kavdchroot/downloads/tf*". #pathprefix = '/var/kavdchroot' e2guardian-5.5.8r/configs/downloadmanagers/000077500000000000000000000000001477372360500207275ustar00rootroot00000000000000e2guardian-5.5.8r/configs/downloadmanagers/Makefile.am000077500000000000000000000007251477372360500227720ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in \ default.conf E2DATADIR = $(E2CONFDIR)/downloadmanagers SUBDIRS = . FLISTS = default.conf EXTRA_DIST = default.conf.in install-data-local: $(mkinstalldirs) $(DESTDIR)$(E2DATADIR) && \ for l in $(FLISTS) ; do \ echo "$(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l"; \ $(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l; \ done uninstall-local: for l in $(FLISTS) ; do \ rm -f $(DESTDIR)$(E2DATADIR)/$$l ; \ done e2guardian-5.5.8r/configs/downloadmanagers/default.conf.in000077500000000000000000000010331477372360500236270ustar00rootroot00000000000000# The default download manager. # This is the safest option for unknown user-agents and content types, and # hence a good one to include last. # Which plugin should be loaded? plugname = 'default' # Regular expression for matching user agents # When not defined, matches all agents. #useragentregexp = '.*' # Lists of mime types and extensions to manage # When not defined, matches everything. # These can be enabled separately; when both enabled, # a request may match either list. #managedmimetypelist = '' #managedextensionlist = '' e2guardian-5.5.8r/configs/e2guardian.conf.in000066400000000000000000001375061477372360500207130ustar00rootroot00000000000000# e2guardian config file for version @PACKAGE_VERSION@ #NOTE This file (and any .Include<> files) are only read at start-up # # but the lists defined in this file are re-read on reload or gentle restart # as is any rooms directory files. ### Config is now split into sections as follows ### ### QUICK_START - Items to check to get you started ### NAMES_PATHS - Names & Path settings ### NETWORK - Network settings ### MITM - SSL MITM settings ### ICAP_SERVICE - ICAP server mode settings ### TRANSPARENT - Transparent proxy settings ### AUTH - Authentication (user and group assignment) ### settings and lists ### ACCESS_LOG - Access log settings ### MONITORING - Monitoring settings ### URL_FILTERING - URL filtering settings ### LIST_SETTINGS - Settings on how lists are handled ### AV_SCANNERS - AV scanner settings and lists ### HEADER - HTTP Header handling ### BLOCK_PAGE - Block Page formats and handling ### DOWNLOAD_MANAGER - Download manager settings ### PHRASES - Content phrase settings ### TUNING - Tuning parameters ### DEBUG - Debug settings ### PROCESS - e2guardian process settings ### OBSOLETE - Obsolete settings ### INFO - Info on new features etc ### ### QUICK_START section ### ### e2guardian will work as a normal http/s proxy server ### listening on port 8080 ### without you making any changes to this file. ### ### This section contains settings that you may want to ### change, e.g language, dockermode, to set ICAP mode or enable SSL MITM ### support ### # language to use from languagedir. language = 'ukenglish' #.Define LISTDIR <@E2CONFDIR@/lists/common> # LISTDIR 'variable' definition # This works similarly to a shell environment variable # The text between <> will replace occurrences of __LISTDIR__ in .conf and # list files. # See INFO section for more details # default LISTDIR value for e2guardian.conf is E2CONFDIR/lists/commom #dockermode = off # # Container mode # the process will not fork into the background AND will log in stdout # In this mode systemd service is disabled ! # # Also sets info, error and warning messages to stderr # Default: off # # This option is retained for backward compatibility # Setting this 'on' is the same as making the following settings: # # nodaemon = on # set_info = 'stderr' # set_error = 'stderr' # set_warning = 'stderr' # set_accesslog = 'stdout' # loop prevention # # For loop prevention purposes list all IPs e2g can be reached on # Include all e2g host server IPs and any VIP used when when in an array. # If squid in front then add ip of squid server and squid port in extracheckports # Specify each IP on an individual checkip line or multiple IP on a single line separated by ':' # #checkip = 127.0.0.1 #checkip = ip_of_server #checkip = 2nd ip of server #checkip = VIP of server # # Defaults: Not set - only loop prevention for 127.0.0.1 requests # #extracheckports = 3128 # # by default e2g will loop protect for all ports defined in filterports # If you are using squid in front or other device which re-assigns ports # then add the user-facing port(s) to extracheckports # Specify each port on an individual extracheckports line or multiple ports on a single line separated by ':' #transparenthttpsport = 8443 # #port for transparent https #NOTE: To make work firewall will need to redirect tcp port 443 on routed # packets to this port and ssl must be enabled with enablessl = on # default 0 - i.e. disabled #icapport = 1344 # #port for ICAP server #if defined enables icap server mode # default is 0 - i.e. disabled #proxyip = 127.0.0.1 # # the ip of upstream proxy - optional - if blank e2g will go direct to sites. # default is "" i.e. no proxy #proxyport = 3128 # # the port e2guardian connects to any upstream proxy on # default 3128 # Is ignored if proxyip is blank. #filtergroups = 1 # # filtergroups sets the number of filter groups. # A filter group is a set of content filtering options you can apply to a # group of users. # The value must be 1 or more. # e2guardian will automatically look for e2guardianfN.conf where N is the filter # group. # default 1 #defaultfiltergroup = 1; # # default filtergroup for standard (explicit proxy) mode # optional defaults to 1 #enablessl = on # # Enable SSL support # This must be present to enable MITM and Cert checking # If on you must also check the MITM section. # default is off ### ### END of QUICK_START section ### NAMES_PATHS section ### # servername = 'my_name" # # Default is to use the system name of the host in logs #daemonuser = '@E2PROXYUSER@' #daemongroup = '@E2PROXYGROUP@' # # Daemon runas user and group # This is the user that e2guardian runs as. Normally the user/group nobody. # Uncomment to use. Defaults to the user/group set at compile time. # # Temp files created during virus scanning are given owner and group read # permmision so, if you have clamdscan plugin enabled, # the two processes must run with either the same group or user ID. languagedir = '@E2DATADIR@/languages' # # The HTML templates within this dir are only used when reportinglevel # is set to 3. When used, e2guardian will display the HTML file instead of # using the perl cgi script. This option is faster, cleaner # and easier to customise the access denied page. # The language file is used no matter what setting however. # #preauthstoryboard = '@E2CONFDIR@/preauth.story' # # default '@E2CONFDIR@/preauth.story' # perroomdirectory = '__LISTDIR__/../rooms/' # # Per-Room definition directory # A directory containing text files containing the room's name followed by IPs or ranges # and optionaly site and url lists # Think of it as bannediplist and/or exceptions on crack # Output of Error, Warning, Info messages # set_error = 'stderr' # set_info = 'stdout' # set_warning = 'stderr' # # change error, info and warning message output from defaults # defaults are: # set_error = 'syslog:LOG_ERR' # set_info = 'syslog:LOG_INFO' # set_warning = 'syslog:LOG_WARNING' # ### ### END of NAMES_PATHS section ### NETWORK section ### #filterip = # # the IP that e2guardian listens on. If left blank e2guardian will # listen on all IPs. That would include all NICs, loopback, modem, etc. # Normally you would have your firewall protecting this, but if you want # you can limit it to a certain IP. To bind to multiple interfaces, # specify each IP on an individual filterip line or specify multiple ips # on a single line separated by ':' # default "" - listen on all IPs #filterports = 8080 #filterports = 8080:8081:8082 # # The port(s) that e2guardian listens to for http proxy traffic. # Specify one line per port used for standard explict proxy or specify multiple ports # on a single line separated by ':' # These ports can also be used for redirected transparent HTTP # Default is to listen on 8080 for proxy traffic #tlsfilterports = 8090 #tlsfilterports = 8090:9091 # # The port(s) that e2guardian listens to for TLS (secure) proxy traffic. # Specify one line per port used for secure explict proxy or # specify multiple ports on a single line separated by ':' # Note that using TLS proxy consumes double the number of file descriptors (4) # and httpsworker threads (2) per session # The MITM root CA must also be installed on the client even when not using # MITM. # Default is none #tlsproxycn = 192.168.1.25 #tlsproxycn = mye2g.anydomain #tlsproxycn = 192.168.1.25:mye2g.anydomain:mye2g.internal.domain # v5.5.6 onwards # # The common name (CN) to be used to generate the server certificate for # TLS proxy. Can be IP or DNS name but must match entry used in clients # proxy settings. # default is system name. # # From v5.5.6 multiple multiple DNS names and/or IP can be can be defined separated by ':' ### ### END of NETWORK section ### MITM section ### # Check these settings if enablessl = on # For instructions on how to set this up # see notes/ssl_mitm #sslcertificatepath = '' # #SSL certificate checking path #Path to CA certificates used to validate the certificates of https sites. # if left blank openssl default ca certificate bundle will be used #Leave as default unless you want to load non-default cert bundle #SSL man in the middle cacertificatepath = '@E2CONFDIR@/private/ca.pem' # #CA certificate path #Path to the CA certificate to use as a signing certificate for #generated certificates. # required if ssl_mitm is enabled. caprivatekeypath = '@E2CONFDIR@/private/ca.key' # #CA private key path #path to the private key that matches the public key in the CA certificate. # required if ssl_mitm is enabled. certprivatekeypath = '@E2CONFDIR@/private/cert.key' # #Cert private key path #The public / private key pair used by all generated certificates # required if ssl_mitm is enabled. generatedcertpath = '@E2CONFDIR@/private/generatedcerts/' # #Generated cert path #The location where generated certificates will be saved for future use. #(must be writable by the e2 user) # required if ssl_mitm is enabled. #Warning: it is advisable to prune the generated certificate # store regularly to avoid the # number of files getting to large and the file system running out # of inodes. An example of a script to do this is: # cd generatedcertpath (replace with actual path on your system) # find . -atime +7 -delete # i.e delete all cached certs not accessed in last 7 days # generatedcertstart = auto # #Generated cert start time (in unix time) - optional # defaults to auto # 'auto' will make generatedcertstart time last January or June and # generatedcertend 12 months after generatedcertstart. This ensures # generated certificates are only changed every 6 months while # limiting life to 12 months # generatedcertend = # #Generated cert end time (in unix time) - optional # defaults to generatedcertstart + 1 years # Note: start/end times are now also checked against root dates # and where needed adjusted to fit the root cert dates. A warning is # issued when this happens. #useopensslconf = off # # Use openssl configuration file # switch this on if you want e2g to read in openssl configuration # This is useful if you want to use a hardware acceleration engine. # default is off # opensslconffile = '@E2CONFDIR@/openssl.conf' # # Alternate openssl configuration file # only used if useopensslconf = on # default is to use standard openssl configuration file # only use this if an alternate openssl configuration file is used for e2g # setcipherlist = "HIGH:!ADH:!MD5:!RC4:!SRP:!PSK:!DSS" # # Sets the cipher list used by openssl # Default is "HIGH:!ADH:!MD5:!RC4:!SRP:!PSK:!DSS" # May be withdrawn in future versions as best defined in openssl.conf # Sites that are impossible or undesirable to MITM # sitelist = 'name=nomitm,path=__LISTDIR__/nomitmsitelist' ipsitelist = 'name=nomitm,path=__LISTDIR__/nomitmsiteiplist' ### ### END of MITM section ### ICAP_SERVICE section ### #defaulticapfiltergroup = 1 # # default filtergroup for ICAP mode # defaults to 1 #icapreqmodurl = 'request' #Url to respond to ICAP reqmod queries # default 'request' #icapresmodurl = 'response' #Url to respond to ICAP respmod queries # default 'response' ### ### END of ICAP_SERVICE section ### TRANSPARENT section ### #defaulttransparentfiltergroup = 1; # # default filtergroup for transparent proxy mode (http and thttps) # optional defaults to 1 #useoriginalip = on # # This option only applies when request is transparent (http or https), # when no upstream proxy is used, and where it is possible to detect # the original destination ip & port # When enabled the upstream request will be directed at the original ip and port # and no DNS lookup will be performed. # This solves the 'snapchat' issue and also should increase speed of connection. # Currently this ONLY works on linux systems. # BSD developers, PLEASE HELP fix this for BSD, pfsense etc! # default = on (linux) ignored (bsd) ### ### END of TRANSPARENT section ### AUTH section ### ### In the context of e2guardian authentication is primarly ### the determination of the filter group to be used. ### ### Some of the plug-in also return a username which is ### then used in the access log # Auth plugins # # Handle the extraction of client usernames and groups from various sources, # enabling requests to be handled according to the settings of the user's # filter group. # ## There are five ways that e2g can be deployed and this affects the auth ## plugins available ## ## ## 'Standalone' - e2g handles client and upstream traffic ## ## 'Proxy-First' - client is logged in by proxy (squid) ## and proxy passes e2g the user name in a 'basic' proxy ## auth header ## ## 'Proxy-After' - client points to e2g which then uses upstream proxy ## This is the method used by dg/e2g until v5. ## If authentication is enabled on proxy, then ## sslreplace, 'Transparent' or IP auth and possibly ## other features will not work. ## NOTE: Withdrawn in v5.5! ## ## 'Transparent' - 80/443 requests are redirected to e2g on gateway ## Can be used with Standalone or Proxy-After mode ## Note: only IP based plugins will be used in this ## and so normaly it is not possible to capture the ## user name. However, devices using transparent ## can be put in their own default group. ## ## 'ICAP mode' - All trafic goes via squid and squid uses e2g as an ## ICAP server. ## ICAP has built in auth as username is supplied in the ICAP header ## by squid. The user is checked against the filtergroupslist to get the ## group. To cater for the situation where user is missing ip based ## plugins such as 'ip' can be used as fall back. ## ## Note that e2g can support multiple methods at the same time, ## e.g. Standalone, Transparent and ICAP server ## There are two types of plugin ## 'Native' or 'Proxy-first' ## 'Native' plugins - these do not require use of a proxy #authplugin = '@E2CONFDIR@/authplugins/ident.conf' # Requires identd running on each client - gives username # Group based on ip or ip range - pseudo username of the ip #authplugin = '@E2CONFDIR@/authplugins/ip.conf' # Group based on e2g port number - pseudo username of the port # for this option the ports have to be declared as multiple filterport line #authplugin = '@E2CONFDIR@/authplugins/port.conf' # User and group obtained from dns entries mapping ip to user/group # dns entries maintained by separate authentication program. #authplugin = '@E2CONFDIR@/authplugins/dnsauth.conf' # HELP - more native plugins needed! 'basic' etc. ## 'Proxy-first' plugin - requires a proxy in front to do the user ## authentication. # Use pf-basic.conf where proxy is doing auth in front of e2g #authplugin = '@E2CONFDIR@/authplugins/pf-basic.conf' # User defined in header - requires interception prior to e2g # to add headers #authplugin = '@E2CONFDIR@/authplugins/proxy-header.conf' # ip plugin can also be used in Proxy first mode. ## 'Proxy-after' plugins - requires a proxy behind. ## These are pass-through plugins which reply on sniffing the ## proxy auth headers between client and proxy to get username ## - DEPRECIATED and are removed from this release ## - Use Proxy-first plugin and squid in front of e2g instead # Basic auth on back-end proxy #authplugin = '@E2CONFDIR@/authplugins/proxy-basic.conf' ## - DEPRECIATED and has been removed from this release # Digest auth on back-end proxy #authplugin = '@E2CONFDIR@/authplugins/proxy-digest.conf' ## - DEPRECIATED and has been removed from this release # NTLM (only v1) auth on back-end proxy #@NTLMSUPPORT@authplugin = '@E2CONFDIR@/authplugins/proxy-ntlm.conf' ## - DEPRECIATED and has been removed from this release # All native plugins can also be used in proxy-after mode # but only when auth is not forced by the upstream proxy ## Auth mapping files - Map users (or client IPs) to filter groups ## Note that from v5.4 lists used by auth plugins are defined here and ## not in auth *.conf files # Generic user to group mapping - used by default by basic, digest, ntlm, # ident & icap plugins maplist = 'name=defaultusermap, path=__LISTDIR__/../authplugins/filtergroupslist' # for ip auth ipmaplist = 'name=ipmap, path=__LISTDIR__/../authplugins/ipgroups' # for port auth maplist = 'name=portmap, path=__LISTDIR__/../authplugins/portgroups' # If on a user without group is considered like unauthenfied # E2guardian tries the next plugin # If off the user is connected with defaultgroup # Defaults to off # authrequiresuserandgroup = off # Authentication exception/banned clients # # bannediplist is ONLY for banned client IP iplist = 'name=bannedclient,messageno=100,logmessageno=103,path=__LISTDIR__/bannediplist' # exceptioniplist is ONLY for exception client IP iplist = 'name=exceptionclient,messageno=600,path=__LISTDIR__/exceptioniplist' reverseclientiplookups = off # Reverse lookups for banned and exception IP clients. # If set to on, e2guardian will look up the forward DNS for the IP # of the connecting computer. # If a client computer is matched against an IP given in the lists, then the # IP will be recorded in any log entries; if forward DNS is successful and a # match occurs against a hostname, the hostname will be logged instead. # It will reduce searching speed somewhat so unless you have a local DNS server, # leave it off. # Put client dns names in bannedclientlist if required #sitelist = 'name=bannedclient,messageno=100,logmessageno=104,path=__LISTDIR__/bannedclientlist' # Put client dns names in exceptionclientlist if required #sitelist = 'name=exceptionclient,messageno=631,path=__LISTDIR__/exceptionclientlist' # authexception lists are for exception sites/urls allowed before authentication # to allow for machines to update without user authentication ipsitelist = 'name=authexception,messageno=602,path=__LISTDIR__/authexceptioniplist' sitelist = 'name=authexception,messageno=602,path=__LISTDIR__/authexceptionsitelist' urllist = 'name=authexception,messageno=603,path=__LISTDIR__/authexceptionurllist' regexpboollist = 'name=browser,path=__LISTDIR__/browserregexplist' # # List of regexp that match match User-agent of browsers # Used to determine if client is a browser # and decide whether to send a block page or go MITM ### ### END of AUTH section ### ACCESS_LOG section ### ## Location and format #set_accesslog = 'file:@E2LOGLOCATION@/access.log' #set_accesslog = 'syslog:LOG_INFO' #set_accesslog = 'stderr' # Sets output for access log # default is 'file:access.log' #loglocation = '@E2LOGLOCATION@/access.log' # Log file location # # Defines the log directory and filename. # Retained for backward compatibility # - ignored if set_accesslog is defined #logsyslog = off # Syslog logging # Use syslog for access logging instead of logging to the file # at the defined or built-in "loglocation" # Retained for backward compatibility # - ignored if set_accesslog is defined #namesuffix = "" #Suffix to append to program name when logging through syslog # Default is blank #logfileformat = 8 # Log File Format # 1 = Dansguardian format (space delimited) # 2 = CSV-style format # 3 = Squid Log File Format # 4 = Tab delimited # Protex format type 5 Tab delimited, squid style format with extra fields # for filter block/result codes, reasons, filter group, and system name # used in arrays so that combined logs show originating server. # 5 = Protex format # Protex format type 6 Same format as above but system name field is blank # used in stand-alone systems. # 6 = Protex format with server field blanked # 7 = Same as 5, but with searchterms and EXTFLAGS added # See notes/New_log_fileds_in_log_format7-8.pdf for details # 8 = Same as 7, but with server field blanked # Default is 8 #anonymizelogs = off # anonymize logs (blank out client usernames & IPs) # default off #tag_logs = on # This option adds a tag to the front of each log line # It is useful where multiple outputs are made to the same # output source (e.g. stderr) to identify which type of of log # entry it is. # default 'off' ## What requests to log # Note: These options may be replaced by storyboard function in v5.5 # With the settings as distributed all requests (apart from ADs) will # be logged. #loglevel = 3 # 0 = none 1 = just denied 2 = all text based 3 = all requests # default 3 #logexceptionhits = 2 # Log Exception Hits # Log if an exception (user, ip, URL, phrase) is matched and so # the page gets let through. Can be useful for diagnosing # why a site gets through the filter. # 0 = never log exceptions # 1 = log exceptions, but do not explicitly mark them as such # 2 = always log & mark exceptions (default) #logadblocks = off # Enable logging of "ADs" category blocks # on|off (defaults to off) ## What extra data is to be logged #showweightedfound = on # Show weighted phrases found # If enabled then the phrases found that made up the total which excedes # the naughtyness limit will be logged and, if the reporting level is # high enough, reported. on | off # default is on #showallweightedfound = off # Show all weighted phrases found # If enabled then the phrases found that made up the total will be logged and, if the reporting level is # high enough, reported. on | off # default is off #logclienthostnames = off # Perform reverse lookups on client IPs for successful requests. # If set to on, e2guardian will look up the forward DNS for the IP # of the connecting computer, and log host names (where available) rather than # IPs against requests. # This is not dependent on reverseclientiplookups being enabled; however, if it # is, enabling this option does not incur any additional DNS requests. #loguseragent = off # Enable logging of client User-Agent # Some browsers will cause a *lot* of extra information on each line! # on|off (defaults to off) #logclientnameandip = on # Enable logging of both client hostname and its IP # If off the hostname will be logged instead of IP # Applies only to log formats 5, 6, 7 & 8. # on|off (defaults to on) # Needs to be turned off if you are using sarg log analysis. #dnsuserloggingdomain = "" # Used to get user/domain from special dns zone for logging purposes only # Similar to dnsauth plugin operation but only for logging. ## Log formating options #usedashforblank = on # use dash ('-') instead of blank fields in log # This is essential for space delimited log formats, and makes all log types easier to read # But can be turned off if this causes a problem with log analysis # on|off (defaults to on) #logtimestamp = off # Add unix timestamp to time field so that date/time in readable format # and unix timestamp - applies only to log formats 1,2 and 4 # default off #logid1 = "" #logid2 = "" # only used in logformats 1, 2 and 4 # default "" #productid = '2' # Used in SG_LOGFORMAT # default 2 # NEW in v5.5.6 #addECHtoFlags = on # add 'E' indicator to EXTFLAGS when Encrypted Client Hello (ECH) TLS extension is present # (only applies to Transparent https requests) # This indicates that SNI may be unreliable # The ECH may be real or fake, it is not possible to tell which, although if connection via # MITM succeeds then is likely to be fake. # When the SNI is trusted then it maybe real or fake as MITM is not used. # This may be useful information to log, but does not change any logic in v5.5 # default is off for backward compatibility ## Other access log options #maxlogitemlength = 2000 # truncate large items in log lines # allowable values 10 to 32000 # default 2000 # unlimited not longer allowed - 0 will now set default of 2000 ### ### END of ACCESS_LOG section ### MONITORING section ### # set_dstatslog = 'file:@E2LOGLOCATION@/dstats.log' # Dymamic statistics log file output # # Defines the dstats file directory and filename. # Once every 'dstatinterval' seconds, stats on number of threads in use, # Q sizes and other useful information is written to this file. # Format is similar to sar. See notes/dstats_format for more details. # Default - do not to write stats. dstatlocation = '@E2LOGLOCATION@/dstats.log' # Dymamic statistics log file location # # Retained for backward compatiblity # Ignored if set_dstatslog is defined #dstatinterval = 300 # = 5 minutes # Interval in seconds between stats output # Default 300 (= 5 mins) # Minimum 10 # Maximum 3600 (= 1 hour) #statshumanreadable = off # Time format for dstat is epoch GMT+0 by default | statshumanreadable # change to local zone # default off # internaltesturl = 'internal.test.e2guardian.org' # # A pretend url for testing e2g is working. # # It returns a small page containing OK if working ok. # # Used by loadbalancers and monitoring software (e.g. smokeping) # to detect if e2g is functioning. # # It is tested for after connection is successful and a worker thread is # assigned, but before user auth and group assignment is made. # # This has been built in to e2g since v3, but this option allows the # url to be changed. # # default 'internal.test.e2guardian.org' # internalstatusurl = 'internal.status.e2guardian.org' # # A pretend url for checking the status of a user. # # It returns a small page providing various information # such as user name, ip, filtering group, server name, # e2guardian version, flags field. # # Designed to used by status software and by technical staff for testing # user access/assignment. # # It is tested for after user auth and group assignment is made, but # before any filtering is performed. # New in v5.4.3, this option allows the # url to be changed. # # default 'internal.status.e2guardian.org' # monitorflagprefix = '@E2PIDDIR@/e2g_flag_' # monitor flag prefix path # If defined path will be used to generate flag files as follows:- # # At start after e2guardian has started listener and worker threads with # 'running' appended # When e2guardian is stopping with 'paused' appended # default '' - flags disabled ### ### END of MONITORING section ### URL_FILTERING section ### reverseaddresslookups = off # Reverse lookups for site and URL lists. # If set to on, e2guardian will look up the forward DNS for an IP URL # address and search for both in the banned site and URL lists. This would # prevent a user from simply entering the IP for a banned address. # It will reduce searching speed somewhat so unless you have a local caching # DNS server, leave it off and use the Blanket IP Block option in the # f1.story file instead. ### ### END of URL_FILTERING section ### LIST_SETTINGS section ### # abortiflistmissing = off # Abort if a list is missing or unreadable # default is to warn but then ignore missing lists # To abort on missing list set to on # default "off" #searchsitelistforip = on #Search sitelist for ip sites # In v5 a separate set of lists has been introduced for IP sites # and normally e2g will no longer check site lists for ip's # If you want to keep backward list compatablity then set this to # 'on' - but note this incurs an overhead - putting IP in ipsitelists # and setting this to off gives the fastest implementation. # default is 'on' ### ### END of LIST_SETTINGS section ### AV_SCANNERS section ### # Content Scanners (Also known as AV scanners) # These are plugins that scan the content of all files your browser fetches # for example to AV scan. You can have more than one content # scanner. The plugins are run in the order you specify. # This is one of the few places you can have multiple options of the same name. # # Some of the scanner(s) require 3rd party software and libraries eg clamav. # See the individual plugin conf file for more options (if any). # #@CLAMDSUPPORT@contentscanner = '@E2CONFDIR@/contentscanners/clamdscan.conf' #@AVASTDSUPPORT@contentscanner = '@E2CONFDIR@/contentscanners/avastdscan.conf' #@KAVDSUPPORT@contentscanner = '@E2CONFDIR@/contentscanners/kavdscan.conf' #@ICAPSUPPORT@contentscanner = '@E2CONFDIR@/contentscanners/icapscan.conf' # Warning: The commandlinescan plugin uses 'fork()' which does not work well # in a large multi-threaded program like e2g. It can cause unpredictable # crashes. # On a small scale system (home user) it may work ok, but not recommended for # larger scale systems. The more active threads, the more likely a crash is. # #@COMMANDLINESUPPORT@contentscanner = '@E2CONFDIR@/contentscanners/commandlinescan.conf' #contentscannertimeout = 60 # Content scanner timeout # Some of the content scanners support using a timeout value to stop # processing (eg AV scanning) the file if it takes too long. # If supported this will be used. # defaults to value of pcontimeout ### ### END of AV_SCANNERS section ### HEADER section ### #addforwardedfor = off # # if on it adds an X-Forwarded-For: to the HTTP request # header. This may help solve some problem sites that need to know the # source ip. on | off # default off # forwardedfor = off # # old name for addforwardedfor - retained for compatiblity. usexforwardedfor = off # # if on it uses the X-Forwarded-For: to determine the client # IP. This is for when you have squid between the clients and e2guardian. # Warning - headers are easily spoofed. on | off # default off # xforwardedforfilterip = # # as mentioned above, the headers can be easily spoofed in order to fake the # request origin by setting the X-Forwarded-For header. If you have the # "usexforwardedfor" option enabled, you may want to specify the IPs from which # this kind of header is allowed, such as another upstream proxy server for # instance If you want authorize multiple IPs, specify each one on an individual # xforwardedforfilterip line. # default no entries # maxheaderlines = 2000 # # Limit number of http header lines in a request/response # (to guard against attacks) # Minimum 10 max 2500 # default 2000 ### ### END of HEADER section ### BLOCK_PAGE section ### #reportinglevel = 3 # # reportinglevel # # -1 = log, but do not block - Stealth mode # 0 = just say 'Access Denied' # 1 = report why but not what denied phrase # 2 = report fully # 3 = use HTML template file (accessdeniedaddress ignored) - recommended # # Defines the global setting - can be overrided in e2guardianf1.conf # # default 3 #usecustombannedimage = on # #custombannedimagefile = '@E2DATADIR@/transparent1x1.gif' # Banned image replacement # Images that are banned due to domain/url/etc reasons including those # in the adverts blacklists can be replaced by an image. This will, # for example, hide images from advert sites and remove broken image # icons from banned domains. # on (default) | off #usecustombannedflash = on # #custombannedflashfile = '@E2DATADIR@/blockedflash.swf' # #Banned flash replacement ### ### END of BLOCK_PAGE section ### DOWNLOAD_MANAGER section ### # Download Managers # These handle downloads of files to be filtered and scanned. # They differ in the method they deal with large downloads. # Files usually need to be downloaded 100% before they can be # filtered and scanned before being sent on to the browser. # Normally the browser can just wait, but with content scanning, # for example to AV, the browser may timeout or the user may get # confused so the download manager has to do some sort of # 'keep alive'. # # There are various methods possible but not all are included. # Also, not all methods work with all # browsers and clients. Specifically some fancy methods don't # work with software that downloads updates. To solve this, # each plugin can support a regular expression for matching # the client's user-agent string, and lists of the mime types # and extensions it should manage. # # Note that these are the matching methods provided by the base plugin # code, and individual plugins may override or add to them. # See the individual plugin conf files for supported options. # # The plugins are matched in the order you specify and the last # one is forced to match as the default, regardless of user agent # and other matching mechanisms. # # NOTE FOR v5.5 - ONLY default download manager is supported # - trickle should be regarded as experimental # - coding for fancy is incomplete and DOES NOT work #downloadmanager = '@E2CONFDIR@/downloadmanagers/trickle.conf' downloadmanager = '@E2CONFDIR@/downloadmanagers/default.conf' #filecachedir = '/tmp' # # File cache dir # Where E2 will download files to be scanned if too large for the # RAM cache. # default "/tmp" #deletedownloadedtempfiles = on # # Delete file cache after user completes download # When a file gets save to temp it stays there until it is deleted. # You can choose to have the file deleted when the user makes a sucessful # download. This will mean if they click on the link to download from # the temp store a second time it will give a 404 error. # You should configure something to delete old files in temp to stop it filling up. # on|off (defaults to on) #initialtrickledelay = 20 # # Initial Trickle delay # This is the number of seconds a browser connection is left waiting # before first being sent *something* to keep it alive. The # *something* depends on the download manager chosen. # Do not choose a value too low or normal web pages will be affected. # A value between 20 and 110 would be sensible # This may be ignored by the configured download manager. # default 20 #trickledelay = 10 # # Trickle delay # This is the number of seconds a browser connection is left waiting # before being sent more *something* to keep it alive. The # *something* depends on the download manager chosen. # This may be ignored by the configured download manager. # default 10 ### ### END of DOWNLOAD_MANAGER section ### PHRASES section ### weightedphrasemode = 2 # # Weighted phrase mode # There are 3 possible modes of operation: # 0 = off = do not use the weighted phrase feature. # 1 = on, normal = normal weighted phrase operation. # 2 = on, singular = each weighted phrase found only counts once on a page. # # IMPORTANT: Note that setting this to "0" turns off all features which # extract phrases from page content, including banned & exception # phrases (not just weighted), search term filtering, and scanning for # links to banned URLs. # #phrasefiltermode = 2 # # Smart, Raw and Meta/Title phrase content filtering options # Smart is where the multiple spaces and HTML are removed before phrase filtering # Raw is where the raw HTML including meta tags are phrase filtered # Meta/Title is where only meta and title tags are phrase filtered (v. quick) # CPU usage can be effectively halved by using setting 0 or 1 compared to 2 # 0 = raw only # 1 = smart only # 2 = both of the above # 3 = meta/title # default 2 #preservecase = 0 # # Lower casing options # When a document is scanned the uppercase letters are converted to lower case # in order to compare them with the phrases. However this can break Big5 and # other 16-bit texts. If needed preserve the case. As of version 2.7.0 accented # characters are supported. # 0 = force lower case (default) # 1 = do not change case # 2 = scan first in lower case, then in original case # Note: # If phrasefiltermode and preserve case are both 2, this equates to 4 phrase # filtering passes. If you have a large enough userbase for this to be a # worry, and need to filter pages in exotic character encodings, it may be # better to run two instances on separate servers: one with preservecase 1 # (and possibly forcequicksearch 1) and non ASCII/UTF-8 phrase lists, and one # with preservecase 0 and ASCII/UTF-8 lists. #hexdecodecontent = off # # Hex decoding options # When a document is scanned it can optionally convert %XX to chars. # If you find documents are getting past the phrase filtering due to encoding # then enable. However this can break Big5 and other 16-bit texts. # off = disabled (default) # on = enabled #forcequicksearch = off # # Force Quick Search rather than DFA search algorithm # The current DFA implementation is not totally 16-bit character compatible # but is used by default as it handles large phrase lists much faster. # If you wish to use a large number of 16-bit character phrases then # enable this option. # off (default) | on (Big5 compatible) ### ### END of PHRASES section ### TUNING section ### #httpworkers = 500 # #sets the number of worker threads to use # # This figure is the maximum number of concurrent connections. # If more connections are made, connections will queue until a worker thread is free. # On large site you might want to try 5000 (max value 20000) # 500 is the default suitable for home or samll office use on 64-bit systems # On 32-bit systems reduce this to 300 to avoid exceeding the <4GB # virtual memory limit and on Linux decrease the thread stack size from # 10MB to 2MB (ulimit -s 2048) # default 500 #maxcontentfiltersize = 2048 # # Max content filter size # Sometimes web servers label binary files as text which can be very # large which causes a huge drain on memory and cpu resources. # To counter this, you can limit the size of the document to be # filtered and get it to just pass it straight through. # This setting also applies to content regular expression modification. # The value must not be higher than maxcontentramcachescansize # Do not set this too low as this will result in pages that contain a # long preamble not being content filtered # The size is in Kibibytes - eg 2048 = 2Mb # default 2048 #maxcontentramcachescansize = 2048 # # Max content ram cache scan size # This is only used if you use a content scanner plugin such as AV # This is the max size of file that e2g will download and cache # in RAM. After this limit is reached it will cache to disk # This value must be less than or equal to maxcontentfilecachescansize. # The size is in Kibibytes - eg 10240 = 10Mb # use 0 to set it to maxcontentfilecachescansize # This option may be ignored by the configured download manager. # default 2048 #maxcontentfilecachescansize = 20000 # # Max content file cache scan size # This is only used if you use a content scanner plugin such as AV # This is the max size file that E2 will download # so that it can be scanned or virus checked. # This value must be greater or equal to maxcontentramcachescansize. # The size is in Kibibytes - eg 10240 = 10Mb # default 20000 #proxytimeout = 5 # # Proxy timeout # Set tcp timeout between the Proxy and e2guardian # This is a connection timeout # If proxy is remote you may need to increase this to 10 or more. # Min 5 - Max 100 # default 5 #connecttimeout = 10 # # Connect timeout # Set tcp timeout between the e2guardian and upstream service (proxy or target host) # This is a connection timeout # For slow remote sites you may need to increase this to 10 or more. # if always using a local proxy decrease this setting to 5 # Min 1 - Max 100 # default 10 # connectretries = 1 # # Connect retries # Set the number of retries to make on connection failure before giving up # Min 1 - Max 100 # default 1 #proxyexchange = 61 # # Proxy header exchange # Set timeout between an upstream Proxy and e2guardian # Min 20 - Max 300 # If this is higher than proxies timeout user will get proxy Gateway error page # If lower e2guardian Gateway error page # default 61 #pcontimeout = 55 # # Pconn timeout # how long a persistent connection will wait for another request before closing # Min 5 - Max 300 # default 55 ### ### END of TUNING section ### DEBUG section ### # Configure standard logging (see notes/HOWTO_Logger.md) # format: # set_{source}={destination}[:{filename}] # example: # set_debug=stdout # defaults: # set_info=stdout # set_error=stderr # set_access=file,access.log # udp_source_port = 39000 # Source port for UDP logging # currently hard coded to 39000 # You need to know this in order to set firewall rules etc # Will be able to be changed in a future release ## Things that can used on production binaries # storyboardtrace = on # # Storyboard tracing # Warning - produces very verbose output - must not be used in production # By default output goes to syslog - destination can be changed by using set_storytrace # default off # Use to debug storyboard logic flow #set_storytrace = "file:/tmp/storytrace.txt" # Output for storyboard tracing # Note this does not enable storyboardtrace but only changes the destination from the standard syslog. #logsslerrors = on # # Logs openssl error 'stack' in syslog # Used to diagnose openssl errors # It is normal for some openssl errors to occur # Can be left on or off # default off #logconnectionhandlingerrors = on # # if on it logs some debug info regarding accept()ing and failed connections # which # can usually be ignored. These are logged by syslog. It is safe to leave # it on or off # default off #set_requestlog = 'none' #set_requestlog = 'file:@E2LOGLOCATION@/request.log' # Set optional request log output # This is for useful for debug purposes to log all requests before processing or setting filter group # See notes/LogRequests for details # default is 'none' #rqloglocation = '@E2LOGLOCATION@/request.log' # # Defines optional request log path # Default is "" - no request log # Retained for backward compatiblity # Ignored if set_requestlog is defined #set_responselog = 'none' #set_responselog = 'file:@E2LOGLOCATION@/response.log' # Set optional response log output # This is for useful for debug purposes to log all responses - unlike access.log all responses are logged # default is 'none' #set_alertlog = 'none' #set_alertlog = 'file:@E2LOGLOCATION@/alert.log' # Set optional alert log output # Output log entries which need an alert, so that external process can generate alerts on user activity # default is 'none' ## The new debug system uses a extended format ## but is backward compatible with the "NewDebug" system. ## debuglevel is used to define the debug categories ## and optionally the destination of the debug logs ## ## format is: ## debuglevel = 'debugtypes[:destination[:filename|sysloglevel|host:udp_port]]' ## ## debugtypes can be single type or muliple types separated by ',' ## 'ALL' can be used to enable all debug types available ## 'HIGH' can be used to enable all high level debug types available ## 'LOW' can be used to enable all low level debug types available ## and types can be switched off by pre-fixing them with '-' ## ## destination can be: ## stdout - output to stdout ## stderr - output to stderr ## syslog - output to syslog at sysloglevel ## file - output to filename ## udp - output to host on udp_port ## ## if destination is omitted then output will be to the default debug ## destination or whatever is defined in the last debuglevel. ## ## Multiple debuglevel options can be used to allow different debug sets ## to be output to different destinations. If a a debug type is repeated ## then the last debuglevel wins. #debuglevel = 'icap:file:/var/log/e2guardian/icap.log' #Enable ICAP debug information only in specified file # #debuglevel = 'ALL:syslog,LOG_DEBUG' # enable all debugs available and output to syslog LOG_DEBUG level # #debuglevel = 'icap,network:stderr' #Enable ICAP and NETWORK debug to stderr # #Subtractive mode: #debuglevel = 'ALL,-icap' #Enable all debug informations but without ICAP debug informations #debuglevel = 'ALL,-icap,-network' #Enable all debug but without ICAP, NETWORK debug informations ## Types of debug available when compliled with E2DEBUG_HIGH ## ## icap - ICAP main debug ## proxy - proxy (normal) debug ## thttps - Transparent HTTPS debug ## auth - authentication plugins debug ## avscan - AV scan debug ## dwload - download manager debug ## Types of debug available when compliled with E2DEBUG_LOW ## (only useful for developers) ## ## trace - trace program flow ## network - low level network functions ## story - storyboard debug ## regexp - regexp debug ## config - debug configuration and lists set-up ## content - debug content checking ## debug - debug not yet included in a more specific type # debugformat = 1 # # There is a choice of debug formats available # 1: thread_id: message function():file:line # 2: thread_id: function():file:line message # 3: thread_id: message # 4: (type) thread_id: message function():file:line # 5: (type) thread_id: function():file:line message # 6: (type) thread_id: message # # Formats 1,3,4,6 are better for developers and bug-fixing # Formats 2,5 for those exploring the program flow. # default 1 ### ### END of DEBUG section ### PROCESS section ### # Process options # (Change these only if you really know what you are doing). # These options allow you to run multiple instances of e2guardian on a single machine. # Remember to edit the log file path also if that is your intention. #pidfilename = '@E2PIDDIR@/e2guardian.pid' # # PID filename # # Defines process id directory and filename. #nodaemon = off # # Disable daemoning # If enabled the process will not fork into the background. # It is not usually advantageous to do this. # on|off (defaults to off) #@EMAILSUPPORT@mailer = '/usr/sbin/sendmail -t' # # Mail program # Path (sendmail-compatible) email program, with options. # Not used if usesmtp is disabled (filtergroup specific). ## Note that this is experimental in v5 - no support from maintainers ### ### END of PROCESS section ### OBSOLETE section ### ### Directives here are depreciated and may already not work ### originalip = off # NOTE: This option is removed in v5.4 - if left and enabled would give too many # false positives. # contentscanexceptions = off # Content scan exceptions // THIS MOVED to e2guardianf1.conf #mapportstoips = off #mapauthtoports = off # Map auth to ports/ports to ip - does not work work correctly # - very confusing options # default off - to be removed in v5.5. # logheadervalue = 'proxy-authorization:' # Log a specific value from header # low case only # only used with logs: 1,5 and 6 #statlocation = "" # url cache/stats no longer in use #blockedcontentstore = "" # no longer in use #softrestart = off # no longer in use #proxyfailureloginterval = 0 # no longer in use #scancleancache = true # no longer in use #urlcachenumber = 0 # no longer in use #groupnamesfile = '' # no longer supportied #urlcacheage= 0 # no longer in use #recheckreplacedurls = off - option does not work - may be removed in v5.5 # # Re-check replaced URLs # As a matter of course, URLs undergo regular expression search/replace (urlregexplist) # *after* checking the exception site/URL/regexpURL lists, but *before* checking against # the banned site/URL lists, allowing certain requests that would be matched against the # latter in their original state to effectively be converted into grey requests. # With this option enabled, the exception site/URL/regexpURL lists are also re-checked # after replacement, making it possible for URL replacement to trigger exceptions based # on them. # Defaults to off. #logchildprocesshandling - will be removed in v5.5 # no longer in use # monitorhelper = '/usr/local/bin/mymonitor' - to remove in v5.5 # monitor helper path # Not recommended - likely to cause crashes as it uses fork() # If defined this script/binary will be called with start or stop appended as follows:- # At start after e2guardian has started listener and worker threads with # ' start' appended # When e2guardian is stopping with ' stop' appended # default '' - monitor helper disabled ### ### END of OBSOLETE section ### INFO section ### ### No settings just info on new(ish) features etc # Relative paths (from v5.4.2) # Relative paths can used in .Include<> and list files. # The directory of current file will be inserted where the file name # does not start with '/' # LISTDIR 'variable' definition (from v5.4.2) # LISTDIR can be defined in .conf files. # This allows for more readable configuration and for templating. # This works similarly to a shell environment variable # The text between <> will replace occurances of __LISTDIR__ in .conf and # list files. # Note: Currently only LISTDIR may be defined. # # The mapping is actioned as the file is read and is valid until another LISTDIR # is defined later in the file, or in an included .conf file. # # The scope of LISTDIR is in the rest of file it is defined in and all # .Include<> files or list files in that portion of the file. # It should be noted that re-definitions of single-line directives will # over write any earlier ones. # The same is true of list definitions. Later unique definitions will # override earlier ones. A unique list definition is formed from the # list type and the name. # # So, # sitelist = 'name=banned,path=x...' # and # urllist = 'name=banned,path=y...' # are both unique # but # sitelist = 'name=banned,path=x...' # and # sitelist = 'name=banned,path=z...' # are not and the later definition will override the first. ### ### END of INFO section e2guardian-5.5.8r/configs/e2guardianf1.conf.in000077500000000000000000001025001477372360500211270ustar00rootroot00000000000000# e2guardian filter group config file for version @PACKAGE_VERSION@ # This file is re-read on gentle restart and any changes actioned ### Filtergroup config is now split into sections as follows ### ### QUICK_START - Items to check to get you started ### MITM - SSL MITM settings ### URL_FILTERING - URL filtering settings ### URL_MAIN_LISTS - URL/SITE lists - for downloaded lists(?) ### URL_LOCAL_LISTS - URL/SITE lists - local ### FILE_TYPES - Lists that control download filetype access ### BLANKET - Lists that control blanket block ### TIMES - Time based lists ### APPS - Lists to control apps/browsers ### REFERER - Lists to allow exceptions based on referer site/url ### URL_MOD - Lists to modify url and ssl site target ### REDIRECT - Redirection list ### LOG_ONLY - Categorise and log but do not filter lists ### POST - Post filtering - not implemented yet in v5 ### PHRASES - Content phrase settings and lists ### SEARCH - Search term filtering settings and lists ### AV_SCANNERS - AV scanner settings and lists ### HEADER - HTTP Header handling ### BLOCK_PAGE - Block Page formats and handling ### BYPASS - Bypass settings and lists ### EMAILER - Emailer settings (experimental) ### OBSOLETE - Obsolete settings ### INFO - Info on new features etc ### ### QUICK_START section ### ### e2guardian will work using examplef1.story ### and the example.group sub-directory of the lists directory ### without you making any changes to this file. ### ### The items you are likely to need to change in production are ### in this section. ### ### To set up for production:- ### ### In config directory:- ### Copy examplef1.story to group1.story ### In list directory:- ### Create a group1 directory ### Copy the lists in lists/sample.group into this directory ### and edit them to your requirements ### Comment out .define.., and storyboard = lines below. ### ### To add group two:- ### ### Copy this file to e2guardianf2.conf ### and repeat above steps, but for group2.story, lists/group2 directory etc ### ### Repeat for any further groups ### ### Remember to adjust the 'filtergroups' setting in e2guardian.conf ### to the number of groups you now have. #groupname = 'my_name_for_group' # # Filter group name # Used to fill in the -FILTERGROUP -placeholder in the HTML template file, and to # name the group in the access logs # Default 'group1' where 1 is the group number storyboard = '@E2CONFDIR@/examplef1.story' # comment out for production # # Storyboard logic for this group # # defaults to '@E2CONFDIR@/group1.story' where 1 is the group number .Define LISTDIR <@E2CONFDIR@/lists/example.group> # comment out for production # The directory path for list files for this group # This path replaces __LISTDIR__ wherever it occurs below or in included files. # See INFO section for full details # defaults (initialized) as <@E2CONFDIR@/lists/group1> where 1 is the group number #naughtynesslimit = 60 # # Naughtiness limit # This the limit over which the page will be blocked. Each weighted phrase is given # a value either positive or negative and the values added up. Phrases to do with # good subjects will have negative values, and bad subjects will have positive # values. See the weightedphraselist file for examples. # As a guide: # 60 is for young children, 100 for old children, 160 for young adults. # default 60 ### ### END of QUICK_START section ### MITM section ### # NOTE to enable SSL MITM # enablessl must be defined as 'on' in e2guardian.conf #sslmitm = on # #SSL man in the middle # Forge ssl certificates for all non-exception sites, decrypt the data then re encrypt it # using a different private key. Used to filter ssl sites #default off # mitmcheckcert = on # # Enable MITM site certificate checking # ignored if sslmitm is off # default (recommended) is 'on' #Do not check ssl certificates for sites listed # Can be used to allow sites with self-signed or invalid certificates # or to reduced CPU load by not checking certs on heavily used sites (e.g. Google, Bing) # Use with caution! # Ignored if mitmcheckcert is 'off' # sitelist = 'name=nocheckcert,path=__LISTDIR__/nocheckcertsitelist' ipsitelist = 'name=nocheckcert,path=__LISTDIR__/nocheckcertsiteiplist' # # automitm = on # # Auto switch to MITM with upstream connection error or to deliver block page # ignored if sslmitm is off # To revert to v4 type behavour switch this off # Default is 'on' # greyssl lists only used in onlymitmsslgrey mode to define sites that # will be MITM sitelist = 'name=greyssl,path=__LISTDIR__/greysslsitelist' ipsitelist = 'name=greyssl,path=__LISTDIR__/greysslsiteiplist' sitelist = 'name=localgreyssl,path=__LISTDIR__/localgreysslsitelist' #ipsitelist = 'name=localgreyssl,path=__LISTDIR__/localgreysslsiteiplist' ### ### END of MITM section ### URL_FILTERING section ### #deepurlanalysis = off # Enable Deep URL Analysis # When enabled, E2 looks for URLs within URLs, checking against the bannedsitelist and # bannedurllist. This can be used, for example, to block images originating from banned # sites from appearing in Google Images search results, as the original URLs are # embedded in the thumbnail GET requests. # (on|off) default = off ### ### END of URL_FILTERING section ### URL_MAIN_LISTS section ### ### Most of the site/ipsite/url lists in this section are used for ### switching on/off standard list categories ### ### It is best to use the LOCAL lists for your own lists of sites/urls ### as these have priority over the main ones. ## For info on the format of list definitions see notes/V5_list_definition ## Order of checking is broadly as follows:- ## ## 1. If in Local Semi Exception lists - set semiexception flag - jump to 4 ## 2. If in Local Exception lists - allow with no further checking ## 3. If in Local Grey lists - retrieve page and content-check ## 4. If in Local Banned lists - block with no further checking ## 5. If semiexception flag still set - allow with no further checking ## 6. If in Main Semi Exception lists - set semiexception flag - jump to 9 ## 7. If in Main Exception lists - allow with no further checking ## 8. If in Main Grey lists - retrieve page and content-check ## 9. If in Main Banned lists (or blanket blocked) - block with no further checking ## 10. If semiexception flag still set - allow with no further checking ## 11. If gets here - retrieve page and content-check ## 12. Check blocked/exception file/mime types. ## 13. Content-check page ## Semi Exception (make exception if not in block lists) Lists # Just sites in these lists sitelist = 'name=semiexception,messageno=602,path=__LISTDIR__/semiexceptionsitelist' ipsitelist = 'name=semiexception,messageno=602,path=__LISTDIR__/semiexceptionsiteiplist' ## Exception lists sitelist = 'name=exception,messageno=602,path=__LISTDIR__/exceptionsitelist' ipsitelist = 'name=exception,messageno=602,path=__LISTDIR__/exceptionsiteiplist' urllist = 'name=exception,messageno=603,path=__LISTDIR__/exceptionurllist' regexpboollist = 'name=exception,messageno=609,path=__LISTDIR__/exceptionregexpurllist' ## Grey (i.e. content check) lists sitelist = 'name=grey,path=__LISTDIR__/greysitelist' ipsitelist = 'name=grey,path=__LISTDIR__/greysiteiplist' urllist = 'name=grey,path=__LISTDIR__/greyurllist' ## Banned sites/urls sitelist = 'name=banned,messageno=500,path=__LISTDIR__/bannedsitelist' ipsitelist = 'name=banned,messageno=510,path=__LISTDIR__/bannedsiteiplist' urllist = 'name=banned,messageno=501,path=__LISTDIR__/bannedurllist' regexpboollist = 'name=banned,messageno=503,path=__LISTDIR__/bannedregexpurllist' # 'bannedssl' lists are not currently used #sitelist = 'name=bannedssl,messageno=520,path=__LISTDIR__/bannedsslsitelist' #ipsitelist = 'name=bannedssl,messageno=520,path=__LISTDIR__/bannedsslsiteiplist' ### ### END of URL_MAINLISTS section ### URL_LOCAL_LISTS section ### ### if you do not want to use local lists ### then comment out (or remove) all the list entries in this section and ### make adjustments to site.story ### ## Local Semi Exception (make exception if not in blocked list) Lists # Just sites in these lists sitelist = 'name=localsemiexception,messageno=602,path=__LISTDIR__/localsemiexceptionsitelist' ipsitelist = 'name=localsemiexception,messageno=602,path=__LISTDIR__/localsemiexceptionsiteiplist' ## Local Exception lists sitelist = 'name=localexception,messageno=662,path=__LISTDIR__/localexceptionsitelist' ipsitelist = 'name=localexception,messageno=662,path=__LISTDIR__/localexceptionsiteiplist' urllist = 'name=localexception,messageno=663,path=__LISTDIR__/localexceptionurllist' ## Local Grey lists sitelist = 'name=localgrey,path=__LISTDIR__/localgreysitelist' ipsitelist = 'name=localgrey,path=__LISTDIR__/localgreysiteiplist' urllist = 'name=localgrey,path=__LISTDIR__/localgreyurllist' ## Local Banned lists sitelist = 'name=localbanned,messageno=560,path=__LISTDIR__/localbannedsitelist' ipsitelist = 'name=localbanned,messageno=560,path=__LISTDIR__/localbannedsiteiplist' urllist = 'name=localbanned,messageno=561,path=__LISTDIR__/localbannedurllist' ## Bannedssl lists are not currently used #sitelist = 'name=localbannedssl,messageno=580,path=__LISTDIR__/localbannedsslsitelist' #ipsitelist = 'name=localbannedssl,messageno=580,path=__LISTDIR__/localbannedsslsiteiplist' ### ### END of URL_LOCAL_LISTS section ### FILE_TYPES section ### # Uncomment the two lines below if want to ONLY allow extentions/mime types in these lists # You will also need to uncomment the checkfiletype function in site.story to enable this #fileextlist = 'name=exceptionextension,path=__LISTDIR__/exceptionextensionlist' #mimelist = 'name=exceptionmime,path=__LISTDIR__/exceptionmimetypelist' # Use the following lists to block specific kinds of file downloads. # fileextlist = 'name=bannedextension,messageno=900,path=__LISTDIR__/bannedextensionlist' mimelist = 'name=bannedmime,messageno=800,path=__LISTDIR__/bannedmimetypelist' # # In either file filtering mode, the following lists can be used to override # MIME type & extension blocks for particular domains & URLs (trusted download sites). # sitelist = 'name=exceptionfile,path=__LISTDIR__/exceptionfilesitelist' ipsitelist = 'name=exceptionfile,path=__LISTDIR__/exceptionfilesiteiplist' urllist = 'name=exceptionfile,path=__LISTDIR__/exceptionfileurllist' ### ### END of FILE_TYPES section ### BLANKET section ### # blankettimelist - times when blanket block is applied # To activate a storyboard change is required - see examplef1.story #timelist = 'name=blankettimes,messageno=122,path=__LISTDIR__/blankettimelist' # NEW in v5.5 - allowedtld - blanket block tld that are NOT in this list # To activate a storyboard change is required - see examplef1.story # Note that this can overriden by entries in exception and grey sites/urls lists #sitelist = 'name=allowedtld,path=__LISTDIR__/allowedtldlist' # NEW in v5.5 - blanketblocktld - blanket block tld that are in this list # To activate a storyboard change is required - see examplef1.story # Note that this can overriden by entries in exception and grey sites/urls lists #sitelist = 'name=blanketblocktld,messageno=752,path=__LISTDIR__/blanketblocktldlist' ### ### END of BLANKET section ### TIMES section ### # bannedtimelist - times when no access is allowed # To activate a storyboard change is required - see examplef1.story #timelist = 'name=bannedtimes,messageno=122,path=__LISTDIR__/bannedtimelist' ### ### END of TIMES section ### APPS section ### ### These lists are useful for allowing/blocking apps or browsers based ### on the user-agent regexpboollist = 'name=banneduseragent,messageno=522,path=__LISTDIR__/bannedregexpuseragentlist' regexpboollist = 'name=exceptionuseragent,messageno=610,path=__LISTDIR__/exceptionregexpuseragentlist' ### ### END of APPS section ### REFERER section ### # refererexception lists are used to make a request an exception based on the # Referer header. sitelist = 'name=refererexception,messageno=620,path=__LISTDIR__/refererexceptionsitelist' ipsitelist = 'name=refererexception,messageno=620,path=__LISTDIR__/refererexceptionsiteiplist' urllist = 'name=refererexception,messageno=620,path=__LISTDIR__/refererexceptionurllist' # Some sites have the referering url in their url. Put these url in these # lists and e2g will extract the embeded url and then check this againist # the refererexception lists. # For best results be as specific as possible sitelist = 'name=embededreferer,path=__LISTDIR__/../common/embededreferersitelist' ipsitelist = 'name=embededreferer,path=__LISTDIR__/../common/embededreferersiteiplist' urllist = 'name=embededreferer,path=__LISTDIR__/../common/embededrefererurllist' ### ### END of REFERER section ### URL_MOD section ### # change list is used to modify url on the fly # use to enforce safe search etc. # Do not try and change target site - use redirect for this. regexpreplacelist = 'name=change,path=__LISTDIR__/urlregexplist' # Replace target connection site for a ssl connection request # Note: this does not change the url in any way. It just changes # where the request is sent upstream and the new target must accept # the original url. regexpreplacelist = 'name=sslreplace,path=__LISTDIR__/sslsiteregexplist' ### ### END of URL_MOD section ### REDIRECT section ### # Used to redirect browser to different site and or url regexpreplacelist = 'name=redirect,path=__LISTDIR__/urlredirectregexplist' ### ### END of REDIRECT section ### LOG_ONLY section ### # Categorise without blocking: # Supply categorised lists here and the category string shall be logged against # matching requests, but matching these lists does not perform any filtering # action. #sitelist = 'name=log,path=__LISTDIR__/logsitelist' #ipsitelist = 'name=log,path=__LISTDIR__/logsiteiplist' #urllist = 'name=log,path=__LISTDIR__/logurllist' #regexpboollist = 'name=log,path=__LISTDIR__/logregexpurllist' ### ### END of LOG_ONLY section ### POST section ### #maxuploadsize = -1 # # POST protection (web upload and forms) # does not block forms without any file upload, i.e. this is just for # blocking or limiting uploads # measured in kibibytes after MIME encoding and header bumph # use 0 for a complete block # use higher (e.g. 512 = 512Kbytes) for limiting # use -1 for no blocking # NOTE: POST PROTECTION IS NOT YET IMPLEMENTED IN V5 #maxuploadsize = 512 #maxuploadsize = 0 # default -1 ### ### END of POST section ### ACCESS_LOG section ### # Do not log sites/urls/ext # Useful to prevent requests not made by user directly being logged # Makes logs more readable sitelist = 'name=nolog,path=__LISTDIR__//../common/nologsitelist' ipsitelist = 'name=nolog,path=__LISTDIR__//../common/nologsiteiplist' urllist = 'name=nolog,path=__LISTDIR__//../common/nologurllist' regexpboollist = 'name=nolog,path=__LISTDIR__//../common/nologregexpurllist' fileextlist = 'name=nolog,path=__LISTDIR__//../common/nologextensionlist' ### ### END of ACCESS_LOG section ### ALERT_LOG section ### # Categories to log into alert.log in addition to access.log # This can be monitored by a separate process to provide real time alerts via email etc # set_alertlog must be enabled in e2guardian.conf for this to work. categorylist = 'name=alert,path=__LISTDIR__/alertcategorylist' ### ### END of ALERT_LOG section ### PHRASES section ### #weightedphrasemode = 0 # Weighted phrase mode # Optional; overrides the weightedphrasemode option in e2guardian.conf # for this particular group. See documentation for supported values in # that file. # textmimetypes = 'application/xhtml+xml,application/xml,application/json,application/javascript,application/x-javascript' # # Phrase filter additional mime types (by default just text/*) # stoptextmimetypes = 'text/javascript' # # Stop list of text mime types # Text mime types that should not be filtered # e.g. text/javascript which is scripting and not relevant text. # default '' categorydisplaythreshold = 0 # # Category display threshold # This option only applies to pages blocked by weighted phrase filtering. # Defines the minimum score that must be accumulated within a particular # category in order for it to show up on the block pages' category list. # All categories under which the page scores positively will be logged; those # that were not displayed to the user appear in brackets. # # -1 = display only the highest scoring category # 0 = display all categories (default) # > 0 = minimum score for a category to be displayed bannedphraselist = '__LISTDIR__/bannedphraselist' weightedphraselist = '__LISTDIR__/weightedphraselist' exceptionphraselist = '__LISTDIR__/exceptionphraselist' ## To use oldphraselists comment out last 3 lines and ## uncomment the follwing 3 lines #bannedphraselist = '__LISTDIR__/oldbannedphraselist' #weightedphraselist = '__LISTDIR__/oldweightedphraselist' #exceptionphraselist = '__LISTDIR__/oldexceptionphraselist' ### ### END of PHRASES section ### SEARCH section ### # Search term blocking # Search terms can be extracted from search URLs and filtered using one or # both of two different methods. # Method 1 is that developed by Protex where specific # search terms are contained in a bannedsearchlist. # (localbannedsearchlist and bannedsearchoveridelist can be used to suppliment # and overide this list as required.) # These lists contain banned search words combinations on each line. # Words are separated by '+' and must be in sorted order within a line. # so to block 'sexy girl' then the list must contain the line # girl+sexy # and this will block both 'sexy girl' and 'girl sexy' # To use this method, the searchregexplist must be enabled and the bannedsearchlist(s) defined # Method 2 is uses the # bannedphraselist, weightedphraselist and exceptionphraselist, with a separate # threshold for blocking than that used for normal page content. # To do this, the searchregexplist must be enabled and searchtermlimit # must be greater than 0. # Search engine regular expression list (need for both options) # List of regular expressions for matching search engine URLs. It is assumed # that the search terms themselves will be contained in the # of output of each expression. regexpreplacelist = 'name=searchterms,path=__LISTDIR__/../common/searchregexplist' # search engine regexp exception (overide) list # Used to prevent urls such os completetion suggestion requests from being detected # as search requests regexpboollist = 'name=searchtermexceptions, path=__LISTDIR__/../common/searchexceptionregexplist' # Search Term list(s) for option 1 searchlist = 'name=banned,path=__LISTDIR__/bannedsearchlist' searchlist = 'name=override,path=__LISTDIR__/bannedsearchoveridelist' searchlist = 'name=localbanned,messageno=581,path=__LISTDIR__/localbannedsearchlist' #searchtermlimit = 0 # # Search term limit (for Option 2) # The limit over which requests will be blocked for containing search terms # which match the weightedphraselist. This should usually be lower than the # 'naughtynesslimit' value above, because the amount of text being filtered # is only a few words, rather than a whole page. # This option must be uncommented if searchregexplist is uncommented. # A value of 0 here indicates that search terms should be extracted, # but no phrase filtering should be performed on the resulting text. # Search term phrase lists (for Option 2) # If the three lines below are uncommented, search term blocking will use # the banned, weighted & exception phrases from these lists, instead of using # the same phrase lists as for page content. This is optional but recommended, # as weights for individual phrases in the "normal" lists may not be # appropriate for blocking when those phrases appear in a much smaller block # of text. # Please note that all or none of the below should be uncommented, not a # mixture. # NOTE: these are phrase lists and still use the old style defines #bannedsearchtermlist = '__LISTDIR__/bannedsearchtermlist' #weightedsearchtermlist = '__LISTDIR__/weightedsearchtermlist' #exceptionsearchtermlist = '__LISTDIR__/exceptionsearchtermlist' ### ### END of SEARCH section ### AV_SCANNERS section ### #disablecontentscan = off # # Disable content scanning # If you enable this option you will disable content scanning for this group. # Content scanning primarily is AV scanning (if enabled) but could include # other types. # (on|off) default = off. #disablecontentscanerror = off # # Disable content scanning with error (timeout, AV crash, etc) # If you enable this option you will allow object with an unexpected result # Content scanning primarily is AV scanning (if enabled) but could include # other types. # With "on" you can allow INFECTED objects # (on|off) default = off. (default and highly recommended) #contentscanexceptions = off # # If 'on' exception sites, urls, users etc will be scanned # This is probably not desirable behavour as exceptions are # supposed to be trusted and will increase load. # Correct use of grey lists are a better idea. # (on|off) default = off #Virus checking exceptions - matched urls will not be virus checked #Note that you also need to amend site.story in order for this to work. #mimelist = 'name=exceptionvirus,path=__LISTDIR__/../contentscanners/exceptionvirusmimetypelist' #fileextlist = 'name=exceptionvirus,path=__LISTDIR__/../contentscanners/exceptionvirusextensionlist' #sitelist = 'name=exceptionvirus,path=__LISTDIR__/../contentscanners/exceptionvirussitelist' #ipsitelist = 'name=exceptionvirus,path=__LISTDIR__/../contentscanners/exceptionvirussiteiplist' #urllist = 'name=exceptionvirus,path=__LISTDIR__/../contentscanners/exceptionvirusurllist' ### ### END of AV_SCANNERS section ### DOWNLOADS section ### #Download manager activation lists # #used with DM plug-ins story_function to determine if a download manager will be # invoked #For trickle DM - this is new and experimental - use with caution! #regexpboollist = 'name=trickleuseragent,path=@E2CONFDIR@/lists/downloadmanagers/trickleregexpuseragentlist' #mimelist = 'name=trickleallow,path=@E2CONFDIR@/lists/downloadmanagers/tricklemimetypelist' #fileextlist = 'name=trickleallow,path=@E2CONFDIR@/lists/downloadmanagers/trickleexttypelist' #For fancy DM - do not use this - it is not yet operational #regexpboollist = 'name=fancyuseragent,path=@E2CONFDIR@/lists/downloadmanagers/fancyregexpuseragentlist' #mimelist = 'name=fancyallow,path=@E2CONFDIR@/lists/downloadmanagers/fancymimetypelist' #fileextlist = 'name=fancyallow,path=@E2CONFDIR@/lists/downloadmanagers/fancyexttypelist' ### ### END of DOWNLOADS section ### HEADER section ### # Outgoing HTTP request header rules: # Lists for blocking based on, and modification of, outgoing HTTP # request headers. Format for headerregexplist is one modification rule per # line, similar to content/URL modifications. Format for # bannedregexpheaderlist is one regular expression per line, with matching # headers causing a request to be blocked. # Headers are matched/replaced on a line-by-line basis, not as a contiguous # block. # Use for example, to remove cookies or prevent certain user-agents. regexpreplacelist = 'name=headermods,path=__LISTDIR__/headerregexplist' regexpboollist = 'name=bannedheader,path=__LISTDIR__/bannedregexpheaderlist' regexpboollist = 'name=exceptionheader,path=__LISTDIR__/exceptionregexpheaderlist' # add cookies etc regexpreplacelist = 'name=addheader,path=__LISTDIR__/addheaderregexplist' # Response HTTP header rules: # Lists for modification or removal of HTTP response headers. # Format for responseheaderregexplist is one rule per line, similar to # content/URL modifications. # Headers are matched/replaced on a line-by-line basis, not as a contiguous # block. # Use for example, to remove protocol upgrade requests. regexpreplacelist = 'name=responseheadermods,path=__LISTDIR__/responseheaderregexplist' ### ### END of HEADER section ### BLOCK_PAGE section ### #reportinglevel = 3 # # # -1 = log, but do not block - Stealth mode # 0 = just say 'Access Denied' # 1 = report why but not what denied phrase # 2 = report fully # 3 = use HTML template file (accessdeniedaddress ignored) - recommended # # If defined, this overrides the global setting in e2guardian.conf for # members of this filter group. #accessdeniedaddress = 'http://YOURSERVER.YOURDOMAIN/cgi-bin/e2guardian.pl' # # accessdeniedaddress is the address of your web server to which the cgi # e2guardian reporting script was copied. Only used in reporting levels # 1 and 2. # # This webserver must be either: # 1. Non-proxied. Either a machine on the local network, or listed as an # exception in your browser's proxy configuration. # 2. Added to the exceptionsitelist. Option 1 is preferable; this option is # only for users using both transparent proxying and a non-local server # to host this script. # #nonstandarddelimiter = off # # Non standard delimiter (only used with accessdeniedaddress) # To help preserve the full banned URL, including parameters, the variables # passed into the access denied CGI are separated using non-standard # delimiters. This can be useful to ensure correct operation of the filter # bypass modes. Parameters are split using "::" in place of "&", and "==" in # place of "=". # Default is enabled, but to go back to the standard mode, disable it. #htmltemplate = 'custom.html' # # HTML Template override # If defined, this specifies a custom HTML template file for members of this # filter group, overriding the global setting in e2guardian.conf. This is # only used in reporting level 3. # # The default template file path is //template.html # e.g. @E2DATADIR@/languages/ukenglish/template.html when using 'ukenglish' # language. # # This option generates a file path of the form: # // # e.g. @E2DATADIR@/languages/ukenglish/custom.html #neterrtemplate = 'custom_neterr_template.html' # #Template for use to report network issues and sites which are not responding # The default template file path is //neterr_template.html # e.g. @E2DATADIR@/languages/ukenglish/neterr_template.html when using # 'ukenglish' language. ### ### END of BLOCK_PAGE section ### BYPASS section ### #bypass = 0 # # Temporary Denied Page Bypass # This provides a link on the denied page to bypass the ban for a few minutes. To be # secure it uses a random hashed secret generated at daemon startup. You define the # number of seconds the bypass will function for before the deny will appear again. # To allow the link on the denied page to appear you will need to edit the template.html # or e2guardian.pl file for your language. # 300 = enable for 5 minutes # 0 = disable ( defaults to 0 ) # -1 - depreciated - for backward compatability enables cgibypass with bypassversion 1 bypassversion = 2 # # Byapss version 2 provides a secure cgi communication (see notes/cgi_bypass documentation) # # Bypass version # can be 1 or 2 # Always use v2 unless you have old style cgi hash generation in use # Default is 1 # cgibypass = 'off' # # cgibypass - Use a separate program/CGI to (in v1 generate) or (in v2 validate) link # 'on' or 'off' (default) #bypasskey = '' # # Temporary Denied Page Bypass Secret Key # Rather than generating a random key you can specify one. It must be more than 8 chars. # '' = generate a random one (recommended and default) # 'Mary had a little lamb.' = an example # '76b42abc1cd0fdcaf6e943dcbc93b826' = an example cgikey = 'you must change this text in order to be secure' # magic key for cgi bypass v2 - used to sign communications between e2g and cgi # default is blank # Users will not be able to bypass sites/urls in these lists sitelist = 'name=bannedbypass,messageno=500,path=__LISTDIR__/domainsnobypass' #ipsitelist = 'name=bannedbypass,messageno=500,path=__LISTDIR__/ipnobypass' #urllist = 'name=bannedbypass,messageno=501,path=__LISTDIR__/urlnobypass' #infectionbypass = 0 # # Infection/Scan Error Bypass # Similar to the 'bypass' setting, but specifically for bypassing files scanned and found # to be infected, or files that trigger scanner errors - for example, archive types with # recognised but unsupported compression schemes, or corrupt archives. # The option specifies the number of seconds for which the bypass link will be valid. # 300 = enable for 5 minutes # 0 = disable (default) # -1 - depreciated - for backward compatability enables cgiinfectionbypass with bypassversion 1 # cgiinfectionbypass = 'off' # # cgiinfectionbypass - Use a separate program/CGI to (v1 generate) or (v2 validate) link # 'on' or 'off' (default) #infectionbypasskey = '' # # Infection/Scan Error Bypass Secret Key # Same as the 'bypasskey' option, but used for infection bypass mode. #infectionbypasserrorsonly = on # # Infection/Scan Error Bypass on Scan Errors Only # Enable this option to allow infectionbypass links only when virus scanning fails, # not when a file is found to contain a virus. # on = enable (default and highly recommended) # off = disable ### ### END of BYPASS section ### EMAILER section ### ### Note this is experimental in v5 - not supported by maintainers # Email reporting - original patch by J. Gauthier #@EMAILSUPPORT@usesmtp = off #NOT YET TESTED # # If on, will enable system wide events to be reported by email. # need to configure mail program (see 'mailer' in global config) # and email recipients # default usesmtp = off #@EMAILSUPPORT@mailfrom = '' # # who the email would come from # example: mailfrom = 'e2guardian@mycompany.com' #@EMAILSUPPORT@avadmin = '' # # who the virus emails go to (if notify av is on) # example: avadmin = 'admin@mycompany.com' #@EMAILSUPPORT@contentadmin = '' # # who the content emails go to (when thresholds are exceeded) # and contentnotify is on # example: contentadmin = 'admin@mycompany.com' #@EMAILSUPPORT@avsubject = 'e2guardian virus block' # # Subject of the email sent when a virus is caught. # only applicable if notifyav is on # default avsubject = 'e2guardian virus block' #@EMAILSUPPORT@contentsubject = 'e2guardian violation' # # Subject of the email sent when violation thresholds are exceeded # default contentsubject = 'e2guardian violation' #@EMAILSUPPORT@notifyav = off # # This will send a notification, if usesmtp/notifyav is on, any time an # infection is found. # Important: If this option is off, viruses will still be recorded like a # content infraction. #@EMAILSUPPORT@notifycontent = off # # This will send a notification, if usesmtp is on, based on thresholds # below #@EMAILSUPPORT@thresholdbyuser = off # # results are only predictable with user authenticated configs # if enabled the violation/threshold count is kept track of by the user #@EMAILSUPPORT@violations = 0 # # number of violations before notification # setting to 0 will never trigger a notification #@EMAILSUPPORT@threshold = 0 # # this is in seconds. If 'violations' occur in 'threshold' seconds, then # a notification is made. # if this is set to 0, then whenever the set number of violations are made a # notifaction will be sent. ### ### END of EMAILER section ### OBSOLETE section ### # groupmode = 1 #DISABLED # Filter group mode IS NOT LONGER SUPPORTED # Unauthenticated users are treated as being in the default filter group. # ssllegacylogic = off # Enable legacy (E2) ssl logic # The option is replaced by storyboard logic #sslcertcheck = off - NOT implemented in V5 yet #SSL certificate checking # Check that ssl certificates for servers on https connections are valid # and signed by a ca in the configured path # ONLY for connections that are NOT MITM # bannedregexwithblanketblock = off # option is replaced by storyboard logic #blockdownloads = off # option is replaced by storyboard logic #embeddedurlweight = 0 # - NOT implemented in v5 # Embedded URL weighting # When set to something greater than zero, this option causes URLs embedded within a # page's HTML (from links, image tags, etc.) to be extracted and checked against the # bannedsitelist and bannedurllist. Each link to a banned page causes the amount set # here to be added to the page's weighting. # The behaviour of this option with regards to multiple occurrences of a site/URL is # affected by the weightedphrasemode setting. # # NB: Currently, this feature uses regular expressions that require the PCRE library. # As such, it is only available if you compiled e2guardian with '--enable-pcre=yes'. # You can check compile-time options by running 'e2guardian -v'. # # Set to 0 to disable. # Defaults to 0. # WARNING: This option is highly CPU intensive! #onlymitmsslgrey = off - ignored in V5 #Limit SSL MITM to sites in greysslsitelist(s) # ignored if sslmitm is off # SSL sites not matching greysslsitelist will be treat as if sslmitm is off. # The option is replaced by storyboard logic #contentregexplist = '__LISTDIR__/contentregexplist' # # not yet implemented in v5 ### ### END of OBSOLETE section ### INFO section ### ### No settings just info on new features etc # Relative paths (from v5.4.2) # Relative paths can used in .Include<> and list files. # The directory of current file will be insert where the file name # does not start with '/' # LISTDIR 'variable' definition (from v5.4.2) # LISTDIR can be defined in .conf files. # This allows for more readable configuration and for templating. # This works similarly to a shell environment variable # The text between <> will replace occurrences of __LISTDIR__ in .conf and # list files. # Note: Currently only LISTDIR may be defined. # # The mapping is actioned as the file is read and is valid until another LISTDIR # is defined later in the file, or in an included .conf file. # # The scope of LISTDIR is in the rest of file it is defined in and all # .Include<> files or list files in that portion of the file. ### ### END of INFO section e2guardian-5.5.8r/configs/examplef1.story.in000066400000000000000000000043211477372360500207730ustar00rootroot00000000000000.Include<@E2CONFDIR@/common.story> .Include<@E2CONFDIR@/site.story> # Add any altered functions for this filtergroup here # They will overwrite library or site level definitions # To allow unfiltered access to this group # uncomment next 4 lines #function(checkrequest) #if(true) return setexception #function(thttps-checkrequest) #if(true) return setexception # To block all access to this group # uncomment next 4 lines #function(checkrequest) #if(true,,105) return setblock #function(sslexceptioncheck) #function(localsslcheckrequest) # To block all access to this group at certain times # define times in bannedtimelist, # uncomment the list definition for 'bannedtimes' in e2guardianfn.conf, # and uncomment the next 2 lines #function(checktimesblocked) #if(timein,bannedtimes) return true # Note: Blanket blocks are checked after exceptions # and can be used to make a 'walled garden' filtergroup # To create blanket block for http (and MITM https) # uncomment next line and one condition line. #function(checkblanketblock) #if(true,,502) return setblock # = ** total blanket #if(siteisip,,505) return setblock # = *ip ip blanket # To use timed blanket block # define times in blankettimelist, # uncomment the list definition for 'blankettimes' in e2guardianfn.conf, # and uncomment the next line #if(timein,blankettimes) return setblock # To create blanket block for SSL # uncomment next line and one condition line. #function(sslcheckblanketblock) #if(true,,506) return setblock # = **s total blanket #if(siteisip,,507) return setblock # = **ips ip blanket # To limit MITM to sslgreylist # replaces onlymitmsslgrey e2guardianf1.conf option # uncomment the next 2 lines #function(sslcheckmitm) #if(true) return sslcheckmitmgreyonly # SNI checking - overrides default action when no SNI or TSL is present on a # THTTPS connection # To allow (tunnell) non-tls and/or non-sni connections uncomment the next 3 lines #function(checksni) #ifnot(tls,,511) return setexception # change to setblock to block only non-tls #ifnot(hassniset,,512) return setexception # automitm - overrides default action when thttps # To disable automitm for trans https uncomment the next 2 lines # function(thttps_automitm) # if(true) unsetautomitm e2guardian-5.5.8r/configs/lists/000077500000000000000000000000001477372360500165405ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/Makefile.am000077500000000000000000000011021477372360500205710ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in E2DATADIR = $(E2CONFDIR)/lists SUBDIRS = oldphraselists phraselists . authplugins rooms common example.group if NEED_CSLISTS SUBDIRS += contentscanners endif if NEED_DMLISTS SUBDIRS += downloadmanagers endif WLISTS = README EXTRA_DIST = install-data-local: $(mkinstalldirs) $(DESTDIR)$(E2DATADIR) && \ for l in $(WLISTS) ; do \ echo "$(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l"; \ $(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l; \ done uninstall-local: for l in $(WLISTS) ; do \ rm -f $(DESTDIR)$(E2DATADIR)/$$l ; \ done e2guardian-5.5.8r/configs/lists/README000066400000000000000000000013611477372360500174210ustar00rootroot00000000000000README for lists directory All lists have now been moved into sub-directories As before plugins have their own directories for lists and mappings used by plugins Sub-directories: authplugins - lists/maps used by authplugins blacklists - placeholder for 3rd party url lists common - lists used by e2guardian.conf and common lists for groups contentscanners - lists used by content/AV scanners downloadmanagers - lists used by downloadmanagers example.group - example lists for group use used by e2guardianf1.conf group1 - lists for group 1 group2 - lists for group 2 ... groupn - lists for group n oldphraselists - old style format phraselists phraselists - lists of phrases rooms - location (ip) based block/allow e2guardian-5.5.8r/configs/lists/authplugins/000077500000000000000000000000001477372360500211035ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/authplugins/Makefile.am000077500000000000000000000007141477372360500231440ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in E2DATADIR = $(E2CONFDIR)/lists/authplugins SUBDIRS = . WLISTS = ipgroups portgroups filtergroupslist EXTRA_DIST = $(WLISTS) install-data-local: $(mkinstalldirs) $(DESTDIR)$(E2DATADIR) && \ for l in $(WLISTS) ; do \ echo "$(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l"; \ $(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l; \ done uninstall-local: for l in $(WLISTS) ; do \ rm -f $(DESTDIR)$(E2DATADIR)/$$l ; \ done e2guardian-5.5.8r/configs/lists/authplugins/filtergroupslist000077500000000000000000000004101477372360500244450ustar00rootroot00000000000000# Filter Groups List file for e2guardian # # Format is =<1-99> where 1-99 are the groups # Legacy format is also recognised: =filter<1-99> where 1-99 are the groups # # Eg: # daniel=2 # # This file is only of use if you have more than 1 filter group # e2guardian-5.5.8r/configs/lists/authplugins/ipgroups000077500000000000000000000004111477372360500226750ustar00rootroot00000000000000# IP-Group list # Used by the IP-based auth plugin to assign IP addresses to filter groups. # # Examples: # Straight IP matching: #192.168.0.1 = filter1 # Subnet matching: #192.168.1.0/255.255.255.0 = filter1 # Range matching: #192.168.1.0-192.168.1.255 = filter1 e2guardian-5.5.8r/configs/lists/authplugins/portgroups000077500000000000000000000002761477372360500232620ustar00rootroot00000000000000# Port-Group list # Used by the Port-based auth plugin to assign Ports to filter groups. # # Note that ports MUST be entered in ascending order # # Examples: #8080 = filter1 #8081 = filter2 e2guardian-5.5.8r/configs/lists/common/000077500000000000000000000000001477372360500200305ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/common/Makefile.am000077500000000000000000000015011477372360500220640ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in E2DATADIR = $(E2CONFDIR)/lists/common SUBDIRS = . WLISTS = authexceptioniplist \ authexceptionsitelist \ authexceptionurllist \ bannedclientlist \ bannediplist \ browserregexplist \ embededreferersiteiplist \ embededreferersitelist \ embededrefererurllist \ exceptionclientlist \ exceptioniplist \ nologextensionlist \ nologregexpurllist \ nologsiteiplist \ nologsitelist \ nologurllist \ nomitmsiteiplist \ nomitmsitelist \ searchregexplist \ searchexceptionregexplist \ README EXTRA_DIST = install-data-local: $(mkinstalldirs) $(DESTDIR)$(E2DATADIR) && \ for l in $(WLISTS) ; do \ echo "$(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l"; \ $(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l; \ done uninstall-local: for l in $(WLISTS) ; do \ rm -f $(DESTDIR)$(E2DATADIR)/$$l ; \ done e2guardian-5.5.8r/configs/lists/common/README000066400000000000000000000034551477372360500207170ustar00rootroot00000000000000README for lists/common directory ### Lists held in this directory:- ## Lists defined in e2guardian.conf # Sites that are impossible or undesirable to MITM # nomitmsitelist nomitmsiteiplist # Banned clients # bannediplist # ONLY for banned client IP # exceptioniplist # ONLY for exception client IP # bannedclientlist # Put client dns names in bannedclientlist if required exceptionclientlist # Put client dns names in exceptionclientlist if required # Auth exceptions # Target Sites/Urls allowed before authentication # to allow for machines to update without user authentication # authexceptioniplist authexceptionsitelist authexceptionurllist browserregexpboollist # # List of regexp that match match User-agent of browsers # Used to determine if client is a browser # and decide whether to send a block page or go MITM ## Lists common to all groups (defined in e2guardianfn.conf) # Embeded referer # Some sites have the referering url in their url. Put these url in these # lists and e2g will extract the embeded url and then check this againist # the refererexception lists. # For best results be as specific as possible embededreferersitelist embededreferersiteiplist embededrefererurllist # Nolog lists # Do not log sites/urls/ext # Useful to prevent requests not made by user directly being logged # Makes logs more readable nologsitelist nologsiteiplist nologurllist nologregexpurllist nologextensionlist # Search engine regular expression list # List of regular expressions for matching search engine URLs. It is assumed # that the search terms themselves will be contained in the # of output of each expression. searchregexplist # # search engine regexp exception (overide) list # Used to prevent urls such os completetion suggestion requests from being detected # as search requests searchexceptionregexplist e2guardian-5.5.8r/configs/lists/common/authexceptioniplist000066400000000000000000000002461477372360500240620ustar00rootroot00000000000000#Access allowed prior to authentication # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/common/authexceptionsitelist000066400000000000000000000000501477372360500244070ustar00rootroot00000000000000#Access allowed prior to authentication e2guardian-5.5.8r/configs/lists/common/authexceptionurllist000066400000000000000000000000501477372360500242450ustar00rootroot00000000000000#Access allowed prior to authentication e2guardian-5.5.8r/configs/lists/common/bannedclientlist000077500000000000000000000002051477372360500232750ustar00rootroot00000000000000# Domain names of client machines to # disallow web access to. # # This is not the the domains of web servers # you want to filter. e2guardian-5.5.8r/configs/lists/common/bannediplist000077500000000000000000000003431477372360500224320ustar00rootroot00000000000000# IP addresses of client machines to # disallow web access to. # # This is not the IP of web servers # you want to filter. # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/common/browserregexplist000066400000000000000000000004231477372360500235440ustar00rootroot00000000000000# Reg Exp to check user-agent shows request is from a browser # # The format is: "extended regular expression" (Firefox/) (Chrome/) (MSIE/) (Opera/) (Safari/) (Edge/) # curl and wget are not browsers - but are often used for testing # so are included below (curl/) (wget/) e2guardian-5.5.8r/configs/lists/common/embededreferersiteiplist000066400000000000000000000003371477372360500250300ustar00rootroot00000000000000# Embeded referer IP sites # sites which may contain embeded exception referer sites in the url # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/common/embededreferersitelist000066400000000000000000000001361477372360500244740ustar00rootroot00000000000000# Embeded referer sites # sites which may contain embeded exception referer sites in the url e2guardian-5.5.8r/configs/lists/common/embededrefererurllist000066400000000000000000000003101477372360500243240ustar00rootroot00000000000000# Embeded referer urls # urls which may contain embeded exception referer sites in the url # e.g. to allow youtube video when embeded in a trusted referer site/urls # www.youtube.com/get_video_info e2guardian-5.5.8r/configs/lists/common/exceptionclientlist000077500000000000000000000006301477372360500240460ustar00rootroot00000000000000# Doamin names of computers from which # web access should not be filtered. # # These would be servers which # need unfiltered access for # updates. Also administrator # workstations which need to # download programs and check # out blocked sites should be # put here. # # To work you must # cater for reverse DNS lookups # on your LAN and enable the # "reverseclientiplookups" option in # e2guardian.conf e2guardian-5.5.8r/configs/lists/common/exceptioniplist000077500000000000000000000011021477372360500231730ustar00rootroot00000000000000# IP addresses of computers from which # web access should not be filtered. # # These would be servers which # need unfiltered access for # updates. Also administrator # workstations which need to # download programs and check # out blocked sites should be # put here. # # Hostnames are NOT allowed here, # put these in exceptionclientlist and # enable the reverseclientlookups option. # # This is not the IP of web servers # you don't want to filter. #192.168.0.1 #192.168.0.2 #192.168.42.2 # Ranges and subnets can also be used, # e.g. # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/common/nologextensionlist000077500000000000000000000004131477372360500237230ustar00rootroot00000000000000# No Log file extension list # requests with extensions in this list will not be logged # Text/web document types .css # Image types #.bmp #.cod #.gif #.ief #.jpe #.jpeg #.jpg #.jfif #.tif #.tiff #.ras #.cmx #.ico #.pnm #.pbm #.pgm #.ppm #.rgb #.xbm #.xpm #.xwd e2guardian-5.5.8r/configs/lists/common/nologregexpurllist000077500000000000000000000001651477372360500237300ustar00rootroot00000000000000# No Log regular expression URL list # # This acts as a list of URL regexes which, if matched, will # not be logged e2guardian-5.5.8r/configs/lists/common/nologsiteiplist000077500000000000000000000003421477372360500232050ustar00rootroot00000000000000# No Log IP site list # # This acts as a list of IP sites which, when found, # will not be logged # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/common/nologsitelist000077500000000000000000000001401477372360500226500ustar00rootroot00000000000000# No Log site list # # This acts as a list of domains which, when found, # will not be logged. e2guardian-5.5.8r/configs/lists/common/nologurllist000077500000000000000000000002201477372360500225050ustar00rootroot00000000000000# No Log URL list # # This acts as a list of URLs which, when found, will # not be logged www.google.com/complete www.google.co.uk/complete e2guardian-5.5.8r/configs/lists/common/nomitmsiteiplist000066400000000000000000000004711477372360500233720ustar00rootroot00000000000000#IP in nomitmsiteiplist #This list is to stop any attempt to mitm these sites, # including trying to MITM to client to deliver an error message #Only put IP sites which cannot be MITM in this list #This applies to sites which are only used by apps #Sites not using http(s) but connecting via https or CONNECT e2guardian-5.5.8r/configs/lists/common/nomitmsitelist000066400000000000000000000007271477372360500230450ustar00rootroot00000000000000#domains in nomitmsitelist #Don't bother with the www. or the http:// #This list is to stop any attempt to mitm these sites, # including trying to MITM to client to deliver an error message #Only put sites which cannot be MITM in this list #This applies to sites which are only used by apps #Sites not using http(s) but connecting via https or CONNECT #NOTE: Sites using just IP should be put into nomitmsiteiplist reports.crashlytics.com # google app crash tool e2guardian-5.5.8r/configs/lists/common/searchexceptionregexplist000066400000000000000000000004161477372360500252470ustar00rootroot00000000000000# This defines urls which will be not search term checked # Overrides searchregexplist # # The format is: "extended regular expression" # #"^http://[0-9a-z]+\.google\.[a-z]+[-%.0-9a-z]*/complete" "^http://www.google.com/complete" "^http://www.google.co.uk/complete" e2guardian-5.5.8r/configs/lists/common/searchregexplist000066400000000000000000000031471477372360500233340ustar00rootroot00000000000000# This defines sites which will be search term checked # # The format is: "extended regular expression"->"search words" # # left is the site matching pattern # right is the search words "^http://[0-9a-z]+\.google\.[a-z]+[-/%.0-9a-z]*\?q=([^&]*)\&.*"->"\1" "^http://[0-9a-z]+\.google\.[a-z]+[-/%.0-9a-z]*/.*\&q=([^&]*).*"->"\1" "^http://[0-9a-z]+\.google\.[a-z]+[-/%.0-9a-z]*\?q=([^&]*)"->"\1" "^http://[0-9a-z]+\.google\.[a-z]+[-/%.0-9a-z]*/.*\&q=([^&]*)"->"\1" "^http://www\.qwant\.com/.*\?q=([^&]*)\&.*"->"\1" "^http://www\.qwant\.com/.*\&q=([^&]*).*"->"\1" "^http://www\.qwant\.com/.*\?q=([^&]*)"->"\1" "^http://www\.qwant\.com/.*\&q=([^&]*)"->"\1" "^http://api\.qwant\.com/.*\?q=([^&]*)\&.*"->"\1" "^http://api\.qwant\.com/.*\&q=([^&]*).*"->"\1" "^http://api\.qwant\.com/.*\?q=([^&]*)"->"\1" "^http://api\.qwant\.com/.*\&q=([^&]*)"->"\1" "^http://duckduckgo.com/\?q=([^&]*)\&.*"->"\1" "^http://duckduckgo.com/.*\&q=([^&]*).*"->"\1" "^http://duckduckgo.com/\?q=([^&]*)"->"\1" "^http://duckduckgo.com/.*\&q=([^&]*)"->"\1" "^http://[0-9a-z]+\.youtube\.[a-z]+[-/%.0-9a-z]*\?search_query=([^&]*).*"->"\1" "^http://[0-9a-z]+\.yahoo\.[a-z]+[-/%.0-9a-z]*/search[^?]*\?p=([^&]*).*"->"\1" "^http://[0-9a-z]+\.yahoo\.[a-z]+[-/%.0-9a-z]*/search[^?]*.*\&p=([^&]*).*"->"\1" "^http://[0-9a-z]+\.answers\.[a-z]+[-/%.0-9a-z]*\?s=([^&]*)\&.*"->"\1" "^http://www\.answers\.[a-z]+[-%.0-9a-z]*/([^&]*).*"->"\1" "^http://[0-9a-z]+\.bing\.com[-/%.0-9a-z]*/search\?q=([^&]*).*"->"\1" "^http://[0-9a-z]+\.bing\.com[-/%.0-9a-z]*\&q=([^&]*).*"->"\1" "^http://www.kiddle.co/s.php\?q=([^&]*)\&.*"->"\1" "^http://www.kiddle.co/s.php\?q=([^&]*)"->"\1" e2guardian-5.5.8r/configs/lists/contentscanners/000077500000000000000000000000001477372360500217475ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/contentscanners/Makefile.am000077500000000000000000000010321477372360500240020ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in E2DATADIR = $(E2CONFDIR)/lists/contentscanners SUBDIRS = . WLISTS = exceptionvirusmimetypelist exceptionvirusextensionlist \ exceptionvirussitelist exceptionvirusurllist EXTRA_DIST = $(WLISTS) install-data-local: $(mkinstalldirs) $(DESTDIR)$(E2DATADIR) && \ for l in $(WLISTS) ; do \ echo "$(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l"; \ $(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l; \ done uninstall-local: for l in $(WLISTS) ; do \ rm -f $(DESTDIR)$(E2DATADIR)/$$l ; \ done e2guardian-5.5.8r/configs/lists/contentscanners/exceptionvirusextensionlist000077500000000000000000000011431477372360500276140ustar00rootroot00000000000000#Exception Virus extension list #This file originally from: #http://dgav.sourceforge.net # 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 .tiff .ico # http://www.kb.cert.org/vuls/id/297462 #.jpg #.jpeg e2guardian-5.5.8r/configs/lists/contentscanners/exceptionvirusmimetypelist000077500000000000000000000007031477372360500274320ustar00rootroot00000000000000# MIME types the virus scanning code ignores. #This file originally from: #http://dgav.sourceforge.net audio/mpeg audio/x-mpeg audio/x-pn-realaudio audio/x-wav audio/x-realaudio audio/x-pn-realaudio audio/vnd.rn-realaudio application/ogg 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 # text/html e2guardian-5.5.8r/configs/lists/contentscanners/exceptionvirussitelist000077500000000000000000000004271477372360500265500ustar00rootroot00000000000000#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. # example.com e2guardian-5.5.8r/configs/lists/contentscanners/exceptionvirusurllist000077500000000000000000000006071477372360500264060ustar00rootroot00000000000000#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 e2guardian-5.5.8r/configs/lists/downloadmanagers/000077500000000000000000000000001477372360500220655ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/downloadmanagers/Makefile.am000077500000000000000000000011401477372360500241200ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in E2DATADIR = $(E2CONFDIR)/lists/downloadmanagers SUBDIRS = . WLISTS = managedmimetypelist managedextensionlist \ trickleregexpuseragentlist \ fancyregexpuseragentlist \ tricklemimetypelist \ fancymimetypelist \ trickleexttypelist \ fancyexttypelist EXTRA_DIST = $(WLISTS) install-data-local: $(mkinstalldirs) $(DESTDIR)$(E2DATADIR) && \ for l in $(WLISTS) ; do \ echo "$(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l"; \ $(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l; \ done uninstall-local: for l in $(WLISTS) ; do \ rm -f $(DESTDIR)$(E2DATADIR)/$$l ; \ done e2guardian-5.5.8r/configs/lists/downloadmanagers/fancyexttypelist000066400000000000000000000001061477372360500254240ustar00rootroot00000000000000#fancyexttypelist # # ext types where fancy download should be used # e2guardian-5.5.8r/configs/lists/downloadmanagers/fancymimetypelist000066400000000000000000000001141477372360500255520ustar00rootroot00000000000000#tricklemimetypelist # # mime types where trickle download should be used # e2guardian-5.5.8r/configs/lists/downloadmanagers/fancyregexpuseragentlist000066400000000000000000000004231477372360500271340ustar00rootroot00000000000000# Reg Exp to check user-agent shows request is from a browser # # The format is: "extended regular expression" (Firefox/) (Chrome/) (MSIE/) (Opera/) (Safari/) (Edge/) # curl and wget are not browsers - but are often used for testing # so are included below (curl/) (wget/) e2guardian-5.5.8r/configs/lists/downloadmanagers/managedextensionlist000066400000000000000000000010561477372360500262370ustar00rootroot00000000000000# Managed extension list # For use as the 'managedextensionlist' option in the configuration of # download managers supporting this feature. # # When enabled, only content matching the extensions given in this list will # be handled by the download manager. # If a managedmimetypelist is also enabled, then only content matching both # a mimetype and an extension in the lists will be handled. .bat .cab .com .crt .exe .hlp .ini .hta .inf .lnk .mdb .pcd .sh .vbs .doc .xls .gz .tar .bz2 .sit .bin .hqx .zip .sxw .doc .iso .pdf .rar .ace .arj .dll .mda .mde e2guardian-5.5.8r/configs/lists/downloadmanagers/managedmimetypelist000066400000000000000000000062541477372360500260610ustar00rootroot00000000000000# Managed mime type list # For use as the 'managedmimetypelist' option in the configuration of # download managers supporting this feature. # # When enabled, only content matching the mime types given in this list will # be handled by the download manager. # If a managedextensionlist is also enabled, then only content matching both # a mimetype and an extension in the lists will be handled. # List originally by David Chewning # MIME Types Researched at http://filext.com/ # bat application/bat application/x-bat # cab application/cab zz-application/zz-winassoc-cab # com application/com application/x-com # crt application/x-x509-ca-cert application/pkix-cert application/keychain_access # exe application/octet-stream application/exe application/x-exe application/dos-exe vms/exe application/x-winexe application/msdos-windows # hlp application/winhlp application/x-helpfile application/x-winhelp zz-application/zz-winassoc-hlp # ini zz-application/zz-winassoc-ini # hta application/hta # inf text/inf application/x-setupscript # lnk application/x-ms-shortcut # mdb application/x-msaccess application/vnd.msaccess application/mdb application/x-mdb zz-application/zz-winassoc-mdb # pcd image/pcd image/x-photo-cd # sh application/x-sh application/x-shar # vbs application/x-vbs text/vbs text/vbscript # doc application/msword application/doc application/vnd.msword application/vnd.ms-word application/winword application/word application/x-msw6 application/x-msword zz-application/zz-winassoc-doc # xls application/msexcel application/x-msexcel application/x-ms-excel application/vnd.ms-excel application/x-excel application/x-dos_ms_excel application/xls application/x-xls zz-application/zz-winassoc-xls # gz application/gzip application/x-gunzip application/gzipped application/gzip-compressed gzip/document # tar application/tar applicaton/x-gtar multipart/x-tar # bz2 application/bzip2 application/x-bz2 application/x-bzip # sit application/stuffit application/x-sit # bin application/macbinary application/x-macbinary application/bin application/binary # hqx application/binhex application/mac-binhex application/mac-binhex40 application/x-winzip # zip application/zip application/x-zip multipart/x-zip # Audio/video types commented out to prevent breakage of streaming apps # mp3 #audio/mpeg #audio/x-mpeg #audio/mp3 #audio/x-mp3 #audio/mpeg3 #audio/x-mpeg3 #audio/mpg #audio/x-mpg #audio/x-mpegaudio # mpeg #video/mpeg # avi #video/avi #video/msvideo #video/x-msvideo #image/avi #video/xmpg2 #application/x-troff-msvideo #audio/aiff #audio/avi # asf #audio/asf #application/asx #video/x-ms-asf-plugin #application/x-mplayer2 #video/x-ms-asf #application/vnd.ms-asf #video/x-ms-asf-plugin #video/x-ms-wm #video/x-ms-wmx # ogg #audio/x-ogg #application/x-ogg # Multiple extensions # bat, com, dll, exe application/x-msdos-program # cab, gz, tar, zip application/x-compress # cab, gz, tar, bz2, zip application/x-compressed # com, dll, exe, bin application/x-msdownload # mda, mdb, mde application/msaccess application/vnd.ms-access # sct, wsc text/scriptlet # sit, bin, hqx application/x-stuffit # gz, hqx application/x-gzip # tar, hqx application/x-tar # hqx, zip application/x-zip-compressed e2guardian-5.5.8r/configs/lists/downloadmanagers/trickleexttypelist000066400000000000000000000001121477372360500257560ustar00rootroot00000000000000#trickleexttypelist # # ext types where trickle download should be used # e2guardian-5.5.8r/configs/lists/downloadmanagers/tricklemimetypelist000066400000000000000000000001141477372360500261070ustar00rootroot00000000000000#tricklemimetypelist # # mime types where trickle download should be used # e2guardian-5.5.8r/configs/lists/downloadmanagers/trickleregexpuseragentlist000066400000000000000000000004241477372360500274720ustar00rootroot00000000000000# Reg Exp to check user-agent is suitable for trickle download # # The format is: "extended regular expression" (Firefox/) (Chrome/) (MSIE/) (Opera/) (Safari/) (Edge/) # curl and wget are not browsers - but are often used for testing # so are included below (curl/) (wget/) e2guardian-5.5.8r/configs/lists/example.group/000077500000000000000000000000001477372360500213265ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/example.group/Makefile.am000077500000000000000000000047421477372360500233740ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in \ bannedphraselist bannedsitelist bannedurllist \ exceptionphraselist weightedphraselist \ oldbannedphraselist \ oldexceptionphraselist oldweightedphraselist E2DATADIR = $(E2CONFDIR)/lists/example.group SUBDIRS = . WLISTS = addheaderregexplist \ alertcategorylist \ allowedtldlist \ bannedsearchoveridelist \ bannedextensionlist \ bannedmimetypelist \ bannedphraselist \ blanketblocktldlist \ oldbannedphraselist \ bannedregexpheaderlist \ bannedregexpurllist \ bannedregexpuseragentlist \ bannedsearchlist \ bannedsiteiplist \ bannedtimelist \ exceptionvirussiteiplist \ bannedsitelist \ bannedsslsiteiplist \ bannedsslsitelist \ bannedurllist \ blankettimelist \ contentregexplist \ exceptionextensionlist \ exceptionvirusextensionlist \ exceptionfilesiteiplist \ exceptionfilesitelist \ exceptionfileurllist \ exceptionmimetypelist \ exceptionphraselist \ oldexceptionphraselist \ exceptionregexpurllist \ exceptionregexpuseragentlist \ exceptionsiteiplist \ exceptionsitelist \ exceptionregexpheaderlist \ exceptionurllist \ greysiteiplist \ greysitelist \ greysslsiteiplist \ greysslsitelist \ greyurllist \ headerregexplist \ localbannedsearchlist \ localbannedsiteiplist \ localbannedsitelist \ localbannedsslsiteiplist \ localbannedsslsitelist \ localbannedurllist \ localexceptionsiteiplist \ localexceptionsitelist \ localexceptionurllist \ localgreysiteiplist \ localgreysitelist \ localgreysslsiteiplist \ localgreysslsitelist \ localgreyurllist \ logregexpurllist \ localsemiexceptionsiteiplist \ localsemiexceptionsitelist \ logsiteiplist \ logsitelist \ logurllist \ nocheckcertsiteiplist \ nocheckcertsitelist \ refererexceptionsiteiplist \ refererexceptionsitelist \ refererexceptionurllist \ responseheaderregexplist \ semiexceptionsiteiplist \ semiexceptionsitelist \ sslsiteregexplist \ urlredirectregexplist \ urlregexplist \ ipnobypass \ domainsnobypass \ urlnobypass \ weightedphraselist \ oldweightedphraselist \ README EXTRA_DIST = bannedphraselist.in \ oldbannedphraselist.in \ bannedsitelist.in \ bannedurllist.in \ exceptionphraselist.in \ oldexceptionphraselist.in \ domainsnobypass.in \ urlnobypass.in \ weightedphraselist.in \ oldweightedphraselist.in install-data-local: $(mkinstalldirs) $(DESTDIR)$(E2DATADIR) && \ for l in $(WLISTS) ; do \ echo "$(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l"; \ $(INSTALL_DATA) $$l $(DESTDIR)$(E2DATADIR)/$$l; \ done uninstall-local: for l in $(WLISTS) ; do \ rm -f $(DESTDIR)$(E2DATADIR)/$$l ; \ done e2guardian-5.5.8r/configs/lists/example.group/README000077500000000000000000000127051477372360500222160ustar00rootroot00000000000000 ### Lists in this directory relate to a single filter group ### each filter group has it's own directory ### ### Many of the exception/grey/banned site/ipsite/url lists are used for ### switching on/off standard list categories ### ### Where available it is best to use the LOCAL lists for your own lists of ### sites/urls as these override the main ones. ## Order of checking is broadly as follows:- ## ## 1. If in Local Exception lists - allow with no further checking ## 2. If in Local Grey lists - retrieve page and content-check ## 3. If in Local Banned lists - block with no further checking ## 4. If in Main Exception lists - allow with no further checking ## 5. If in Main Grey lists - retrieve page and content-check ## 6. If in Main Banned lists (or blanket blocked) - block with no further checking ## 7. If gets here - retrieve page and content-check ## 8. Check blocked/exception file/mime types. ## 9. Content-check page ## Lists in this directory:- # Nocheckcert lists # Do not check ssl certificates for sites listed # Can be used to allow sites with self-signed or invalid certificates # or to reduced CPU load by not checking certs on heavily used sites (e.g. Google, Bing) # Use with caution! # Ignored if mitmcheckcert is 'off' # nocheckcertsitelist nocheckcertsiteiplist # greyssl lists only used in onlymitmsslgrey mode to define sites that # will be MITM greysslsitelist greysslsiteiplist localgreysslsitelist localgreysslsiteiplist ## Exception lists ## Put sites you trust in these lists ## they override Grey and Banned entries exceptionsitelist exceptionsiteiplist exceptionurllist exceptionregexpurllist ## Grey (i.e. content check) lists ## Put sites you want content-checked in these lists ## they override Banned entries greysitelist greysiteiplist greyurllist ## Banned sites/urls bannedsitelist bannedsiteiplist bannedurllist bannedregexpurllist # 'bannedssl' lists are not currently used bannedsslsitelist bannedsslsiteiplist ## Local Exception lists localexceptionsitelist localexceptionsiteiplist localexceptionurllist ## Local Grey lists localgreysitelist localgreysiteiplist localgreyurllist ## Local Banned lists localbannedsitelist localbannedsiteiplist localbannedurllist ## Bannedssl lists are not currently used localbannedsslsitelist localbannedsslsiteiplist # File type/extensions exceptionextensionlist exceptionmimetypelist # Use the following lists to block specific kinds of file downloads. bannedextensionlist bannedmimetypelist # In either file filtering mode, the following lists can be used to override # MIME type & extension blocks for particular domains & URLs (trusted download sites). # exceptionfilesitelist exceptionfilesiteiplist exceptionfileurllist # bannedtimelist - times when no access is allowed # To activate a storyboard change is required - see examplef1.story bannedtimelist # blankettimelist - times when blanket block is applied # To activate a storyboard change is required - see examplef1.story blankettimelist #allowedtldlist - blanket block if NOT in this list # To activate a storyboard change is required - see examplef1.story allowedtldlist #blanketblocktldlist - blanket block if IS in this list # To activate a storyboard change is required - see examplef1.story blanketblocktldlist ### These lists are useful for allowing/blocking apps or browsers based ### on the user-agent bannedregexpuseragentlist exceptionregexpuseragentlist # refererexception lists are used to make a request an exception based on the # Referer header. refererexceptionsitelist refererexceptionsiteiplist refererexceptionurllist # Used to modify url on the fly # use to enforce safe search etc. # Do not try and change target site - use redirect for this. urlregexplist # Replace target connection site for a ssl connection request # Note: this does not change the url in any way. It just changes # where the request is sent upstream and the new target must accept # the original url. sslsiteregexplist # Used to redirect browser to different site and or url urlredirectregexplist # Categorise without blocking: # Supply categorised lists here and the category string shall be logged against # matching requests, but matching these lists does not perform any filtering # action. logsitelist logsiteiplist logurllist logregexpurllist # Phrase lists bannedphraselist weightedphraselist exceptionphraselist oldbannedphraselist oldweightedphraselist oldexceptionphraselist # Search Term list(s) for option 1 bannedsearchlist bannedsearchoveridelist localbannedsearchlist # Outgoing HTTP request header rules: # Lists for blocking based on, and modification of, outgoing HTTP # request headers. Format for headerregexplist is one modification rule per # line, similar to content/URL modifications. Format for # bannedregexpheaderlist is one regular expression per line, with matching # headers causing a request to be blocked. # Headers are matched/replaced on a line-by-line basis, not as a contiguous # block. # Use for example, to remove cookies or prevent certain user-agents. headerregexplist bannedregexpheaderlist exceptionregexpheaderlist # add cookies or other headers etc to matched urls addheaderregexplist # Response HTTP header rules: # Lists for modification or removal of HTTP response headers. # Format for reponseheaderregexplist is one rule per line, similar to # content/URL modifications. # Headers are matched/replaced on a line-by-line basis, not as a contiguous # block. # Use for example, to remove protocol upgrade requests. responseheaderregexplist e2guardian-5.5.8r/configs/lists/example.group/addheaderregexplist000066400000000000000000000013361477372360500252640ustar00rootroot00000000000000#Add header where url matches # ## to enable restricted YouTube #"(^http://www\.youtube\.com/.*$)"->"YouTube-Restrict: Strict" #"(^http://m\.youtube\.com/.*$)"->"YouTube-Restrict: Strict" #"(^http://youtubei\.googleapis\.com/.*$)"->"YouTube-Restrict: Strict" #"(^http://youtube\.googleapis\.com/.*$)"->"YouTube-Restrict: Strict" #"(^http://www\.youtube-nocookie\.com/.*$)"->"YouTube-Restrict: Strict" #"(^http://www\.youtube\.com$)"->"YouTube-Restrict: Strict" #"(^http://m\.youtube\.com$)"->"YouTube-Restrict: Strict" #"(^http://youtubei\.googleapis\.com$)"->"YouTube-Restrict: Strict" #"(^http://youtube\.googleapis\.com$)"->"YouTube-Restrict: Strict" #"(^http://www\.youtube-nocookie\.com$)"->"YouTube-Restrict: Strict" e2guardian-5.5.8r/configs/lists/example.group/alertcategorylist000066400000000000000000000004151477372360500250120ustar00rootroot00000000000000# Categories to log into alert.log in addition to access.log # This can be monitored by a separate process to provide real time alerts via email etc # set_alertlog must be enabled in e2guardian.conf for this to work. # # one category per line - case is ignored # e2guardian-5.5.8r/configs/lists/example.group/allowedtldlist000077500000000000000000000003521477372360500243030ustar00rootroot00000000000000# TLD allowed - used with blanket block to block all TLDs NOT in this list # # Does NOT override exception lists #.tld so for example you can match .gov for example # can also be used for SLD e.g. sch.uk .com .uk .org .edu .io .gov e2guardian-5.5.8r/configs/lists/example.group/bannedextensionlist000077500000000000000000000115251477372360500253400ustar00rootroot00000000000000#Banned extension list # File extensions with executable code # The following file extensions can contain executable code. # This means they can potentially carry a virus to infect your computer. .ade # Microsoft Access project extension .adp # Microsoft Access project .asx # Windows Media Audio / Video .bas # Microsoft Visual Basic class module .bat # Batch file .cab # Windows setup file .chm # Compiled HTML Help file .cmd # Microsoft Windows NT Command script .com # Microsoft MS-DOS program .cpl # Control Panel extension .crt # Security certificate .dll # Windows system file .exe # Program .hlp # Help file .ini # Windows system file .hta # HTML program .inf # Setup Information .ins # Internet Naming Service .isp # Internet Communication settings # .js # JScript file - often needed in web pages # .jse # Jscript Encoded Script file - often needed in web pages .lnk # Windows Shortcut .mda # Microsoft Access add-in program .mdb # Microsoft Access program .mde # Microsoft Access MDE database .mdt # Microsoft Access workgroup information .mdw # Microsoft Access workgroup information .mdz # Microsoft Access wizard program .msc # Microsoft Common Console document .msi # Microsoft Windows Installer package .msp # Microsoft Windows Installer patch .mst # Microsoft Visual Test source files .pcd # Photo CD image, Microsoft Visual compiled script .pif # Shortcut to MS-DOS program .prf # Microsoft Outlook profile settings .reg # Windows registry entries .scf # Windows Explorer command .scr # Screen saver .sct # Windows Script Component .sh # Shell script .shs # Shell Scrap object .shb # Shell Scrap object .sys # Windows system file .url # Internet shortcut .vb # VBScript file .vbe # VBScript Encoded script file .vbs # VBScript file .vxd # Windows system file .wsc # Windows Script Component .wsf # Windows Script file .wsh # Windows Script Host Settings file .otf # Font file - can be used to instant reboot 2k and xp .ops # Office XP settings # Files which one normally things as non-executable but # can contain harmful macros and viruses .doc # Word document .xls # Excel document .pps # Other files which may contain files with executable code .gz # Gziped file .tar # Tape ARchive file .zip # Windows compressed file .tgz # Unix compressed file .bz2 # Unix compressed file .cdr # Mac disk image .dmg # Mac disk image .smi # Mac self mounting disk image .sit # Mac compressed file .sea # Mac compressed file, self extracting .bin # Mac binary compressed file .hqx # Mac binhex encoded file .rar # Similar to zip # Time/bandwidth wasting files .mp3 # Music file .mpeg # Movie file .mpg # Movie file .avi # Movie file .asf # this can also exploit a security hole allowing virus infection .iso # CD ISO image .ogg # Music file .wmf # Movie file .bin # CD ISO image .cue # CD ISO image # Banned Media extension list (Audio , Video , Streaming) # Arrange Alphabetically # Some have no Description #.3g2 # #.3gp # Nokia Movie File #.3gp2 #.3gpp #.3gpp2 #.aac # AAC Audio #.acp # AAC for SD Media #.adts #.aif #.aifc #.aiff # AIFF Audio #.amc # AMC Media #.amr # narrow-Band Content #.asf # Media / this can also exploit a security hole allowing virus infection #.asx # Windows Media Audio / Video #.au # uLaw/AU Audio #.avi # Movie file #.awb # AMR Wide-Band Content #.bwf #.caf # CAF Audio #.cda # Audio CD File #.cdda # Audio CD File #.cel #.cue # CD ISO image #.dif #.divx # Compress Movie #.dv # Video Format used in Portable Camera #.flc # Autodesk Animator #.fli #.flv # Internet Movies #.gsm #.ivf #.kar # Karaoke Media Files #.m15 #.m1a #.m1s #.m1v #.m2v #.m3u # MP3 Playlist #.m4a # AAC Audio #.m4b #.m4e #.m4p # AAC Audio (Protected) #.m4v # Video (Protected) #.m75 #.mid # Midi Audio Files #.midi # Midi Audio Files #.mjpg #.mov # Movie Files #.mp1 #.mp2 #.mp3 # Music file #.mp4 # Mpeg-4 Media #.mpa #.mpe #.mpeg # Movie file #.mpg # Movie file #.mpga #.mpm #.mps #.mpv #.mpv2 #.mqv # Quicktime Movies #.mv #.ogg # Music file #.ogm # Ogg Based Movie Files #.pls # Shoutcast type of radio #.qcp # Qualcomm Purevoice Audio #.qt # Quicktime File #.qtc #.qtl # Quicktime Movies #.ra # Real Audio #.ram # Real Audio Media #.rm # Real Media Files #.rmi #.rmm #.rmp #.rmvb # Real Media Video #.rnx #.rp # Real Player Files #.rt #.rts #.rtsp #.rv #.sd2 # Sound Designer II #.sdp # Stream Descriptor #.sdv # SD Video #.sf #.smf #.smi # #.smil # SMIL Multimedia Presentation (Video and Audio Presentation #.snd #.ssm # Streaming Media Metafile #.swa # MP3 Audio #.swf # Shockwave Streaming files #.ulw #.vfw # Video for Windows #.wav #.wax #.wm #.wma #.wmf # Movie file #.wmp #.wmv # Windows Media Video #.wmx #.wvx #.xpl e2guardian-5.5.8r/configs/lists/example.group/bannedmimetypelist000077500000000000000000000004341477372360500251520ustar00rootroot00000000000000# banned MIME types audio/mpeg audio/x-mpeg audio/x-pn-realaudio audio/x-wav video/mpeg video/x-mpeg2 video/acorn-replay video/quicktime video/x-msvideo video/msvideo application/gzip application/x-gzip application/zip application/compress application/x-compress application/java-vm e2guardian-5.5.8r/configs/lists/example.group/bannedphraselist.in000077500000000000000000000031071477372360500252100ustar00rootroot00000000000000# BANNEDPHRASELIST - INSTRUCTIONS FOR USE # # To block any page with the word "sex". # < sex > # # To block any page with words that contain the string "sex". (ie. sexual) # # # To block any page with the string "sex magazine". # # # To block any page containing the words/strings "sex" and "fetish". # , # # < test> will match any word with the string 'test' at the beginning # will match any word with the string 'test' at the end # will match any word with the string 'test' at any point in the word # < test > will match only the word 'test' # will match that exact phrase # , will match if both words are found in the page # A combination of the above can also be used eg < test>, # # All phrases need to be within < and > to work, othewise they will be # ignored. # Note: Use these sparingly - banned phrases tend to overblock #listcategory: "Banned Phrases" # The following banned phraselists are included in the default e2g distribution. .Include<@E2CONFDIR@/lists/phraselists/ukenglish/pornography/banned> #.Include<@E2CONFDIR@/lists/phraselists/ukenglish/gambling/banned> #.Include<@E2CONFDIR@/lists/phraselists/ukenglish/badwords/banned> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/gambling/banned> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/intolerance/banned> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/personals/banned> #.Include<@E2CONFDIR@/lists/phraselists/ukenglish/gambling/banned> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/gambling/banned> e2guardian-5.5.8r/configs/lists/example.group/bannedregexpheaderlist000077500000000000000000000005011477372360500257570ustar00rootroot00000000000000#Banned outgoing HTTP headers based on regular expressions # # E.g. 'User-Agent: .*MSIE' would block several versions of Internet Explorer # (assuming the user-agent is not being spoofed by the client) # # Headers are matched line-by-line, not as a single block. #listcategory: "Banned Regular Expression HTTP Headers" e2guardian-5.5.8r/configs/lists/example.group/bannedregexpurllist000077500000000000000000000125651477372360500253460ustar00rootroot00000000000000#Banned URLs based on Regular Expressions # # E.g. 'sex' would block sex.com and middlesex.com etc #listcategory: "Banned Regular Expression URLs" #Banned URLs based on Regular Expressions #These examples should be used with extreme caution # as most regexp url patterns tend to overblock # Be as specific as possible as this helps avoid this ###################################################### # Pornography, Modelling and Adult Sites ###################################################### #\b(big|cyber|(? #You can have multiple .Includes. # Time limiting syntax: # #time: # Example: ##time: 9 0 17 0 01234 # Remove the first # from the line above to enable this list only from # 9am to 5pm, Monday to Friday. # List categorisation #listcategory: "Banned Sites" #List other sites to block: # badboys.com # NOTE: From v5 Blanket blocks are now implimented using Storyboarding # WARNING: Old style Blanket blocks in this file will be silently ignored # The squidGuard advert domain/URL lists are now included by default. # To work with advanced ad blocking & the logadblocks option, advert # phrase/site/URL lists should have the string "ADs" in their listcategory. # .Include<@E2CONFDIR@/lists/blacklists/ads/domains> #Remove the # from the following and edit as needed to use a stock #squidGuard/urlblacklists collection. #.Include<@E2CONFDIR@/lists/blacklists/adult/domains> #.Include<@E2CONFDIR@/lists/blacklists/aggressive/domains> #.Include<@E2CONFDIR@/lists/blacklists/artnudes/domains> #.Include<@E2CONFDIR@/lists/blacklists/audio-video/domains> #.Include<@E2CONFDIR@/lists/blacklists/beerliquorinfo/domains> #.Include<@E2CONFDIR@/lists/blacklists/beerliquorsale/domains> #.Include<@E2CONFDIR@/lists/blacklists/chat/domains> #.Include<@E2CONFDIR@/lists/blacklists/childcare/domains> #.Include<@E2CONFDIR@/lists/blacklists/clothing/domains> #.Include<@E2CONFDIR@/lists/blacklists/culinary/domains> #.Include<@E2CONFDIR@/lists/blacklists/dialers/domains> #.Include<@E2CONFDIR@/lists/blacklists/drugs/domains> #.Include<@E2CONFDIR@/lists/blacklists/entertainment/domains> #.Include<@E2CONFDIR@/lists/blacklists/forums/domains> #.Include<@E2CONFDIR@/lists/blacklists/frencheducation/domains> #.Include<@E2CONFDIR@/lists/blacklists/gambling/domains> #.Include<@E2CONFDIR@/lists/blacklists/government/domains> #.Include<@E2CONFDIR@/lists/blacklists/hacking/domains> #.Include<@E2CONFDIR@/lists/blacklists/homerepair/domains> #.Include<@E2CONFDIR@/lists/blacklists/hygiene/domains> #.Include<@E2CONFDIR@/lists/blacklists/jewelry/domains> #.Include<@E2CONFDIR@/lists/blacklists/jobsearch/domains> #.Include<@E2CONFDIR@/lists/blacklists/kidstimewasting/domains> #.Include<@E2CONFDIR@/lists/blacklists/mail/domains> #.Include<@E2CONFDIR@/lists/blacklists/news/domains> #.Include<@E2CONFDIR@/lists/blacklists/onlineauctions/domains> #.Include<@E2CONFDIR@/lists/blacklists/onlinegames/domains> #.Include<@E2CONFDIR@/lists/blacklists/onlinepayment/domains> #.Include<@E2CONFDIR@/lists/blacklists/personalfinance/domains> #.Include<@E2CONFDIR@/lists/blacklists/pets/domains> #.Include<@E2CONFDIR@/lists/blacklists/porn/domains> #.Include<@E2CONFDIR@/lists/blacklists/proxy/domains> #.Include<@E2CONFDIR@/lists/blacklists/publicite/domains> #.Include<@E2CONFDIR@/lists/blacklists/redirector/domains> #.Include<@E2CONFDIR@/lists/blacklists/ringtones/domains> #.Include<@E2CONFDIR@/lists/blacklists/sportnews/domains> #.Include<@E2CONFDIR@/lists/blacklists/sports/domains> #.Include<@E2CONFDIR@/lists/blacklists/vacation/domains> #.Include<@E2CONFDIR@/lists/blacklists/violence/domains> #.Include<@E2CONFDIR@/lists/blacklists/virusinfected/domains> #.Include<@E2CONFDIR@/lists/blacklists/warez/domains> # You will need to edit to add and remove categories you want e2guardian-5.5.8r/configs/lists/example.group/bannedsslsiteiplist000066400000000000000000000007421477372360500253370ustar00rootroot00000000000000#IP sites in banned ssl list #This list is only used for SSL (or CONNECT) requests #Unlike the bannedsitelist it overides all other lists # so can be used ban an https site white the http is allowed or made an exception # #Only list sites where you only want the https site blocked #Use bannedsiteiplist for sites where you want both http & https blocked # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/bannedsslsitelist000066400000000000000000000007021477372360500250020ustar00rootroot00000000000000#domains in banned ssl list #Don't bother with the www. or the https:// #This list is only used for SSL (or CONNECT) requests # and will not have any effect when MITM is enabled. #Unlike the bannedsitelist it overides all other lists # so can be used ban an https site white the http is allowed or made an exception # #Only list sites where you only want the https site blocked #Use bannedsitelist for sites where you want both http & https blocked e2guardian-5.5.8r/configs/lists/example.group/bannedtimelist000066400000000000000000000005611477372360500242550ustar00rootroot00000000000000#timebands when internet blocked #You can have multiple .Includes. # Tiime banding syntax: # # Note figures are 0 based - hours (0-23) mins (0-59) days (0-6) # Example: # 22 0 23 59 01234 # 0 0 07 0 01234 # Uncomment the lines above to block access from # 0am to 5am and 10pm to 11:59pm, Monday to Friday. e2guardian-5.5.8r/configs/lists/example.group/bannedurllist.in000077500000000000000000000047111477372360500245320ustar00rootroot00000000000000#URLs in banned list #Don't bother with the http:// or the www #The bannedurllist is for blocking PART of a site #The bannedsitelist is for blocking ALL of a site #The 'grey' lists override the 'banned' lists. #The 'exception' lists override the 'banned' lists also. #The difference is that the 'exception' lists completely switch #off *all* other filtering for the match. 'grey' lists only #stop the URL filtering and allow the normal filtering to work. #An example of grey list use is when in Blanket Block (whitelist) #mode and you want to allow some sites but still filter as normal #on their content #Another example of grey list use is when you ban a site but want #to allow part of it. #To include additional files in this list use this example: #.Include<@E2CONFDIR@/anotherbannedurllist> #You can have multiple .Includes. #listcategory: "Banned URLs" #List other URLs to block: # members.home.net/uporn # The squidGuard advert domain/URL lists are now included by default. # To work with advanced ad blocking & the logadblocks option, advert # phrase/site/URL lists should have the string "ADs" in their listcategory. #.Include<@E2CONFDIR@/lists/blacklists/ads/urls> #Remove the # from the following and edit as needed to use a stock #squidGuard/urlblacklist blacklists collection. #.Include<@E2CONFDIR@/lists/blacklists/adult/urls> #.Include<@E2CONFDIR@/lists/blacklists/aggressive/urls> #.Include<@E2CONFDIR@/lists/blacklists/audio-video/urls> #.Include<@E2CONFDIR@/lists/blacklists/chat/urls> #.Include<@E2CONFDIR@/lists/blacklists/drugs/urls> #.Include<@E2CONFDIR@/lists/blacklists/entertainment/urls> #.Include<@E2CONFDIR@/lists/blacklists/frencheducation/urls> #.Include<@E2CONFDIR@/lists/blacklists/gambling/urls> #.Include<@E2CONFDIR@/lists/blacklists/government/urls> #.Include<@E2CONFDIR@/lists/blacklists/hacking/urls> #.Include<@E2CONFDIR@/lists/blacklists/jobsearch/urls> #.Include<@E2CONFDIR@/lists/blacklists/kidstimewasting/urls> #.Include<@E2CONFDIR@/lists/blacklists/mail/urls> #.Include<@E2CONFDIR@/lists/blacklists/news/urls> #.Include<@E2CONFDIR@/lists/blacklists/porn/urls> #.Include<@E2CONFDIR@/lists/blacklists/proxy/urls> #.Include<@E2CONFDIR@/lists/blacklists/publicite/urls> #.Include<@E2CONFDIR@/lists/blacklists/redirector/urls> #.Include<@E2CONFDIR@/lists/blacklists/violence/urls> #.Include<@E2CONFDIR@/lists/blacklists/virusinfected/urls> #.Include<@E2CONFDIR@/lists/blacklists/warez/urls> # You will need to edit to add and remove categories you want e2guardian-5.5.8r/configs/lists/example.group/blanketblocktldlist000077500000000000000000000003251477372360500253070ustar00rootroot00000000000000# TLD blanket block - used with blanket block to block all TLDs IN this list # # Does NOT override exception lists #.tld so for example you can match .xxx for example # can also be used for SLD e.g. .co.uk .xxx e2guardian-5.5.8r/configs/lists/example.group/blankettimelist000066400000000000000000000010171477372360500244430ustar00rootroot00000000000000#timebands when blanket block is applied # (only exception sites will be allowed) # To enable this list edit e2guardianfn.conf and filter group i # storyboard fn.story - see examplef1.story #You can have multiple .Includes. # Tiime banding syntax: # # Note figures are 0 based - hours (0-23) mins (0-59) days (0-6) # Example: # 22 0 23 59 01234 # 0 0 07 0 01234 # Uncomment the lines above to blanket block from # 0am to 5am and 10pm to 11:59pm, Monday to Friday. e2guardian-5.5.8r/configs/lists/example.group/contentregexplist000077500000000000000000000115571477372360500250460ustar00rootroot00000000000000#Content modifying Regular Expressions # # The format is: "extended regular expression"->"replacement straight string" # E.g. "shit"->"censored" would replace all occurances of shit in any case. # Far more complicated matches are possible. See other sources for examples # of extended regular expressions. # These are just some examples. If you write any, for example, to # remove popups etc, please send them to author at e2guardian.org. # #"" #"=[ ]*?window\.open[ ]*?\("->"=fwo(" #""->"" # Fix Firefox <= 1.0.7 DoS # http://www.whitedust.net/speaks/1432/ #"(("->"$1dosremovedtext" # Disable ActiveX objects. #"]*application\/x-oleobject[^>]*>.*?<\/object>"->"" #"]*(application/x-oleobject).*?>(.*?)?"->"" # Warn about address bar spoofing. #"(]*href[^>]*)(\x01|\x02|\x03|%0[012])"->"$1MALICIOUS-LINK" # Disable all popups in JavaScript and HTML. It may cause unavoidable # Javascript warnings or errors. Do not enable at the same time as other # popup removing lines. #"((\W\s*)(window|this|parent)\.)open\s*\\?\("->"$1concat(" #"\starget\s*=\s*(['"]?)_?(blank|new)\1?"->" notarget" # Removes the APPLET tag which is generally used Java applets. #"]*>.*?<\/applet>"->"" # Disable the BLINK and MARQUEE tags. #"]*>"->"" # Warn about potential cross-site-scripting vulnerability described here: # http://online.securityfocus.com/archive/1/298748/2002-11-02/2002-11-08/2 #"f\("javascript:location.replace\('mk:@MSITStore:C:'\)"\);"->"alert\("This page looks like it tries to use a vulnerability described here:\n http://online.securityfocus.com/archive/1/298748/2002-11-02/2002-11-08/2"\);" # Removes the SCRIPT tag with JavaScript. This will likely break sites that are # badly written and thus rely on JavaScript. This should not be used at the same # time as the 'script' category. #""->"
WARNING: This Server is infected with Nimda!" # Disable onunload (page close) popups. #"(]*)onunload"->"$1never" #"()"->"$1never" # Removes the SCRIPT tag which could include JavaScript, perlscript and vbscript. # This will likely break sites that are badly written and thus rely on client # side scripts. This should not be used at the same time as the 'javascript' line. #"]*>.*?<\/script>"->"" # Disable Sockwave Flash objects. #"]*macromedia[^>]*>.*?<\/object>"->"" #"]*(application/x-shockwave-flash\|\.swf).*?>(.*?)?"->"" # Disable unsolicited popups. #"([^'"]\s*)(?=\s*[^'"])"->"$1" #"([^\w\s.]\s*)((window|this|parent)\.)?open\s*\("->"$1SWGuardianWindowOpen(" #"([^'"]\s*)(?!\s*(\\n|'|"))"->"$1" # Remove 1x1 GIFs used for user tracking. #"]*(?:(width)|(height))\s*=\s*['"]?[01](?=\D)[^>]*(?:(width)|(height))\s*=\s*['"]?[01](?=\D)[^>]*?>"->"" # Prevent windows from resizing and moving themselves. #"(?:window|this|self|top)\.(?:move|resize)(?:to|by)\("->"''.concat(" e2guardian-5.5.8r/configs/lists/example.group/domainsnobypass.in000077500000000000000000000056571477372360500251070ustar00rootroot00000000000000# User are not allowed to bypass domains in this list #Don't bother with the www. or the http:// #NOTE: Sites using just IP should be put into bannedsiteiplistwithbypass #You can include #.tld so for example you can match .gov for example #.Include<@E2CONFDIR@/anotherbannedurllist> #You can have multiple .Includes. # WARNING: Old style Blanket blocks in this file will be silently ignored # .Include<@E2CONFDIR@/lists/blacklists/ads/domains> #Remove the # from the following and edit as needed to use a stock #squidGuard/urlblacklists collection. #.Include<@E2CONFDIR@/lists/blacklists/adult/domains> #.Include<@E2CONFDIR@/lists/blacklists/aggressive/domains> #.Include<@E2CONFDIR@/lists/blacklists/artnudes/domains> #.Include<@E2CONFDIR@/lists/blacklists/audio-video/domains> #.Include<@E2CONFDIR@/lists/blacklists/beerliquorinfo/domains> #.Include<@E2CONFDIR@/lists/blacklists/beerliquorsale/domains> #.Include<@E2CONFDIR@/lists/blacklists/chat/domains> #.Include<@E2CONFDIR@/lists/blacklists/childcare/domains> #.Include<@E2CONFDIR@/lists/blacklists/clothing/domains> #.Include<@E2CONFDIR@/lists/blacklists/culinary/domains> #.Include<@E2CONFDIR@/lists/blacklists/dialers/domains> #.Include<@E2CONFDIR@/lists/blacklists/drugs/domains> #.Include<@E2CONFDIR@/lists/blacklists/entertainment/domains> #.Include<@E2CONFDIR@/lists/blacklists/forums/domains> #.Include<@E2CONFDIR@/lists/blacklists/frencheducation/domains> #.Include<@E2CONFDIR@/lists/blacklists/gambling/domains> #.Include<@E2CONFDIR@/lists/blacklists/government/domains> #.Include<@E2CONFDIR@/lists/blacklists/hacking/domains> #.Include<@E2CONFDIR@/lists/blacklists/homerepair/domains> #.Include<@E2CONFDIR@/lists/blacklists/hygiene/domains> #.Include<@E2CONFDIR@/lists/blacklists/jewelry/domains> #.Include<@E2CONFDIR@/lists/blacklists/jobsearch/domains> #.Include<@E2CONFDIR@/lists/blacklists/kidstimewasting/domains> #.Include<@E2CONFDIR@/lists/blacklists/mail/domains> #.Include<@E2CONFDIR@/lists/blacklists/news/domains> #.Include<@E2CONFDIR@/lists/blacklists/onlineauctions/domains> #.Include<@E2CONFDIR@/lists/blacklists/onlinegames/domains> #.Include<@E2CONFDIR@/lists/blacklists/onlinepayment/domains> #.Include<@E2CONFDIR@/lists/blacklists/personalfinance/domains> #.Include<@E2CONFDIR@/lists/blacklists/pets/domains> #.Include<@E2CONFDIR@/lists/blacklists/porn/domains> #.Include<@E2CONFDIR@/lists/blacklists/proxy/domains> #.Include<@E2CONFDIR@/lists/blacklists/publicite/domains> #.Include<@E2CONFDIR@/lists/blacklists/redirector/domains> #.Include<@E2CONFDIR@/lists/blacklists/ringtones/domains> #.Include<@E2CONFDIR@/lists/blacklists/sportnews/domains> #.Include<@E2CONFDIR@/lists/blacklists/sports/domains> #.Include<@E2CONFDIR@/lists/blacklists/vacation/domains> #.Include<@E2CONFDIR@/lists/blacklists/violence/domains> #.Include<@E2CONFDIR@/lists/blacklists/virusinfected/domains> #.Include<@E2CONFDIR@/lists/blacklists/warez/domains> # You will need to edit to add and remove categories you want e2guardian-5.5.8r/configs/lists/example.group/exceptionextensionlist000077500000000000000000000007401477372360500261040ustar00rootroot00000000000000# Exception file extension list # Use as a filter group's "exceptionextensionlist", # to override a blanket download block. # (blockdownloads = on) # # DOES NOT override content/virus scanning or site/URL bans. # # Default list: # Unblock web pages & graphics # Text/web document types .css .html .shtml .htm .stm .asp .php .txt .rtx .xml .xsl .cgi .pl # Image types .bmp .cod .gif .ief .jpe .jpeg .jpg .jfif .tif .tiff .ras .cmx .ico .pnm .pbm .pgm .ppm .rgb .xbm .xpm .xwd e2guardian-5.5.8r/configs/lists/example.group/exceptionfilesiteiplist000077500000000000000000000006451477372360500262310ustar00rootroot00000000000000# Exception file site ip list # Use this list to define ip sites from which files can be downloaded, # overriding a blanket download block (blockdownloads = on) or the # banned MIME type and extension lists (blockdownloads = off). # # DOES NOT override content/virus scanning or site/URL bans. # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/exceptionfilesitelist000077500000000000000000000016201477372360500256720ustar00rootroot00000000000000# Exception file site list # Use this list to define sites from which files can be downloaded, # overriding a blanket download block (blockdownloads = on) or the # banned MIME type and extension lists (blockdownloads = off). # # DOES NOT override content/virus scanning or site/URL bans. # 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 # Time limiting syntax: # #time: # Example: ##time: 9 0 17 0 01234 # Remove the first # from the line above to enable this list only from # 9am to 5pm, Monday to Friday. windowsupdate.microsoft.com update.microsoft.com download.windowsupdate.com e2guardian-5.5.8r/configs/lists/example.group/exceptionfileurllist000077500000000000000000000015021477372360500255270ustar00rootroot00000000000000# Exception file URL list # Use this list to define URLs from which files can be downloaded, # overriding a blanket download block (blockdownloads = on) or the # banned MIME type and extension lists (blockdownloads = off). # # DOES NOT override content/virus scanning or site/URL bans. # 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 # Time limiting syntax: # #time: # Example: ##time: 9 0 17 0 01234 # Remove the first # from the line above to enable this list only from # 9am to 5pm, Monday to Friday. e2guardian-5.5.8r/configs/lists/example.group/exceptionmimetypelist000077500000000000000000000012151477372360500257170ustar00rootroot00000000000000# Exception MIME type list # Use as a filter group's "exceptionmimetypelist", # to override a blanket download block. # (blockdownloads = on) # # DOES NOT override content/virus scanning or site/URL bans. # # Default list: # Unblock web pages & graphics # Text/web document types text/plain text/html text/css text/xml text/xsl text/richtext # Image types image/bmp image/cis-cod image/gif image/ief image/jpeg image/pipeg image/png image/tiff image/x-cmu-raster image/x-cmx image/x-icon image/x-portable-anymap image/x-portable-bitmap image/x-portable-graymap image/x-portable-pixmap image/x-rgb image/x-xbitmap image/x-xpixmap image/x-xwindowdump e2guardian-5.5.8r/configs/lists/example.group/exceptionphraselist.in000077500000000000000000000010411477372360500257520ustar00rootroot00000000000000# EXCEPTIONPHRASELIST - INSTRUCTIONS FOR USE # # If any of the phrases listed below appear in a web page # then it will bypass the filtering and be allowed through # eg # < medical > # # # Combinations # Unblock the page if the following phrases are found on the same page. # Each line is a new combination. # eg #,, # # See the bannedphraselist for more examples. # Use this sparingly as can easily make exceptions of unsuitable urls #.Include<@E2CONFDIR@/lists/phraselists/ukenglish/goodphrases/exception_email> e2guardian-5.5.8r/configs/lists/example.group/exceptionregexpheaderlist000077500000000000000000000005021477372360500265270ustar00rootroot00000000000000#Allowed outgoing HTTP headers based on regular expressions # # E.g. 'User-Agent: .*MSIE' would allow several versions of Internet Explorer # (assuming the user-agent is not being spoofed by the client) # # Headers are matched line-by-line, not as a single block. #listcategory: "Allowed Regular Expression HTTP Headers" e2guardian-5.5.8r/configs/lists/example.group/exceptionregexpurllist000077500000000000000000000005171477372360500261070ustar00rootroot00000000000000#Exception URLs based on Regular Expressions # # E.g. 'news' would unblock news.bbc.com etc # Example #news # Prevent content scanning of CSS and/or JavaScript files #^[^?]*\.css($|\?) #^[^?]*\.jsp?($|\?) # Allow Facebook plugin applications like # http://apps.facebook.com/neighborhoods/Setup.aspx and others. mock_ajax_proxy.php e2guardian-5.5.8r/configs/lists/example.group/exceptionregexpuseragentlist000077500000000000000000000004611477372360500273000ustar00rootroot00000000000000#Exception User-Agent based on regular expressions # # E.g. ' .*MSIE' would allow several versions of Internet Explorer # (assuming the user-agent is not being spoofed by the client) # # Usefull for allowing 'apps' to work # E.g. 'Kindle' would make kindle app an exception #listcategory: "user-agent" e2guardian-5.5.8r/configs/lists/example.group/exceptionsiteiplist000077500000000000000000000002321477372360500253610ustar00rootroot00000000000000#IP Sites in exception list # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/exceptionsitelist000077500000000000000000000012771477372360500250420ustar00rootroot00000000000000#Sites in exception list #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'. # # IP must be put in exceptionsiteiplist # #.tld so for example you can match .gov for example # Time limiting syntax: # #time: # Example: ##time: 9 0 17 0 01234 # Remove the first # from the line above to enable this list only from # 9am to 5pm, Monday to Friday. # NOTE: From v5 Blanket exceptions are now implimented using Storyboarding # WARNING: Old style Blanket blocks in this file will be silently ignored windowsupdate.microsoft.com e2guardian-5.5.8r/configs/lists/example.group/exceptionurllist000077500000000000000000000005511477372360500246720ustar00rootroot00000000000000#URLs in exception list #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/ e2guardian-5.5.8r/configs/lists/example.group/exceptionvirusextensionlist000077500000000000000000000005451477372360500272000ustar00rootroot00000000000000# Exception file extension list # Use as a filter group's "exceptionvirusextensionlist", # # Default list: # Unblock web pages & graphics # Text/web document types .css .html .shtml .htm .stm .asp .php .txt .rtx .xml .xsl .cgi .pl # Image types .bmp .cod .gif .ief .jpe .jpeg .jpg .jfif .tif .tiff .ras .cmx .ico .pnm .pbm .pgm .ppm .rgb .xbm .xpm .xwd e2guardian-5.5.8r/configs/lists/example.group/exceptionvirussiteiplist000077500000000000000000000003501477372360500264530ustar00rootroot00000000000000# IP sites in exceptionvirussiteiplist #The exceptionvirussiteiplist is for allowing ALL of an IP site # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/greysiteiplist000077500000000000000000000006741477372360500243430ustar00rootroot00000000000000# IP site in grey list #The 'grey' lists override the 'banned' lists. #The 'exception' lists override the 'banned' lists also. #The difference is that the 'exception' lists completely switch #off *all* other filtering for the match. 'grey' lists only #stop the URL filtering and allow the normal filtering to work. # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/greysitelist000077500000000000000000000035641477372360500240130ustar00rootroot00000000000000#domains in grey list #Don't bother with the www. or the http:// #The 'grey' lists override the 'banned' lists. #The 'exception' lists override the 'banned' lists also. #The difference is that the 'exception' lists completely switch #off *all* other filtering for the match. 'grey' lists only #stop the URL filtering and allow the normal filtering to work. #An example of grey list use is when in Blanket Block (whitelist) #mode and you want to allow some sites but still filter as normal #on their content #Another example of grey list use is when you ban a site but want #to allow part of it. #The greyurllist is for partly unblocking PART of a site #The greysitelist is for partly unblocking ALL of a site #As of DansGuardian 2.7.3 you can now include #.tld so for example you can match .gov for example #To include additional files in this list use this example: #.Include #You can have multiple .Includes. # Time limiting syntax: # #time: # Example: ##time: 9 0 17 0 01234 # Remove the first # from the line above to enable this list only from # 9am to 5pm, Monday to Friday. # Blanket match. To greylist all sites except those in the # exceptionsitelist and greysitelist files, remove # the # from the next line to leave only a '**': #** # Blanket SSL/CONNECT match. To greylist all SSL # and CONNECT tunnels except to addresses in the # exceptionsitelist and greysitelist files, remove # the # from the next line to leave only a '**s': #**s # Blanket IP match. To greylist all sites specified only as an IP, # remove the # from the next line to leave only a '*ip': #*ip # Blanket SSL/CONNECT IP match. To greylist all SSL and CONNECT # tunnels to sites specified only as an IP, # remove the # from the next line to leave only a '*ips': #*ips #List other sites to greylist: #www.bbc.co.uk e2guardian-5.5.8r/configs/lists/example.group/greysslsiteiplist000066400000000000000000000002331477372360500250510ustar00rootroot00000000000000# IP sites in SSL grey list # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/greysslsitelist000066400000000000000000000002041477372360500245160ustar00rootroot00000000000000#domains in SSL grey list #Don't bother with the www. or the https:// #This 'grey' lists override the 'banned' lists for SSL only. e2guardian-5.5.8r/configs/lists/example.group/greyurllist000077500000000000000000000016041477372360500236420ustar00rootroot00000000000000#URLs in grey list #Don't bother with the http:// or the www #The greyurllist is for partly unblocking PART of a site #The greysitelist is for partly unblocking ALL of a site #The 'grey' lists override the 'banned' lists. #The 'exception' lists override the 'banned' lists also. #The difference is that the 'exception' lists completely switch #off *all* other filtering for the match. 'grey' lists only #stop the URL filtering and allow the normal filtering to work. #An example of grey list use is when in Blanket Block (whitelist) #mode and you want to allow some sites but still filter as normal #on their content #Another example of grey list use is when you ban a site but want #to allow part of it. #To include additional files in this list use this example: #.Include #You can have multiple .Includes. #List other URLs to block: #members.home.net/nice e2guardian-5.5.8r/configs/lists/example.group/headerregexplist000077500000000000000000000012261477372360500246140ustar00rootroot00000000000000# Outgoing HTTP request header modifying Regular Expressions # # The format is: "extended regular expression"->"replacement straight string" # E.g. "shit"->"censored" would replace all occurances of shit in any case. # Far more complicated matches are possible. See other sources for examples # of extended regular expressions. # # Headers are run through replacements line-by-line, not as a single block. # # To remove a header put "X-E2G-IgnoreMe:" as the replacement # e.g. to remove 'upgrade:' headers #"upgrade:.*$"->"X-E2G-IgnoreMe:" # Windows Live Search cookie replacement - force filtering on #"cookie:(.*)&ADLT=(OFF|DEMOTE)"->"Cookie:$1&ADLT=STRICT" e2guardian-5.5.8r/configs/lists/example.group/ipnobypass000066400000000000000000000003351477372360500234410ustar00rootroot00000000000000# User are not allowed to bypass IP sites in this list # # This is not the IP of web servers # you want to filter. # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/localbannedsearchlist000066400000000000000000000010511477372360500255720ustar00rootroot00000000000000#Local Banned Search Words # #Words must be in alphabetic order within a single line # and separated by a '+' sign. #All combinations of the words will be blocked # e.g. girl+naughty # will block naughty+girl as well as girl+naughty #.Include #.Include #.Include #.Include #.Include e2guardian-5.5.8r/configs/lists/example.group/localbannedsiteiplist000066400000000000000000000002371477372360500256270ustar00rootroot00000000000000# IP sites in local banned list # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/localbannedsitelist000066400000000000000000000004431477372360500252750ustar00rootroot00000000000000#domains in local banned list #.Include #.Include #.Include #.Include #.Include e2guardian-5.5.8r/configs/lists/example.group/localbannedsslsiteiplist000066400000000000000000000004201477372360500263430ustar00rootroot00000000000000# IP sites in local banned ssl list #This list is only used for SSL (or CONNECT) requests #Unlike the bannedsitelist it overides all other lists # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/localbannedsslsitelist000066400000000000000000000003361477372360500260200ustar00rootroot00000000000000#domains in banned ssl list #Don't bother with the www. or the https:// #This list is only used for SSL (or CONNECT) requests # and has no effect when MITM is enabled #Unlike the bannedsitelist it overides all other lists e2guardian-5.5.8r/configs/lists/example.group/localbannedurllist000066400000000000000000000000331477372360500251260ustar00rootroot00000000000000#URLs in local banned list e2guardian-5.5.8r/configs/lists/example.group/localexceptionsiteiplist000066400000000000000000000002411477372360500263710ustar00rootroot00000000000000#IP Sites in local exception list # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/localexceptionsitelist000066400000000000000000000000371477372360500260430ustar00rootroot00000000000000#Sites in local exception list e2guardian-5.5.8r/configs/lists/example.group/localexceptionurllist000066400000000000000000000000361477372360500257000ustar00rootroot00000000000000#URLs in local exception list e2guardian-5.5.8r/configs/lists/example.group/localgreysiteiplist000066400000000000000000000002351477372360500253440ustar00rootroot00000000000000# IP sites in local grey list # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/localgreysitelist000066400000000000000000000000341477372360500250100ustar00rootroot00000000000000#domains in local grey list e2guardian-5.5.8r/configs/lists/example.group/localgreysslsiteiplist000066400000000000000000000002411477372360500260630ustar00rootroot00000000000000# IP sites in local SSL grey list # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/localgreysslsitelist000066400000000000000000000005521477372360500255370ustar00rootroot00000000000000#domains in SSL grey list #Don't bother with the www. or the https:// #This 'grey' lists overrides the main site 'exception' lists for SSL allowing MITM to be enabled in order to check https full url. # Use to overcome issue where ssl url is in local blocked url list but is overriden by main site exception list forcing tunnel mode and so url never checked. e2guardian-5.5.8r/configs/lists/example.group/localgreyurllist000066400000000000000000000000311477372360500246430ustar00rootroot00000000000000#URLs in local grey list e2guardian-5.5.8r/configs/lists/example.group/localsemiexceptionsiteiplist000066400000000000000000000002461477372360500272540ustar00rootroot00000000000000#IP Sites in local semi exception list # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/localsemiexceptionsitelist000066400000000000000000000000441477372360500267170ustar00rootroot00000000000000#Sites in local semi exception list e2guardian-5.5.8r/configs/lists/example.group/logregexpurllist000077500000000000000000000011551477372360500246710ustar00rootroot00000000000000# Log regular expression URL list # # This acts as a list of URL regexes which, if matched, will have their category # recorded but no specific filtering action taken. It is only really useful # in conjunction with log analysers, to perform meaningful categorisation and # analysis upon non-blocked/exception requests, and so is disabled and empty # by default. # # If you would like to enable this feature, uncomment "logregexpurllist" in your # e2guardianf*.conf file(s), and place .Include<> statements here for the # categories you would like to log. Included list files must contain a # "#listcategory" directive. e2guardian-5.5.8r/configs/lists/example.group/logsiteiplist000077500000000000000000000013251477372360500241500ustar00rootroot00000000000000# Log IP site list # # This acts as a list of IP sites which, when found, will have their category # recorded but no specific filtering action taken. It is only really useful # in conjunction with log analysers, to perform meaningful categorisation and # analysis upon non-blocked/exception requests, and so is disabled and empty # by default. # # If you would like to enable this feature, uncomment "logsitelist" in your # e2guardianf*.conf file(s), and place .Include<> statements here for the # categories you would like to log. Included list files must contain a # "#listcategory" directive. # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/logsitelist000077500000000000000000000011221477372360500236120ustar00rootroot00000000000000# Log site list # # This acts as a list of domains which, when found, will have their category # recorded but no specific filtering action taken. It is only really useful # in conjunction with log analysers, to perform meaningful categorisation and # analysis upon non-blocked/exception requests, and so is disabled and empty # by default. # # If you would like to enable this feature, uncomment "logsitelist" in your # e2guardianf*.conf file(s), and place .Include<> statements here for the # categories you would like to log. Included list files must contain a # "#listcategory" directive. e2guardian-5.5.8r/configs/lists/example.group/logurllist000077500000000000000000000011151477372360500234520ustar00rootroot00000000000000# Log URL list # # This acts as a list of URLs which, when found, will have their category # recorded but no specific filtering action taken. It is only really useful # in conjunction with log analysers, to perform meaningful categorisation and # analysis upon non-blocked/exception requests, and so is disabled and empty # by default. # # If you would like to enable this feature, uncomment "logurllist" in your # e2guardianf*.conf file(s), and place .Include<> statements here for the # categories you would like to log. Included list files must contain a # "#listcategory" directive. e2guardian-5.5.8r/configs/lists/example.group/nocheckcertsiteiplist000066400000000000000000000007361477372360500256610ustar00rootroot00000000000000# IP sites which are NOT to have their certificates checked (when in MITM mode) # # Do not check ssl certificates for IP sites listed # # Can be used to allow sites with self-signed or invalid certificates # or to reduced CPU load by not checking certs on heavily used sites (e.g. Google, Bing) # Use with caution! # Ignored if mitmcheckcert is 'off' # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/nocheckcertsitelist000066400000000000000000000006031477372360500253210ustar00rootroot00000000000000# domains which are NOT to have their certificates checked (when in MITM mode) # # Do not check ssl certificates for sites/domains listed # # Can be used to allow sites with self-signed or invalid certificates # or to reduced CPU load by not checking certs on heavily used sites (e.g. Google, Bing) # Use with caution! # Ignored if mitmcheckcert is 'off' #Don't bother with the https:// e2guardian-5.5.8r/configs/lists/example.group/oldbannedphraselist.in000066400000000000000000000036171477372360500257120ustar00rootroot00000000000000# BANNEDPHRASELIST - INSTRUCTIONS FOR USE # # To block any page with the word "sex". # < sex > # # To block any page with words that contain the string "sex". (ie. sexual) # # # To block any page with the string "sex magazine". # # # To block any page containing the words/strings "sex" and "fetish". # , # # < test> will match any word with the string 'test' at the beginning # will match any word with the string 'test' at the end # will match any word with the string 'test' at any point in the word # < test > will match only the word 'test' # will match that exact phrase # , will match if both words are found in the page # A combination of the above can also be used eg < test>, # # # Extra phrase-list files to include # .Include # # # All phrases need to be within < and > to work, othewise they will be # ignored. # MORE EXAMPLE LISTS CAN BE DOWNLOADED FROM DANSGUARDIAN.ORG # Phrase Exceptions are no longer listed in this file, they are now # listed in the exceptionphraselist file. # #listcategory: "Banned Phrases" # The following banned phraselists enable Website Content Labeling systems. These are enabled by default, but may also be activated using phraselists. .Include<@E2CONFDIR@/lists/oldphraselists/safelabel/banned> #.Include<@E2CONFDIR@/lists/oldphraselists/rta/banned_portuguese> # The following banned oldphraselists are included in the default DG distribution. .Include<@E2CONFDIR@/lists/oldphraselists/pornography/banned> ##.Include<@E2CONFDIR@/lists/oldphraselists/pornography/banned_portuguese> #.Include<@E2CONFDIR@/lists/oldphraselists/illegaldrugs/banned> #.Include<@E2CONFDIR@/lists/oldphraselists/gambling/banned> ##.Include<@E2CONFDIR@/lists/oldphraselists/gambling/banned_portuguese> #.Include<@E2CONFDIR@/lists/oldphraselists/googlesearches/banned> e2guardian-5.5.8r/configs/lists/example.group/oldexceptionphraselist.in000066400000000000000000000010241477372360500264470ustar00rootroot00000000000000# EXCEPTIONPHRASELIST - INSTRUCTIONS FOR USE # # If any of the phrases listed below appear in a web page # then it will bypass the filtering and be allowed through # eg # < medical > # # # Combinations # Unblock the page if the following phrases are found on the same page. # Each line is a new combination. # eg #,, # # See the bannedphraselist for more examples. .Include<@E2CONFDIR@/lists/oldphraselists/goodphrases/exception> #.Include<@E2CONFDIR@/lists/oldphraselists/goodphrases/exception_email> e2guardian-5.5.8r/configs/lists/example.group/oldweightedphraselist.in000066400000000000000000000150031477372360500262530ustar00rootroot00000000000000# E2GUARDIAN weightedphraselist INSTRUCTIONS FOR USE # New in v5.2 - per cent feature # If weighting is a % then that % of the naughtynesslimit is used as the weight # e.g. # < slut ><10%> # - If naughtynesslimit is 200 will add 20 to the count against the word 'slut' # # This allows phrases to be used which will have the same effect in all # filtergroups # # Examples: # # <10> # - Adds 10 to the count against the string 'slut'. ie. sluts, slut!, abslutxyz. # # < slut ><10> # - Adds 10 to the count against the word 'slut'. ie. Sally is a slut that smells. # # ,<50> # - Adds 50 to the count when the strings 'slut' and 'horny' are found on the same page. # # ,<-30> # - Subtracts 30 from the count when 'breast' and 'medical' are on the one page. # # <-25> # - Subtracts 25 from the count when 'education' is on the page. # # See the bannedphraselist for more examples. # # Extra weighted-list files to include # .Include<@E2CONFDIR@/lists/oldphraselists/weightedphraselist.topic> # # Help by contributing customised lists and/or new keyword lists. # Email: pornmastergeneral@e2guardian.org or phrasemaster@e2guardian.org # # NOTE: New lists are commented out as ALPHA or BETA depending on how much the # lists have been tested. # ALPHA - Brand new and/or incomplete - little testing has been done # BETA - Relatively new - tested in several locations # #listcategory: "Weighted Phrases" #To enable several non-PICS self-labelling and self-rating systems. #Enabled as a bannedsitelist by default. Disable there before enabling as a phraselist. ##.Include<@E2CONFDIR@/lists/oldphraselists/safelabel/weighted> #Good Phrases (to allow medical, education, news and other good sites) .Include<@E2CONFDIR@/lists/oldphraselists/goodphrases/weighted_general> .Include<@E2CONFDIR@/lists/oldphraselists/goodphrases/weighted_news> .Include<@E2CONFDIR@/lists/oldphraselists/goodphrases/weighted_general_danish> .Include<@E2CONFDIR@/lists/oldphraselists/goodphrases/weighted_general_dutch> .Include<@E2CONFDIR@/lists/oldphraselists/goodphrases/weighted_general_malay> .Include<@E2CONFDIR@/lists/oldphraselists/goodphrases/weighted_general_polish> .Include<@E2CONFDIR@/lists/oldphraselists/goodphrases/weighted_general_portuguese> .Include<@E2CONFDIR@/lists/oldphraselists/goodphrases/weighted_general_swedish> #Pornography .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted> .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_chinese> #ALPHA# .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_danish> #ALPHA# .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_dutch> #BETA# .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_french> .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_german> .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_italian> #.Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_japanese> #ALPHA# #Disabled due to overblocking# .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_malay> #BETA# .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_norwegian> #BETA# .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_polish> .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_portuguese> .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_russian> #BETA# .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_russian_utf8> #BETA# .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_spanish> #ALPHA# .Include<@E2CONFDIR@/lists/oldphraselists/pornography/weighted_swedish> .Include<@E2CONFDIR@/lists/oldphraselists/nudism/weighted> #Bad Words - swearing .Include<@E2CONFDIR@/lists/oldphraselists/badwords/weighted_dutch> .Include<@E2CONFDIR@/lists/oldphraselists/badwords/weighted_french> .Include<@E2CONFDIR@/lists/oldphraselists/badwords/weighted_german> #ALPHA# .Include<@E2CONFDIR@/lists/oldphraselists/badwords/weighted_portuguese> #ALPHA# .Include<@E2CONFDIR@/lists/oldphraselists/badwords/weighted_spanish> #ALPHA# #Drugs #.Include<@E2CONFDIR@/lists/oldphraselists/drugadvocacy/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/illegaldrugs/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/illegaldrugs/weighted_portuguese> #.Include<@E2CONFDIR@/lists/oldphraselists/legaldrugs/weighted> #Violence and intolerance #.Include<@E2CONFDIR@/lists/oldphraselists/intolerance/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/intolerance/weighted_portuguese> #.Include<@E2CONFDIR@/lists/oldphraselists/gore/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/gore/weighted_portuguese> #.Include<@E2CONFDIR@/lists/oldphraselists/violence/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/violence/weighted_portuguese> #.Include<@E2CONFDIR@/lists/oldphraselists/weapons/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/weapons/weighted_portuguese> #Chat #.Include<@E2CONFDIR@/lists/oldphraselists/chat/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/chat/weighted_italian> #Webmail #.Include<@E2CONFDIR@/lists/oldphraselists/webmail/weighted> #Note that if you enable the webmail weighted list you should also disable #the "exception_email" list in the exceptionphraselist file. #Forums #.Include<@E2CONFDIR@/lists/oldphraselists/forums/weighted> #BETA# #Gambling #.Include<@E2CONFDIR@/lists/oldphraselists/gambling/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/gambling/weighted_portuguese> #Productivity #.Include<@E2CONFDIR@/lists/oldphraselists/games/weighted> #ALPHA# #.Include<@E2CONFDIR@/lists/oldphraselists/news/weighted> #ALPHA# #.Include<@E2CONFDIR@/lists/oldphraselists/personals/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/personals/weighted_portuguese> #.Include<@E2CONFDIR@/lists/oldphraselists/sport/weighted> #ALPHA# #.Include<@E2CONFDIR@/lists/oldphraselists/travel/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/music/weighted> #System Management and Security #.Include<@E2CONFDIR@/lists/oldphraselists/domainsforsale/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/idtheft/weighted> .Include<@E2CONFDIR@/lists/oldphraselists/malware/weighted> #BETA# .Include<@E2CONFDIR@/lists/oldphraselists/proxies/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/translation/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/upstreamfilter/weighted> .Include<@E2CONFDIR@/lists/oldphraselists/warezhacking/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/peer2peer/weighted> #Miscellaneous #.Include<@E2CONFDIR@/lists/oldphraselists/conspiracy/weighted> #.Include<@E2CONFDIR@/lists/oldphraselists/secretsocieties/weighted> e2guardian-5.5.8r/configs/lists/example.group/refererexceptionsiteiplist000066400000000000000000000003411477372360500267320ustar00rootroot00000000000000# Exception referer IP sites # sites which are refered to by these sites will be made exceptions # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/refererexceptionsitelist000066400000000000000000000001371477372360500264040ustar00rootroot00000000000000# Exception referer sites # sites which are refered to by these sites will be made exceptions e2guardian-5.5.8r/configs/lists/example.group/refererexceptionurllist000066400000000000000000000001071477372360500262370ustar00rootroot00000000000000# Referer URLs - Sites refered to by these URLs will be made exception e2guardian-5.5.8r/configs/lists/example.group/responseheaderregexplist000077500000000000000000000010261477372360500263710ustar00rootroot00000000000000# HTTP response header modifying Regular Expressions # # The format is: "extended regular expression"->"replacement straight string" # E.g. "shit"->"censored" would replace all occurances of shit in any case. # Far more complicated matches are possible. See other sources for examples # of extended regular expressions. # # Headers are run through replacements line-by-line, not as a single block. # # To remove a header put "X-E2G-IgnoreMe:" as the replacement # e.g. to remove 'upgrade:' headers #"upgrade:.*$"->"X-E2G-IgnoreMe:" e2guardian-5.5.8r/configs/lists/example.group/semiexceptionsiteiplist000077500000000000000000000003361477372360500262440ustar00rootroot00000000000000#IP Sites in semi exception list # These sites are made exception unless url is in banned list. # IP site addresses # # Single IPs, ranges and subnets can be used, # e.g. # 192.168.0.1 # 10.0.0.1-10.0.0.3 # 10.0.0.0/24 # e2guardian-5.5.8r/configs/lists/example.group/semiexceptionsitelist000077500000000000000000000013551477372360500257150ustar00rootroot00000000000000#Sites in semi exception list # These sites are made exception unless url is in banned list. #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'. # # IP must be put in semiexceptionsiteiplist # #.tld so for example you can match .gov for example # Time limiting syntax: # #time: # Example: ##time: 9 0 17 0 01234 # Remove the first # from the line above to enable this list only from # 9am to 5pm, Monday to Friday. # NOTE: From v5 Blanket exceptions are now implimented using Storyboarding # WARNING: Old style Blanket blocks in this file will be silently ignored e2guardian-5.5.8r/configs/lists/example.group/sslsiteregexplist000066400000000000000000000023701477372360500250500ustar00rootroot00000000000000#SSL site modifying Regular Expressions # # The format is: "extended regular expression"->"replacement straight string" # E.g. "shit"->"censored" would replace all occurances of shit in any case. # Far more complicated matches are possible. See other sources for examples # of extended regular expressions. # # Users are pointed at the replaced site transparently. # This is used to 'cname' ssl sites and avoids having to adjust DNS # and allows option of switching off/on depending on filter group. # This list applies to the site only (not full URL) for # HTTPS sites (regardless of SSL MITM configuration). # Do not use patterns with full urls in this list as they can never # match the site name. # Enforce restricted mode in YouTube # #"(^https://www.youtube.com)"->"https://restrict.youtube.com" #"(^https://m.youtube.com)"->"https://restrict.youtube.com" #"(^https://youtubei.googleapis.com)"->"https://restrict.youtube.com" #"(^https://youtube.googleapis.com)"->"https://restrict.youtube.com" #"(^https://www.youtube-nocookie.com)"->"https://restrict.youtube.com" # # Enforce restricted mode in Google # #"(^https://www\.google\.[a-zA-Z0-9_.]*$)"->"https://forcesafesearch.google.com" #"(^https://.*\.gstatic\.com$)"->"https://forcesafesearch.google.com" e2guardian-5.5.8r/configs/lists/example.group/urlnobypass.in000077500000000000000000000040541477372360500242450ustar00rootroot00000000000000# User are not allowed to bypass url sites in this list #Don't bother with the www. or the http:// #NOTE: Sites using just IP should be put into bannedsiteiplistwithbypass #You can have multiple .Includes. # WARNING: Old style Blanket blocks in this file will be silently ignored #To include additional files in this list use this example: #.Include<@E2CONFDIR@/anotherbannedurllist> #You can have multiple .Includes. #listcategory: "Banned URLs" #List other URLs to block: # members.home.net/uporn # The squidGuard advert domain/URL lists are now included by default. # To work with advanced ad blocking & the logadblocks option, advert # phrase/site/URL lists should have the string "ADs" in their listcategory. #.Include<@E2CONFDIR@/lists/blacklists/ads/urls> #Remove the # from the following and edit as needed to use a stock #squidGuard/urlblacklist blacklists collection. #.Include<@E2CONFDIR@/lists/blacklists/adult/urls> #.Include<@E2CONFDIR@/lists/blacklists/aggressive/urls> #.Include<@E2CONFDIR@/lists/blacklists/audio-video/urls> #.Include<@E2CONFDIR@/lists/blacklists/chat/urls> #.Include<@E2CONFDIR@/lists/blacklists/drugs/urls> #.Include<@E2CONFDIR@/lists/blacklists/entertainment/urls> #.Include<@E2CONFDIR@/lists/blacklists/frencheducation/urls> #.Include<@E2CONFDIR@/lists/blacklists/gambling/urls> #.Include<@E2CONFDIR@/lists/blacklists/government/urls> #.Include<@E2CONFDIR@/lists/blacklists/hacking/urls> #.Include<@E2CONFDIR@/lists/blacklists/jobsearch/urls> #.Include<@E2CONFDIR@/lists/blacklists/kidstimewasting/urls> #.Include<@E2CONFDIR@/lists/blacklists/mail/urls> #.Include<@E2CONFDIR@/lists/blacklists/news/urls> #.Include<@E2CONFDIR@/lists/blacklists/porn/urls> #.Include<@E2CONFDIR@/lists/blacklists/proxy/urls> #.Include<@E2CONFDIR@/lists/blacklists/publicite/urls> #.Include<@E2CONFDIR@/lists/blacklists/redirector/urls> #.Include<@E2CONFDIR@/lists/blacklists/violence/urls> #.Include<@E2CONFDIR@/lists/blacklists/virusinfected/urls> #.Include<@E2CONFDIR@/lists/blacklists/warez/urls> # You will need to edit to add and remove categories you want e2guardian-5.5.8r/configs/lists/example.group/urlredirectregexplist000066400000000000000000000001211477372360500256760ustar00rootroot00000000000000# # redirect browser to different url # # 'regexp_pattern'->'url_to_redirect_to' e2guardian-5.5.8r/configs/lists/example.group/urlregexplist000077500000000000000000000056571477372360500242020ustar00rootroot00000000000000#URL modifying Regular Expressions # #This list applies to the full URL for HTTP sites, and if configured #for SSL MITM, also HTTPS urls. # # The format is: "extended regular expression"->"replacement straight string" # E.g. "shit"->"censored" would replace all occurances of shit in any case. # Far more complicated matches are possible. See other sources for examples # of extended regular expressions. # # Users are pointed at the replaced URL transparently. # Manipulates the URL to automatically switch on safe searching in Google, # Singingfish, Ilse, KEL, Lycos, Alltheweb, Yahoo, Hotbot, Wisenut, # Metacrawler. # Google, Go etc. - remove 'safe=...' #"(^http://[0-9a-z]+\.google\.[a-z]+[-/%.0-9a-z]*/images\?)(.*)(&?)(safe=[^&]*)"->"\1\2\3" # ... and add 'safe=vss' #"(^http://[0-9a-z]+\.google\.[a-z]+[-/%.0-9a-z]*/images\?)"->"\1safe=vss&" # Singingfish - remove 'ff=...' and add 'ff=1' #"(^http://search\.singingfish\.com/[-/%.0-9a-z]*\?)(.*)(&?)(ff=[^&]*)"->"\1\2\3" #"(^http://search\.singingfish\.com/[-/%.0-9a-z]*\?)"->"\1ff=1&" # Ilse - remove 'family=...' and add 'family=yes' #"(^http://www\.ilse\.nl/searchresults\.dbl\?)(.*)(&?)(family=[^&]*)"->"\1\2\3" #"(^http://www\.ilse\.nl/searchresults\.dbl\?)"->"\1family=yes&" # KEL - remove 'Realm%3AErotiek=...' #"(^http://www\.kel\.nl/search/search.cgi\?)(.*)(&?)(Realm%3AErotiek=[^&]*)"->"\1\2\3" # Lycos.com - family filter only available in advanced mode. # Remove 'adv=...' and 'adf=...' and add 'adv=1&adf=on' #"(^http://[^/]*search[^/]*\.lycos\.com/[-/%.0-9a-z]*\?)(.*)(&?)(adv=[^&]*)"->"\1\2\3" #"(^http://[^/]*search[^/]*\.lycos\.com/[-/%.0-9a-z]*\?)(.*)(&?)(xadult\.)(.*)(xadult\.)"->"\1\2\3\5" #"(^http://[^/]*search[^/]*\.lycos\.com/[-/%.0-9a-z]*\?)"->"\1adv=1&adf=on&" # Lycos.nl - remove 'family=...' and add 'family=on' #"(^http://zoek\.lycos\.nl/[-/%.0-9a-z]*\?)(.*)(&?)(family=[^&]*)"->"\1\2\3" #"(^http://zoek\.lycos\.nl/[-/%.0-9a-z]*\?)"->"\1family=on&" # Alltheweb - change the customize url so that 'offensive' cannot be turned off #"(^http://www\.alltheweb\.com/customize\?)(.*)(&?)(copt_offensive=[^&]*)"->"\1\2\3copt_offensive=on" # Yahoo - remove 'vm=...' and add 'vm=r' #"(^http://[.0-9a-z]+\.yahoo\.[a-z]+[-/%.0-9a-z]*/search)(.*)(&?)(vm=[^&]*)"->"\1\2\3" #"(^http://[.0-9a-z]+\.yahoo\.[a-z]+[-/%.0-9a-z]*/search+.*\?)"->"\1vm=r&" # Hotbot - remove 'adf=...' and add 'adf=on' #"(^http://[0-9a-z]+\.hotbot\.[a-z]+/default\.asp\?)(.*)(&?)(adf=[^&]*)"->"\1\2\3" #"(^http://[0-9a-z]+\.hotbot\.[a-z]+/default\.asp\?)"->"\1adf=on&" # Wisenut - change the customize url so that 'wisepatrol' cannot be turned off #"(^http://www\.wisenut\.com/preferences/savePreferences\.[^?]*\?)(.*)(&?)(wisepatrol=[^&]*)"->"\1\2\3wisepatrol=1" # Metacrawler - remove 'familyfilter=...' and add 'familyfilter=1' #"(^http://www\.metacrawler\.com/info\.metac/search/[-/%.0-9a-z]*\?)(.*)(&?)(familyfilter=[^&]*)"->"\1\2\3" #"(^http://www\.metacrawler\.com/info\.metac/search/[-/%.0-9a-z]*\?)"->"\1familyfilter=1&" e2guardian-5.5.8r/configs/lists/example.group/weightedphraselist.in000077500000000000000000000122521477372360500255620ustar00rootroot00000000000000# E2GUARDIAN weightedphraselist INSTRUCTIONS FOR USE # New in v5.2 - per cent feature # If weighting is a % then that % of the naughtynesslimit is used as the weight # e.g. # < slut ><10%> # - If naughtynesslimit is 200 will add 20 to the count against the word 'slut' # # This allows phrases to be used which will have the same effect in all # filtergroups # # Examples: # # <10> # - Adds 10 to the count against the string 'slut'. ie. sluts, slut!, abslutxyz. # # < slut ><10> # - Adds 10 to the count against the word 'slut'. ie. Sally is a slut that smells. # # ,<50> # - Adds 50 to the count when the strings 'slut' and 'horny' are found on the same page. # # ,<-30> # - Subtracts 30 from the count when 'breast' and 'medical' are on the one page. # # <-25> # - Subtracts 25 from the count when 'education' is on the page. # # See the bannedphraselist for more examples. # # Help by contributing customised lists and/or new keyword lists. # Email: maintianer@e2guardian.org # #listcategory: "Weighted Phrases" #To enable several non-PICS self-labelling and self-rating systems. #Enabled as a bannedsitelist by default. Disable there before enabling as a phraselist. ##.Include<@E2CONFDIR@/lists/phraselists/ukenglish/safelabel/weighted> #Good Phrases (to allow medical, education, news and other good sites) .Include<@E2CONFDIR@/lists/phraselists/ukenglish/goodphrases/weighted> .Include<@E2CONFDIR@/lists/phraselists/ukenglish/goodphrases/weighted_news> #.Include<@E2CONFDIR@/lists/phraselists/danish/goodphrases/weighted> #.Include<@E2CONFDIR@/lists/phraselists/dutch/goodphrases/weighted> #.Include<@E2CONFDIR@/lists/phraselists/malay/goodphrases/weighted> #.Include<@E2CONFDIR@/lists/phraselists/polish/goodphrases/weighted> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/goodphrases/weighted> #.Include<@E2CONFDIR@/lists/phraselists/swedish/goodphrases/weighted> #Pornography .Include<@E2CONFDIR@/lists/phraselists/ukenglish/pornography/weighted> #.Include<@E2CONFDIR@/lists/phraselists/chinesebig5/pornography/weighted> #ALPHA# #.Include<@E2CONFDIR@/lists/phraselists/chinesegb2312/pornography/weighted> #ALPHA# #.Include<@E2CONFDIR@/lists/phraselists/danish/pornography/weighted> #ALPHA# #.Include<@E2CONFDIR@/lists/phraselists/dutch/pornography/weighted> #BETA# #.Include<@E2CONFDIR@/lists/phraselists/french/pornography/weighted> #.Include<@E2CONFDIR@/lists/phraselists/german/pornography/weighted> #.Include<@E2CONFDIR@/lists/phraselists/italian/pornography/weighted> ##.Include<@E2CONFDIR@/lists/phraselists/japanese/pornography/weighted> #ALPHA# #Disabled due to overblocking# #.Include<@E2CONFDIR@/lists/phraselists/malay/pornography/weighted> #BETA# #.Include<@E2CONFDIR@/lists/phraselists/norwegian/pornography/weighted> #BETA# #.Include<@E2CONFDIR@/lists/phraselists/polish/pornography/weighted> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/pornography/weighted> ##.Include<@E2CONFDIR@/lists/phraselists/russian-1251/pornography/weighted> #BETA# #.Include<@E2CONFDIR@/lists/phraselists/russian-koi8-r/pornography/weighted> #BETA# #.Include<@E2CONFDIR@/lists/phraselists/spanish/pornography/weighted> #ALPHA# #.Include<@E2CONFDIR@/lists/phraselists/swedish/pornography/weighted> #Nudism #.Include<@E2CONFDIR@/lists/phraselists/ukenglish/nudism/weighted> #Bad Words - swearing .Include<@E2CONFDIR@/lists/phraselists/ukenglish/badwords/weighted> #.Include<@E2CONFDIR@/lists/phraselists/dutch/badwords/weighted> #.Include<@E2CONFDIR@/lists/phraselists/french/badwords/weighted> #.Include<@E2CONFDIR@/lists/phraselists/german/badwords/weighted> #ALPHA# #.Include<@E2CONFDIR@/lists/phraselists/portuguese/badwords/weighted> #ALPHA# #.Include<@E2CONFDIR@/lists/phraselists/spanish/badwords/weighted> #ALPHA# #Drugs #.Include<@E2CONFDIR@/lists/phraselists/ukenglish/drugadvocacy/weighted> #.Include<@E2CONFDIR@/lists/phraselists/ukenglish/illegaldrugs/weighted> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/illegaldrugs/weighted> #Violence and intolerance .Include<@E2CONFDIR@/lists/phraselists/ukenglish/intolerance/weighted> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/intolerance/weighted> .Include<@E2CONFDIR@/lists/phraselists/ukenglish/gore/weighted> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/gore/weighted> .Include<@E2CONFDIR@/lists/phraselists/ukenglish/violence/weighted> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/violence/weighted> .Include<@E2CONFDIR@/lists/phraselists/ukenglish/weapons/weighted> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/weapons/weighted> #Chat #.Include<@E2CONFDIR@/lists/phraselists/ukenglish/chat/weighted> #.Include<@E2CONFDIR@/lists/phraselists/italian/chat/weighted> #Gambling #.Include<@E2CONFDIR@/lists/phraselists/ukenglish/gambling/weighted> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/gambling/weighted> #Productivity #.Include<@E2CONFDIR@/lists/phraselists/ukenglish/games/weighted> #.Include<@E2CONFDIR@/lists/phraselists/ukenglish/personals/weighted> #.Include<@E2CONFDIR@/lists/phraselists/portuguese/personals/weighted> #System Management and Security .Include<@E2CONFDIR@/lists/phraselists/ukenglish/proxies/weighted> .Include<@E2CONFDIR@/lists/phraselists/ukenglish/warezhacking/weighted> e2guardian-5.5.8r/configs/lists/oldphraselists/000077500000000000000000000000001477372360500216005ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/oldphraselists/Makefile.am000077500000000000000000000025611477372360500236430ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in E2DATADIR = $(E2CONFDIR)/lists/oldphraselists PHRASELISTS = badwords chat drugadvocacy gambling games goodphrases \ googlesearches gore illegaldrugs intolerance legaldrugs \ malware news nudism peer2peer personals pornography \ proxies sport violence warezhacking weapons webmail \ forums rta conspiracy domainsforsale idtheft safelabel \ travel upstreamfilter secretsocieties translation music install-data-local: for l in $(PHRASELISTS); do \ $(mkinstalldirs) $(DESTDIR)$(E2DATADIR)/$$l && \ for f in $(srcdir)/$$l/weighted* $(srcdir)/$$l/exception* $(srcdir)/$$l/banned*; do \ if test -f $$f ; then \ echo "$(INSTALL_DATA) $$f $(DESTDIR)$(E2DATADIR)/$$l"; \ $(INSTALL_DATA) $$f $(DESTDIR)$(E2DATADIR)/$$l; \ fi \ done \ done uninstall-local: for l in $(PHRASELISTS); do \ for f in $(srcdir)/$$l/weighted* $(srcdir)/$$l/exception*; do \ rm -f $(DESTDIR)$(E2DATADIR)/$$l/`basename $$f`; \ done \ done dist-hook: for phrase in $(PHRASELISTS); do \ if test "$$phrase" = .; then :; else \ test -d $(distdir)/$$phrase \ || mkdir $(distdir)/$$phrase \ || exit 1; \ for f in $(srcdir)/$$phrase/weighted* $(srcdir)/$$phrase/banned* $(srcdir)/$$phrase/exception*; do \ if test -f $$f ; then \ cp -p $$f $(distdir)/$$phrase ;\ fi; \ done; \ fi; \ done e2guardian-5.5.8r/configs/lists/oldphraselists/badwords/000077500000000000000000000000001477372360500234055ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/oldphraselists/badwords/weighted_dutch000077500000000000000000000021051477372360500263200ustar00rootroot00000000000000# # Dutch Swear Words Weighted Phrases # Taken from swif.zip from dansguardian website # #listcategory: "Bad words (Dutch)" < apenaaier ><75> #monkey-fucker < bruinweker ><50> #literally: brown-creeper = fagget < droogkloot ><25> #literally: dry-nut (as in: testicle) < flikker ><50> #fagget/sissy < hoer ><25> #hooker < hoerenjong ><65> #son-of-bitch < hondelul ><50> #cock-of-a-dog (commonly used, also by kids) < kak ><10> #shit < klootzak ><50> #ass-hole < kut ><5> #cunt/pussy < kutwijf ><90> #fucking bitch < lul ><50> #dick/cock < moederneuker ><75> #motherfucker < nauwe gaatje ><50> #literally: ass-hole < paardenlul ><50> #horse-cock < pik omhoog ><80> #boner < reetneuker ><70> #literally: ass-fucker < rukker ><50> #jerk-off < schijt ><50> #shit < schijtlul ><50> #literally: shit-cock; commonly used for a chicken-shit < slet ><50> #slut < smerige kankerhoer ><75> #fucking whore (bad..) < spast ><50> #spastic < stront ><25> #shit < sufkut ><75> #literally; sleepy-cunt < sukkel ><20> #dumbo < vuile kankerlijer ><75> #fucking ass-hole (bad one) < zakkewasser ><42> #ass-hole e2guardian-5.5.8r/configs/lists/oldphraselists/badwords/weighted_french000077500000000000000000000016431477372360500264640ustar00rootroot00000000000000# # French Swear Words Weighted Phrases # #listcategory: "Bad words (French)" < merde ><50> < tu m'emmerdes ><80> < tu me fais chier ><50> # you are pissing me off < retourne enculer ><80> # go and fuck yourself < encul ><70> < salope ><60> < conasse ><40> < poufiasse ><40> < ordure ><20> # mess / rubbish < chier ><50> # to shit < faire foutre ><80> < fils de pute ><90> # son of a whore < branler ><50> < bique ><50> # cock < trou du cul ><80> # literally hole of arse < le con ><60> < la chatte ><90> # pussy < baiser ><60> < conneries ><30> < fait moi jouir ><80> # make me cum < tte moi le dard ><80> < tte moi le noeud ><80> < pd ><40> < tantouze ><50> < couilles ><60> # balls < putain ><60> # whore < pute ><60> < cul ><40> < pauvre con ><60> # poor bastard < pisser dans ><50> < fils de pute ><90> < salope ><60> < Va te branler ><70> < Va te tripoter ><80> < Va te faire enculer ><90> < choleque de merde ><60> e2guardian-5.5.8r/configs/lists/oldphraselists/badwords/weighted_german000077500000000000000000000013321477372360500264630ustar00rootroot00000000000000# # German Swear Words Weighted Phrases # Taken from swif.zip from dansguardian website # #listcategory: "Bad words (German)" < arschloch ><20> < scheiss ><20> < scheisse ><20> < fotze ><20> < schwuchtl ><20> < lesbe ><20> < mutterficker ><20> < hurensohn ><20> < hure ><20> < arschgesicht ><20> < fick ><20> < scheissdreck ><20> < arschficker ><20> < arsch ><20> < scheisskopf ><20> < schnoodle noodle ><20> < verpiss dich ><20> < wichser ><20> < arschgeige ><20> < hosenscheisser ><20> < schwanz ><20> < affenschwanz ><20> < schlampe><20> # - tramp or slut < schweinebacke><20> # - double crossing so and so < dumpfbacke><20> # - idiot (mainly for female) < arschkriecher><20> # - person who kisses bosses arse e2guardian-5.5.8r/configs/lists/oldphraselists/badwords/weighted_portuguese000077500000000000000000000003401477372360500274120ustar00rootroot00000000000000# # Brazilian Portuguese Swear Words Weighted Phrases # #listcategory: "Bad words (Portuguese)" <80> <10> <10> <10> <10> <5> <10> <10> < viad><15> e2guardian-5.5.8r/configs/lists/oldphraselists/badwords/weighted_spanish000077500000000000000000000021241477372360500266570ustar00rootroot00000000000000# # Spanish Swear Words Weighted Phrases # Taken from swif.zip from dansguardian website # #listcategory: "Bad words (Spanish)" < marica ><20> < maricn ><20> < mariconazo ><20> < mariquita ><20> < chapero ><20> < bastardo ><20> < cabron ><20> < polla ><20> < nabo ><20> < rabo ><20> < verga ><20> < cipote ><20> < gilipollas ><20> < tontopollas ><20> < pichacorta ><20> < cojones ><20> < pelotas ><20> < huevos ><20> < cojonazos ><20> < coo ><20> < chocho ><20> < raja ><20> < chichi ><20> < pelos de los huevos ><20> < pelos de los coo ><20> < mierda ><20> < cagar ><20> < jiar ><20> < chupame la polla ><20> < chupamela ><20> < chupar ><20> < puta ><20> < zorra ><20> < guarra ><20> < follar ><20> < jode ><20> < chingar ><20> < me cago en ti ><20> < que te jodan ><20> < culo ><20> < cabron ><20> < calientapollas ><20> #South American spanish: < mariposa ><20> < joto ><20> < pinga ><20> < chilito ><20> < panocha ><20> < chimba ><20> < chichis ><20> < Chinga ><20> < pendejo ><20> < pendeja ><20> < cago en tu leche ><20> < hazte cojer ><20> < pinche cabron ><20> < concha ><20> < coger ><20> e2guardian-5.5.8r/configs/lists/oldphraselists/chat/000077500000000000000000000000001477372360500225175ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/oldphraselists/chat/weighted000077500000000000000000000010521477372360500242430ustar00rootroot00000000000000#listcategory: "Chat" < javachat ><50> < webbchat ><50> < webbtjatt ><50> < webchat ><50> < chatkamer ><50> < chatroom ><50> < irc ><50> < ircd ><50> < javachat ><50> < javatjatt ><50> #Web-based Instant Messaging < gaim ><30> < icq ><30> <30> <30> < wbmsn ><30> <30> < kiwibox ><30> < webmessenger ><30> < e-messenger ><30> < web2messenger ><30> < onlinemessenger ><30> < msnger><30> < jpager><30> < easymessage ><30> <30> < instant messenger ><10> ,,< msn ><50> ,<30> e2guardian-5.5.8r/configs/lists/oldphraselists/chat/weighted_italian000077500000000000000000000011761477372360500257530ustar00rootroot00000000000000#listcategory: "Chat (Italian)" < scegliere la ragazza><30> < scegliere le ragazze><30> <30> <30> ,< ragazza >,< preferita><30> ,< ragazza >,< che preferisci><30> < scegliere >,< ragazza >,< che preferisci><30> < scegliere >,< ragazza >,< preferita><30> < conoscere >,< ragazz><10> < incontrare>,< ragazz><10> < dal vivo><30> < incontri eccitanti><30> < incontrare persone><10> < interagire con><10> < chat><10> <10> <10> < spregiudicat><10> < eros ><10> < video-chat><20> < scambio coppie><30> < videocamer>,< nascost><10> < telecamer>,< nascost><10> e2guardian-5.5.8r/configs/lists/oldphraselists/conspiracy/000077500000000000000000000000001477372360500237525ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/oldphraselists/conspiracy/weighted000077500000000000000000000017431477372360500255050ustar00rootroot00000000000000# Originally created by David Burkholder # # # listcategory: "conspiracy" # <30> <40> <40> <40> <40> <25> <40> <40> <40> <40> <40> <40> <40> <40> <40> <40> <40> <40> <40> <40> <40> <40> <40> <40> <40> <20> <40> <40> <40> <40> <40> <55> #New world order <40> <40> <40> <40> <40> <75> <75> #Ufo < ufo><40> <40> <40> <40> <40> <40> <40> <40> <40> <40>e2guardian-5.5.8r/configs/lists/oldphraselists/domainsforsale/000077500000000000000000000000001477372360500246065ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/oldphraselists/domainsforsale/weighted000077500000000000000000000013551477372360500263400ustar00rootroot00000000000000# # Phraselists to block parked domains - domains that are for sale # Originally created by Fernand Jonker # # If you use this list please send feedback to phrasemaster@dansguardian.org # Waiting for feedback and input # #listcategory: "Domain for Sale" ,<150> <50> <50> <50> <50> <50> <50> <50> <50> <50> < sedo><50> <50> <150> ,<150> #Companies <100> <50> <50> <50>e2guardian-5.5.8r/configs/lists/oldphraselists/drugadvocacy/000077500000000000000000000000001477372360500242535ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/oldphraselists/drugadvocacy/weighted000077500000000000000000000003621477372360500260020ustar00rootroot00000000000000#listcategory: "Drug Advocacy" < american cannabis society ><40> < drogenpolitik ><60> < ganga ><50> < ganja ><50> < marihuanabeleid ><60> < marijuanalagstiftning ><60> < marijuanalovgivning ><60> < medicinal marijuana ><40> < spliff ><60> e2guardian-5.5.8r/configs/lists/oldphraselists/forums/000077500000000000000000000000001477372360500231135ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/oldphraselists/forums/weighted000077500000000000000000000020671477372360500246460ustar00rootroot00000000000000# # Phraselists to block forum sites # Originally Created by Fernand Jonker # #listcategory: "Forums" #General < Member>,< register><20> < Search>,< faq><20> < Login>,< home><20> < Moderator><20> <20> <20> <20> <20> <20> <20> ,,<20> <20> <20> <20> <20> <20> <20> <20> <20> <20> #PHPBB <100> #UBB <100> #Web Wiz Forums <100> #vBulletin <100> <100> #Simple Machines <100> < SMF >,<100> #PostNuke <100> #Snitz <100> #Invision Power Board <100> <20> #FUDForum <100> #Groupee Community <100> #Mailgust <100> #php Bulletin Board <40> e2guardian-5.5.8r/configs/lists/oldphraselists/gambling/000077500000000000000000000000001477372360500233605ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/oldphraselists/gambling/banned000077500000000000000000000002571477372360500245410ustar00rootroot00000000000000#listcategory: "Gambling" < loto > < lotto > < video poker > e2guardian-5.5.8r/configs/lists/oldphraselists/gambling/banned_portuguese000077500000000000000000000002721477372360500270200ustar00rootroot00000000000000#listcategory: "Gambling (Portuguese)" < cassino virtual > < cassinos virtuais > < loteria virtual > < loterias virtuais > < lotomania virtual > < megasena virtual > < vegas virtual > e2guardian-5.5.8r/configs/lists/oldphraselists/gambling/weighted000077500000000000000000000013641477372360500251120ustar00rootroot00000000000000# # Phraselists to block gambling sites # #listcategory: "Gambling" < bet >,<40> < betting ><30> < blackjack ><30> < casino ><30> < casinon ><30> < casinos ><30> < gamblers ><30> < gambling ><30> < jackpot ><20> < jackpott ><20> < kasino><30> < kasinon ><30> < loto ><30> < lotteri ><30> < lotterier ><30> < lotteries ><20> < lottery ><30> < lotto ><30> < prispott ><20> < roulette ><30> < snake eyes ><20> < snakeeyes ><20> < sports book ><10> < sportsbook ><10> < totalisator ><30> < video poker ><30> < wagering ><50> < wager ><30> <10> <30> <30> ,<50> ,<50> ,<50> <30> <30> <30> <30> e2guardian-5.5.8r/configs/lists/oldphraselists/gambling/weighted_portuguese000077500000000000000000000003331477372360500273670ustar00rootroot00000000000000#listcategory: "Gambling (Portuguese)" < aposta ><10> < apostadores ><10> < apostando ><10> < casino><20> < cassino><20> < jogo de vinte e um ><10> < jogos de vinte e um ><10> < jogo de azar><10> < jogos de azar><10> e2guardian-5.5.8r/configs/lists/oldphraselists/games/000077500000000000000000000000001477372360500226745ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/oldphraselists/games/weighted000077500000000000000000000021611477372360500244220ustar00rootroot00000000000000# # Phraselists to block gaming websites # Originally created by Fernand Jonker # # This list is seriously ALPHA - it blocks many pages, but may overblock. # This list has NOT been tested in a production environment! # If you use this list please send feedback to phrasemaster@dansguardian.org # Waiting for input :-) # #listcategory: "Games" < games><10> < puzzles><10> <40> <30> <30> <20> #game names <30> <30> <30> <30> <30> <30> <30> <30> <10> <30> <30> <30> <30> <30> <30> <30> <30> <30> <30> #game companies <30> <10> <10> <10> <20> ,< psp ><20> <20> <20> <20> <20> <20> #general gaming ,<30> #,<30> ,< simulation><30> ,< strategy><30> ,< adventure><30> #,< action><30> ,< rpg><30> ,< sport><30> e2guardian-5.5.8r/configs/lists/oldphraselists/goodphrases/000077500000000000000000000000001477372360500241165ustar00rootroot00000000000000e2guardian-5.5.8r/configs/lists/oldphraselists/goodphrases/exception000077500000000000000000000001201477372360500260330ustar00rootroot00000000000000 #< ringtone > # e2guardian-5.5.8r/configs/lists/oldphraselists/goodphrases/exception_email000077500000000000000000000003041477372360500272060ustar00rootroot00000000000000 , e2guardian-5.5.8r/configs/lists/oldphraselists/goodphrases/weighted_general000077500000000000000000000211321477372360500273400ustar00rootroot00000000000000# #Generally Good Phrases to balance out bad phrases tagged on a page. # #Following Added to exempt DansGuardian blocking sites ,<-200> <-20> <-20> <-20> <-20> < logrotat>,<-200> < weightedphraselist><-50> < weightedphrasemode>,<-200> < configuring>,<-200> < building><-20> < apache>,<-20> < binary><-20> < naughtynesslimit><-50> # < smoothwall ><-20> <-20> <-50> < dns ><-20> < wikipedia ><-30> <-50> <-50> < vpn ><-20> <-50> <-50> <-10> <-10> <-10> <-50> <-10> <-10> <-10> <-10> <-20> <-20> <-20> <-30> <-10> <-50> <-20> ,,<-50> ,,<-50> ,,<-50> ,,<-50> ,<-50> ,,<-50> <-50> ,<-30> ,<-30> ,<-30> <-20> ,,<-50> < amd ><-20> <-50> <-50> <-100> <-20> <-50> <-50> <-20> <-10> <-50>
,<-30>
,<-30>
,<-30>
,<-30>
,<-30>
,<-30>
,<-30>
,<-30>
,<-30> <-20> <-30> <-50> ,<-30> ,<-30> ,<-30> <-10> <-10> <-10> <-50> <-20> ,,<-20> <-20> <-20> <-20> <-10> <-20> <-10> <-30> <-10> <-50> <-20> <-40> <-10> <-20> <-20> <-50> <-50> ,,<-50> ,<-30> <-50> <-10> <-30> ,<-30> ,<-30> ,<-30> ,<-30> ,<-30> ,<-30> ,<-30> <-20> <-10> <-10> <-20> <-10> <-20> <-50> <-40> <-10> <-40> <-40> <-20> <-30> <-40> <-60> <-10> <-10> <-30> <-20> <-20> <-20> <-20> <-10> <-10> <-1000> <-10> <-10> <-30> <-20> <-30> <-50> <-50> <-10> <-10> <-10> <-10> <-30> <-30> ,<-50> <-50> ,< vinyl><-50> <-50> <-20> <-30> <-20> <-50> <-20> <-20> <-40> <-40> < e-card><-10> < ecard><-10> <-20> ,<-20> ,<-20> <-10> <-20> <-20> <-10> <-20> <-30> <-50> < ex-><-20> <-10> <-5> <-40> <-40> ,<-20> ,<-20> ,<-60> ,<-10> <-20> <-5> <-5> <-5> <-30> <-20> <-30> <-40> <-40> <-10> <-20> <-30> <-30> <-50> <-20> <-30> <-30> <-20> <-50> <-20> <-30> <-50> <-40> <-30> <-40> <-40> <-40> <-30> <-20> < health ><-10> <-30> <-30> ,<-10> ,<-20> ,<-20> ,<-20> ,<-20> ,<-20> <-20> <-20> < h.i.v ><-50> < hiv ><-50> <-20> ,<-30> <-50> ,<-30> ,<-30> <-10> <-10> <-30> <-40> < intel ><-20> <-20> <-80> <-20> <-10> <-50> <-500> <-40> <-20> ,<-30> ,<-30> <-10> <-30> ,<-50> ,<-50> <-10> ,<-50> ,<-50> <-30> <-40> <-20> <-30> <-60> <-20> <-100> <-100> <-20> <-50> <-30> <-20> ,<-20> <-50> <-50> <-50> <-10> <-10> <-50> <-20> <-40> <-20> <-10> <-50> <-20> <-30> <-50> <-5> <-20> <-50> <-20> <-10> <-80> <-40> <-10> <-5> <-20> <-40> <-50> <-30> <-30> <-50> <-30> <-30> <-30> <-30> <-30> <-30> <-50> <-50> <-20> <-20> <-50> <-10> <-20> <-20> <-20> <-20> <-20> <-50> ,<-30> <-20> <-50> <-40> <-40> <-40> <-40> <-50> <-50> <-50> <-40> <-30> ,,<-30> <-15> <-50> < research ><-20> <-20> <-10> <-30> <-20> <-50> <-50> <-20> <-30> <-30>
ACCESS DENIED

Oops! You have tried visiting a website which has been deemed inappropriate:



-URL-

Because

Category: -CATEGORIES-   -REASONGIVEN- -SERVERNAME-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Acknowledge

e2guardian-5.5.8r/data/000077500000000000000000000000001477372360500146635ustar00rootroot00000000000000e2guardian-5.5.8r/data/Makefile.am000077500000000000000000000011101477372360500167130ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in SUBDIRS= languages . scripts DATATOPDIR = $(datadir)/$(PACKAGE_NAME) FLISTS = transparent1x1.gif e2guardian.pl blockedflash.swf EXTRA_DIST = $(FLISTS) install-data-local: $(mkinstalldirs) $(DESTDIR)$(DATATOPDIR) && \ $(mkinstalldirs) $(DESTDIR)$(E2LOGLOCATION) && \ $(mkinstalldirs) $(DESTDIR)$(E2PIDDIR) && \ for l in $(FLISTS) ; do \ echo "$(INSTALL_DATA) $$l $(DESTDIR)$(DATATOPDIR)/$$l"; \ $(INSTALL_DATA) $$l $(DESTDIR)$(DATATOPDIR)/$$l; \ done uninstall-local: for l in $(FLISTS) ; do \ rm -f $(DESTDIR)$(DATATOPDIR)/$$l ; \ done e2guardian-5.5.8r/data/blockedflash.swf000077500000000000000000000052471477372360500200400ustar00rootroot00000000000000CWS xW XW>3 ]>@I    tfxbauKKd-XG RaZ+Zum-궟 Zn/3ws xX@; ʧIJz:KD̜ 2E-D("@"ST\"G Dg5di # Zӱ3!ܓL?'(h2Jk"I=OEBI)i"ԉ,E 0xE$HH$.T,c8IXON}R4B**e RVH; )ԓe s,G 9A#خOuT6yGҴ^(SjJ*qLMbbI$4&F:¦gtEB4*`og ^hcݽRQ*i6!JU Ik*#ىQQrR1J,14rop-[sp?/ϖd# G\ bKyA 1(,cY?]SݬսVLq*b xXޏ|OW9݄=,4~x"'wѠOY"G2?y,mlp8 Q]?JoQ ~~jU%XxA]lKSIFDa-tgcWj:[~[_jUǂN { ڲԺj$olJ5/-+Z#6Ë`}yɋgw3;xYm۷O˱Vk\Ň~|FG\O."}vwK#Fܶ=w=s?t\yҿ{25r<[ѸtyojKcYo3j@i%?lY҄}gx|A?msgRY\mQ \Ų>ͨ't [Q-r~{w5Hb>N}//8]c@UgR*j~29ڬqI0ۦx*kۯ,==C{/^i \ѶɎn7P ՠ |m\Eu]s۹}Ys_ٺf}:qz> z7N ވUܡvVV377]E;N#}jyE5Sօv쀼oNz8DR^ۮgJ!.,(zU̶ȷ%vlz,jS<;兝%&~[_N >ↁ? @7@mH:y63-N5sܺډ^1}/ ϼma\R=~&O8k(^1mO)m킞3OoR]!E\ud 4{Wt2pE-q߼ ΎLLLX1ػZP aGMG3MS`X! >rwU[85j[7/Uy#u{f{ng>5yb^Ҁ 2ܺGf.ܥ0.pl f*&K:| yoNX9`@j?d%J%ع)a }lkP(>D6T-LmK>⛴~>k:@J0{ gwβ .M2b}7bG; @֢n(Å,&*BIL zN,i ˢ[gH"qd,v^hx#RPY âUA|J8XR$kRh"‹\}ת6*vVUĸn7}uګLz Sdg] 5;J,a]"AgiD$HJ!MyvfroV~e~lx?w<ߓb;Bh3WM䬡^jAtf8ÑgIO{ A iS$RRi^!(QƄDfBJD+X: 4qn_ zWزeKkjNxTt ƣNMֈz#PjtxT0x>'dMEX&﹪-yks LNB5b?1NnMn|1+Up7IJdK2æ3 $ },m!Y3: !|cC#.œ1]!9 cChL1]p!t!c8YiJ/>e2guardian-5.5.8r/data/e2guardian.pl000077500000000000000000000050511477372360500172450ustar00rootroot00000000000000#!/usr/bin/perl $allow_html_code = 0; &ReadEnvs; $deniedurl = $in{'DENIEDURL'}; $reason = $in{'REASON'}; $user = $in{'USER'}; $ip = $in{'IP'}; $cats = $in{'CATEGORIES'}; # originating hostname - can be undefined $host = $in{'HOST'}; # virus/filter bypass hashes # if bypass modes have been set to > 0, # then the GBYPASS or GIBYPASS variable will contain the filter/infection bypass hash. # if bypass modes have been set to -1, # then the HASH variable will be set to 1 if the CGI should generate a GBYPASS hash (filter bypass), # or 2 if the CGI should generate a GIBYPASS hash (infection bypass). $fbypasshash = $in{'GBYPASS'}; # filter bypass hash - can be undefined $ibypasshash = $in{'GIBYPASS'}; # infection bypass hash - can be undefined $hashflag = $in{'HASH'}; # hash flag - can be undefined; 1 = generate GBYPASS; 2 = generate GIBYPASS print "Content-type: text/html\n\n"; print 'e2guardian - Access Denied'; print '

ACCESS HAS BEEN DENIED

'; if (length($user) > 0) { print "
$user, access to the page:

"; } else { print '
Access to the page:

'; } print "$deniedurl"; print '

... has been denied for the following reason:

'; print "$reason"; if (length($cats) > 0) { print '

Categories:

'; print "$cats"; } print '

Your username, IP address, date, time and URL have been logged.'; print '

You are seeing this error because the page you attempted
'; print 'to access contains, or is labelled as containing, material that'; print '
has been deemed inappropriate.
'; print '

If you have any queries contact your ICT Co-ordinator or Network Manager.
'; print '

Powered by e2guardian'; print '

'; exit; sub ReadEnvs { local($cl, @clp, $pair, $name, $value); if ( $ENV{'REQUEST_METHOD'} eq 'POST' ) { read(STDIN, $cl, $ENV{'CONTENT_LENGTH'} ); } else { $cl = $ENV{'QUERY_STRING'}; } @clp = split(/::/, $cl); foreach $pair (@clp) { ($name, $value) = split(/==/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~ s///g; if ($allow_html_code != 1) { $value =~ s/<([^>]|\n)*>//g; } $in{$name} = $value; } } e2guardian-5.5.8r/data/languages/000077500000000000000000000000001477372360500166315ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/Makefile.am000077500000000000000000000026051477372360500206730ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in LANGDIR = $(datadir)/$(PACKAGE_NAME)/languages LANGUAGES = czech hebrew turkish \ bulgarian danish indonesian russian-1251 ukenglish \ chinesebig5 dutch italian russian-koi8-r \ chinesegb2312 french lithuanian polish slovak \ german portuguese swedish spanish hungarian \ ptbrazilian japanese malay mxspanish arspanish EXTRA_DIST= ReadMe install-data-local: @for l in $(LANGUAGES); do \ $(mkinstalldirs) $(DESTDIR)$(LANGDIR)/$$l && \ for f in $(srcdir)/$$l/messages $(srcdir)/$$l/template.html $(srcdir)/$$l/neterr_template.html $(srcdir)/$$l/fancydmtemplate.html; do \ echo "$(INSTALL_DATA) $$f $(DESTDIR)$(LANGDIR)/$$l"; \ $(INSTALL_DATA) $$f $(DESTDIR)$(LANGDIR)/$$l; \ done \ done uninstall-local: @for l in $(LANGUAGES); do \ for f in $(srcdir)/$$l/messages $(srcdir)/$$l/template.html $(srcdir)/$$l/neterr_template.html $(srcdir)/$$l/fancydmtemplate.html; do \ rm -f $(DESTDIR)$(LANGDIR)/$$l/`basename $$f`; \ done \ done dist-hook: @ for lang in $(LANGUAGES); do \ if test "$$lang" = .; then :; else \ test -d $(distdir)/$$lang \ || mkdir $(distdir)/$$lang \ || exit 1; \ cp -p $(srcdir)/$$lang/messages $(srcdir)/$$lang/template.html $(srcdir)/$$l/neterr_template.html $(srcdir)/$$lang/fancydmtemplate.html $(distdir)/$$lang \ || exit 1; \ fi; \ done e2guardian-5.5.8r/data/languages/ReadMe000077500000000000000000000012471477372360500177200ustar00rootroot00000000000000To use these messages files edit the language option in the e2guardian.conf file. If your language is not here, please email author/e2guardian.org with your translation and it will be included in a later release. Or, if you want to improve the existing messages we will consider using ones you submit instead. When editing, the line may not contain a " as this is used to denote the start and end. Instead use a '. Maintainers, if you add a new message option, please update the message_master file (in english) and then run the update_messages script from this directory to update all the language files with the new message(s) which will be marked 'needs translation'. e2guardian-5.5.8r/data/languages/arspanish/000077500000000000000000000000001477372360500206215ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/arspanish/fancydmtemplate.html000077500000000000000000000142251477372360500246730ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/arspanish/messages000066400000000000000000000156571477372360500223710ustar00rootroot00000000000000# e2guardian messages file in AR Spanish # Translated by Roberto Quiroga "0","Message number absent" # needs translation "1","Acceso Denegado" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Su dirección IP no esta autorizada a visitar: " "101","Su dirección IP no esta autorizada navegar." "102","El usuario no esta autorizado a visitar: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","La URL solicitada esta mal formada." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Se encontró la frase no permitida: " "301","Se encontró una frase no permitida" "400","Combinación de frases no permitida: " "401","Combinación de frases no permitida." "402","Límite de ponderación de frases de " "403","Límite de ponderación de frases excedido" "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Sitio no permitido: " "501","URL no permitida: " "502","El sitio no esta en la lista permitida por el bloqueo activo." "503","Banned Regular Expression URL: " "503","URL bloqueada por Expresion Regular: " "504","URL bloqueada por Expresion Regular." "505","Solo se suministro la direccion IP y el bloqueo esta activo." "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","Se encontró una excepción por IP de origen." "601","Se encontró una excepción por usuario de origen." "602","Se encontró una excepción por lugar." "603","Se encontró una excepción por URL." "604","Se encontró una excepción por la frase: " "605","Se encontró una excepción por la combinación de frases: " "606","Bypass URL excepción." "607","Bypass cookie excepción." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " # 606,607 by Daniel Barron - corrections welcome "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","La subida esta bloqueada." "701","Límite de subida excedido." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Clase MIME bloqueada: " "900","Extension bloqueada: " "1000","Clasificación PICS excedida en el sitio indicado." "1100","Virus or bad content detected." "1101","Advert blocked" "1200","Please wait - downloading to be scanned..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","File no longer available" e2guardian-5.5.8r/data/languages/arspanish/neterr_template.html000066400000000000000000001407071477372360500247120ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/arspanish/template.html000077500000000000000000000030011477372360500233170ustar00rootroot00000000000000e2guardian - Acceso Denegado

EL ACCESO HA SIDO DENEGADO -USER-


EL ACCESO A LA PAGINA:

-URL-

... ha sido denegado por la siguiente razón:

-REASONGIVEN-

Ud. esta viendo este mensaje de error porque la página a la que
intenta acceder contiene, o esta clasificada como conteniendo,
material que se considera inapropiado.

Si lo desea contacte al Administrador de Sistemas.

Powered by -SERVERNAME- e2guardian

e2guardian-5.5.8r/data/languages/bulgarian/000077500000000000000000000000001477372360500205755ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/bulgarian/fancydmtemplate.html000077500000000000000000000142251477372360500246470ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/bulgarian/messages000066400000000000000000000140371477372360500223340ustar00rootroot00000000000000# e2guardian messages file in Bulgarian # by Pavel Constantinov "0","Message number absent" # needs translation "1"," " "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100"," IP : " "101"," IP ." "102"," : " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200"," URL ." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300"," : " "301"," ." "400"," : " "401"," ." "402"," " "403"," ." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500"," : " "501"," URL: " "502"," Blanket Block ." "503"," URL : " "504"," URL ." "505"," Blanket IP Block IP address." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600"," IP ." "601"," IP ." "602"," ." "603"," URL-." "604"," : " "605"," : " "606","Bypass URL exception." "607","Bypass cookie exception." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " # 606,607 by Daniel Barron - corrections welcome "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700"," ." "701"," ." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800"," MIME: " "900"," : " "1000"," ." "1100","Virus or bad content detected." "1101","Advert blocked" "1200","Please wait - downloading to be scanned..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","File no longer available" e2guardian-5.5.8r/data/languages/bulgarian/neterr_template.html000066400000000000000000001407071477372360500246660ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/bulgarian/template.html000077500000000000000000000033501477372360500233020ustar00rootroot00000000000000 e2guardian - Access Denied

, -USER-

:

-URL-

... :

-REASONGIVEN-

, ,
, .
, ICT .

Powered by -SERVERNAME- e2guardian

e2guardian-5.5.8r/data/languages/chinesebig5/000077500000000000000000000000001477372360500210165ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/chinesebig5/fancydmtemplate.html000077500000000000000000000142251477372360500250700ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/chinesebig5/messages000066400000000000000000000140011477372360500225440ustar00rootroot00000000000000# e2guardian messages file # Translated to Traditional Chinese (Big5) by: Fr. Visminlu Vicente L. Chua, S.J. 2002-10-10 # 2004-08-20: Translated 606 and 607 # 2008/10/16: Translated 608, 609, 1100, 1101, 1200, 1210, 1220, 1230 "0","Message number absent" # needs translation "1","ڵŪ" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","zϥΪ IP }\sںG" "101","zϥΪ IP }\sںC" "102","zϥΪbW٤\sںG" "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","zҽШD URL 榡~C" "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","QTyryG" "301","QTyryC" "400","QTզXyryG" "401","QTզXyryC" "402","[vyry" "403","WL[vyryC" "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","QT}G" "501","QT URLG" "502","ͮġAӨ}buզWv̡C" "503","uWܡvoOQT URLG" "504","uWܡvQT URLC" "505","ϥ IP ͮġAӨӦa}uO@IPa}C" "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","Pҥ~Ȥ IP ۦPC" "601","Pҥ~ȤbۦPC" "602","Pҥ~}ۦPC" "603","Pҥ~URLۦPC" "604","ҥ~yryG" "605","զXҥ~yryG" "606","VL URL ҥ~C" "607","VL cookie ҥ~C" "608","yɲL URL ҥ~C" "609","Wܨҥ~ URL kXG" "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","TWǡC" "701","WLWǷC" "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","QTMIMEwAG" "900","QTɦWG" "1000","W}WLFPICSХܼhC" "1100","frΤ}eC" "1101","si" "1200","еy - bUAԱy ..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","UC}ly ..." "1220","yC

Io̤UG " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","ɮפwsb" e2guardian-5.5.8r/data/languages/chinesebig5/neterr_template.html000066400000000000000000001407071477372360500251070ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/chinesebig5/template.html000077500000000000000000000027121477372360500235240ustar00rootroot00000000000000 e2guardian - TŪ

T -USER- Ū


ŪG

-URL-

... QT]FHUzѡG

-REASONGIVEN-

zݨoӿ~]znŪAơC

pzDpICTխκ޲zC

Powered by -SERVERNAME- e2guardian

e2guardian-5.5.8r/data/languages/chinesegb2312/000077500000000000000000000000001477372360500210705ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/chinesegb2312/fancydmtemplate.html000077500000000000000000000142251477372360500251420ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/chinesegb2312/messages000066400000000000000000000140151477372360500226230ustar00rootroot00000000000000# e2guardian messages file in chinese gb2312 # By Chen YiFei "0","Message number absent" # needs translation "1","ܾ" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100"," IP ַȨ web : " "101"," IP ַȨ web ʡ" "102","ʺȨ web ʡ " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200"," URL ʽд" "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","ֱֹĶ: " "301","ֱֹĶ" "400","ֱֹ϶: " "401","ֱֹ϶" "402"," ȨΪ: " "403","Ȩơ" "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","ֹվ: " "501","ֹ URL: " "502","ȫֹЧվ㲢ڰС" "503","ʽֹ URL: " "504","ֱʽֹ URL" "505","ȫֹʹ IP Чõַһ IP ַ" "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","ƥһĿͻ IP ַ" "601","ƥһĿͻʺš" "602","ƥһվ㡣" "603","ƥһ URL" "604","Ķ:" "605","϶: " "606","Bypass URL exception." "607","Bypass cookie exception." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " # 606,607 by Daniel Barron - corrections welcome "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","ֹϴļ" "701","ļϴ޶" "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","ֹ MIME : " "900","ֹļչ: " "1000","վ PICS ǩ" "1100","Virus or bad content detected." "1101","Advert blocked" "1200","Please wait - downloading to be scanned..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","File no longer available" e2guardian-5.5.8r/data/languages/chinesegb2312/neterr_template.html000066400000000000000000001407071477372360500251610ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/chinesegb2312/template.html000077500000000000000000000024541477372360500236010ustar00rootroot00000000000000e2guardian - Access Denied

ʱܾ -USER-


ʸҳ:

-URL-

... ԭܾ:

-REASONGIVEN-

Ϣԭͼʵҳвʵݡ

κʣϵ ICT ЭԱܡ

Powered by -SERVERNAME- e2guardian

e2guardian-5.5.8r/data/languages/czech/000077500000000000000000000000001477372360500177255ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/czech/fancydmtemplate.html000077500000000000000000000142221477372360500237740ustar00rootroot00000000000000 Stahuji -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/czech/messages000066400000000000000000000161021477372360500214570ustar00rootroot00000000000000# DansGuardian messages file in Czech # by Richard Bukovansky bukovansky@atcomp.cz # updates,recode to utf8 by Jan Bubík bubik@acvyskov.cz "0","Message number absent" # needs translation "1","Přístup zamítnut" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Vaši IP adrese není povoleno prohlížet: " "101","Vaše IP adresa nemá povoleno prohlížení." "102","Vaše uživatelské jméno nemá povoleno prohlížet: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","Požadované URL je špatně zadáno." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Nalezena zakázaná fráze: " "301","Nalezena zakázaná fráze." "400","Nalezena zakázaná kombinace frází: " "401","Nalezena zakázaná kombinace frází." "402","Limit vážených frází: " "403","Byl překročen limit pro vážené fráze." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Zakázaný webový server: " "501","Zakázaná webová adresa: " "502","Celoplošné blokování přístupu je aktivováno a tento webový server není povolen." "503","Nalezena zakázaná webová adresa dle regularního výrazu: " "504","Nalezena zakázaná webová adresa dle regularního výrazu." "505","Celoplošné blokovaní IP adres je aktivováno a byla zadána IP adresa." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","Nalezena IP adresa s vyjímkou." "601","Nalezen uživatel s vyjímkou." "602","Nalezen webový server s vyjímkou." "603","Nalezena webová adresa s vyjímkou." "604","Nalezena fráze s vyjímkou: " "605","Nalezena kombinace frází s vyjímkou: " "606","Nalezen uživatelem vyžádaný přístup k webové adrese." "607","Nalezen uživatelem vyžádaný přístup k položce cookie." "608","Nalezen uživatelem vyžádaný přístup ke škodlivému kódu." "609","Nalezen regulární výraz s vyjímkou webové adresy: " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","Nahrávání souborů na web je zakázáno." "701","Limit nahrávání souborů na web byl překročen." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Zakázaný typ obsahu: " "900","Zakázaná přípona souboru: " "1000","Byle překročena úroveň hodnocení PICS na tomto webovém serveru." "1100","Detekován virus nebo škodlivý obsah." "1101","Blokována reklama" "1200","Prosím vyčkejte - stahuji data k prověření..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Stahování dokončeno. Prověřuji..." "1220","Prověřeno.

Pro stáhnutí klikněte zde: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","Soubor již neexistuje" e2guardian-5.5.8r/data/languages/czech/neterr_template.html000066400000000000000000001407071477372360500240160ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/czech/template.html000077500000000000000000000040021477372360500224250ustar00rootroot00000000000000 DansGuardian - Přístup zakázán

PŘÍSTUP ZAKÁZÁN -USER-


Přístup na tuto stránku:

-URL-

... byl zakázán z důvodu:

-REASONGIVEN-

Zobrazení tohoto chybového hlášení způsobila stránka, na kterou jste se pokusil(a)
přistoupit a obsahuje nebo je označena jako obsahující nepatřičný materiál.

Kategorizace obsahu: -CATEGORIES-

Máte-li nějaké dotazy, obraťte se prosím na vašeho správce sítě či osobu pověřenou správou sítě.

Powered by -SERVERNAME- DansGuardian

e2guardian-5.5.8r/data/languages/danish/000077500000000000000000000000001477372360500200775ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/danish/fancydmtemplate.html000077500000000000000000000142251477372360500241510ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/danish/messages000066400000000000000000000152361477372360500216400ustar00rootroot00000000000000# e2guardian messages file in Danish # # Translated by Peter Kilsgaard "0","Message number absent" # needs translation "1","Adgang ngtet" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Din IP address har ikke lov til at bruge internet: " "101","Din IP address har ikke lov til at bruge internet." "102","Dit brugernavn har ikke lov til at bruge internet: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","Den indtastede adresse er ikke valid." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Forbudt stning fundet: " "301","Forbudt stning fundet." "400","Forbudt stningskombination fundet: " "401","Forbudt stningskombination fundet." "402","Vgtede stningsgrnse p " "403","Vgtede stningsgrnse overskredet." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Forbudt side: " "501","Forbudt Adresse: " "502","Total blokering er aktiveret og den forspurgte side er ikke p positiv listen." "503","Forbudt ord i adressefelt: " "504","Forbudt ord i adressefelt fundet." "505","IP Blokering er aktiveret og den forspurgte side har IP ingen DNS navn." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","Undtagelse PC IP match." "601","Undtagelse brugernavn match." "602","Undtagelse side match." "603","Undtagelse adresse match." "604","Undtagelses stning fundet: " "605","Kombinations undtagelse stning fundet: " "606","Bypass URL undtagelse." "607","Bypass cookie undtagelse." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " # 606,607 by Daniel Barron - corrections welcome "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","Web upload er forbudt." "701","Web upload grnse overskredet." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Forbudt MIME Type: " "900","Forbudt fil format: " "1000","PICS niveau overskredet p den pgldende side." "1100","Virus or bad content detected." "1101","Advert blocked" "1200","Please wait - downloading to be scanned..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","File no longer available" e2guardian-5.5.8r/data/languages/danish/neterr_template.html000066400000000000000000001407071477372360500241700ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/danish/template.html000077500000000000000000000042641477372360500226110ustar00rootroot00000000000000 e2guardian - Access Denied

ADGANG NÆGTET -USER-


Adgang til denne side:

-URL-

... er blevet nægtet på grund af:

-REASONGIVEN-

Denne meddelse kommer fordi, siden du forsøger at få adgang til indeholder, eller er blevet katagoriseret som, uegnet materiale .

Hvis du har spørgsmål til ovennævnte,
bedes du kontakte netværksadministratoren

Powered by e2guardian


e2guardian-5.5.8r/data/languages/dutch/000077500000000000000000000000001477372360500177405ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/dutch/fancydmtemplate.html000077500000000000000000000142251477372360500240120ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/dutch/messages000066400000000000000000000153451477372360500215020ustar00rootroot00000000000000# e2guardian berichten in het Nederlands # Door S.A. de Heer # Updates door Eric Hameleers "0","Message number absent" # needs translation "1","Toegang geweigerd" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Uw IP adres mag niet websurfen: " "101","Uw IP adres mag niet websurfen." "102","Uw gebruikersnaam mag niet websurfen: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","De gevraagde URL is syntactisch incorrect." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Geblokkeerd woord gevonden: " "301","Geblokkeerd woord gevonden." "400","Geblokkerde combinatie van woorden gevonden: " "401","Geblokkerde combinatie van woorden gevonden." "402","Gewogen woorden limiet van: " "403","Gewogen woorden limiet overschreden." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Geblokkeerde website: " "501","Geblokkeerde URL: " "502","Web blokkade is actief en deze website is niet in de toegangslijst." "503","Geblokkeerde reguliere expressie in de URL: " "504","Geblokkeerde reguliere expressie in de URL gevonden." "505","IP adressen zijn geblokkeerd en dat adres is enkel een IP adres." "506","SSL is geblokkeerd en deze website komt niet voor op de toegangslijsten." "507","SSL IP adressen zijn geblokkeerd en dat adres is enkel een IP addres." "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","Uitzonderings IP adres gevonden." "601","Uitzonderings gebruiker gevonden." "602","Uitzonderings website gevonden." "603","Uitzonderings URL gevonden." "604","Uitzonderings woord gevonden: " "605","Uitzonderings combinatie van woorden gevonden: " "606","URL omzeiling uitzondering." "607","Cookie omzeiling uitzondering." "608","Scan omzeiling URL uitzondering." "609","Uitzondering reguliere expressie URL gevonden: " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","Uploaden via het web is geblokkerd." "701","Web upload limiet overschreden." "750","Bestands-download is geblokkeerd en dit MIME type komt niet voor op de goedkeuringslijst: " "751","Bestands-download is geblokkeerd en dit bestand komt niet voor op de goedkeuringslijsten: " "752","Site has a TLD that is not allowed by default" # needs translation "800","Geblokkeerd MIME Type: " "900","Geblokkeerd bestandstype: " "1000","'PICS labeling' niveau overschrijding op bovenvermelde site." "1100","Virus of onbetamelijke inhoud ontdekt." "1101","Advertentie geblokkeerd." "1200","Geduld a.u.b. - bezig met downloaden om te scannen..." "1201","Waarschuwing: bestand te groot om te scannen. Vermoedt u dat dit bestand groter is dan " "1202",", ververs dan deze pagina om direct te downloaden." "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Compleet. Scan wordt gestart..." "1220","Scan compleet.

Klik hier om te downloaden: " "1221","Download compleet; bestand niet gescand.

Klik hier om te downloaden: " "1222","Bestand te groot voor tussenopslag.

Klik hier om opnieuw te downloaden zonder scan: " "1230","Bestand niet langer beschikbaar" e2guardian-5.5.8r/data/languages/dutch/neterr_template.html000066400000000000000000001407071477372360500240310ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/dutch/template.html000077500000000000000000000051161477372360500224470ustar00rootroot00000000000000 e2guardian - toegang geweigerd

De toegang werd geweigerd!!
-USER- 
YOUR ORG NAME Toegang tot de pagina:

-URL-

... werd geweigerd om de volgende reden:

-REASONGIVEN-

Categorieën:

-CATEGORIES-



U ziet deze melding omdat de pagina die u probeerde te benaderen, materiaal lijkt te bevatten dat ongeschikt is om te bekijken, of gemarkeerd is als ongeschikt om te bekijken.

Als u hier vragen over heeft neem dan contact op met uw netwerkbeheerder.



Verzorgd door e2guardian
e2guardian-5.5.8r/data/languages/french/000077500000000000000000000000001477372360500200765ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/french/fancydmtemplate.html000077500000000000000000000147151477372360500241540ustar00rootroot00000000000000 Téléchargement de -FILENAME- (-FILESIZE- octets) e2guardian-5.5.8r/data/languages/french/messages000066400000000000000000000163441477372360500216400ustar00rootroot00000000000000# e2guardian messages file in French # Translated by: Bernard Wanadoo and Jacques Theys # Improvements by Jeanuel # Improvements by Mathieu Parent (2011) # # ! Quote "'" changed by backquote "`" to avoid bad interpretation ! "0","Message number absent" # needs translation "1","Accès interdit" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","Accès interdit" "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Votre addresse IP n`est pas autorisée à naviguer sur le site : " "101","Votre addresse IP n`est pas autorisée à naviguer." "102","Votre compte utilisateur n`est pas autorisé à afficher le site : " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server`s SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","L`URL demandée n'est pas bien formée." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Phrase interdite : " "301","Phrase interdite trouvée." "400","Combinaison de mots interdite : " "401","Combinaison de mots interdite trouvée." "402","Limite de pondération " "403","Limite de pondération dépassée." "450","Terme de recherche interdit : " "451","Terme de recherche interdit trouvé." "452","Combinaison de termes de recherche interdite : " "453","Combinaison de termes de recherche interdite trouvée." "454","Limite de pondération de termes de recherche " "455","Limite de pondération de termes de recherche dépassée." "456","Exception de combinaison de termes de recherche autorisée : " "457","Exception de terme de recherche autorisé : " "500","Site interdit : " "501","URL interdite : " "502","Le mode liste blanche (Blanket Block) est actif et ce site n`est pas dans la liste autorisée, blanche ou grise." "503","Expression régulière d`URL interdite : " "504","Expression régulière d`URL interdite trouvée." "505","Le mode liste blanche d`IPs (IP Blanket Block) est actif et cette addresse est une adresse IP uniquement." "506","Le mode liste blanche SSL (SSL Blanket Block) est actif et ce site n`est pas dans la liste autorisée, blanche ou grise." "507","Le mode liste blanche SSL d`IPs (SSL IP Blanket Block) est actif et cette addresse est une adresse IP uniquement." "508","Expression régulière d`entète HTTP interdite : ", "509","Expression régulière d`entète HTTP interdite trouvée." "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","Exception d`adresse IP client trouvée." "601","Exception d`utilisateur client trouvée." "602","Exception de site trouvée : " "603","Exception d`URL trouvée : " "604","Exception de phrase trouvée : " "605","Exception de combinaison de mots trouvée : " "606","Exception d`URL sans controle trouvée." "607","Exception de cookie sans controle trouvée." "608","Exception d`URL sans inspection des logiciels malveillants trouvée." "609","Exception d`expression régulière d'URL trouvée : " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local): " # needs translation "663","URL (local): " # needs translation "700","L`upload web est interdit." "701","Dépassement de la limite d`upload Web." "750","Le mode liste blanche de téléchargement (Blanket file download) est actif et ce type MIME n`est pas dans la liste blanche : " "751","Le mode liste blanche de téléchargement (Blanket file download) est actif et ce fichier n`est pas dans la liste blanche." "752","Site has a TLD that is not allowed by default" # needs translation "800","Type MIME interdit: " "900","Type de fichier interdit : " "1000","Niveau PICS depassé sur le site entier." "1100","Logiciel malveillant détecté." "1101","Publicité bloqué" "1200","Veuillez patienter - téléchargement du fichier en cours ..." "1201","Avertissement : fichier trop volumineux pour ètre inspecté. Si vous pensez que le fichier est plus volumineux que " "1202",", raffraîchissez la page pour le télécharger directement." "1203","WARNING: Could not perform content scan!" # needs translation "1210","Téléchargement terminé. Inspection des logiciels malveillants en cours ..." "1220","Inspection du fichier terminée.

Cliquez ici pour le télécharger : " "1221","Télécharge terminé. fichier non inspecté.

Cliquez ici pour le télécharger : " "1222","Fichier trop volumineux pour ètre mis en cache.

Cliquez ici pour le télécharger à nouveau, sans inspection des logiciels malveillants : " "1230","Le fichier n`est plus disponible" e2guardian-5.5.8r/data/languages/french/neterr_template.html000066400000000000000000001407071477372360500241670ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/french/template.html000077500000000000000000000051041477372360500226020ustar00rootroot00000000000000 e2guardian - Acc&eagrav;s interdit

Accès interdit !
-USER- 
YOUR ORG NAME L'accès à la page :

-URL-

... a été interdit pour les raisons suivantes :

-REASONGIVEN-

Catégories:

-CATEGORIES-



Vous voyez ce message parce que vous tentez d'accéder à une page qui contient, ou est réputée contenir des élements qui ont été déclarés inappropriés.

Pour toute question, contactez votre administrateur réseau.



Propulsé par e2guardian
e2guardian-5.5.8r/data/languages/german/000077500000000000000000000000001477372360500201025ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/german/fancydmtemplate.html000077500000000000000000000210741477372360500241540ustar00rootroot00000000000000 Herunterladen -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/german/messages000066400000000000000000000142131477372360500216350ustar00rootroot00000000000000# e2guardian 3 messages file in German # # Translated and adapted to Unicode by Peter Vollmar, Klaus Tachtler. "0","Message number absent" # needs translation "1","Zugriff verweigert" "10","IP-Limit erreicht. Limit bei " "11"," IP-Limit wurde gesetzt." "50"," von " "51","VERTRAUENSWÜRDIG" "52","VERWEIGERT" "53","INFIZIERT" "54","ÜBERPRÜFT" "55","INHALT MODIFIZIERT" "56","URL MODIFIZIERT" "57","HEADER MODIFIZIERT" "58","HEADER HINZUGEFÜGT" "59","NETZWERKFEHLER" "60","SEMI-TRUSTED" # needs translation "70","SSL SEITE" "71","IP-Limit" "72","Inhalt überprüfen" "100","Ihre Arbeitsstation hat keine Erlaubnis zum Surfen auf: " "101","Ihre Arbeitsstation hat keine Erlaubnis zum Surfen" "102","Ihr Benutzername hat keine Erlaubnis zum Surfen auf: " "103","Client-IP geblockt" "104","Lokation geblockt" "105","Benutzer geblockt" "110","Proxy authentication error" # needs translation "121","Von Ihrer Lokation aus ist nur ein begrenzter Zugriff möglich" "122","You not allowed access at this time of day" # needs translation "150","Das vom Server ausgelieferte Zertifikat ist ungültig" "151","Es konnte keine SSL-Verbindung aufgebaut werden" "152","Es wurde kein Zertifikat gefunden" "153","Es konnte kein privater Schlüssel geladen werden" "154","Es konnte keine Verbindung zum Client hergestellt werden" "155","Es wurde kein SSL-Zertifikat vom Server ausgeliefert" "156","Das vom Server ausgelieferte SSL-Zertifikat, passt nicht zum Namen der Domain" "157","Es konnte kein Verbindung zum lokalen Proxy hergestellt werden" "158","Verbindungsaufbau fehlgeschlagen" "159","Es konnte kein Verbindung zum Proxy-Server aufgebaut werden" "160","Die SSL-Verbindung zum Server konnte nicht hergestellt werden" "200","Die angeforderte URL ist ungültig" "201","Antwort vom Upstream-Proxy nicht möglich (Zeitüberschreitung)" "202","Antwort vom Upstream-Proxy nicht möglich (Fehler)" "203","Die angeforderte Seite antwortet nicht" "204"," - Bitte versuchen Sie es später noch einmal" "205","Upstream-Proxy antwortet nicht (Netzwerkfehler)" "206"," - Bitte versuchen Sie es später noch einmal" "207","Die angeforderte Seite existiert nicht" "208","Die angeforderte Seite hat keine IPv4-Adresse" "209","Vorübergehender Ausfall des DNS-Dienstes - Bitte versuchen Sie es erneut" "210","DNS-Dienstfehler - Bitte versuchen Sie es später noch einmal" "212","Loop request blocked" # needs translation "300","Verbotener Ausdruck gefunden: " "301","Verbotener Ausdruck gefunden" "400","Verbotene Kombination von Ausdrücken gefunden: " "401","Verbotene Kombination von Ausdrücken gefunden" "402","Gewichtete Ausdrucksbeschränkung von " "403","Gewichtete Ausdrucksbeschränkung überschritten" "450","Verbotener Suchausdruck gefunden: " "451","Verbotener Suchausdruck gefunden." "452","Verbotene Suchausdrücke gefunden: " "453","Verbotene Suchausdrücke gefunden." "454","Gewichtetes Suchausdruck-Limit erreicht: " "455","Gewichtetes Suchausdruck-Limit wurde erreicht." "456","Kombination von erlaubten Ausdrücken wurde gefunden: " "457","Erlaubter Ausdruck gefunden: " "500","Verbotene Seite: " "501","Verbotene URL: " "502","Totalsperre für Nur-IP-Adressen aktiv, diese Seite ist nicht auf der Erlaubt-Liste" "503","Aufgrund von regulären Ausdrücken verbotene URL: " "504","Aufgrund von regulären Ausdrücken verbotene URL gefunden" "505","Totalsperre für IP-Adressen aktiv, diese Adresse ist nur eine IP." "506","HTTPS-Verbindungen sind nur zu vertrauenswürdige Seiten erlaubt." "507","HTTPS-Verbindungen zu IP-Adressen sind nicht erlaubt." "508","Verbindungen mit diesem Browser (oder dieser Anwendungen) sind nicht gestattet: " "509","Verbindungen mit diesem Browser (oder dieser Anwendungen) sind nicht gestattet." "510","Geblockte Seite (IP) " "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Geblockte HTTPS-Seite: " "521","Geblockte Ausdrücke: " "522","Geblockter Benutzer-Agent: " "560","Geblockte Seite (lokal): " "561","Geblockte URL (lokal): " "580","Geblockte HTTPS-Seite (lokal): " "581","Geblockte Ausdrücke (lokal): " "600","Übereinstimmung mit Client-IP in Ausnahmeliste" "601","Übereinstimmung mit Client-Benutzer in Ausnahmeliste" "602","Übereinstimmung mit Seite in Ausnahmeliste" "603","Übereinstimmung mit URL in Ausnahmeliste" "604","Ausnahme-Ausdruck gefunden: " "605","Kombination von Ausnahme Ausdrücken gefunden: " "606","Umgehungs-URL gefunden" "607","Umgehungs-Cookie gefunden" "608","Überprüfe Umgehungs-URL Ausnahme." "609","Ausnahme von regular expression URL gefunden: " "610","Benutzer-Agent Suchmuster-Abgleich: " "620","Ursprungsseite gefunden: " "630","URL gefunden in " "631"," erlaubte Lokations-Liste" "632","Lokation überschreibt die Liste der zulässigen Seiten" "662","Seite (lokal)." "663","URL (lokal)." "700","Web-Upload verboten" "701","Web-Upload-Schwellwert erreicht" "750","Globale Datei-Download Überprüfung ist aktiv und dieser Datei-Typ (MIME type) ist nicht auf der Erlaubt-Liste: " "751","Globale Datei-Download Überprüfung ist aktiv und diese Datei ist nicht auf der Erlaubt-Liste" "752","Site has a TLD that is not allowed by default" # needs translation "800","Verbotener Datei-Typ (MIME Type): " "900","Verbotene Datei-Erweiterung: " "1000","PICS-Kennzeichnungsschwellwert überschritten" "1100","Ein Virus oder unerlaubter Inhalt wurde gefunden." "1101","Werbung blockiert" "1200","Bitte warten - die heruntergeladene Datei wird überprüft..." "1201","Warnung: Datei ist zu gross um überprüft zu werden. Wenn Sie glauben das die Datei größer als " "1202"," ist, rufen Sie die Seite erneut auf, um ein direktes herunterladen durchzuführen." "1203","WARNUNG: Es konnte keine Inhaltsüberprüfung durchgeführt werden!" "1210","Herunterladen abgeschlossen. Starte Überprüfung..." "1220","Überprüfung abgeschlossen.

Zum herunterladen hier klicken: " "1221","Herunterladen abgeschlossen. Datei konnte nicht überprüft werden.

Zum herunterladen hier klicken: " "1222","Datei zu gross zum zwischenspeichern.

Zum erneuten herunterladen, OHNE Überprüfung, hier klicken: " "1230","Die Datei wurde bereits abgerufen und ist daher nicht mehr gespeichert!" e2guardian-5.5.8r/data/languages/german/neterr_template.html000066400000000000000000001407151477372360500241720ustar00rootroot00000000000000 E2Guardian - Laden der Webseite nicht möglich
Page unavailable

Ups! Es gab ein Problem beim Aufruf dieser Seite:



-URL-

Grund

-REASONGIVEN-


Ihre Details:

Benutzer: -USER-   Gruppe: -FILTERGROUP-   IP: -IP-


Zurück zur vorherigen Seite
e2guardian-5.5.8r/data/languages/german/template.html000077500000000000000000000042461477372360500226140ustar00rootroot00000000000000 e2guardian - Zugriff verweigert

Zugriff verweigert!
-USER- 
IHRE FIRMA Der Zugriff auf die Seite

-URL-

wurde mit folgender Begründung verweigert:

-REASONLOGGED-



Sie sehen diese Fehlermeldung, weil die von Ihnen gewünschte Seite unangebrachte Inhalte aufweist oder als solche gekennzeichnet ist.

Bei Fragen oder Beschwerden wenden Sie sich bitte an Ihren Netzwerkadministrator.



Powered by -SERVERNAME- e2guardian
e2guardian-5.5.8r/data/languages/hebrew/000077500000000000000000000000001477372360500201055ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/hebrew/fancydmtemplate.html000077500000000000000000000142251477372360500241570ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/hebrew/messages000066400000000000000000000132521477372360500216420ustar00rootroot00000000000000# e2guardian messages file in Hebrew # by Nitzo Tomer "0","Message number absent" # needs translation "1"," " "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100"," -IP : " "101"," -IP ." "102"," : " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200"," ." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300"," : " "301"," ." "400"," : " "401"," ." "402"," " "403"," ." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500"," : " "501"," : " "502"," " ." "503"," : " "504"," ." "505"," " ." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600"," IP ." "601"," ." "602"," ." "603"," ." "604"," : " "605"," : " "606"," ." "607"," ." "608"," URL ." "609"," URL : " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700"," ." "701"," ." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800"," MIME : " "900"," : " "1000"," PICS " ." "1100"," ." "1101"," " "1200"," - ..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210"," . ..." "1220"," .

: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230"," e2guardian-5.5.8r/data/languages/hebrew/neterr_template.html000066400000000000000000001407071477372360500241760ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/hebrew/template.html000077500000000000000000000062061477372360500226150ustar00rootroot00000000000000 e2guardian -

!
-USER-
YOUR ORG NAME :

-URL-

... :

-REASONGIVEN-



, , .

.



Powered by -SERVERNAME- e2guardian
e2guardian-5.5.8r/data/languages/hungarian/000077500000000000000000000000001477372360500206055ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/hungarian/fancydmtemplate.html000077500000000000000000000142251477372360500246570ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/hungarian/messages000066400000000000000000000150741477372360500223460ustar00rootroot00000000000000# e2guardian 3 messages file in Hungarian "0","Message number absent" # needs translation "1","A hozzfrs tiltott" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Az n IP cme nem engedlyezett ehhez a bngszhz: " "101","Az n IP cme nem engedlyezett ehhez a bngszhz." "102","A felhasznlneve nem engedlyezett ehhez e abngszhz: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","A krt URL formtuma nem tnik biztonsgosnak." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Tiltott kifejezs: " "301","Tiltott kifejezs." "400","Tiltott sszettel kifejezs: " "401","Tiltott sszettel kifejezs." "402","A slyozott kifejezs hatra " "403","A slyozott kifejezs elrte a hatrt." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Tiltott oldal: " "501","Tiltott URL: " "502","A domain-alap szrs aktv s ez az oldal sem a 'fehr', sem a 'szrke' listn nincs fent." "503","Tiltott regulris kifejezs URL: " "504","Tiltott regulris kifejezs URL." "505","A cmtartomny alap szrs aktv s ez csak egy egyszer IP-cm." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","Kifogsolhat kliens IP egyezs." "601","Kifogsolhat kliens felhasznl egyezs." "602","Kifogsolhat oldalegyezs." "603","Kifogsolhat URL-egyezs." "604","Kifogsolhat kifejezs: " "605","Kombincis kifogsolhat kifejezs: " "606","Tovbbugrat URL kifogs." "607","Tovbbugrat sti kifogs." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","A Webre trtn feltlts tiltva." "701","A Webre trtn feltlts korltjt elrte." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Tiltott MIME Tpus: " "900","Tiltott kiterjeszts: " "1000","A PICS szervezet jellsi szintjt elrte a fels oldal." "1100","Vrussal fetztt llomny." "1101","Advert blocked" "1200","Please wait - downloading to be scanned..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","File no longer available" e2guardian-5.5.8r/data/languages/hungarian/neterr_template.html000066400000000000000000001407071477372360500246760ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/hungarian/template.html000077500000000000000000000042471477372360500233200ustar00rootroot00000000000000 e2guardian - A hozzfrs tiltva

A hozzfrs tiltva!
-USER- 
YOUR ORG NAME A hozzfrs ehhez az oldalhoz:

-URL-

... nem lehetsges a kvetkez ok miatt:

-REASONGIVEN-



n azrt ltja ezt a hibazenetet, mert gy tnik, amit n megksrelt elrni tartalmaz vagy besorolsa alapjn tartalmazhat helytelennek vlt anyagokat.

Amennyiben nnek ezzel kapcsolatban agglyai merltek fel, vegye fel a kapcsolatot az n Informcis- s kommunkcitechnolgiai koordintorval vagy a hlzati menedzservel.



Powered by -SERVERNAME- e2guardian
e2guardian-5.5.8r/data/languages/indonesian/000077500000000000000000000000001477372360500207605ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/indonesian/fancydmtemplate.html000077500000000000000000000142251477372360500250320ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/indonesian/messages000066400000000000000000000154131477372360500225160ustar00rootroot00000000000000# e2guardian messages file in Indonesian # # Indonesian translation by: Kumoro Wisnu Wibowo # "0","Message number absent" # needs translation "1","Akses Ditolak" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Alamat IP Anda tidak diperbolehkan untuk browsing: " "101","Alamat IP Anda tidak diperbolehkan untuk browsing." "102","Username Anda tidak diperbolehkan untuk browsing: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","Format alamat URL yang diminta tidak benar." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Frase yang ditolak ditemukan: " "301","Frase yang ditolak ditemukan." "400","Kombinasi frase yang ditolak ditemukan: " "401","Kombinasi frase yang ditolak ditemukan." "402","Bobot batas frase dari " "403","Bobot batas frase terlewati." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Situs yang ditolak: " "501","Alamat URL yang ditolak: " "502","Blanket Block aktif dan situs tersebut tidak dalam white list." "503","Alamat Regular Expression URL yang ditolak: " "504","Alamat Regular Expression URL ditemukan." "505","Blanket IP Block aktif dan alamat tersebut adalah hanya alamat IP saja." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","IP klien pengecualian cocok." "601","User klien pengecualian cocok." "602","Situs pengecualian cocok." "603","Alamat URL pengecualian cocok." "604","Frase pengecualian ditemukan: " "605","Kombinasi frase pengecualian ditemukan: " "606","Bypass URL cocok." "607","Bypass cookie cocok." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " # 606,607 by Daniel Barron - corrections welcome "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","Web upload ditolak." "701","Batas untuk web upload terlewati." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Tipe MIME yang ditolak: " "900","Ekstensi yang ditolak: " "1000","Level Label PICS terlewati pada situs di atas." "1100","Virus or bad content detected." "1101","Advert blocked" "1200","Please wait - downloading to be scanned..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","File no longer available" e2guardian-5.5.8r/data/languages/indonesian/neterr_template.html000066400000000000000000001407071477372360500250510ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/indonesian/template.html000077500000000000000000000030131477372360500234610ustar00rootroot00000000000000e2guardian - Akses Ditolak

ACCESS DITOLAK -USER-


Akses ke halaman:

-URL-

... telah ditolak untuk alasan sebagai berikut:

-REASONGIVEN-

Anda melihat pesan kesalahan ini karena halaman yang ingin Anda akses
mengandung isi yang telah dianggap sebagai tidak pantas.

Apabila Anda mempunyai pertanyaan-pertanyaan, silakan kontak Manager IT atau Manager Network Anda.

Powered by -SERVERNAME- e2guardian

e2guardian-5.5.8r/data/languages/italian/000077500000000000000000000000001477372360500202525ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/italian/fancydmtemplate.html000077500000000000000000000142251477372360500243240ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/italian/messages000066400000000000000000000135201477372360500220050ustar00rootroot00000000000000# e2guardian messages file in IT Italian # Translated by andrea at dicle # Translated by Albano Battistella 2023 "0","Numero messaggio assente" "1","Accesso Negato" "10","Limite IP superato. C'è un " "11"," Limite IP impostato." "50"," in " "51","FIDATO" "52","NEGATO" "53","INFETTO" "54","SCANSIONATO" "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","ERRORE DI RETE" "60","SEMI-FIDATO" "70","SITO SSL" "71","Limite IP" "72","Scansione del contenuto" "100","Al tuo indirizzo IP non e' permesso di navigare il web: " "101","Al tuo indirizzo IP non e' permesso di navigare il web." "102","Al tuo username non permesso di navigare il web: " "103","IP client bannato" "104","Posizione bannato" "105","Utente bannato" "110","Errore di autenticazione proxy" "121","È consentito solo un accesso limitato dalla tua posizione" "122","Non è consentito l'accesso a quest'ora del giorno" "150","Il certificato fornito dal server non era valido" "151","Impossibile aprire la connessione SSL" "152","Impossibile ottenere il certificato SSL" "153","Impossibile caricare la chiave privata SSL" "154","Impossibile negoziare la connessione SSL al client" "155","Nessun certificato SSL fornito dal server" "156","Il certificato SSL del server non corrisponde al nome di dominio" "157","Impossibile creare il tunnel tramite proxy locale" "158","L'apertura del tunnel è fallita" "159","Impossibile connettersi al server proxy" "160","Impossibile negoziare la connessione SSL al server" "200","L'URL richiesta e' malformata." "201","Impossibile ottenere risposta dal proxy upstream (timeout)" "202","Impossibile ottenere risposta dal proxy upstream (errore)" "203","Il sito richiesto non risponde" "204"," - Si prega di riprovare più tardi" "205","Il proxy upstream non risponde (errore di rete)" "206"," - Si prega di riprovare più tardi" "207","Il sito richiesto non esiste" "208","Il sito richiesto non ha un indirizzo IPv4" "209","Errore temporaneo del servizio DNS - riprovare" "210","Errore del servizio DNS. Riprova più tardi" "212","Richiesta loop bloccata" "300","Trovata una frase vietata: " "301","Trovata una frase vietata." "400","Trovata una combinazione di frasi vietata: " "401","Trovata una combinazione di frasi vietata." "402","Limite di peso della frase di " "403","E' stato superato il limite per le frasi pesate." "450","Tovato termine di ricerca vietato: " "451","Termine di ricerca vietato trovato." "452","Trovato termine di ricerca combinato vietato: " "453","Trovato termine di ricerca combinato vietato." "454","Limite del termine di ricerca ponderato di " "455","Limite del termine di ricerca ponderato superato." "456","Termine di ricerca combinazione eccezione trovato: " "457","Termine di ricerca di eccezione trovato: " "500","Sito vietato: " "501","URL vietato: " "502","Il blocco 'Blanket' e' attivo e il sito non e' nella white list." "503","URL con espressione regolare vietata: " "504","URL con espressione regolare vietata trovato." "505","Il blocco 'Blanket' e' attivo e l'indirizzo e' formato dal solo indirizzo IP." "506","L'accesso HTTPS è consentito solo ai siti attendibili." "507","L'accesso HTTPS tramite indirizzo IP non è consentito." "508","Accesso non consentito utilizzando questo browser (o app): " "509","Accesso non consentito utilizzando questo browser (o app)." "510","Sito IP bloccato " "511","La connessione https trasparente non è TLS: " "512","La connessione https trasparente non ha SNI: " "520","Sito HTTPS bloccato: " "521","Parole di ricerca vietate: " "522","User-Agent bloccato: " "560","Sito bloccato (locale): " "561","URL bloccato (locale):" "580","Sito HTTPS bloccato (locale): " "581","Parole di ricerca vietate (locali): " "600","Corrispondenza eccezione IP client." "601","Exception client user match." "602","Corrispondenza eccezione del sito." "603","Corrispondenza eccezione dell'URL." "604","Frase di eccezione trovata: " "605","Frase di eccezione della combinazione trovata: " "606","Ignora eccezione URL." "607","Ignora l'eccezione del cookie." "608","Eccezione dell'URL di bypass della scansione." "609","Corrispondenza eccezione dell'URL dell'espressione regolare: " "610","Corrispondenza del modello User-Agent: " "620","Corrispondenza di riferimento: " "630","Corrispondenza URL in " "631","Elenco di posizioni consentite" "632","Location overide allow list matched" # needs translation "662","Sito (locale)." "663","URL (locale)." "700","Il Web-upload e' vietato." "701","E' stato superato il limite per il Web upload." "750","Il download del file globale è attivo e questo tipo MIME non è nella white list: " "751","Il download del file blanket è attivo e questo file non trova corrispondenza nelle white list." "752","Il sito ha un TLD che non è consentito per impostazione predefinita" "800","MIME Type vietato: " "900","Estensione vietata: " "1000","Si e' oltrepassato il limite del livello PICS sul sito." "1100","Rilevato virus o contenuti non validi." "1101","Annuncio bloccato" "1200","Si prega di attendere - download da scansionare..." "1201","Avviso: file troppo grande per la scansione. Se sospetti che questo file sia più grande di " "1202",",aggiornare questa pagina per scaricare direttamente." "1203","ATTENZIONE: Impossibile eseguire la scansione del contenuto!" "1210","Download completato. Avvio della scansione..." "1220","Scansione completa.

Clicca qui per scaricare: " "1221","Download completato; file non scansionato.

Clicca qui per scaricare: " "1222","File troppo grande per la cache.

Fare clic qui per eseguire nuovamente il download, ignorando la scansione: " "1230","File non più disponibile" e2guardian-5.5.8r/data/languages/italian/neterr_template.html000066400000000000000000001407071477372360500243430ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/italian/template.html000077500000000000000000000030211477372360500227520ustar00rootroot00000000000000e2guardian - Accesso Negato

L'ACCESSO E' STATO NEGATO -USER-


L'accesso alla pagina:

-URL-

... e' stato negato per il seguente motivo:

-REASONGIVEN-

Stai vedendo questo errore perchè la pagina che hai cercato
di accedere contiene, o è marcata come contenente, materiale che
e' stato ritenuto non appropriato.

Se hai ulteriori domande, contatta il tuo coordinatore ICT o Network Manager.

Powered by -SERVERNAME- e2guardian

e2guardian-5.5.8r/data/languages/japanese/000077500000000000000000000000001477372360500204175ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/japanese/fancydmtemplate.html000077500000000000000000000142251477372360500244710ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/japanese/messages000066400000000000000000000173541477372360500221630ustar00rootroot00000000000000# e2guardian messages file in 日本語 "0","Message number absent" # needs translation "1","アクセス拒否" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","あなたのIPアドレスは閲覧の許可がありません: " "101","あなたのIPアドレスは閲覧の許可がありません。" "102","あなたのユーザ名は閲覧の許可がありません: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","要求されたURLは不適切です。" "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","禁止されたフレーズが見つかりました: " "301","禁止されたフレーズが見つかりました。" "400","拒否された結合フレーズが見つかりました: " "401","拒否された結合フレーズが見つかりました。" "402","重み付けされたフレーズの制限を越えました(規定値:測定値) " "403","重み付けされたフレーズの制限を越えました。" "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","拒否されたサイト: " "501","拒否されたURL: " "502","統括的な遮断は有効です。そしてそれらのサイトはホワイトリストかグレーリストにありません。" "503","拒否された正規表現のURL: " "504","拒否された正規表現のURLが見つかりました。" "505","統括的なIPの遮断は有効です。そしてそれらのアドレスはIPのみのアドレスです。" "506","統括的なSSLの遮断は有効です。そしてそれらのサイトはホワイトリストかグレーリストにありません。" "507","統括的なSSL IPの遮断は有効です。そしてそれらのアドレスはIPのみのアドレスです。" "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","例外クライアントIPマッチ。" "601","例外クライアントユーザマッチ。" "602","例外サイトマッチ。" "603","例外URLマッチ。" "604","例外フレーズが見つかりました: " "605","結合例外フレーズがみつかりました: " "606","例外迂回URL" "607","例外迂回Cookie。" "608","例外スキャン迂回URL" "609","例外正規表現URLマッチ: " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","Webのアップロードは拒否されました。" "701","Webのアップロードは制限を越えました。" "750","統括的なファイルのダウンロードは有効です。そしてこのMIMEタイプはホワイトリストにありません: " "751","統括的なファイルのダウンロードは有効です。そしてこのファイルはホワイトリストにマッチしませんでした。" "752","Site has a TLD that is not allowed by default" # needs translation "800","拒否されたMIMEタイプ: " "900","拒否された拡張: " "1000","PICSラベリングレベルは上のサイトで超えました。" "1100","ウィルスか不正なコンテンツを検出しました。" "1101","広告は拒否されました" "1200","お待ちください。 - ダウンロードファイルのスキャン中です・・・" "1201","警告: ファイルはスキャンするのに大きすぎます。 このふぁいるが" "1202","より大きいと疑うならば直接ダウンロードするためにこのページをリフレッシュしてください。" "1203","WARNING: Could not perform content scan!" # needs translation "1210","ダウンロード完了。スキャンを開始します..." "1220","スキャンが完了しました。

ダウンロードをするにはここをクリックしてください: " "1221","ダウンロードは完了しました。ファイルはスキャンされませんでした。

ダウンロードをするにはここをクリックしてください: " "1222","ファイルはキャッシュするのに大きすぎます。

スキャンを迂回させて、再ダウンロードするためにここをクリックしてください: " "1230","ファイルは長いこと利用可能ではありません" e2guardian-5.5.8r/data/languages/japanese/neterr_template.html000066400000000000000000001407071477372360500245100ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/japanese/template.html000077500000000000000000000030671477372360500231310ustar00rootroot00000000000000 Access Denied

アクセスは拒否されました。
-USER- 
www.shonbori.com アクセス先URL:

-URL-

は以下の理由により拒否されました。:

-REASONGIVEN-

カテゴリ:

-CATEGORIES-



不適切と思われるコンテンツがページ内に含まれている・含まれていると思われる場合に このエラーページが表示されます

何か質問がありましたら、以下に連絡してください。
webmaster@shonbori.com



Powered by -SERVERNAME- e2guardian
e2guardian-5.5.8r/data/languages/lithuanian/000077500000000000000000000000001477372360500207655ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/lithuanian/fancydmtemplate.html000077500000000000000000000142251477372360500250370ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/lithuanian/messages000066400000000000000000000154641477372360500225310ustar00rootroot00000000000000# e2guardian 3 messages file # Lithuanian version by Nerijus Baliūnas and Mantas Kriaučiūnas # charset=utf-8 "0","Message number absent" # needs translation "1","Tinklalapis uždraustas" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Iš jūsų IP adreso negalima pasiekti tinklalapio: " "101","Draudžiama naršyti iš jūsų IP adreso." "102","Jūsų naudotojui negalima pasiekti tinklalapio: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","Neteisingas URL." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "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." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Uždraustas adresas (site): " "501","Uždraustas URL: " "502","Leidžiamas priėjimas tik prie tinklalapių, esančių 'baltame' ar 'pilkame' sąraše, bet šio tinklalapio juose 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." "506","Aktyvus Blanket SSL Block ir šis tinklalapis nėra 'baltame' ar 'pilkame' sąraše." "507","Aktyvus Blanket SSL IP Block ir šis adresas yra tik IP adresas." "508","Uždrausta reguliarioji išraiška (regexp) HTTP antraštėje: ", "509","Rasta draudžiama reguliarioji išraiška (regexp) HTTP antraštėje." "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "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." "608","URL skanavimo apėjimo išimtis." "609","Regexp URL yra išimčių sąraše: " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","Nusiuntimas (upload) yra uždraustas." "701","Viršytas nusiuntimo (upload) limitas." "750","Aktyvus Blanket failų atsiuntimas ir šis MIME tipas nėra 'baltame' sąraše: " "751","Aktyvus Blanket failų atsiuntimas ir šis failas netinka pagal 'baltus' sąrašus." "752","Site has a TLD that is not allowed by default" # needs translation "800","Uždraustas MIME tipas: " "900","Uždraustas praplėtimas: " "1000","Viršytas šio tinklalapio PICS vertinimo kriterijus." "1100","Aptiktas virusas arba negeras turinys." "1101","Reklama užblokuota" "1200","Palaukite - siunčiame tikrinimui nuo virusų..." "1201","Dėmesio: failo negalime patikrinti, nes jis per didelis. Jei manote, kad šis failas yra didesnis nei " "1202",", tuomet nueikite į šį puslapį iš naujo (refresh) ir atsisiųskite nepatikrintą failą." "1203","WARNING: Could not perform content scan!" # needs translation "1210","Atsiųsta. Tikriname nuo virusų..." "1220","Patikrinta.

Atsisiųskite: " "1221","Siuntimas baigtas; failas nebuvo patikrintas.

Spustelėkite čia jei norite atsisiųsti: " "1222","Failas per didelis.

Jei norite atsisiųsti nepatikrintą failą - spustelėkite čia: " "1230","Failas neprieinamas" e2guardian-5.5.8r/data/languages/lithuanian/neterr_template.html000066400000000000000000001407071477372360500250560ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/lithuanian/template.html000077500000000000000000000040111477372360500234650ustar00rootroot00000000000000 e2guardian - Tinklalapis uždraustas

TINKLALAPIS UŽDRAUSTAS -USER-


Interneto turinio filtravimo sistema neleidžia Jums atidaryti šio tinklalapio:

-URL-

dėl šios priežasties:

-REASONGIVEN-

tinklalapis pateko į šias kategorijas:

-CATEGORIES-

Tinklalapis, kurį Jūs bandėte pasiekti, pripažintas nepriimtinu.

Jei turite klausimų, kreipkitės į kompiuterio ar tinklo administratorių.

Powered by -SERVERNAME- e2guardian

e2guardian-5.5.8r/data/languages/malay/000077500000000000000000000000001477372360500177345ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/malay/fancydmtemplate.html000077500000000000000000000142251477372360500240060ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/malay/messages000066400000000000000000000165601477372360500214760ustar00rootroot00000000000000# e2guardian 3 messages file in Malay language # Edited by Sazarul Izam "0","Message number absent" # needs translation "1","Akses Tidak Dibenarkan" # Access Denied "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","IP anda tidak dibenarkan untuk melayari web: " "101","IP anda tidak dibenarkan untuk melayari web." "102","Nama pengguna anda tidak dibenarkan untuk melayari web: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","URL yang diminta adalah malformed." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Frasa larangan dikesan: " # Banned Phrase found: "301","Frasa larangan dikesan." # Banned phrase found. "400","Kombinasi frasa larangan dikesan: " # Banned combination phrase found: "401","Kombinasi frasa larangan dikesan." # Banned combination phrase found. "402","Had beban frasa oleh " # Weighted phrase limit of "403","Melebihi had beban frasa." # Weighted phrase limit exceeded. "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Laman larangan: " # Banned site: "501","URL larangan: " # Banned URL: "502","Blok Blanket adalah aktif dan laman tersebut tidak tersenarai dalam white atau grey list." # Blanket Block is active and that site is not on the white or grey list. "503","URL Ekspresi Biasa larangan: " # Banned Regular Expression URL: "504","URL Ekspresi Biasa larangan dikesan." # Banned Regular Expression URL found. "505","Blok Blanket IP adalah aktif dan alamat tersebut hanyalah alamat IP." #Blanket IP Block is active and that address is an IP only address. "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","IP pengecualian klien sepadan." # Exception client IP match. "601","Pengguna pengecualian klien sepadan." # Exception client user match. "602","Laman pengecualian sepadan." # Exception site match. "603","URL pengecualian sepadan." # Exception URL match. "604","Frasa pengecualian sepadan: " # Exception phrase found: "605","Kombinasi frasa pengecualian sepadan: " # Combination exception phrase found: "606","URL pintasan sepadan." # Bypass URL exception. "607","Cookie pintasan sepadan." # Bypass cookie exception. "608","Scan bypass URL." # needs translation "609","URL pattern match: " # needs translation "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","Muatnaik laman dilarang." # Web upload is banned. "701","Muatnaik laman melebihi had." # Web upload limit exceeded. "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Jenis MIME larangan: " # Banned MIME Type: "900","Extension larangan: " # Banned extension: "1000","Penglabelan PICS melebihi peringkat pada laman di atas." # PICS labeling level exceeded on the above site. "1100","Virus or bad content detected." "1101","Advert blocked" "1200","Please wait - downloading file for scanning..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " "1202",", then refresh this page to download directly." "1203","WARNING: Could not perform content scan!" "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " "1222","File too large to cache.

Click here to re-download, bypassing scan: " "1230","File no longer available" e2guardian-5.5.8r/data/languages/malay/neterr_template.html000066400000000000000000001407071477372360500240250ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/malay/template.html000077500000000000000000000041721477372360500224440ustar00rootroot00000000000000e2guardian - Akses Tidak Dibenarkan

D a n s G u a r d i a n   h a l a m a n   t i d a k   b a i k   b e r h e n t i   d i   s i n i
H A L A N G A N
Akses ke halaman berkenaan tidak dibenarkan

URL: -URL-
-REASONGIVEN-

Sila hubungi Pentadbir Rangkaian jika terdapat kesilapan berhubung perkara ini

Penapis Kandungan Web oleh e2guardian
e2guardian-5.5.8r/data/languages/messages_master000066400000000000000000000115531477372360500217430ustar00rootroot00000000000000# e2guardian master messages file (in UK English) # # Insert new message lines in the correct order in this master file # Then run ./update_messages to merge the new messages into all language # files. '# needs translation' will be appended to new lines. # Messages 0-4999 are reserved for standard use. # Please use numbers above 5000 for localy defined messages "0","Message number absent" "1","Access Denied" "10","IP limit exceeded. There is a " "11"," IP limit set." "50"," in " "51","TRUSTED" "52","DENIED" "53","INFECTED" "54","SCANNED" "55","CONTENTMOD" "56","URLMOD" "57","HEADERMOD" "58","HEADERADD" "59","NETERROR" "60","SEMI-TRUSTED" "70","SSL SITE" "71","IP Limit" "72","Content scanning" "100","Your IP address is not allowed to web browse: " "101","Your IP address is not allowed to web browse." "102","Your username is not allowed to web browse: " "103","Banned Client IP" "104","Banned Location" "105","Banned User" "110","Proxy authentication error" "121","Only limited access allowed from your location" "122","You not allowed access at this time of day" "150","Certificate supplied by server was not valid" "151","Could not open ssl connection" "152","Failed to get ssl certificate" "153","Failed to load ssl private key" "154","Failed to negotiate ssl connection to client" "155","No SSL certificate supplied by server" "156","Servers SSL certificate does not match domain name" "157","Unable to create tunnel through local proxy" "158","Opening tunnel failed" "159","Could not connect to proxy server" "160","Failed to negotiate ssl connection to server" "200","The requested URL is malformed." "201","Unable to connect to upstream proxy (timeout)" "202","Unable to connect to upstream proxy (network error)" "203","The site requested is not responding" "204"," - Please try again later" "205","Upstream proxy is not responding (network error)" "206"," - Please try again later" "207","The site requested does not exist" "208","The site requested does not have an IPv4 address" "209","Temporary DNS service failure - please try again" "210","DNS service failure - please try again later" "212","Loop request blocked" "300","Banned phrase found: " "301","Banned phrase found." "400","Banned combination phrase found: " "401","Banned combination phrase found." "402","Content Check limit of " "403","Blocked by Content Checking." "450","Banned search term found: " "451","Banned search term found." "452","Banned combination search term found: " "453","Banned combination search term found." "454","Weighted search term limit of " "455","Weighted search term limit exceeded." "456","Exception combination search term found: " "457","Exception search term found: " "500","Blocked site: " "501","Blocked URL: " "502","Walled Garden is on and the site is not available to you." "503","Banned pattern matched URL: " "504","Blocked URL." "505","Access to sites by IP address is not allowed." "506","HTTPS access is only allowed to trusted sites." "507","HTTPS access by IP address is not allowed." "508","Access not allowed using this browser (or app): " "509","Access not allowed using this browser (or app)." "510","Blocked IP site " "511","Tranparent https connection is not TLS: " "512","Tranparent https connection does not have SNI: " "520","Blocked HTTPS site: " "521","Banned Search Words: " "522","Blocked User-Agent: " "560","Blocked site (local): " "561","Blocked URL (local): " "580","Blocked HTTPS site (local): " "581","Banned Search Words (local): " "600","Client IP match." "601","Client user match." "602","Site match: " "603","URL match: " "604","Phrase found: " "605","Combination phrase found: " "606","Bypass URL." "607","Bypass cookie." "608","Scan bypass URL." "609","URL pattern match: " "610","User-Agent pattern match: " "620","Referer match: " "630","URL match in " "631"," location allow list" "632","Location overide allow list matched" "662","Site (local): " "663","URL (local): " "700","Web upload is banned." "701","Web upload limit exceeded." "750","Blanket file download is active and this MIME type is not on the white list: " "751","Blanket file download is active and this file is not matched by the white lists." "752","Site has a TLD that is not allowed by default" "800","Banned MIME file type: " "900","Banned file extension: " "1100","Virus or bad content detected." "1101","Advert blocked" "1200","Please wait - downloading file for scanning..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " "1202",", then refresh this page to download directly." "1203","WARNING: Could not perform content scan!" "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " "1222","File too large to cache.

Click here to re-download, bypassing scan: " "1230","File no longer available" "9999","Dummy so master always has higher number" e2guardian-5.5.8r/data/languages/mxspanish/000077500000000000000000000000001477372360500206435ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/mxspanish/fancydmtemplate.html000077500000000000000000000142251477372360500247150ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/mxspanish/messages000066400000000000000000000161361477372360500224040ustar00rootroot00000000000000# e2guardian messages file in MX Spanish # Translated by Vladimir Gomez # Typo corrected by Pedro Fortuny 2003/10/13 "0","Message number absent" # needs translation "1","Acceso Denegado" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Su dirección IP no tiene permiso para ver: " "101","Su dirección IP no esta autorizada a navegar en Internet." "102","Su usuario no tiene autorizació para ver: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","El URL solicitado esta mal formado." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Frase no permitida: " "301","Frase no permitida." "400","Combinación de palabras/frases no permitida: " "401","Combinación de palabras/frases no permitida." "402","La frase excede el nivel " "403","La página solicitada excede el nivel de frases permitidas." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Sitio no permitido: " "501","URL no permitido: " "502","Blanket Block esta activado y el sitio no se encuentra en la lista de permitidos." "503","URL bloqueada por Expresión Regular: " "504","URL bloqueada por Expresión Regular." "505","No se puede accesar un sitio por su dirección IP cuando Blanket IP Block está activado." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","Dirección ip del cliente presente en la lista de excepciones." "601","Usuario presente en la lista de excepciones." "602","Sitio presente en la lista de excepciones." "603","URL presente en la lista de excepciones." "604","Frase presente en la lista de excepciones." "605","Combinación de frases presente en la lista de excepciones: " "606","Puente URL excepciones." "607","Puente cookie excepciones." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " # 606,607 by Daniel Barron - corrections welcome "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","No esta permitido enviar archivos a sitios en Internet." "701","El archivo que intenta enviar excede el tamaño permitido." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Clase MIME no permitida: " "900","Extensión de archivo bloqueada: " "1000","Las etiquetas del sitio exceden el nivel PICS." "1100","Virus or bad content detected." # needs translation "1101","Advert blocked" "1200","Please wait - downloading to be scanned..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","File no longer available" e2guardian-5.5.8r/data/languages/mxspanish/neterr_template.html000066400000000000000000001407071477372360500247340ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/mxspanish/template.html000077500000000000000000000030741477372360500233530ustar00rootroot00000000000000e2guardian - Acceso Denegado

ACCESO DENEGADO -USER-


El acceso a la páina:

-URL-

... ha sido denegado por la siguiente razón:

-REASONGIVEN-

Usted esta viendo esta página de error porque
el sitio que está tratando de ver o su contenido
han sido catalogados como inapropiados.

Si requiere acceso a esta página por favor pongase en contacto
con el Administrador de Sistemas o el Administrador de la Red.

Powered by -SERVERNAME- e2guardian

e2guardian-5.5.8r/data/languages/polish/000077500000000000000000000000001477372360500201275ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/polish/fancydmtemplate.html000077500000000000000000000142251477372360500242010ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/polish/messages000066400000000000000000000155241477372360500216700ustar00rootroot00000000000000# e2guardian messages file # Polish version by Piotr Kapczuk # charset=iso-8859-2 "0","Message number absent" # needs translation "1","Dostp Zabroniony" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Zakazano dostpu z twojego adresu IP do strony: " "101","Przegldanie stron www z twojego adresu IP jest niedozwolone." "102","Zakazano dostpu z twoj nazw uytkownika do strony: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","dany URL jest le skonstruowany." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Znaleziono zakazan fraz: " "301","Znaleziono zakazan fraz." "400","Znaleziono zakazan kombinacj fraz: " "401","Znaleziono zakazan kombinacj fraz." "402","Limit fraz liczonych wagowo " "403","Przekroczono limit fraz liczonych wagowo." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Zakazany adres (site): " "501","Zakazany URL: " "502","Dostp zezwolony jedynie do wybranych stron, a ta strona nie jest na licie dozwolonych." "503","Zakazane Wyraenie Regularne dla URL: " "504","Znaleziono zakazane Wyraenie Regularne w URL." "505","Dostp po samych adresach IP jest zakazany, a ten adres jest jedynie adresem IP." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","IP klienta pasuje do wyjtkw." "601","Nazwa uytkownika pasuje do wyjtkw." "602","Adres pasuje do wyjtkw." "603","URL pasuje do wyjtkw." "604","Znaleziono fraz, ktra pasuje do wyjtkw: " "605","Znaleziono kombinacj fraz, ktra pasuje do wyjtkw: " "606","Bypass URL wyjtkw." "607","Bypass cookie wyjtkw." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " # 606,607 by Daniel Barron - corrections welcome "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","Wgrywanie do sieci jest zakazane." "701","Przekroczono limit wgrywanych danych." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Zakazany typ MIME: " "900","Zakazane rozszerzenie: " "1000","Wedle etykietowania PICS przekroczono dopuszczalny poziom zakazanych treci." "1100","Virus or bad content detected." "1101","Advert blocked" "1200","Please wait - downloading to be scanned..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","File no longer available" e2guardian-5.5.8r/data/languages/polish/neterr_template.html000066400000000000000000001407071477372360500242200ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/polish/template.html000077500000000000000000000032061477372360500226340ustar00rootroot00000000000000e2guardian - Dostp Zabroniony

ZABRONIONO DOSTPU -USER-


Dostp do strony:

-URL-

... zosta zabroniony z nastpujcego powodu:

-REASONGIVEN-

Ten bd pojawia si, poniewa strona, do ktrej prbowano uzyska dostp,
zawiera lub te jest oznakowana jako zawierajca treci uznane za nieodpowiednie.

Jeli masz jakie pytania lub wtpliwoci skontaktuj si ze swoim Administratorem Sieci

Powered by -SERVERNAME- e2guardian

e2guardian-5.5.8r/data/languages/portuguese/000077500000000000000000000000001477372360500210335ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/portuguese/fancydmtemplate.html000077500000000000000000000142251477372360500251050ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/portuguese/messages000066400000000000000000000166031477372360500225730ustar00rootroot00000000000000# e2guardian 3 messages in Brazilian Portuguese # Mensagens do e2guardian 3 file em Portugus do Brasil (pt-BR) # Translated/traduzido (?) by/por Henrique Araujo - Sys Admin # henrique@colegiosaogoncalo.g12.br # Cuiab - MT, Brasil "0","Message number absent" # needs translation "1","Acesso negado." "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Seu endereço IP não tem permissão de acesso à  web: " "101","Seu endereço IP não tem permissão de acesso à  web: " "102","Esse nome de usuário não tem permissão de acesso à  web: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","A URL requerida está mal formada." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Encontrada frase proibida: " "301","Encontrada frase proibida: " "400","Encontrada combinação de frases proibida: " "401","Encontrada combinação de frases proibida: " "402","Limite de frase ponderada de " "403","Limite de frase ponderada excedido." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Sítio proibido: " "501","URL proibida: " "502","Bloqueio geral está ativo e aquele sítio não está na lista livre." "503","Expressão Regular proibida em URL: " "504","Encontrada Expressão Regular proibida em URL: " "505","Bloqueio geral de IP está ativo e aquele endereço não possui nome." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","Cliente IP está na lista de exceções." "601","Usuário está na lista de exceções." "602","Sítio está na lista de exceções." "603","URL está na lista de exceções." "604","Encontrada frase na lista de exceções: " "605","Encontrada combinação de frases na lista de exceções: " "606","Desvio temporário de bloqueio (Bypass URL.)" "607","Desvio temporário de bloqueio (Bypass cookie.)" "608","Scan bypass URL exception." "609","Exception regular expression URL match: " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","Transferência de arquivos para web proibida." "701","Limite de transferência de arquivos para web excedido." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Tipo MIME proibido: " "900","Extensão proibida: " "1000","Nível de rotulagem PICS excedido no sítio acima." # 1,1000 by Andson Gomes - html internet "1100","Virus or bad content detected." "1101","Advert blocked" "1200","Please wait - downloading to be scanned..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","File no longer available" e2guardian-5.5.8r/data/languages/portuguese/messagesaccents000077500000000000000000000031751477372360500241370ustar00rootroot00000000000000# e2guardian 3 messages in Brazilian Portuguese # Mensagens do e2guardian 3 file em Portugus do Brasil (pt-BR) # Translated/traduzido (?) by/por Henrique Araujo - Sys Admin # henrique@colegiosaogoncalo.g12.br # Cuiab - MT, Brasil "1","Acesso negado." "100","Seu endereo IP no tem permisso de acesso web: " "101","Seu endereo IP no tem permisso de acesso web: " "102","Esse nome de usurio no tem permisso de acesso web: " "200","A URL requerida est malformada." "300","Encontrada frase proibida: " "301","Encontrada frase proibida: " "400","Encontrada combinao de frases proibida: " "401","Encontrada combinao de frases proibida: " "402","Limite de frase ponderada de " "403","Limite de frase ponderada excedido." "500","Stio proibido: " "501","URL proibida: " "502","Bloqueio geral est ativo e aquele stio no est na lista livre." "503","Expresso Regular proibida em URL: " "504","Encontrada Expresso Regular proibida em URL: " "505","Bloqueio geral de IP est ativo e aquele endereo no possui nome." "600","Cliente IP est na lista de excees." "601","Usurio est na lista de excees." "602","Stio est na lista de excees." "603","URL est na lista de excees." "604","Encontrada frase na lista de excees: " "605","Encontrada combinao de frases na lista de excees: " "606","Desvio temporrio de bloqueio (Bypass URL.)" "607","Desvio temporrio de bloqueio (Bypass cookie.)" "700","Transferncia de arquivos para web proibida." "701","Limite de transferncia de arquivos para web excedido." "800","Tipo MIME proibido: " "900","Extenso proibida: " "1000","Nvel de rotulagem PICS excedido no stio acima." e2guardian-5.5.8r/data/languages/portuguese/neterr_template.html000066400000000000000000001407071477372360500251240ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/portuguese/template.html000077500000000000000000000043471477372360500235470ustar00rootroot00000000000000 e2guardian - Access Denied

O acesso foi negado!
Usurio: -USER- 
Empresa S/A O acesso a pgina:

-URL-

... foi negado devido a seguinte razo:

-REASONGIVEN-

Categorias:

-CATEGORIES-



Voc est vendo esta mensagem porque o que voc tentou acessar parece conter material que foi julgado imprprio.

Se voc tiver alguma dvida favor entrar em contato com a equipe de suporte de sua rede.



Powered by -SERVERNAME- e2guardian
e2guardian-5.5.8r/data/languages/ptbrazilian/000077500000000000000000000000001477372360500211505ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/ptbrazilian/fancydmtemplate.html000077500000000000000000000146731477372360500252310ustar00rootroot00000000000000 Baixando -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/ptbrazilian/messages000066400000000000000000000144441477372360500227110ustar00rootroot00000000000000# e2guardian messages file in Brazilian Portuguese # Arquivo de mensagens do e2guardian em Português Brasil # Traduzido por Renato C. Pacheco - renato.camarao@gmail.com # Em 2015-04-15 # "0","Message number absent" # needs translation "1","Acesso Negado" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," em " "51","EXCEÇÃO" "52","NEGADO" "53","INFECTADO" "54","VERIFICADO" "55","CONTENTMOD" "56","URLMOD" "57","HEADERMOD" "58","HEADERADD" "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Seu endereço IP está impedido de navegar: " "101","Seu endereço IP está impedido de navegar." "102","Seu usuário está impedido de navegar: " "103","IP de Cliente Proibido" "104","Espaço Bloqueado" "105","Usuário Proibido" "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","A URL solicitada está mal formada." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Frase proibida encontrada: " "301","Frase proibida encontrada." "400","Combinação de frase proibida encontrada: " "401","Combinação de frase proibida encontrada." "402","Limite de frase ponderada de " "403","Limite de frase ponderada excedida." "450","Termo de busca proibido encontrado: " "451","Termo de busca proibido encontrado." "452","Combinação de termo de busca proibido encontrado: " "453","Combinação de termo de busca proibido encontrado." "454","Limite de termo de busca ponderado de " "455","Limite de termo de busca ponderado excedido." "456","Exceção da combinação do termo de busca encontrado: " "457","Exceção do termo de busca encontrado: " "500","Sítio proibido: " "501","URL Proibida: " "502","Bloco Restrito está ativado e este sítio não está na lista de exceções." "503","URL Expressão Regular Proibida: " "504","URL Expressão Regular Proibida encontrada." "505","Bloco de IP Restrito está ativado e este endereço é apenas um endereço IP." "506","Bloco Restrito SSL está ativado e este sítio não está na lista de exceções." "507","Bloco de IP SSL Restrito está ativado e este endereço é apenas um endereço IP." "508","Expressão Regular de Cabeçalho HTTP proibida: ", "509","Expressão Regular de Cabeçalho HTTP proibida encontrada." "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Sítio HTTPS Proibido: " "521","Palavras de Busca Proibidas: " "522","Blocked User-Agent: " # needs translation "560","Sítio bloqueado (local): " "561","URL bloqueada (local): " "580","Sítio HTTPS bloqueado (local): " "581","Palavras de Busca Proibidas (local): " "600","Exceção IP do cliente confere." "601","Exceção do usuário cliente confere." "602","Exceção do sítio confere." "603","Exceção da URL confere." "604","Exceção da frase encontrada: " "605","Combinação de exceção da frase encontrada: " "606","Exceção de URL ignorada." "607","Exceção de cookie ignorada." "608","Verificação de exceção de URL ignorada." "609","Exceção de expressão de URL confere: " "610","User-Agent pattern match: " # needs translation "620","Campo cabeçalho HTTP Referer confere: " "630","URL confere em " "631"," Lista de espaço permitida" "632","Lista permitida além do espaço conferida" "662","Sítio (local)." "663","URL (local)." "700","Upload é proibido." "701","Upload excedeu o limite." "750","Download de arquivos restrito está ativado e este MIME type não está nas listas de exceções: " "751","Download de arquivos restrito está ativado e este arquivo não confere com as listas de exceções." "752","Site has a TLD that is not allowed by default" # needs translation "800","MIME Type Proibido: " "900","Extensão Proibida: " "1000","Nível de rotulagem PICS excedido no sítio acima." "1100","Malware detectado." "1101","Anúncio bloqueado" "1200","Por favor, espere - baixando arquivo para verificação..." "1201","Aviso: arquivo muito grande para verificar. Se você suspeita que este arquivo maior que " "1202",", então recarregue esta página para baixar diretamente." "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download completo. Iniciando a verificação..." "1220","Verificação completa.

Clique aqui para download: " "1221","Download completo; arquivo não verificado.

Clique aqui para download: " "1222","Arquivo muito grande para ser armazenado no cache.

Clique aqui para baixar novamente, ignorando a verificação: " "1230","Arquivo não está mais disponível" e2guardian-5.5.8r/data/languages/ptbrazilian/neterr_template.html000066400000000000000000001407071477372360500252410ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/ptbrazilian/template.html000077500000000000000000001415711477372360500236650ustar00rootroot00000000000000 E2Guardian - Acesso negado
ACCESS DENIED

Opa! Você tentou visitar um site que foi considerado inadequado:



-URL-

Motivo

-REASONGIVEN-  -CATEGORIES-


Detalhes da conexão:

-USER- 


Confirmar e continuar

e2guardian-5.5.8r/data/languages/russian-1251/000077500000000000000000000000001477372360500207035ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/russian-1251/fancydmtemplate.html000077500000000000000000000142251477372360500247550ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/russian-1251/messages000066400000000000000000000135651477372360500224470ustar00rootroot00000000000000# e2guardian 3 messages file in russian # charset=windows-1251 # version by Shipitsin Igor "0","Message number absent" # needs translation "1"," " "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100"," IP :" "101"," IP ." "102"," :" "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200"," URL ." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300"," :" "301"," ." "400"," :" "401"," ." "402"," " "403"," ." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500"," :" "501"," URL:" "502"," - ." "503"," URL:" "504"," URL." "505"," IP - IP, ." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600"," IP ." "601"," ." "602"," ." "603"," URL." "604"," :" "605"," :" "606"," URL ." "607"," cookie ." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700"," ." "701"," ." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800"," MIME:" "900"," :" "1000"," PICS ." "1100"," " "1101","Advert blocked" "1200","Please wait - downloading to be scanned..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","File no longer available" e2guardian-5.5.8r/data/languages/russian-1251/neterr_template.html000066400000000000000000001407071477372360500247740ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/russian-1251/template.html000077500000000000000000000041761477372360500234170ustar00rootroot00000000000000 e2guardian -

!
-USER- 
:

-URL-

... :

-REASONGIVEN-



, , , , .

.



e2guardian
e2guardian-5.5.8r/data/languages/russian-koi8-r/000077500000000000000000000000001477372360500214245ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/russian-koi8-r/fancydmtemplate.html000077500000000000000000000142251477372360500254760ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/russian-koi8-r/messages000066400000000000000000000175071477372360500231700ustar00rootroot00000000000000# e2guardian messages file in Russian KOI8-R "0","Message number absent" # needs translation "1","Доступ запрещён" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," в " "51","ИСКЛЮЧЕНИЕ" "52","ЗАПРЕЩЕН" "53","ЗАРАЖЕН" "54","ОТСКАНИРОВАН" "55","CONTENTMOD" "56","URLMOD" "57","HEADERMOD" "58","HEADERADD" "59","NETERROR" "60","SEMI-TRUSTED" # needs translation "70","SSL САЙТ" "71","IP Limit" "72","Сканирование контента" "100","Вашему IP адресу запрещен веб-просмотр: " "101","Вашему IP адресу запрещен веб-просмотр." "102","Пользователю запрещён веб-просмотр: " "103","Заблокированный IP клиента" "104","Заблокированная локация" "105","Заблокированный пользователь" "110","Proxy authentication error" # needs translation "121","С вашей локации разрешен только ограниченный доступ" "122","You not allowed access at this time of day" # needs translation "150","Сертификат сервера не является действительным" "151","Невозможно открыть SSL соединение" "152","Не удалось получить SSL сертификат" "153","Не удалось загрузить приватный ключ сертификата" "154","Не удалось согласовать SSL соединение с клиентом" "155","Сервер не предоставил SSL сертификат" "156","SSL сертификат сервера не совпадает с именем домена" "157","Не удалось создать тоннель через локальный прокси" "158","Не удалось открыть тоннель" "159","Не удалось подключить к прокси серверу" "160","Не удалось согласовать SSL соединение с сервером" "200","Неверный URL" "201","Не удалось подключиться к прокси серверу (таймаут)" "202","Не удалось подключиться к прокси серверу (ошибка сети)" "203","Запрашиваемый сайт не отвечает" "204"," - Пожалуйста попробуйте ещё раз" "205","Прокси сервер не отвечает (ошибка сети)" "206"," - Пожалуйста попробуйте ещё раз позже" "207","Запрашиваемый сайт не существует" "208","Запрашиваемый сайт не имеет IPv4 адреса" "209","Ошибка сервиса DNS - попробуйте снова" "210","Ошибка сервиса DNS - попробуйте снова" "212","Loop request blocked" # needs translation "300","Найдена запрещённая фраза: " "301","Найдена запрещенная фраза." "400","Найдена запрещённая комбинация фраз: " "401","Найдена запрещённая комбинация фраз." "402","Лимит проверки контента " "403","Заблокирован системой проверки контента." "450","Найден запрещённый поисковой запрос: " "451","Найден запрещённый поисковой запрос." "452","Найдена запрещённая комбинация поисковых фраз: " "453","Найдена запрещённая комбинация поисковых фраз." "454","Лимит веса поискового запроса " "455","Достигнут лимит веса поискового запроса." "456","Найден доверенный поисковой запрос: " "457","Найден доверенный поисковой запрос: " "500","Заблокированный сайт: " "501","Заблокированная ссылка: " "502","Включен режим белого списка. Данный сайт отсутствует в нём." "503","Ссылка совпала с запрещённым паттерном : " "504","Заблокированная ссылка." "505","Доступ по IP запрещён." "506","HTTPS доступен только к доверенным адресам." "507","HTTPS доступ по IP адресу запрещён." "508","Доступ с этого браузера запрещён: " "509","Доступ с этого браузера запрещён." "510","Заблокированный IP сайт " "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Заблокированный HTTPS сайт: " "521","Заблокированная поисковая фраза: " "522","Заблокированный User-Agent: " "560","Заблокированный сайт (локально): " "561","Заблокированная ссылка (локально): " "580","Заблокированный HTTPS сайт (локально): " "581","Заблокированная поисковая фраза (локально): " "600","Совпал IP клиента." "601","Совпал пользователь." "602","Совпал сайт: " "603","Совпала ссылка: " "604","Найдена фраза: " "605","Найдена комбинация фраз: " "606","Ссылка на обход." "607","Cookie обхода." "608","Отсканировать ссылку обхода." "609","Совпал паттерн ссылки: " "610","Совпал паттерн User-Agent: " "620","Referer match Совпал реферер: " "630","Совпала ссылка " "631"," списка доступа локации" "632","Совпадение со списком доступа локации" "662","Сайт (локально): " "663","Ссылка (локально): " "700","Загрузка заблокирована." "701","Достигнут лимит загрузки." "750","Включен режим белого списка для загрузок и этот MIME type в нем отсутствует: " "751","Включен режим белого списка для загрузок и этот MIME type в нем отсутствует." "752","Site has a TLD that is not allowed by default" # needs translation "800","Заблокированный MIME type: " "900","Заблокированное расширение файла: " "1100","Найден плохой, либо вирусный контент." "1101","Реклама заблокирована" "1200","Пожалуйста подождите. Загрузка файла для сканирования..." "1201","Внимание: файл слишком большой для сканирования. Если этот файл больше " "1202",", обновите страницу для прямого скачивания." "1203","ВНИМАНИЕ: Невозможно произвести сканирование контента!" "1210","Загрузка завершена. Сканирование..." "1220","Сканирование завершено.

Нажмите здесь для загрузки: " "1221","Загрузка завершена; файл не был отсканирован.

Нажмите здесь для загрузки: " "1222","Файл слишком большой для кэша.

Нажмите здесь для скачивания без сканирования: " "1230","Файл недоступен" "9999","Dummy so master always has higher number" e2guardian-5.5.8r/data/languages/russian-koi8-r/neterr_template.html000066400000000000000000001407071477372360500255150ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/russian-koi8-r/template.html000077500000000000000000000042261477372360500241340ustar00rootroot00000000000000 e2guardian - Access Denied

!
-USER- 
:

-URL-

... :

-REASONGIVEN-



, , ( , ) .

, .



Powered by -SERVERNAME- e2guardian
e2guardian-5.5.8r/data/languages/slovak/000077500000000000000000000000001477372360500201305ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/slovak/fancydmtemplate.html000077500000000000000000000142251477372360500242020ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/slovak/messages000066400000000000000000000157011477372360500216660ustar00rootroot00000000000000# e2guardian messages file in Slovak # by Dušan Biroščák biroscak@gmail.com # corrections Peter Tuhársky tuharsky@misbb.sk # charset=utf-8 "0","Message number absent" # needs translation "1","Prístup bol odopretý "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Vašej IP adrese nie je dovolené prehliadať: " "101","Vaša IP adresa nemá dovolené prohliadanie." "102","Vaše používateľské meno nemá dovolené prehliadať: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","Požadovaná URL je chybne zadaná." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Našla sa zakázaná fráza: " "301","Našla sa zakázaná fráza." "400","Našla sa zakázaná kombinácia fráz: " "401","Našla sa zakázaná kombinácia fráz." "402","Limit vážených fráz: " "403","Bol prekročený limit pre vážené frázy." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Zakázaná doména: " "501","Zakázaná URL: " "502","Celoplošné blokovanie prístupu je aktivované a táto webová doména nie je povolená." "503","Našla sa zakázaná URL adresa podľa regulárneho výrazu: " "504","Našla sa zakázaná URL adresa podľa regulárneho výrazu." "505","Celoplošné blokovanie IP adries je aktivované a bola zadaná len IP adresa." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","Našla sa výnimková IP adresa klienta." "601","Našiel sa výnimkový používateľ." "602","Našla sa výnimková webová doména." "603","Našla sa výnimková URL adresa." "604","Našla sa výnimková fráza: " "605","Našla sa kombinácia výnimkových fráz: " "606","Prekonať výnimku URL." "607","Prekonať cookie výnimku." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","Odosielanie na web je zakázané." "701","Limit pre odosielanie na web bol prekročený." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Zakázaný obsah súboru (MIME): " "900","Zakázaná prípona súboru: " "1000","Na tejto webovej doméne bola prekročená úroveň označovania PICS." "1100","V tomto obsahu sa našiel vírus." "1101","Advert blocked" "1200","Please wait - downloading to be scanned..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","File no longer available" e2guardian-5.5.8r/data/languages/slovak/neterr_template.html000066400000000000000000001407071477372360500242210ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/slovak/template.html000077500000000000000000000054121477372360500226360ustar00rootroot00000000000000 e2guardian - Prístup bol odopretý

Prístup na túto stránku bol odopretý.
-USER- 
YOUR ORG NAME Prístup na túto stránku:

-URL-

... bol odopretý z týchto dôvodov:

-REASONGIVEN-



Zobrazenie tohto hlásenia spôsobila webstránka, ktorú ste sa pokúsili otvoriť, pretože obsahuje alebo je označená ako obsahujúca nepatričný obsah.

Ak máte nejaké otázky alebo námietky, prosím obráťte sa na svojho správcu siete.



Beží na e2guardian
e2guardian-5.5.8r/data/languages/spanish/000077500000000000000000000000001477372360500202765ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/spanish/fancydmtemplate.html000077500000000000000000000142251477372360500243500ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/spanish/messages000066400000000000000000000143711477372360500220360ustar00rootroot00000000000000# e2guardian 3 messages file in Spanish # Translated by Roberto Quiroga, adapted for Unicode by Peter Vollmar # Translated by Leonardo Yanes Batista "0","Número de mensaje ausente" "1","Acceso Denegado" "10","IP limit exceeded. There is a " # needs translation "11"," Límite de IP establecido." "50"," en " "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Analizando contenido" "100","Su dirección IP no está autorizada a visitar: " "101","Su dirección IP no está autorizada a navegar." "102","El usuario no está autorizado a visitar: " "103","IP de cliente no permitida" "104","Ubicación no permitida" "105","Usuario no permitido" "110","Error de autenticación de proxy" "121","Solo se permite el acceso limitado desde su ubicación" "122","Usted no está autorizado a acceder a esta hora del día" "150","El certificado proporcionado por el servidor no era válido" "151","No se pudo abrir la conexión SSL" "152","No se pudo obtener el certificado SSL" "153","No se pudo cargar la clave privada SSL" "154","No se pudo negociar la conexión SSL con el cliente" "155","El servidor no proporcionó un certificado SSL" "156","El certificado SSL del servidor no coincide con el nombre de dominio" "157","No se puede crear un túnel a través del proxy local" "158","Error al abrir el túnel" "159","No se pudo conectar al servidor proxy" "160","No se pudo negociar la conexión SSL con el servidor" "200","La URL solicitada está mal formada." "201","No se pudo obtener respuesta del proxy superior (timeout)" "202","No se pudo obtener respuesta del proxy superior (error)" "203","El sitio solicitado no responde" "204","- Por favor, inténtelo de nuevo más tarde" "205","El proxy superior no responde (error de red)" "206","- Por favor, inténtelo de nuevo más tarde" "207","El sitio solicitado no existe" "208","El sitio solicitado no tiene una dirección IPv4" "209","Fallo temporal del servicio DNS - inténtelo de nuevo" "210","Fallo del servicio DNS - inténtelo de nuevo más tarde" "212","Solicitud de bucle bloqueada" "300","Se encontró la frase no permitida: " "301","Se encontró una frase no permitida." "400","Combinación de frases no permitida: " "401","Combinación de frases no permitida." "402","Límite de ponderación de frases de " "403","Límite de ponderación de frases excedido" "450","Se encontró un término de búsqueda prohibido: " "451","Se encontró un término de búsqueda prohibido." "452","Se encontró un término de búsqueda combinado prohibido: " "453","Se encontró un término de búsqueda combinado prohibido." "454","Límite de término de búsqueda ponderado de " "455","Se superó el límite de términos de búsqueda ponderado." "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Sitio no permitido: " "501","URL no permitida: " "502","Bloqueo general de IP está activo y el sitio deseado no está en la lista de IPs permitidas." "503","URL bloqueada por expresión regular: " "504","URL bloqueada por expresión regular." "505","Bloqueo general de IP está activo y la dirección deseada consiste únicamente en IP." "506","El acceso mediante HTTPS solo está permitido a sitios de confianza." "507","No se permite el acceso HTTPS mediante direcciones IP." "508","No se permite el acceso mediante este navegador (o aplicación): " "509","No se permite el acceso mediante este navegador (o aplicación)." "510","El sitio IP se encuentra bloqueado " "511","La conexión https transparente no es TLS: " "512","La conexión https transparente no tiene SNI: " "520","Sitio HTTPS bloqueado: " "521","Palabras de búsqueda prohibidas: " "522","Navegador bloqueado: " "560","Sitio bloqueado (local): " "561","URL bloqueada (local): " "580","Sitio HTTPS bloqueado (local): " "581","Palabras de búsqueda prohibidas (local): " "600","Dirección IP del cliente presente en la lista de excepciones." "601","Usuario presente en la lista de excepciones." "602","Sitio presente en la lista de excepciones." "603","URL presente en la lista de excepciones." "604","Frase presente en la lista de excepciones." "605","Combinación de frases presente en la lista de excepciones: " "606","Puente URL en la lista de excepciones." "607","Puente cookie en la lista de excepciones." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL coincide en: " "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Sitio (local)." "663","URL (local)." "700","La subida está bloqueada." "701","Límite de subida excedido." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Clase MIME bloqueada: " "900","Extensión bloqueada: " "1000","Clasificación PICS excedida en el sitio indicado." "1100","Virus o mal contenido detectado." "1101","Anuncio bloqueado" "1200","Espere, descargando para ser analizado..." "1201","Advertencia: archivo demasiado grande para analizar. Si sospecha que este archivo es mayor que " "1202",", luego actualice esta página para descargar directamente." "1203","ADVERTENCIA: ¡No se pudo realizar el análisis de contenido!" "1210","Descarga completada. Comenzando el análisis..." "1220","Análisis completado.

Haga click aquí para descargar: " "1221","Descarga completa; el archivo no se ha analizado.

Haga click aquí para descargar: " "1222","Archivo demasiado grande para almacenar en caché.

Haga clic aquí para volver a descargar, sin pasar por el análisis de contenido :" "1230","El archivo ya no está disponible" e2guardian-5.5.8r/data/languages/spanish/neterr_template.html000066400000000000000000001407071477372360500243670ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/spanish/template.html000077500000000000000000000044231477372360500230050ustar00rootroot00000000000000 e2guardian Acceso denegado

Acceso denegado!
-USER- 
SU COMPANIA El acceso a la página web

-URL-

ha sido denegado por la siguiente razón:

-REASONGIVEN-



Usted está viendo este mensaje de error porque la página a la que
intenta acceder contiene, o está clasificada como conteniendo,
material que se considera inapropiado.

Si tiene preguntas, por favor póngase en contacto
con el Administrador de Sistemas o el Administrador de la Red.



Powered by -SERVERNAME- e2guardian
e2guardian-5.5.8r/data/languages/swedish/000077500000000000000000000000001477372360500202775ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/swedish/fancydmtemplate.html000077500000000000000000000142251477372360500243510ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/swedish/messages000066400000000000000000000156121477372360500220360ustar00rootroot00000000000000# e2guardian messages file in Swedish # Swedish translation by: David Hed "0","Message number absent" # needs translation "1","åtkomst nekad" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Din IP adress har inte tillstånd att surfa: " "101","Din IP adress har inte tillstånd att surfa." "102","Din användaridentitet har inte tillstånd att surfa: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","Begärd URL är felformaterad." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Förbjuden fras funnen: " "301","Förbjuden fras funnen." "400","Förbjuden kombination av fraser funnen: " "401","Förbjuden kombination av fraser funnen." "402","Viktad frasbedömning av " "403","Viktad fras begränsning överskriden." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Förbjuden webbplats: " "501","Förbjuden adress: " "502","Blanket Block är aktiverat och webbplatsen är inte grå eller vitlistad." "503","Förbjudet ord i adressfältet: " "504","Förbjudet ord i adressfältet funnen." "505","Blanket IP Block är aktiverat och denna adress är en endast IP adress." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","Undantagen Dators IP-adress matchar." "601","Undantagen användaridentitet matchar." "602","Undantagen webbplats matchar." "603","Undantagen URL matchar." "604","Undantagsfras funnen: " "605","Kombinerad undantagsfras funnen: " "606","Kringgå URL begränsning." "607","Kringgå cookie begränsning." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","Webbuppladdning är förbjuden." "701","Storlek för webbuppladdning är överskriden." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Förbjuden MIME typ: " "900","Förbjuden filändelse: " "1000","PICS klassificiering överskriden på ovanstående webbplats." "1100","Virusinfekterat innehåll funnet." "1101","Advert blocked" "1200","Please wait - downloading to be scanned..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","File no longer available" e2guardian-5.5.8r/data/languages/swedish/neterr_template.html000066400000000000000000001407071477372360500243700ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/swedish/template.html000077500000000000000000000047441477372360500230140ustar00rootroot00000000000000 e2guardian - Åtkomst nekad

Åtkomst förhindrad av proxy!
-USER- 
YOUR ORG NAME Åtkomst till sidan:

-URL-

... har blivit nekad på grund av följande skäl:

-REASONGIVEN-



Den centrala proxyservern är inställd på att filtrera undan eventuell skadlig kod eller av policyskäl begränsade nerladdningar. Bedömningen är automatisk så programmet kan ibland på felaktiga grunder förhindra åtkomst.

Anser du att denna blockering är felaktig eller har andra frågor, kontakta din systemadministratör.



Powered by -SERVERNAME- e2guardian
e2guardian-5.5.8r/data/languages/turkish/000077500000000000000000000000001477372360500203225ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/turkish/fancydmtemplate.html000077500000000000000000000142251477372360500243740ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/turkish/messages000066400000000000000000000156051477372360500220630ustar00rootroot00000000000000# e2guardian messages file in Turkish # Translation by Ozgur Karatas # trdocmaster at e2guardian dot org "0","Message number absent" # needs translation "1","Erisim Engellendi" "10","IP limit exceeded. There is a " # needs translation "11"," IP limit set." # needs translation "50"," in " # needs translation "51","TRUSTED" # needs translation "52","DENIED" # needs translation "53","INFECTED" # needs translation "54","SCANNED" # needs translation "55","CONTENTMOD" # needs translation "56","URLMOD" # needs translation "57","HEADERMOD" # needs translation "58","HEADERADD" # needs translation "59","NETERROR" # needs translation "60","SEMI-TRUSTED" # needs translation "70","SSL SITE" # needs translation "71","IP Limit" # needs translation "72","Content scanning" # needs translation "100","Bu IP adresi ile internete erisim engellenmistir: " "101","Bu IP ile internete erisim engellenmistir." "102","Bu kullanici adi ile internete erisim engellenmistir.: " "103","Banned Client IP" # needs translation "104","Banned Location" # needs translation "105","Banned User" # needs translation "110","Proxy authentication error" # needs translation "121","Only limited access allowed from your location" # needs translation "122","You not allowed access at this time of day" # needs translation "150","Certificate supplied by server was not valid" # needs translation "151","Could not open ssl connection" # needs translation "152","Failed to get ssl certificate" # needs translation "153","Failed to load ssl private key" # needs translation "154","Failed to negotiate ssl connection to client" # needs translation "155","No SSL certificate supplied by server" # needs translation "156","Server's SSL certificate does not match domain name" # needs translation "157","Unable to create tunnel through local proxy" # needs translation "158","Opening tunnel failed" # needs translation "159","Could not connect to proxy server" # needs translation "160","Failed to nogotiate ssl connection to server" # needs translation "200","Gitmek istediginiz URL hatalidir." "201","Unable to get response from upstream proxy (timeout)" # needs translation "202","Unable to get response from upstream proxy (error)" # needs translation "203","The site requested is not responding" # needs translation "204"," - Please try again later" # needs translation "205","Upstream proxy is not responding (network error)" # needs translation "206"," - Please try again later" # needs translation "207","The site requested does not exist" # needs translation "208","The site requested does not have an IPv4 address" # needs translation "209","Temporary DNS service failure - please try again" # needs translation "210","DNS service failure - please try again later" # needs translation "212","Loop request blocked" # needs translation "300","Yasaklanmis bir ifade iceren sayfaya girmeye calisiyorsunuz: " "301","Sistem yoneticisi tarafindan engellenmis bir ifade tespit edildi." "400","Yasaklanmis kelimeler bulundu: " "401","Yasaklanmis kelimeler tespit edildi." "402","Engellenmis ifadelerin limiti asildi: " "403","Engellenmis kelime limiti asildi." "450","Banned search term found: " # needs translation "451","Banned search term found." # needs translation "452","Banned combination search term found: " # needs translation "453","Banned combination search term found." # needs translation "454","Weighted search term limit of " # needs translation "455","Weighted search term limit exceeded." # needs translation "456","Exception combination search term found: " # needs translation "457","Exception search term found: " # needs translation "500","Yasaklanmis Site: " "501","Yasaklanmis URL: " "502","Erisilebilir listesinde bulunmayan bir siteye giremezsiniz." "503","Yasaklanmis dzensiz ifade (URL): " "504","Yasaklanmis duzensiz ifadeye rastlandi." "505","Bu IP adresinin asagidaki siteye girisi engellenmistir." "506","HTTPS access is only allowed to trusted sites." # needs translation "507","HTTPS access by IP address is not allowed." # needs translation "508","Access not allowed using this browser (or app): " # needs translation "509","Access not allowed using this browser (or app)." # needs translation "510","Blocked IP site " # needs translation "511","Tranparent https connection is not TLS: " # needs translation "512","Tranparent https connection does not have SNI: " # needs translation "520","Blocked HTTPS site: " # needs translation "521","Banned Search Words: " # needs translation "522","Blocked User-Agent: " # needs translation "560","Blocked site (local): " # needs translation "561","Blocked URL (local): " # needs translation "580","Blocked HTTPS site (local): " # needs translation "581","Banned Search Words (local): " # needs translation "600","Ayricalikli IP adresine sahipsiniz." "601","Ayricalikli kullanici adina sahipsiniz." "602","Ayricalikli bir siteye girdiniz." "603","Ayricalikli bir URL adresine girdiniz." "604","Ayricalikli ifade bulundu: " "605","Kombinasyonu ertelenmis ifade bulundu: " "606","URL adresi ayricalikli listesine alinmalidir." "607","Bu sitede cookie kesfedildi." "608","Ayricalikli URL adresi taraniyor." "609","Ayricalikli duzenlenmis url adresi: " "610","User-Agent pattern match: " # needs translation "620","Referer match: " # needs translation "630","URL match in " # needs translation "631"," location allow list" # needs translation "632","Location overide allow list matched" # needs translation "662","Site (local)." # needs translation "663","URL (local)." # needs translation "700","Internet zerinden dosya gnderimi yasaklanmistir." "701","Internet zerinden dosya gnderimi sinirini astiniz." "750","Blanket file download is active and this MIME type is not on the white list: " # needs translation "751","Blanket file download is active and this file is not matched by the white lists." # needs translation "752","Site has a TLD that is not allowed by default" # needs translation "800","Yasaklanmis MIME Tr: " "900","Yasaklanmis dosya uzantisi: " "1000","Bu sitedeki resim siniri asildi." "1100","Virus ve kotu icerige rastlandi." "1101","Site bloklandi. Adware veya spyware tespit edildi." "1200","Lutfen bekleyiniz, icerik taranarak yukleniyor.." "1201","Warning: file too large to scan. If you suspect that this file is larger than " # needs translation "1202",", then refresh this page to download directly." # needs translation "1203","WARNING: Could not perform content scan!" # needs translation "1210","Yukleme bitti, taramadan geciriliyor.." "1220","Tarama bitti.

Yuklemek icin tiklayiniz: " "1221","Download complete; file not scanned.

Click here to download: " # needs translation "1222","File too large to cache.

Click here to re-download, bypassing scan: " # needs translation "1230","Dosya uzunlugu gecersizdir." e2guardian-5.5.8r/data/languages/turkish/neterr_template.html000066400000000000000000001407071477372360500244130ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/turkish/template.html000077500000000000000000001416351477372360500230400ustar00rootroot00000000000000 E2Guardian - Erisim Engellendi
ACCESS DENIED

Asagidaki adrese erisiminiz engellenmistir:



-URL-

Erisim kisitlama sebebi:

Category: -CATEGORIES-   -REASONGIVEN-


Sistem yöneticiniz tarafindan girilmesine izin verilmeyen bir sayfaya erisim yapmaya calisiyorsunuz.

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


e2guardian-5.5.8r/data/languages/ukenglish/000077500000000000000000000000001477372360500206225ustar00rootroot00000000000000e2guardian-5.5.8r/data/languages/ukenglish/fancydmtemplate.html000077500000000000000000000142251477372360500246740ustar00rootroot00000000000000 Downloading -FILENAME- (-FILESIZE- bytes) e2guardian-5.5.8r/data/languages/ukenglish/messages000066400000000000000000000110661477372360500223600ustar00rootroot00000000000000# e2guardian messages file in UK English "0","Message number absent" "1","Access Denied" "10","IP limit exceeded. There is a " "11"," IP limit set." "50"," in " "51","TRUSTED" "52","DENIED" "53","INFECTED" "54","SCANNED" "55","CONTENTMOD" "56","URLMOD" "57","HEADERMOD" "58","HEADERADD" "59","NETERROR" "60","SEMI-TRUSTED" "70","SSL SITE" "71","IP Limit" "72","Content scanning" "100","Your IP address is not allowed to web browse: " "101","Your IP address is not allowed to web browse." "102","Your username is not allowed to web browse: " "103","Banned Client IP" "104","Banned Location" "105","Banned User" "110","Proxy authentication error" "121","Only limited access allowed from your location" "122","You not allowed access at this time of day" "150","Certificate supplied by server was not valid" "151","Could not open ssl connection" "152","Failed to get ssl certificate" "153","Failed to load ssl private key" "154","Failed to negotiate ssl connection to client" "155","No SSL certificate supplied by server" "156","Servers SSL certificate does not match domain name" "157","Unable to create tunnel through local proxy" "158","Opening tunnel failed" "159","Could not connect to proxy server" "160","Failed to negotiate ssl connection to server" "200","The requested URL is malformed." "201","Unable to get response from upstream proxy (timeout)" "202","Unable to connect to upstream proxy (network error)" "203","The site requested is not responding" "204"," - Please try again later" "205","Upstream proxy is not responding (network error)" "206"," - Please try again later" "207","The site requested does not exist" "208","The site requested does not have an IPv4 address" "209","Temporary DNS service failure - please try again" "210","DNS service failure - please try again later" "212","Loop request blocked" "300","Banned phrase found: " "301","Banned phrase found." "400","Banned combination phrase found: " "401","Banned combination phrase found." "402","Content Check limit of " "403","Blocked by Content Checking." "450","Banned search term found: " "451","Banned search term found." "452","Banned combination search term found: " "453","Banned combination search term found." "454","Weighted search term limit of " "455","Weighted search term limit exceeded." "456","Exception combination search term found: " "457","Exception search term found: " "500","Blocked site: " "501","Blocked URL: " "502","Walled Garden is on and the site is not available to you." "503","Banned pattern matched URL: " "504","Blocked URL." "505","Access to sites by IP address is not allowed." "506","HTTPS access is only allowed to trusted sites." "507","HTTPS access by IP address is not allowed." "508","Access not allowed using this browser (or app): " "509","Access not allowed using this browser (or app)." "510","Blocked IP site " "511","Tranparent https connection is not TLS: " "512","Tranparent https connection does not have SNI: " "520","Blocked HTTPS site: " "521","Banned Search Words: " "522","Blocked User-Agent: " "560","Blocked site (local): " "561","Blocked URL (local): " "580","Blocked HTTPS site (local): " "581","Banned Search Words (local): " "600","Client IP match." "601","Client user match." "602","Site match: " "603","URL match: " "604","Phrase found: " "605","Combination phrase found: " "606","Bypass URL." "607","Bypass cookie." "608","Scan bypass URL." "609","URL pattern match: " "610","User-Agent pattern match: " "620","Referer match: " "630","URL match in " "631"," location allow list" "632","Location overide allow list matched" "662","Site (local): " "663","URL (local): " "700","Web upload is banned." "701","Web upload limit exceeded." "750","Blanket file download is active and this MIME type is not on the white list: " "751","Blanket file download is active and this file is not matched by the white lists." "752","Site has a TLD that is not allowed by default" # needs translation "800","Banned MIME file type: " "900","Banned file extension: " "1100","Virus or bad content detected." "1101","Advert blocked" "1200","Please wait - downloading file for scanning..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " "1202",", then refresh this page to download directly." "1203","WARNING: Could not perform content scan!" "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " "1222","File too large to cache.

Click here to re-download, bypassing scan: " "1230","File no longer available" "9999","Dummy so master always has higher number" e2guardian-5.5.8r/data/languages/ukenglish/messages.alt000066400000000000000000000071401477372360500231350ustar00rootroot00000000000000# e2guardian messages file in UK English # alternative version with messages similar to dansguardian # # This file needs translation # "1","Access Denied" "50"," in " "51","EXCEPTION" "52","DENIED" "53","INFECTED" "54","SCANNED" "55","CONTENTMOD" "56","URLMOD" "57","HEADERMOD" "58","HEADERADD" "70","SSL SITE" "100","Your IP address is not allowed to web browse: " "101","Your IP address is not allowed to web browse." "102","Your username is not allowed to web browse: " "103","Banned Client IP" "104","Banned Room" "105","Banned User" "150","Certificate supplied by server was not valid" "151","Could not open ssl connection" "152","Failed to get ssl certificate" "153","Failed to load ssl private key" "154","Failed to negotiate ssl connection to client" "155","No SSL certificate supplied by server" "156","Server's SSL certificate does not match domain name" "200","The requested URL is malformed." "200","The requested URL is malformed." "300","Banned phrase found: " "301","Banned phrase found." "400","Banned combination phrase found: " "401","Banned combination phrase found." "402","Weighted phrase limit of " "403","Weighted phrase limit exceeded." "450","Banned search term found: " "451","Banned search term found." "452","Banned combination search term found: " "453","Banned combination search term found." "454","Weighted search term limit of " "455","Weighted search term limit exceeded." "456","Exception combination search term found: " "457","Exception search term found: " "500","Banned site: " "501","Banned URL: " "502","Blanket Block is active and that site is not on the white or grey list." "503","Banned Regular Expression URL: " "504","Banned Regular Expression URL found." "505","Blanket IP Block is active and that address is an IP only address." "506","Blanket SSL Block is active and that site is not on the white or grey list." "507","Blanket SSL IP Block is active and that address is an IP only address." "508","Banned Regular Expression HTTP header: ", "509","Banned Regular Expression HTTP header found." "520","Blocked HTTPS site: " "521","Banned Search Words: " "560","Blocked site (local): " "561","Blocked URL (local): " "580","Blocked HTTPS site (local): " "581","Banned Search Words (local): " "600","Exception client IP match." "601","Exception client user match." "602","Exception site match." "603","Exception URL match." "604","Exception phrase found: " "605","Combination exception phrase found: " "606","Bypass URL exception." "607","Bypass cookie exception." "608","Scan bypass URL exception." "609","Exception regular expression URL match: " "620","Referer match: " "630","URL match in " "631"," Room allow list" "632","Room overide allow list matched" "662","Site (local)." "663","URL (local)." "700","Web upload is banned." "701","Web upload limit exceeded." "750","Blanket file download is active and this MIME type is not on the white list: " "751","Blanket file download is active and this file is not matched by the white lists." "800","Banned MIME Type: " "900","Banned extension: " "1000","PICS labeling level exceeded on the above site." "1100","Virus or bad content detected." "1101","Advert blocked" "1200","Please wait - downloading file for scanning..." "1201","Warning: file too large to scan. If you suspect that this file is larger than " "1202",", then refresh this page to download directly." "1210","Download Complete. Starting scan..." "1220","Scan complete.

Click here to download: " "1221","Download complete; file not scanned.

Click here to download: " "1222","File too large to cache.

Click here to re-download, bypassing scan: " "1230","File no longer available" e2guardian-5.5.8r/data/languages/ukenglish/neterr_template.html000066400000000000000000001407071477372360500247130ustar00rootroot00000000000000 E2Guardian - Unable to load website
Page unavailable

Oops! There seems to be an issue loading this page:



-URL-

Because

-REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Go Back to previous page
e2guardian-5.5.8r/data/languages/ukenglish/template.html000077500000000000000000001417021477372360500233330ustar00rootroot00000000000000 E2Guardian - Access Denied
ACCESS DENIED

Oops! You have tried visiting a website which has been deemed inappropriate:



-URL-

Because

Category: -CATEGORIES-   -REASONGIVEN-


Your details are below:

User: -USER-   Group: -FILTERGROUP-   IP: -IP-


Acknowledge

e2guardian-5.5.8r/data/languages/update_messages000077500000000000000000000024621477372360500217340ustar00rootroot00000000000000#!/bin/bash # updates language files from ukenglish master master=./messages_master targets=*/messages #read master into an array i=0 for l in `cat ${master}|tr " " "^"` do #echo ${l} if [ "${l:0:1}" = '"' ] then n=`echo ${l}|cut -f1 -d,|tr -d '"'` d=`echo ${l}|cut -f2- -d,|tr "^" " "` # echo ${n} ${d} ma[${n}]=${d} mi[i]=${n} let i=i+1 #echo $i fi done nmi=${i} #echo ${i} for t in ${targets} do echo "Checking ${t} ..." ( cm=0 cmi=${mi[${cm}]} if [ -n "${cmi}" ] then for l in `cat ${t}|tr " " "^"` do if [ -z "${cmi}" ] then cmi=9999 fi if [ "${l:0:1}" != '"' ] then echo ${l}|tr "^" " " continue else n=`echo ${l}|cut -f1 -d,|tr -d '"'` while [ ${n} -gt ${cmi} ] && [ ${cm} -lt ${nmi} ] do echo "\"${cmi}\",${ma[${mi[${cm}]}]} # needs translation" let cm=cm+1 cmi=${mi[${cm}]} done echo ${l}|tr "^" " " let cm=cm+1 cmi=${mi[${cm}]} fi done fi while [ ${cm} -lt ${nmi} ] do echo "\"${cmi}\",${ma[${mi[${cm}]}]}" let cm=cm+1 cmi=${mi[${cm}]} done ) > ${t}.new if diff ${t} ${t}.new >/dev/null then rm ${t}.new : else mv ${t} ${t}.sav mv ${t}.new ${t} git add ${t} fi done exit 0 e2guardian-5.5.8r/data/scripts/000077500000000000000000000000001477372360500163525ustar00rootroot00000000000000e2guardian-5.5.8r/data/scripts/Makefile.am000077500000000000000000000011751477372360500204150ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in SUBDIRS= . DATATOPDIR = $(datadir)/$(PACKAGE_NAME)/scripts FLISTS = e2guardian logrotation bsd-init\ solaris-init systemv-init\ e2guardian.service EXTRA_DIST = e2guardian.in logrotation.in bsd-init.in\ solaris-init.in systemv-init.in\ e2guardian.service.in install-data-local: $(mkinstalldirs) $(DESTDIR)$(DATATOPDIR) && \ for l in $(FLISTS) ; do \ echo "$(INSTALL_DATA) $$l $(DESTDIR)$(DATATOPDIR)/$$l"; \ $(INSTALL_DATA) $$l $(DESTDIR)$(DATATOPDIR)/$$l; \ done uninstall-local: for l in $(FLISTS) ; do \ rm -f $(DESTDIR)$(DATATOPDIR)/$$l ; \ done e2guardian-5.5.8r/data/scripts/bsd-init.in000077500000000000000000000014051477372360500204160ustar00rootroot00000000000000#!/bin/sh # # BSD startup script for e2guardian # partly based on httpd startup script # # description: A web content filtering plugin for web \ # proxies, developed to filter using lists of \ # banned phrases, MIME types, filename \ # extensions and PICS labelling. # processname: e2guardian # See how we were called. case "$1" in start) [ -x @E2BINDIR@/e2guardian ] && @E2BINDIR@/e2guardian > /dev/null && echo -e ' e2guardian\c' ;; stop) @E2BINDIR@/e2guardian -q [ -r /tmp/.dguardianipc ] && echo -e ' e2guardian\c' rm -f /tmp/.dguardianipc ;; restart) $0 stop $0 start ;; *) echo "Usage: configure {start|stop|restart}" >&2 ;; esac exit 0 e2guardian-5.5.8r/data/scripts/e2guardian.in000077500000000000000000000002741477372360500207310ustar00rootroot00000000000000@E2LOGLOCATION@/access.log { rotate 5 daily prerotate service e2guardian stop > /dev/null 2>&1 || true endscript postrotate service e2guardian start > /dev/null 2>&1 endscript } e2guardian-5.5.8r/data/scripts/e2guardian.service.in000066400000000000000000000004571477372360500223700ustar00rootroot00000000000000[Unit] Description=E2guardian Web filtering After=network.target [Service] TasksMax=infinity LimitNOFILE=65535 LimitSTACK=infinity:infinity Type=forking IgnoreSIGPIPE=no GuessMainPID=no ExecStart=@E2BINDIR@/e2guardian ExecReload=@E2BINDIR@/e2guardian -r UMask=027 [Install] WantedBy=multi-user.target e2guardian-5.5.8r/data/scripts/logrotation.in000077500000000000000000000007221477372360500212470ustar00rootroot00000000000000#!/bin/sh # E2Guardian logrotation script for version @PACKAGE_VERSION@ LOG_DIR=@E2LOGLOCATION@ NUM_LOGS=4 LOG=$LOG_DIR/access.log @E2BINDIR@/e2guardian -q # Keep a maximum of $NUM_LOGS logs around. if [ -f $LOG.$NUM_LOGS ]; then rm -f $LOG.$NUM_LOGS; fi n=$(( $NUM_LOGS - 1 )) while [ $n -gt 0 ]; do if [ -f $LOG.$n ]; then mv $LOG.$n $LOG.$(( $n + 1 )) fi n=$(( $n - 1 )) done if [ -f $LOG ]; then mv $LOG $LOG.1 fi sleep 5 @E2BINDIR@/e2guardian e2guardian-5.5.8r/data/scripts/solaris-init.in000077500000000000000000000021651477372360500213260ustar00rootroot00000000000000#!/bin/sh # # Solaris startup script for e2guardian # partly based on httpd startup script # # description: A web content filtering plugin for web \ # proxies, developed to filter using lists of \ # banned phrases, MIME types, filename \ # extensions and PICS labelling. #ident "@(#)e2guardian @PACKAGE_VERSION@ 02/08/05 DB" # See how we were called. case "$1" in start) if [ -f @E2BINDIR@/e2guardian ] && [ -f @E2CONFDIR@/e2guardian.conf ]; then @E2BINDIR@/e2guardian echo "E2Guardian started." fi ;; stop) if [ -f @E2PIDDIR@/e2guardian.pid ]; then @E2BINDIR@/e2guardian -q /bin/rm -f @E2PIDDIR@/e2guardian.pid /bin/rm -f /tmp/.dguardianipc echo "E2Guardian stopped." fi ;; restart) $0 stop sleep 3 $0 start ;; status) if [ -f @E2BINDIR@/e2guardian ]; then @E2BINDIR@/e2guardian -s fi ;; *) echo "Usage: $0 {start|stop|restart}" >&2 ;; esac exit 0 e2guardian-5.5.8r/data/scripts/systemv-init.in000077500000000000000000000066561477372360500213750ustar00rootroot00000000000000#!/bin/sh # # Startup script for e2guardian # # chkconfig: 35 92 8 # description: A web content filtering plugin for web \ # proxies, developed to filter using lists of \ # banned phrases, MIME types, filename \ # extensions and PICS labelling. # processname: e2guardian # pidfile: @E2PIDDIR@/e2guardian.pid # config: @E2CONFDIR@/e2guardian.conf ### BEGIN INIT INFO # Provides: e2guardian # Required-Start: squid # Should-Start: # Required-Stop: squid # Should-Stop: # Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: E2guardian web content filter # Description: E2guardian web content filter ### END INIT INFO # File includes changes by Thomas Jarosch function wait_for_pid() { local PID=$1 local RET=0 if [ $PID -eq 0 ] ; then return $RET fi # give 60 secs then KILL local COUNTDOWN=60 while [ -d /proc/${PID} ] && [ $COUNTDOWN -gt 0 ] ; do sleep 1 COUNTDOWN=$[$COUNTDOWN-1] done if [ -d /proc/${PID} ]; then COMMAND=`ps h -o command ${PID}` logger "e2guardian: timeout waiting for PID ${PID}: ${COMMAND}; sending SIGKILL" kill -KILL $PID >/dev/null 2>&1 RET=1 fi return $RET } # See how we were called. case "$1" in start) if [ -f @E2BINDIR@/e2guardian ] && [ -f @E2CONFDIR@/e2guardian.conf ]; then echo -n "Starting e2guardian: " if @E2BINDIR@/e2guardian 2> /dev/null; then echo -e "\\033[60G\c" echo -e "[ \\033[1;32m\c" echo -e "OK\c" echo -e "\\033[0;39m\c" echo " ]" [ -d /var/lock/subsys ] && touch /var/lock/subsys/e2guardian else echo -e "\\033[60G\c" echo -e "[ \\033[1;31m\c" echo -e "FAILED\c" echo -e "\\033[0;39m\c" echo " ]" fi fi ;; stop) echo -n "Shutting down e2guardian: " WAITPID=0 if [ -f @E2PIDDIR@/e2guardian.pid ] ; then WAITPID=`cat @E2PIDDIR@/e2guardian.pid` fi if @E2BINDIR@/e2guardian -q 2> /dev/null; then if wait_for_pid $WAITPID ; then echo -e "\\033[60G\c" echo -e "[ \\033[1;32m\c" echo -e "OK\c" echo -e "\\033[0;39m\c" echo " ]" else echo -e "\\033[60G\c" echo -e "[ \\033[1;31m\c" echo -e "FAILED\c" echo -e "\\033[0;39m\c" echo " ]" fi /bin/rm -f @E2PIDDIR@/e2guardian.pid /bin/rm -f /tmp/.dguardianipc [ -d /var/lock/subsys ] && /bin/rm -f /var/lock/subsys/e2guardian else echo -e "\\033[60G\c" echo -e "[ \\033[1;31m\c" echo -e "FAILED\c" echo -e "\\033[0;39m\c" echo " ]" fi ;; restart) $0 stop $0 start ;; status) if [ -f @E2BINDIR@/e2guardian ]; then @E2BINDIR@/e2guardian -s fi ;; *) echo "Usage: $0 {start|stop|restart|status}" >&2 ;; esac exit 0 e2guardian-5.5.8r/data/transparent1x1.gif000077500000000000000000000000671477372360500202530ustar00rootroot00000000000000GIF89a!,ڋ>;e2guardian-5.5.8r/doc/000077500000000000000000000000001477372360500145175ustar00rootroot00000000000000e2guardian-5.5.8r/doc/AuthPlugins000077500000000000000000000115051477372360500167120ustar00rootroot00000000000000Authentication plugins ====================== See the "Plugins" document for a more general overview of plugin implementation. An "authentication plugin" in e2Guardian encapsulates the functionality necessary for identifying users and assigning them to groups. To perform user identification, authentication plugins are given access to both the client and server (parent proxy) connections and the HTTP request headers from the client; a number of meaningful return values allow multiple authentications to be loaded at once, and queried in order. When an authentication plugin successfully identifies a user, the returned string is passed back to the plugin to allow it to determine their filter group number. The AuthPlugin base class ========================= The AuthPlugin class provides the following methods: - init(): Called just after construction, allowing the plugin to load in any custom lists, etc. See the "ip.cpp" plugin for an example. Should return 0 on success, less than 0 on error, or greater than 0 to indicate a non-fatal warning condition. Default implementation just returns 0. - quit(): Called just before destruction. Interpretation of return values is the same as for init(). Default implementation just returns 0. - identify(): Called when the application wants to know the username, typically just after the client HTTP headers have been retrieved and before anything has yet been sent to the parent proxy (unless a badly-behaved auth plugin did something to the connections, failed, but gave a return value allowing plugins beneath it to continue being queried). Default implementation is a pure virtual, so this function MUST be implemented in plugins, if nothing else. Valid return values are as follows: - DGAUTH_OK: Success; a username was found. Put the username in the "string" parameter ("std::string& string"). - DGAUTH_NOMATCH: Necessary information was not found; continue querying subsequent plugins. Example: NTLM plugin was queried when the client was not initiating an NTLM handshake. - DGAUTH_REDIRECT: The user should be sent an HTTP redirect to the address specified in "string". Useful for integration with external web-based authentication services. - Any return code less than zero indicates error. Connection will be terminated. - determineGroup(): Called after a call to identify() has returned DGAUTH_OK. The string returned from identify() is passed in, along with an integer reference into which the user's numeric filter group (starting at 0 for filter1) should be placed on success. Default implementation looks at the username/group pairs specified in the "filtergroupslist" file; for an example of overriding this behaviour, look at "ip.cpp". Return values: - DGAUTH_OK: Success; a group number was found. Put the group number in the "fg" parameter ("int& fg"). - DGAUTH_NOMATCH: No group information was found for the user, continue querying subsequent plugins. Bear in mind that subsequent plugins may not be guaranteed to function correctly if a plugin has performed some action on the client or server connections during identify() prior to returning this. - DGAUTH_NOUSER: The plugin did not fail, but there is no group mapping for the given user. Place the user in group 0 (filter1) and do not query subsequent plugins. - Any return code less than 0 indicates error. Connection will be terminated. Considerations ============== In ideal conditions, the auth plugins are queried with connections in their "virgin" states; i.e. the HTTP request headers have been retrieved, but no further I/O has been performed. Any plugin seeking to implement a multi-stage handshake should complete ALL stages of the handshake before returning control to the calling code - see the NTLM plugin for an example of this. Any plugin that sends HTTP headers to the upstream proxy, or that otherwise makes use of the client IP, should be sure to implement the behaviour of the "forwardedfor" and "usexforwardedfor" options unless there is good reason not to do so. See the NTLM and IP plugins for examples, in particular the usage of the HTTPHeader::getXForwardedForIP() and HTTPHeader::addXForwardedFor() functions. Ideas for the future ==================== A commonly requested feature in DansGuardian is LDAP integration for retrieving group mappings. This feature is present in SmoothGuardian, SmoothWall Limited's commercially-supported version, but relies upon integration with an external, proprietary authentication daemon. One elegant way of implementing this for the GPL release would be to create an authentication "meta-plugin": one that implemented an LDAP-capable determineGroup() method, but that was capable of containing instances of other plugins to make use of their identify() methods. That way, LDAP integration would "just work" for all existing plugins. This is left as an exercise for the reader. -- Phil A. philip.allison/smoothwall.net e2guardian-5.5.8r/doc/ContentScanners000077500000000000000000000141611477372360500175570ustar00rootroot00000000000000Content scanning plugins ======================== See the "Plugins" document for a more general overview of plugin implementation. Content scanning plugins are tasked with scanning content and giving a quick clean/not clean answer. They are queried as a last step, after all other forms of filtering; this is such that content from sites in the exceptionsitelist can still be scanned, and time is not wasted performing content scanning on content blocked by simpler filtering rules. Support for modification of content has not been implemented. The original focus for content scanners is performing anti-virus scanning; this is reflected in the naming of some of the return value constants, the default wording of a few translation strings, and in the decision to block content if scanning fails (as it may be harmful). In similar fashion to how not all methods of download management are suitable for all HTTP clients, not all file types are suitable for content scanning. In particular, streaming audio and video cannot be scanned using the current implementation, as it relies upon downloading content in its entirety before scanning it. (Where AV is concerned, scanning of partial content is not generally useful or supported anyway.) Matching mechanisms =================== To allow content to be exempted from scanning, either for administrative reasons or to work around "unscannable" content, plugins are queried as to whether or not they wish to scan an object. This happens after the HTTP response headers have been retrieved, but before the content itself - this is because download requirements change if an object is to be scanned. Typically, a non-text file such as a JPEG would be streamed directly to the client; if it needs scanning, it must be downloaded locally and scanned before forwarding to the client (hence the need for download management - see the DownloadManagers document). In the base class, support has been implemented for exempting content from scanning based on domain name, URL, extension or MIME type. The CSPlugin base class ======================= The CSPlugin base class provides the following methods: - init(): Called just after construction, allowing the plugin to perform initialisation steps and return a value (which constructors can't do). Should return 0 for success, less than 0 on error, and greater than 0 to indicate a non-fatal warning. Default implementation reads in the standard exception domain, URL, extension and MIME type lists. - quit(): Called just before destruction. Interpretation of return values is the same as for init(). Default implementation just returns 0. - scanTest(): Called to ask a plugin whether or not it would like to scan a particular object. This happens after the HTTP response headers have been retrieved, but before any of the actual content. The request headers, response headers, and the client's username, IP and filter group number are passed in. Return E2CS_NOSCAN if the plugin does not wish to scan the content, and E2CS_NEEDSCAN if it does. Any return code < 0 signifies error. Default implementation checks the exceptionvirusmimetypelist, exceptionvirusextensionlist, exceptionvirussitelist and exceptionvirusurllist, returning E2CS_NOSCAN if it any of these is matched. - scanFile(): Called to get a plugin to scan a temporary file containing some downloaded content. The request headers, response headers, user details (username, filter group and IP) and filename are passed in. The plugin is free to process the file in any way it sees fit, provided that the file is treated as read-only. Return values are: E2CS_CLEAN for clean files, E2CS_INFECTED for infected/otherwise undesirable files, and E2CS_SCANERROR or any other value less than 0 for error. This function is pure virtual in the base class, so it MUST be implemented by plugins. - scanMemory(): Called to get a plugin to scan an in-memory buffer containing some downloaded content. Parameters are similar to scanFile(), except the filename is replaced with a pointer to the buffer (type "const char*") and the buffer length. Return values are the same as for scanFile(). Default implementation writes the buffer to disk (using writeMemoryTempFile()), calls scanFile() on the resulting file, then deletes the file and returns the return value of scanFile(). - getLastVirusName(): After scanFile() or scanMemory() has returned E2CS_INFECTED, this function may be called to retrieve the virus name (or other reason for denial), for display and logging purposes. The default implementation simply returns the value of the "lastvirusname" data member. - getLastMessage(): After scanFile() or scanMemory() has returned E2CS_SCANERROR or less than 0, this function may be called to retrieve the error message, for display and logging purposes. The default implementation simply returns the value of the "lastmessage" data member. Protected methods: - readStandardLists(): Reads in the exception site, URL, extension and MIME type lists. Returns true on success. - makeTempFile(): Creates a temporary file in filecachedir. Returns -1 on failure, or on success, returns an open file descriptor and puts the file's name in the passed-in String object. - writeMemoryTempFile(): Writes data from the given buffer into a temporary file. On success, returns E2CS_OK and fills in the given String object with the filename; returns E2CS_ERROR on failure. Uses makeTempFile() internally. Considerations ============== Although client connections are kept alive by download management plugins during download, nothing is being sent to the client during scanning itself. For this reason, and also for general performance reasons (bearing in mind that multiple users may be using the filter concurrently), content scanners should endeavour not to perform overly time-consuming processing. If possible, the value of "contentscannertimeout" (o.content_scanner_timeout) should be obeyed in your scanFile() and scanMemory() implementations. The return value to use on timeout is entirely up to the implementor and the nature of scanning performed by the plugin, considering - for example - whether or not failure to scan files is a potential security risk. -- Phil A. philip.allison/smoothwall.net e2guardian-5.5.8r/doc/DownloadManagers000077500000000000000000000156721477372360500177050ustar00rootroot00000000000000Download management plugins =========================== See the "Plugins" document for a more general overview of plugin implementation. Download managers are tasked with preventing client connections from timing out whilst large files are downloaded for virus scanning, bearing in mind that it is not acceptable to send the client the (whole) file before scanning has completed. Exactly what is sent in lieu of the actual download is up to the plugin implementation. Given that interrupting the normal HTTP download process in this manner leaves clients unable to report download speeds and progress in the usual way, download managers may wish to present this information themselves in an alternative way. However, non-interactive HTTP clients - such as wget - will interpret any information sent to them as the content of the download itself, as they are unable to display HTML, interpret JavaScript, etc. Therefore, just as it is important to let users know that their downloads haven't stalled, it is equally important for any manager that breaks the normal HTTP download process to stay out of the way when this is not appropriate. Matching mechanisms =================== There are two standard mechanisms by which downloads are paired to a handler: a regular expression applied to the User-Agent string, and a set of extensions/MIME types to be handled. These are both optional, but their intended use is: - Prevent text-only or non-JavaScript-capable browsers from having downloads handled by a download manager which relies on JavaScript, such as the "fancy" plugin. - Prevent the download manager being triggered for "in-line" files; for example, a browser receiving an HTML progress bar instead of a page's title JPEG will be unable to render its content correctly. When something is downloaded and needs to be virus scanned, details of the query are passed to the download managers in the order in which they are specified (in e2guardian.conf), and the download handled by the first manager that agrees to do so. The last download manager to be loaded is treated as a fall-back, and always handles any downloads not taken by preceding plugins; therefore, only managers which do not break the standard download process (default, trickle) are recommended for use as default. The DMPlugin base class ======================= The DMPlugin class provides the following methods: - init(): Called just after construction, allowing the plugin to perform initialisation steps and return a value (which constructors can't do). Should return 0 for success, less than 0 on error, and greater than 0 to indicate a non-fatal warning. Default implementation compiles the User-Agent regular expression and reads in the extension and MIME type lists (if enabled). - quit(): Called just before destruction. Interpretation of return values is the same as for init(). Default implementation just returns 0. - willHandle(): Called to determine whether or not a download that needs virus scanning should be managed by this plugin. Passed in the original request and response headers (the actual object has obviously, at this point, not yet been downloaded). Return true to handle, or false to defer to subsequent plugins. Default implementation checks the client's user-agent against the given regular expression, and the file's MIME type and extension against the type lists, if enabled. - in(): Download the actual file. Called with the DataBuffer into which the object should be placed, the client and server (parent proxy) connections, the HTTP request and response headers, a flag indicating whether or not the file is being downloaded for virus scanning, and pointers to the current client header status (i.e. what portion, if any, of the response headers have already been sent to the client) and a boolean which should be set if the entire file is not downloaded. - sendLink(): Called after download if the file is not blocked, and if the in() method set the "dontsendbody" flag on the given DataBuffer. Download managers such as "fancy", which send the client content other than the file being requested, cannot function by appending the file content to what the client has already received; instead, they need a mechanism of providing a file download link which is compatible with whatever UI has been presented. Protected methods: - readStandardLists(): Read in the standard extension and MIME type lists based on the "managedextensionlist" and "managedmimetypelist" options in the plugin's configuration file. Returns 0 on success, less than 0 on error. Called from the default init() method; you only need to be concerned with calling this if your plugin overrides the init() method, yet you wish to use the default willHandle() (or a replacement which still makes use of the lists). Data members: - mimetypelist and extensionlist: ListContainers of MIME types and extensions which this plugin should handle. Set up by readStandardLists(). - mimelistenabled and extensionlistenabled: Flags indicating whether or not each list is enabled. Set up by readStandardLists(). You should only need to concern yourself with these members if you are overriding init() or willHandle(). Considerations ============== Download managers are complex. There are four "core" options which they can, and should, take into account: - maxcontentramcachescansize, above which the file should start being stored on disk; maxcontentfilecachescansize, above which the file will no longer be virus scanned (and so may or may not have its download halted) - maxcontentfiltersize, above which the file will no longer be phrase filtered (obey if the file is not being downloaded for AV scanning) - initialtrickledelay, the initial delay in seconds before *anything* should be sent to the client (such that smaller files can be downloaded without interrupting the standard HTTP download process) - trickledelay, the delay in seconds between sending portions of data (dummy headers, HTML, whatever the DM uses to stop the client timing out) Handling all the various situations robustly is an awkward task, not helped by the fact that the DataBuffer class itself contains various data members which must be kept up to date. Creating a new download manager from scratch is therefore not recommended; the source of the existing download managers should be studied before attempting this, to see which variables and flags should be updated in which cases, and it may be wise to use existing source as a starting point. Luckily, it is not likely that new download managers will need to be created: there is one which does not send any parts of the file (default), one which sends real data but holds back part of it until scanning is complete (trickle), and one which sends something completely different, followed eventually by a link from which the actual file can be retrieved (fancy). Whilst these may contain bugs, requiring entirely new methods of download management is not likely, as it is a theme on which there are not many useful variations IMHO. -- Phil A. philip.allison/smoothwall.net e2guardian-5.5.8r/doc/FAQ000077500000000000000000000445751477372360500150730ustar00rootroot00000000000000Q. Do I have to use Squid? Can other software be used as the parent proxy? A. In theory you can use any old HTTP proxy, however DG is only regularly tested with Squid. Q. Is the content (anti-virus) scanning support in DansGuardian 2.10 and above related to the "DansGuardian Anti-Virus Plugin" (DGAV) project? A. There are certain common elements, such as the naming of some of the virus scanning plugins and configuration options, but code itself has not been taken from the project, primarily for legal reasons (both projects are released under the GPL, but code contributed to anything other than DansGuardian itself has different ownership). With regards to the 2.8 series, DGAV is certainly viewed as a friendly fork. Q. How do I set up virus scanning? A. Make sure DG is built with at least one content scanning plugin enabled ("--enable-clamd", "--enable-kavd", "--enable-icap", or "--enable-commandline"). After compilation and installation, uncomment the relevant "contentscanner" line in e2guardian.conf to enable loading the plugin. Also take a look in the plugin's own configuration file, the path of which is in the "contentscanner" option, and perform any necessary customisations - for example, settings the "clamdudsfile" option in clamdscan.conf. For new installations, the "clamdscan" plugin is recommended; this requires you to have ClamD running on the same machine as DG, but is flexible (using ClamD's own configuration file for extra control) and simple to set up. Q. Can the clamdscan plugin use a remote instance of ClamD? A. Not currently, no. The primary reason for this limitation is that by restricting it to local instances only, content does not have to be sent over the network for scanning. This could be achieved with the current version either by hooking up ClamD to an ICAP server and using DG's ICAP plugin. (Another potential method, which hasn't been tested, might be putting DG's filecachedir on NFS and using something like socat (http://www.dest-unreach.org/socat/) to mirror a remote ClamD's UNIX socket.) Q. Why do downloads take a long time to start when using virus scanning? A. Because DG has to download the whole file and perform scanning before it can be sent on to the client. Keep-alive data is sent to clients during this process by "download manager" plugins: "default" simply sends dummy HTTP headers, whilst "fancy" sends a full-blown HTML/JavaScript download progress bar (see the included DownloadManagers document for more info on how a manager is chosen for a given transfer). Q. Why doesn't streaming media work when using virus scanning? A. See above - whole files have to be downloaded before virus scanning can be performed. To DG, streaming media looks like either a single large file, or - in the case of "never ending" streams such as Icecast radio - an "infinitely large" file. In both cases, the streaming server will not have reported the size of the content in its HTTP response headers, so DG does not have any basis for immediately deciding that the file is too large. It will resort to downloading up to "maxcontentfilecachescansize" bytes of data (the maximum amount of data it is allowed to cache to a temporary file on disk) with the intention of performing a virus scan, after which it will give up and forward all data to the client, streaming any extra data that may arrive. The virus exception lists - exceptionvirusmimetypelist, exceptionvirussitelist, etc. - will need to be used to let DG know not to scan streaming media. The default extension and MIME type lists contain a variety of entries which should cover most types of media, but they are not exhaustive. Streaming media accessed over HTTP can be argued to be an abuse of the protocol; any media accessed via dedicated streaming protocols will continue to function as normal, as it is not proxied by DG. Note that some (older?) Icecast servers do not actually output standard HTTP response headers, and notably do not output either a file extension or a MIME type, meaning that the media can only be excepted from scanning by matching the domain/URL. Q. Is there any way to send partial files to clients whilst a file is being virus scanned? DGAV does this and I miss it. A. If you have HTTP clients that don't work even with the "default" download manager, or really, really want to give end users the impression that files are downloading, then build and enable the "trickle" plugin. This slowly sends parts of not-yet-scanned files to clients, which enables them to report download progress (although reported progress will be slower than the actual download), and only lets the transfer complete for clean files. Note that there is some debate as whether or not this is entirely safe, as some malware is incredibly small, and some files can be processed before they are completely downloaded, e.g. progressive JPEGs. This explains the decision not to enable this mode by default. Q. There appear to be 2 content scanning plugins that invoke ClamAV ("clamdscan" and "commandlinescan"). What is the difference, and which should I be using? A. The "clamdscan" scanner saves all content to disk, and passes filenames to an instance of ClamD. The example configuration for "commandlinescan" achieves the same thing, but does so by invoking an external command-line utility instead of communicating with ClamD directly. This method should not be used to invoke ClamD - it *will* be slower than the "clamdscan" plugin, and is purely intended as an example of how to use "commandlinescan". Q. What happened to the "clamav" content scanning plugin? A. It has been removed. This plugin used the ClamAV library directly, as opposed to the "clamdscan" plugin's usage of a running instance of ClamD. However, it was awkward to maintain, provided no performance benefit (since the data still had to be saved to disk before scanning), didn't give full control over ClamAV's options, and generally served only to confuse people. Q. How do I know whether or not something was virus scanned? OR: I have my AV software set to ignore some file types, but DG is claiming the files have been scanned. A. Any content that has been scanned is marked with "*SCANNED*" in DG's access log. Technically, this means it was sent to the loaded content scanning plugins; what these and any external software they may rely on actually did with it is entirely their business. For example, an ICAP server may be configured not to virus scan JPEGs, but unless DG itself is configured similarly, it will send them to it all the same, and log that it has done so. Q. How do I set up multiple filter groups? A. Duplicate the e2guardianf1.conf file, so that there is one per group (e2guardianf2.conf, e2guardianf3.conf, etc.). Set the "filtergroups" parameter in e2guardian.conf accordingly. Duplicate the list files referenced by the filter group config - "bannedsitelist", "bannedurllist" etc. - once per group, if you wish to customise them separately. (These are the "top-level" list files, in that they mostly consist of include statements for other lists.) Uncomment one of the "authplugin" lines in e2guardian.conf and, if choosing proxy-basic or proxy-ntlm, configure Squid to require basic or NTLM authentication accordingly. Fill in the username/group mappings in the "filtergroupslist" file. If you wish to enforce authentication, map all users to groups higher than 1, and set group 1's groupmode to 0 (banned), as this is the default group for unidentified users. Q. How do I assign users to groups when using the "ip" auth plugin? A. Use the "/etc/e2guardian/lists/authplugins/ipgroups" file. This is separate from the filtergroupslist because it has a different syntax, catering for IP range and subnet matches instead of static usernames, and is only used by the one plugin. Q. I cannot use the exceptionuserlist and banneduserlist any more. What happened? A. These lists were abandoned when the "groupmode" parameter was added to the filter group configuration (e2guardianf*.conf). Banned and exception IP lists still exist, but they are intended for identifying particular machines, not people, and primarily for temporary use only - for example, quickly banning access from a spyware-infested machine, or providing unfiltered access for servers needing to download updates. If you want full identification by IP, use the "ip" auth plugin and multiple filter groups. Q. Looking in Squid's access.log, all entries have the client IP given as the box running DansGuardian (typically 127.0.0.1). How can I log the original client IPs? A. Turn on the "forwardedfor" option in e2guardian.conf, and get Squid to obey the "X-Forwarded-For" header in incoming requests. For Squid 2.5, you will have to apply the following patch: http://devel.squid-cache.org/follow_xff/follow_xff-2.5.patch This has become a core feature in Squid 2.6-STABLE1 and above. Be wary of also enabling the "usexforwardedfor" option in DG, as it is trivial for clients to spoof headers in the original request. You can limit client with "xforwardedforfilterip" option. Q. I used to use a "sandwich" configuration (Squid -> DG -> Squid) to implement NTLM support; how do I migrate to DG's native NTLM support? A. Disable the first Squid entirely, and configure the second Squid for NTLM instead of "basic" authentication. Make sure you configure your DG build with "--enable-ntlm" (enabled by default), and uncomment the "authplugin" line in e2guardian.conf corresponding to the "proxy-ntlm" plugin. Q. How can I disable persistent connections? Will this break NTLM? A. Disable "client_persistent_connections" in Squid to prevent clients making a persistent connection to the proxy. DansGuardian itself has no related configuration options, as it simply follows the relevant RFCs as best it can, obeying the instructions in the request and response headers (i.e. if Squid disallows persistency, so does DG). This won't break NTLM: it is true that NTLM-over-HTTP requires persistent connections, but Squid always allows persistency during the auth handshake. You can also disable "server_persistent_connections" if you don't want Squid to make persistent connections to origin servers; these are completely de-coupled from persistent connections to clients. Q. Can users be a member of more than one filter group? A. No, they cannot. A filter group in DG is a self-contained set of options, not something that can be stacked. If all you really want is to share customised lists between groups, create them as separate files, and put include statements for them in the top-level lists for all applicable groups. Q. How does DansGuardian verify passwords? Or: why am I getting authentication prompts with no authplugins enabled? A. DansGuardian does not verify passwords, and will never initiate an authentication handshake on its own. As per the "basic" auth support in the 2.8 series, DG simply passes authentication requests from Squid to the browser, and sniffs the username from the credentials the browser sends back. It is Squid that supplies the authentication request, and Squid that performs the password checking; it does such a good job that we see no reason to re-invent that particular wheel. Q. Is it possible to disable content filtering, and perform AV scanning only? A. Yes! There are two options; the easy way, and the hard way. The hard way consists of turning off the individual filtering options one by one: setting "weightedphrasemode = 0", blanking the bannedsitelist, etc. The easy way is simply to set "groupmode = 2" in your filter group configuration files (e2guardianf*.conf), and enable "contentscanexceptions" in e2guardian.conf. Q. Can I use DG as a pure URL filter? A. Yes, although you will be missing out on a lot. Set the "weightedphrasemode" to 0, comment out any "contentscanner" lines, and truncate the banned/exception MIME type, extension and regexp lists. Q. How does DG identify adverts? A. Adverts are identified on the basis of the string "ADs" appearing in the categories under which the site is blocked. For example, a URL list containing '#listcategory "ADs"' will identify its contents as adverts. Q. Why is the HTML block page not shown when blocking adverts? A. This is primarily so as not to disrupt the rendering of pages containing adverts in IFRAMEs. Showing the block page in an IFRAME would look just as distracting as the advert itself, and may break page layout. Q. How can I embed images into the HTML template? A. If customising the template to include images, you will need to run a webserver on your LAN to host them, and ensure that you use absolute URLs in your image tags. DG is not a webserver; even if you put the images in the same directory as the HTML template, it will not be capable of serving them. Q. How do I use reportinglevel 2? A. You need to run a webserver to host your custom block page/script, and configure DG's "accessdeniedaddress" to point at it. You cannot point the "accessdeniedaddress" at the filter IP and port; DG is not a webserver, it is only capable of serving its built-in HTML template. Q. How do I force users to enter a username and password to use the filter bypass feature? A. To do this, you need to be using reportinglevel 2, and create a CGI or similar for generating your own bypass URLs. Set the "bypass" option in your filter group configuration files to -1 to prevent DG generating bypass hashes automatically (as users would otherwise be able sniff these as they are passed to your block script, and bypass the authentication), and set a static "bypasskey" option, as this secret is used during both hash generation and verification to prevent forgery. Once you have your external bypass URL generator working, you can implement any desired form of web-based authentication as a required step before giving out bypass URLs. A basic summary of what actually happens is as follows: 1. The user requests a webpage, and DG determines that it is blocked. 2. DG sends a redirect to the user's browser, pointing them at the URL configured in "accessdeniedaddress", and passing in various bits of information (denied site, reason, etc.) as URL parameters. 3. The user clicks the bypass link/button on your custom block page, which forwards them to your hash generation CGI (also forwarding the necessary info from DG, either as URL or form parameters). 4. The user is forced to enter a username and password, either by Apache htaccess or some other mechanism. 5. Assuming the user authenticates successfully, the hash generator runs, and redirects the user's browser straight to the bypass URL and/or presents it to them as a link. More information can be found at http://contentfilter.futuragts.com/wiki/index.php?title=Bypass_Hash_Usage Q. How can I set up time-limited exceptions, e.g. allow webmail access during lunchtime? A. Create a new site or URL list containing a "#time:" directive (documentation for this can be found in the default bannedsitelist file). Add an include statement (".Include") for your new list to the top-level banned or exception site/URL list, e.g. exceptionurllist, depending on whether you want to create bans or exceptions. The contents of your new file will only be included in the list at the times specified in the time directive. Q. Why don't I see HTTPS requests in DG's access log? A. If you are using transparent/interception proxying, you won't, as HTTPS cannot be proxied in this manner due to the end-to-end encryption. Otherwise, first check to see if such requests are showing up in Squid's log - if not, then again, the requests probably aren't going through the filter. If the requests do show up in Squid's log, check DG's "loglevel" setting. The default is 2, which logs all requests for textual content (i.e. HTML but not images); the MIME type of HTTPS requests is not sent in the clear, and so they are not logged since it is not known whether or not content is text. Q. Why don't I get the full HTML template when an HTTPS request is blocked? Can I customise the message returned? A. Because some browsers cannot handle unencrypted error pages of more than a few bytes in response to an HTTPS request. Currently this message can only be customised by modifying source code. Q. Help! I have DansGuardian installed on my gateway, and strangers are using my proxy/filter. How do I stop them? A. Use a firewall (iptables) to prevent external connections to DG and Squid's listening ports, and/or configure DG and Squid to listen only on LAN interfaces. In DG, a non-blank "filterip" line causes DG to bind its listening socket to the given IP; multiple "filterip" lines can be given if needed. Squid allows specification of bind IPs in the "http_port" directive. Also note that if you do not have any machines/users on your network which are allowed to bypass the filter, then Squid only needs to be configured to listen on the loopback interface (127.0.0.1). Q. DG seems to be very slow to access a site initially, but repeated accesses to the same site are fine. What's going on? A. You quite possibly have DNS performance issues. Enable the cache manager in Squid and monitor the average time for DNS requests; ideally values should be in the low single figures. One possibility for reducing DNS load is disabling the "reverseaddresslookups" option in DG; however, people will be able to bypass the site/URL filtering (but not the content or virus filtering) by visiting websites by IP directly. Q. What permissions does DG need to create its PID file, access.log file and IPC sockets? A. DG creates and updates its PID file as root, so permissions should not be an issue here. However, it must be able to write to its access.log file as an unprivileged user: this is the user/group set by the "daemonuser" and "daemongroup" options; by default, "nobody:nobody". The IPC sockets (UNIX domain sockets) must be in a location writable by the unprivileged user; by default they are in "/tmp" and so not generally an issue. Q. I want to stop people from using MSN, ban filesharing, virus-scan their FTP transfers, put quotas on their email usage, remove all spyware and implement world peace! A. DansGuardian is an HTTP proxy. It does not speak any other protocol. FTP via HTTP proxy only works because Squid is capable of translating between the two; if you want to use DG as a "real" FTP proxy, for clients other than web browsers, it will not work. It is true that some IM clients can tunnel conversations over HTTP - DG doesn't implement any special features to detect or block this, but incoming messages may be content filtered if they are in the clear, and usage of such clients blocked based on the destination IPs/domains in the HTTP requests. e2guardian-5.5.8r/doc/FAQ.html000077500000000000000000000643061477372360500160300ustar00rootroot00000000000000 DansGuardian 2.10 FAQ

Contents

  1. Do I have to use Squid? Can other software be used as the parent proxy?
  2. Is the content (anti-virus) scanning support in DansGuardian 2.10 and above related to the "DansGuardian Anti-Virus Plugin" (DGAV) project?
  3. How do I set up virus scanning?
  4. Can the clamdscan plugin use a remote instance of ClamD?
  5. Why do downloads take a long time to start when using virus scanning?
  6. Why doesn't streaming media work when using virus scanning?
  7. Is there any way to send partial files to clients whilst a file is being virus scanned? DGAV does this and I miss it.
  8. There appear to be 2 content scanning plugins that invoke ClamAV ("clamdscan" and "commandlinescan"). What is the difference, and which should I be using?
  9. What happened to the "clamav" content scanning plugin?
  10. How do I know whether or not something was virus scanned? OR: I have my AV software set to ignore some file types, but DG is claiming the files have been scanned.
  11. How do I set up multiple filter groups?
  12. How do I assign users to groups when using the "ip" auth plugin?
  13. I cannot use the exceptionuserlist and banneduserlist any more. What happened?
  14. Looking in Squid's access.log, all entries have the client IP given as the box running DansGuardian (typically 127.0.0.1). How can I log the original client IPs?
  15. I used to use a "sandwich" configuration (Squid -> DG -> Squid) to implement NTLM support; how do I migrate to DG's native NTLM support?
  16. How can I disable persistent connections? Will this break NTLM?
  17. Can users be a member of more than one filter group?
  18. How does DansGuardian verify passwords? Or: why am I getting authentication prompts with no authplugins enabled?
  19. Is it possible to disable content filtering, and perform AV scanning only?
  20. Can I use DG as a pure URL filter?
  21. How does DG identify adverts?
  22. Why is the HTML block page not shown when blocking adverts?
  23. How can I embed images into the HTML template?
  24. How do I use reportinglevel 2?
  25. How do I force users to enter a username and password to use the filter bypass feature?
  26. How can I set up time-limited exceptions, e.g. allow webmail access during lunchtime?
  27. Why don't I see HTTPS requests in DG's access log?
  28. Why don't I get the full HTML template when an HTTPS request is blocked? Can I customise the message returned?
  29. Help! I have DansGuardian installed on my gateway, and strangers are using my proxy/filter. How do I stop them?
  30. DG seems to be very slow to access a site initially, but repeated accesses to the same site are fine. What's going on?
  31. What permissions does DG need to create its PID file, access.log file and IPC sockets?
  32. I want to stop people from using MSN, ban filesharing, virus-scan their FTP transfers, put quotas on their email usage, remove all spyware and implement world peace!

FAQ

^ 1. Do I have to use Squid? Can other software be used as the parent proxy?

In theory you can use any old HTTP proxy, however DG is only regularly tested with Squid.

^ 2. Is the content (anti-virus) scanning support in DansGuardian 2.10 and above related to the "DansGuardian Anti-Virus Plugin" (DGAV) project?

There are certain common elements, such as the naming of some of the virus scanning plugins and configuration options, but code itself has not been taken from the project, primarily for legal reasons (both projects are released under the GPL, but code contributed to anything other than DansGuardian itself has different ownership). With regards to the 2.8 series, DGAV is certainly viewed as a friendly fork.

^ 3. How do I set up virus scanning?

Make sure DG is built with at least one content scanning plugin enabled ("--enable-clamd", "--enable-kavd", "--enable-icap", or "--enable-commandline"). After compilation and installation, uncomment the relevant "contentscanner" line in e2guardian.conf to enable loading the plugin. Also take a look in the plugin's own configuration file, the path of which is in the "contentscanner" option, and perform any necessary customisations - for example, settings the "clamdudsfile" option in clamdscan.conf.

For new installations, the "clamdscan" plugin is recommended; this requires you to have ClamD running on the same machine as DG, but is flexible (using ClamD's own configuration file for extra control) and simple to set up.

^ 4. Can the clamdscan plugin use a remote instance of ClamD?

Not currently, no. The primary reason for this limitation is that by restricting it to local instances only, content does not have to be sent over the network for scanning. This could be achieved with the current version either by hooking up ClamD to an ICAP server and using DG's ICAP plugin. (Another potential method, which hasn't been tested, might be putting DG's filecachedir on NFS and using something like socat (http://www.dest-unreach.org/socat/) to mirror a remote ClamD's UNIX socket.)

^ 5. Why do downloads take a long time to start when using virus scanning?

Because DG has to download the whole file and perform scanning before it can be sent on to the client. Keep-alive data is sent to clients during this process by "download manager" plugins: "default" simply sends dummy HTTP headers, whilst "fancy" sends a full-blown HTML/JavaScript download progress bar (see the included DownloadManagers document for more info on how a manager is chosen for a given transfer).

^ 6. Why doesn't streaming media work when using virus scanning?

See above - whole files have to be downloaded before virus scanning can be performed. To DG, streaming media looks like either a single large file, or - in the case of "never ending" streams such as Icecast radio - an "infinitely large" file. In both cases, the streaming server will not have reported the size of the content in its HTTP response headers, so DG does not have any basis for immediately deciding that the file is too large. It will resort to downloading up to "maxcontentfilecachescansize" bytes of data (the maximum amount of data it is allowed to cache to a temporary file on disk) with the intention of performing a virus scan, after which it will give up and forward all data to the client, streaming any extra data that may arrive.

The virus exception lists - exceptionvirusmimetypelist, exceptionvirussitelist, etc. - will need to be used to let DG know not to scan streaming media. The default extension and MIME type lists contain a variety of entries which should cover most types of media, but they are not exhaustive.

Streaming media accessed over HTTP can be argued to be an abuse of the protocol; any media accessed via dedicated streaming protocols will continue to function as normal, as it is not proxied by DG. Note that some (older?) Icecast servers do not actually output standard HTTP response headers, and notably do not output either a file extension or a MIME type, meaning that the media can only be excepted from scanning by matching the domain/URL.

^ 7. Is there any way to send partial files to clients whilst a file is being virus scanned? DGAV does this and I miss it.

If you have HTTP clients that don't work even with the "default" download manager, or really, really want to give end users the impression that files are downloading, then build and enable the "trickle" plugin. This slowly sends parts of not-yet-scanned files to clients, which enables them to report download progress (although reported progress will be slower than the actual download), and only lets the transfer complete for clean files. Note that there is some debate as whether or not this is entirely safe, as some malware is incredibly small, and some files can be processed before they are completely downloaded, e.g. progressive JPEGs. This explains the decision not to enable this mode by default.

^ 8. There appear to be 2 content scanning plugins that invoke ClamAV ("clamdscan" and "commandlinescan"). What is the difference, and which should I be using?

The "clamdscan" scanner saves all content to disk, and passes filenames to an instance of ClamD. The example configuration for "commandlinescan" achieves the same thing, but does so by invoking an external command-line utility instead of communicating with ClamD directly. This method should not be used to invoke ClamD - it *will* be slower than the "clamdscan" plugin, and is purely intended as an example of how to use "commandlinescan".

^ 9. What happened to the "clamav" content scanning plugin?

It has been removed. This plugin used the ClamAV library directly, as opposed to the "clamdscan" plugin's usage of a running instance of ClamD. However, it was awkward to maintain, provided no performance benefit (since the data still had to be saved to disk before scanning), didn't give full control over ClamAV's options, and generally served only to confuse people.

^ 10. How do I know whether or not something was virus scanned? OR: I have my AV software set to ignore some file types, but DG is claiming the files have been scanned.

Any content that has been scanned is marked with "*SCANNED*" in DG's access log. Technically, this means it was sent to the loaded content scanning plugins; what these and any external software they may rely on actually did with it is entirely their business. For example, an ICAP server may be configured not to virus scan JPEGs, but unless DG itself is configured similarly, it will send them to it all the same, and log that it has done so.

^ 11. How do I set up multiple filter groups?

Duplicate the e2guardianf1.conf file, so that there is one per group (e2guardianf2.conf, e2guardianf3.conf, etc.). Set the "filtergroups" parameter in e2guardian.conf accordingly. Duplicate the list files referenced by the filter group config - "bannedsitelist", "bannedurllist" etc. - once per group, if you wish to customise them separately. (These are the "top-level" list files, in that they mostly consist of include statements for other lists.)

Uncomment one of the "authplugin" lines in e2guardian.conf and, if choosing proxy-basic or proxy-ntlm, configure Squid to require basic or NTLM authentication accordingly. Fill in the username/group mappings in the "filtergroupslist" file. If you wish to enforce authentication, map all users to groups higher than 1, and set group 1's groupmode to 0 (banned), as this is the default group for unidentified users.

^ 12. How do I assign users to groups when using the "ip" auth plugin?

Use the "/etc/e2guardian/lists/authplugins/ipgroups" file. This is separate from the filtergroupslist because it has a different syntax, catering for IP range and subnet matches instead of static usernames, and is only used by the one plugin.

^ 13. I cannot use the exceptionuserlist and banneduserlist any more. What happened?

These lists were abandoned when the "groupmode" parameter was added to the filter group configuration (e2guardianf*.conf). Banned and exception IP lists still exist, but they are intended for identifying particular machines, not people, and primarily for temporary use only - for example, quickly banning access from a spyware-infested machine, or providing unfiltered access for servers needing to download updates. If you want full identification by IP, use the "ip" auth plugin and multiple filter groups.

^ 14. Looking in Squid's access.log, all entries have the client IP given as the box running DansGuardian (typically 127.0.0.1). How can I log the original client IPs?

Turn on the "forwardedfor" option in e2guardian.conf, and get Squid to obey the "X-Forwarded-For" header in incoming requests. For Squid 2.5, you will have to apply the following patch: http://devel.squid-cache.org/follow_xff/follow_xff-2.5.patch

This has become a core feature in Squid 2.6-STABLE1 and above. Be wary of also enabling the "usexforwardedfor" option in DG, as it is trivial for clients to spoof headers in the original request. You can limit client with "xforwardedforfilterip" option.

^ 15. I used to use a "sandwich" configuration (Squid -> DG -> Squid) to implement NTLM support; how do I migrate to DG's native NTLM support?

Disable the first Squid entirely, and configure the second Squid for NTLM instead of "basic" authentication. Make sure you configure your DG build with "--enable-ntlm" (enabled by default), and uncomment the "authplugin" line in e2guardian.conf corresponding to the "proxy-ntlm" plugin.

^ 16. How can I disable persistent connections? Will this break NTLM?

Disable "client_persistent_connections" in Squid to prevent clients making a persistent connection to the proxy. DansGuardian itself has no related configuration options, as it simply follows the relevant RFCs as best it can, obeying the instructions in the request and response headers (i.e. if Squid disallows persistency, so does DG). This won't break NTLM: it is true that NTLM-over-HTTP requires persistent connections, but Squid always allows persistency during the auth handshake. You can also disable "server_persistent_connections" if you don't want Squid to make persistent connections to origin servers; these are completely de-coupled from persistent connections to clients.

^ 17. Can users be a member of more than one filter group?

No, they cannot. A filter group in DG is a self-contained set of options, not something that can be stacked. If all you really want is to share customised lists between groups, create them as separate files, and put include statements for them in the top-level lists for all applicable groups.

^ 18. How does DansGuardian verify passwords? Or: why am I getting authentication prompts with no authplugins enabled?

DansGuardian does not verify passwords, and will never initiate an authentication handshake on its own. As per the "basic" auth support in the 2.8 series, DG simply passes authentication requests from Squid to the browser, and sniffs the username from the credentials the browser sends back. It is Squid that supplies the authentication request, and Squid that performs the password checking; it does such a good job that we see no reason to re-invent that particular wheel.

^ 19. Is it possible to disable content filtering, and perform AV scanning only?

Yes! There are two options; the easy way, and the hard way. The hard way consists of turning off the individual filtering options one by one: setting "weightedphrasemode = 0", blanking the bannedsitelist, etc. The easy way is simply to set "groupmode = 2" in your filter group configuration files (e2guardianf*.conf), and enable "contentscanexceptions" in e2guardian.conf.

^ 20. Can I use DG as a pure URL filter?

Yes, although you will be missing out on a lot. Set the "weightedphrasemode" to 0, comment out any "contentscanner" lines, and truncate the banned/exception MIME type, extension and regexp lists.

^ 21. How does DG identify adverts?

Adverts are identified on the basis of the string "ADs" appearing in the categories under which the site is blocked. For example, a URL list containing '#listcategory "ADs"' will identify its contents as adverts.

^ 22. Why is the HTML block page not shown when blocking adverts?

This is primarily so as not to disrupt the rendering of pages containing adverts in IFRAMEs. Showing the block page in an IFRAME would look just as distracting as the advert itself, and may break page layout.

^ 23. How can I embed images into the HTML template?

If customising the template to include images, you will need to run a webserver on your LAN to host them, and ensure that you use absolute URLs in your image tags. DG is not a webserver; even if you put the images in the same directory as the HTML template, it will not be capable of serving them.

^ 24. How do I use reportinglevel 2?

You need to run a webserver to host your custom block page/script, and configure DG's "accessdeniedaddress" to point at it. You cannot point the "accessdeniedaddress" at the filter IP and port; DG is not a webserver, it is only capable of serving its built-in HTML template.

^ 25. How do I force users to enter a username and password to use the filter bypass feature?

To do this, you need to be using reportinglevel 2, and create a CGI or similar for generating your own bypass URLs. Set the "bypass" option in your filter group configuration files to -1 to prevent DG generating bypass hashes automatically (as users would otherwise be able sniff these as they are passed to your block script, and bypass the authentication), and set a static "bypasskey" option, as this secret is used during both hash generation and verification to prevent forgery. Once you have your external bypass URL generator working, you can implement any desired form of web-based authentication as a required step before giving out bypass URLs.

A basic summary of what actually happens is as follows:

1. The user requests a webpage, and DG determines that it is blocked.

2. DG sends a redirect to the user's browser, pointing them at the URL configured in "accessdeniedaddress", and passing in various bits of information (denied site, reason, etc.) as URL parameters.

3. The user clicks the bypass link/button on your custom block page, which forwards them to your hash generation CGI (also forwarding the necessary info from DG, either as URL or form parameters).

4. The user is forced to enter a username and password, either by Apache htaccess or some other mechanism.

5. Assuming the user authenticates successfully, the hash generator runs, and redirects the user's browser straight to the bypass URL and/or presents it to them as a link.

More information can be found at http://contentfilter.futuragts.com/wiki/index.php?title=Bypass_Hash_Usage

^ 26. How can I set up time-limited exceptions, e.g. allow webmail access during lunchtime?

Create a new site or URL list containing a "#time:" directive (documentation for this can be found in the default bannedsitelist file). Add an include statement (".Include<filename>") for your new list to the top-level banned or exception site/URL list, e.g. exceptionurllist, depending on whether you want to create bans or exceptions. The contents of your new file will only be included in the list at the times specified in the time directive.

^ 27. Why don't I see HTTPS requests in DG's access log?

If you are using transparent/interception proxying, you won't, as HTTPS cannot be proxied in this manner due to the end-to-end encryption. Otherwise, first check to see if such requests are showing up in Squid's log - if not, then again, the requests probably aren't going through the filter. If the requests do show up in Squid's log, check DG's "loglevel" setting. The default is 2, which logs all requests for textual content (i.e. HTML but not images); the MIME type of HTTPS requests is not sent in the clear, and so they are not logged since it is not known whether or not content is text.

^ 28. Why don't I get the full HTML template when an HTTPS request is blocked? Can I customise the message returned?

Because some browsers cannot handle unencrypted error pages of more than a few bytes in response to an HTTPS request. Currently this message can only be customised by modifying source code.

^ 29. Help! I have DansGuardian installed on my gateway, and strangers are using my proxy/filter. How do I stop them?

Use a firewall (iptables) to prevent external connections to DG and Squid's listening ports, and/or configure DG and Squid to listen only on LAN interfaces. In DG, a non-blank "filterip" line causes DG to bind its listening socket to the given IP; multiple "filterip" lines can be given if needed. Squid allows specification of bind IPs in the "http_port" directive.

Also note that if you do not have any machines/users on your network which are allowed to bypass the filter, then Squid only needs to be configured to listen on the loopback interface (127.0.0.1).

^ 30. DG seems to be very slow to access a site initially, but repeated accesses to the same site are fine. What's going on?

You quite possibly have DNS performance issues. Enable the cache manager in Squid and monitor the average time for DNS requests; ideally values should be in the low single figures. One possibility for reducing DNS load is disabling the "reverseaddresslookups" option in DG; however, people will be able to bypass the site/URL filtering (but not the content or virus filtering) by visiting websites by IP directly.

^ 31. What permissions does DG need to create its PID file, access.log file and IPC sockets?

DG creates and updates its PID file as root, so permissions should not be an issue here. However, it must be able to write to its access.log file as an unprivileged user: this is the user/group set by the "daemonuser" and "daemongroup" options; by default, "nobody:nobody". The IPC sockets (UNIX domain sockets) must be in a location writable by the unprivileged user; by default they are in "/tmp" and so not generally an issue.

^ 32. I want to stop people from using MSN, ban filesharing, virus-scan their FTP transfers, put quotas on their email usage, remove all spyware and implement world peace!

DansGuardian is an HTTP proxy. It does not speak any other protocol. FTP via HTTP proxy only works because Squid is capable of translating between the two; if you want to use DG as a "real" FTP proxy, for clients other than web browsers, it will not work. It is true that some IM clients can tunnel conversations over HTTP - DG doesn't implement any special features to detect or block this, but incoming messages may be content filtered if they are in the clear, and usage of such clients blocked based on the destination IPs/domains in the HTTP requests.

e2guardian-5.5.8r/doc/Makefile.am000077500000000000000000000003471477372360500165620ustar00rootroot00000000000000DISTCLEANFILES = Makefile.in docdir = ${datadir}/doc/${PACKAGE} program_transform_name = s,x,x, dist_man_MANS = e2guardian.8 dist_doc_DATA = AuthPlugins ContentScanners DownloadManagers FAQ FAQ.html Plugins EXTRA_DIST = faqconv.pl e2guardian-5.5.8r/doc/Plugins000077500000000000000000000117151477372360500160730ustar00rootroot00000000000000Plugins in DansGuardian ======================= Since the 2.9 series, DansGuardian uses "plugins" for authentication, content scanning and download management. Originally, plugins were intended to be built as shared objects, allowing third-party plugins to be maintained as separate projects and easily integrated. However, getting initial implementations of this idea to compile on platforms other than Linux - in fact, even just on machines other than Daniel's development box, by some accounts - was problematic, and held up progress considerably. Eventually the idea was dropped, opting instead for plugins simply being implementations of common base classes, compiled directly into the main binary. Although less elegant, and less conducive to the development of third-party plugins, this has proven simpler to develop and support across the various platforms supported by earlier releases, allowing us to stop wrestling with libtool and get some "real" work done. See the AuthPlugins, DownloadManagers and ContentScanners documents for more information about implementing a specific plugin type. How plugins are hooked into the build system ============================================ The source files for plugins themselves live in "src/contentscanners", "src/downloadmanagers" or "src/authplugins", according to plugin type. Configuration files live in "configs/" ("/etc/e2guardian/" after installation). For a plugin to be built, its source file must be included in the "e2guardian_SOURCES" variable in "src/Makefile.am", and code to instance the plugin when encountering the relevant "plugname" string - taken from the "plugname" directive in the configuration file - must be added to the relevant top-level source file. The top-level source files are Auth.cpp for authentication plugins, ContentScanner.cpp for content scanning plugins, and DownloadManager.cpp for download management plugins. These files contain a list of externs referring to builder functions for the various plugin classes, the default implementations of any non-abstract methods of the base class, and a "*_plugin_load" function consisting mainly of code to check the plugname and return an instance of the desired plugin. As an example, here's the bare minimum necessary to build and load a new, hypothetical "fooscan" content scanner. To create and compile the plugin: ----- src/contentscanners/fooscan.cpp: Include "../ContentScanner.hpp" here and implement the ContentScanner class however you see fit. Make sure there is a "foocreate" function, which accepts a ConfigVar& and returns a CSPlugin*, i.e. creates an instance of your plugin. src/ContentScanner.cpp: Add "extern cscreate_t foocreate;" to the top of the file. Add an 'if (plugname == "fooscan")' section to cs_plugin_load. src/Makefile.am: Add "contentscanners/fooscan.cpp" to the end of "e2guardian_SOURCES". ----- For a plugin to be loaded, there must be a line in e2guardian.conf referencing a config file which contains your new plugin's plugname, e.g.: ----- /etc/e2guardian/e2guardian.conf: contentscanner = "/etc/e2guardian/contentscanners/fooscan.conf" /etc/e2guardian/contentscanners/fooscan.conf: plugname = "fooscan" ----- This can be built and installed by adding a default fooscan.config file to "configs/contentscanners", and appending the filename to the FLISTS variable inside "configs/contentscanners/Makefile.am". You will have to run autogen.sh after modifying any Makefile.am files, to regenerate the corresponding Makefile.in files, then configure, make and install as usual. Optional compilation of plugins =============================== If you want compilation of your plugin to be controllable by a configure argument, you will have to understand a thing or two about autotools. You will notice inside src/Makefile.am and configs//Makefile.am that the addition of source files to targets is bounded by "ENABLE_*" conditionals, and that entries in src/ContentScanner.cpp and friends are bounded by #ifdefs. These flags are set (or not set, if disabled) by calls to AM_CONDITIONAL and AC_DEFINE within configure.ac - it is recommended that you examine existing option tests in that file to see how this is typically done. Run autogen.sh after modifying configure.ac, then configure, make, make install. Building multiple third-party plugins ====================================== This is not something that is currently handled well - or even at all. Since adding plugins requires additions to parts of the build system, the files are, unfortunately, moving targets and so not easily patched. If anyone wants to write a tool that aids in this process, perhaps scanning plugin directories for code snippets and automatically inserting them where necessary, please do so. This is something that shall hopefully be addressed in future versions, possibly with a second attempt at going the libtool route. Note, however, that there aren't actually any third-party plugins available at time of writing. -- Phil A. philip.allison/smoothwall.net e2guardian-5.5.8r/doc/UPGRADING000077500000000000000000000021761477372360500157730ustar00rootroot00000000000000HOW TO UPGRADE FROM DansGuardian ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Both the main configuration file and the filter group configuration file(s) have changed. Check the script(s) in the migration_installation directory for help with this and read the e2guardian.release file which describes the main feature and enhancements in this version of e2guardian. On the other hand, list files (phrase lists, domain lists, etc.) have not changed format, and should largely "just work". Before installing the new copy, first make a backup of your existing config files. For example: cp -r /etc/dansguardian/ ~/dansguardian.old/ Then install the new copy. After doing this, you should re-apply the old settings, importing them by hand; i.e. any setting you had changed from the default should be changed in the new e2guardian.conf file, and the same for other files such as e2guardianf1.conf. Every option should be documented by comments in the default configuration file; these comments are one of the best sources of information as to what a particular setting means. This document is modified from wording orginaly in the dansguardian distribution. e2guardian-5.5.8r/doc/e2guardian.8000077500000000000000000000063461477372360500166450ustar00rootroot00000000000000.\" ------> /usr/share/man/man8/e2guardian.8.gz <------ .\" .TH "e2guardian" "8" "Juin 2015" "Frederic Bourgeois" "e2guardian" .SH "NAME" e2guardian2 \- web content filter .SH "SYNOPSIS" .PP \fBe2guardian\fR [ \-\fBc\fR \fIconfig\-file\fR ] [ \-\fBv\fR ] [ \-\fBh\fR ] [ \-\fBN\fR ] [ \-\fBq\fR ] [ \-\fBQ\fR ] [ \-\fBs\fR ] [ \-\fBr\fR ] [ \-\fBg\fR ] .SH "SUMMARY" e2guardian is a web content filtering proxy(1) for Linux, NetBSD, FreeBSD, OpenBSD, and Solaris that uses Squid(2) to do all the fetching. It filters using multiple methods. These methods include URL and domain filtering, content phrase filtering, PICS filtering, MIME type filtering, file extension filtering, POST limiting and content (AV) scanning. The content phrase filtering will check for pages that contain profanities and phrases often associated with pornography and other undesirable content. The POST filtering allows you to block or limit web uploads. The URL and domain filtering is able to handle huge lists and is significantly faster than squidGuard. Content scanning enables downloaded content to be passed through ClamD, KAVD, any local program with machine\-parseable console output, and any other AV scanner available as an ICAP server. The filtering has configurable domain, user and source IP exception lists. SSL tunneling is supported; however, due to the encrypted nature of HTTPS requests, filtering is only supported on destination domain or with SSL MITM configuration. The configurable logging produces a log in an easy to read format which has the option to only log the text\-based pages, thus significantly reducing redundant information such as every image on a page. e2guardian is under continuous development and so it is best to visit the web site for the latest information. (1) Technically e2guardian is more of a filtering pass\-through than a true proxy \- but don't let that worry you! (2) e2guardian should work with any proxy, not just Squid. For example, it is known to work with Oops. .SH "DOCUMENTATION" .PP For all further information including copyright, support, FAQ, docs, mailing list \- please go to: http://e2guardian.org/ Available documentation can be found at http://www.e2guardian.org The e2guardian FAQ is at http://www.e2guardian.org Some useful HOWTOs that cover specific implementations of e2guardian and caching/filtering in general can be found at http://www.e2guardian.org Information about Debian specific changes and configuration can be found in /usr/share/doc/e2guardian/changelog.Debian.gz . .SH "OPTIONS" .TP \-c \fIconfig\-file\fR Use the given \fIconfig\-file\fR\&. .TP \-v gives the version number and build options\&. .TP \-h Output a short summary of available command line options\&. .TP \-N Do not go into the background\&. .TP \-q causes e2guardian to kill any running copy\&. .TP \-Q kill any running copy AND start a new one with current options\&. .TP \-s shows the parent process PID and exits\&. .TP \-r closes all connections and reloads config files by issuing a HUP, but this does not reset the maxchildren option (amongst others)\&. .TP \-g gently restarts by not closing all current connections; only reloads filter group config files. (Issues a USR1)\& .SH "COPYRIGHT" e2guardian is copyright E2BN and Frederic Bourgeois e2guardian-5.5.8r/doc/faqconv.pl000077500000000000000000000034411477372360500165160ustar00rootroot00000000000000#!/bin/env perl use strict; my $fname = $ARGV[0]; my @questions; my @answers; if (!-e $fname) { die "File does not exist\n"; } open(FILE, "<$fname"); while() { chomp; if (length($_) == 0) { next; } if ($_ =~ /^Q(?: \d+){0,1}\. (.*)$/) { my $question = $1; my $current = \$question; my $answer; my $break = 0; while () { chomp; if (length($_) == 0) { if ($break == 1) { last; } else { next; }; } my $line = $_; if ($_ =~ /^A\. (.*)$/) { $current = \$answer; $line = $1; $break = 1; } $$current .= $line."\n"; } $question =~ s/&/&/g; $question =~ s/"/"/g; #" $question =~ s//>/g; $answer =~ s/&/&/g; $answer =~ s/"/"/g; #" $answer =~ s//>/g; $answer =~ s/(http:\/\/[^\s]*)/$1<\/a>/g; push(@questions, $question); push(@answers, $answer); } } close(FILE); print qq| DansGuardian 2.10 FAQ

Contents

    |; my $count = 1; foreach my $question (@questions) { print qq|
  1. $question
  2. \n|; $count++; } print qq|

FAQ

|; $count = 0; foreach my $question (@questions) { my $answer = $answers[$count++]; chomp $answer; $answer =~ s/\n/<\/p>

/g; print qq|

^ $count. $question

$answer

|; } print qq| |; e2guardian-5.5.8r/e2guardian.release000066400000000000000000000034201477372360500173340ustar00rootroot00000000000000This the v5.5. stable branch ---------------------------- See notes/NEWIN_v5 for details of development progress and target new features and improvements. Changed and Added features in e2guardian 5.0 -------------------------------------------- Complete change to list handling and logic control. Many new and changed configuration options and files. HTTPS transparent proxy, optional upstream proxy and ICAP server modes now supported. Please see notes/NEWIN_v5 and revised example configuration files for details. If upgrading from a previous version see also notes/Upgrading_to_V5 Changed and Added features in e2guardian 4.0.0 ---------------------------------------------- Major internal rewrite to improve code quality, stability and performance The main advantage is the engine itself. It is more faster and powerful than before and tuning FD_SETSIZE is not needed anymore Tuning settings introduced/changed in v4.0.0:- -------------------------------------------- e2guardian.conf - httpworkers added - specifies number of worker threads to start - enablessl added - global enablement of SSL Cert checking and/or MITM If this is off then sslmitm options in e2guardianfn.conf files will be ignored - authrequiresuserandgroup - If on it a user without group is considered like unauthenfied Options removed from e2guardian.conf - proxyfailureloginterval, urlcachenumber, urlcacheage, scancleancache, createlistcachefiles, prefercachedlists, maxchildren, minchildren, minsparechildren, preforkchildren, maxsparechildren, maxagechildren, gentlechunk, maxips, ipcfilename, urlipcfilename, ipipcfilename, mailer, monitorhelper, monitorstart If present in the e2guardian.conf file removed options are silently ignored. See notes/NEWIN_v4 for more information on changed logic etc. e2guardian-5.5.8r/gitlabci/000077500000000000000000000000001477372360500155305ustar00rootroot00000000000000e2guardian-5.5.8r/gitlabci/armdebian.yml000066400000000000000000000121131477372360500201730ustar00rootroot00000000000000build:arm: stage: build image: debian:bookworm-slim resource_group: builddarm artifacts: expire_in: 60 minutes paths: - $CI_PROJECT_DIR variables: OS: "raspbian" script: - source $CI_PROJECT_DIR/scripts/debian_package/variables - echo $VFULL - apt update - apt-get -y upgrade - echo "deb-src http://deb.debian.org/debian bullseye main contrib non-free" >> /etc/apt/sources.list - apt-get install --no-upgrade --no-install-recommends --no-install-suggests -y curl unzip automake coreutils debianutils diffutils e2fsprogs findutils grep unzip ncurses-base libevent-pthreads-* libevent-dev ncurses-bin login sysvinit-utils tar libc6-dev libc-dev gcc g++ make dpkg-dev autotools-dev debhelper dh-autoreconf libclamav-dev libpcre3-dev zlib1g-dev pkg-config libssl-dev ca-certificates lsb-release inotify-tools curl - cd $CI_PROJECT_DIR && make clean - ./autogen.sh - ./configure --host=arm-linux-gnueabihf --prefix=/usr --enable-clamd=yes --with-proxyuser=e2guardian --with-proxygroup=e2guardian --sysconfdir=/etc --localstatedir=/var --enable-icap=yes --enable-commandline=yes --enable-email=yes --enable-ntlm=yes --enable-pcre=yes --enable-sslmitm=yes - make -j 4 - find $CI_PROJECT_DIR -name ".git" -exec rm -r "{}" + - chown -Rf 1161 $CI_BUILDS_DIR tags: - arm package:debarm: stage: create-package image: debian:bookworm-slim dependencies: - build:arm artifacts: expire_in: 60 minutes paths: - $CI_PROJECT_DIR variables: PACKPATH: "$CI_PROJECT_DIR" PACKDST: "$CI_PROJECT_DIR/scripts/debian_package/e2debian-arm64_package" OS: "debian-arm64" script: - source $CI_PROJECT_DIR/scripts/debian_package/variables - apt update - apt install -y curl git lsb-release binutils curl ca-certificates util-linux binutils libpcre3 libssl3 - echo $VFULL - VERSION=`lsb_release -cs` - SIZE=`stat -c %s ${PACKDST}/data` && echo $SIZE && sed -i "s/Installed-Size:.*$/Installed-Size:\ $SIZE/g" ${PACKDST}/control/control - sed -i "s/Version:.*$/Version:\ $VFULL/g" ${PACKDST}/control/control - cat ${PACKDST}/control/control - cp ${PACKPATH}/src/e2guardian ${PACKDST}/data/usr/sbin/e2guardian - chmod +x ${PACKDST}/data/usr/sbin/e2guardian - cp -Rf ${PACKPATH}/configs ${PACKDST}/data/etc/e2guardian - mkdir -p ${PACKPATH}/share/e2guardian/languages - cp -Rf ${PACKPATH}/data/languages ${PACKDST}/data/usr/share/e2guardian/ - cp ${PACKPATH}/data/*.gif ${PACKDST}/data/usr/share/e2guardian/ - cp ${PACKPATH}/data/*swf ${PACKDST}/data/usr/share/e2guardian/ - cp ${PACKPATH}/data/scripts/e2guardian.service ${PACKDST}/data/lib/systemd/system - find ${PACKDST}/ -type f -name "Makefil*" -delete - find ${PACKDST}/ -type f -name "*.in" -delete - cd ${PACKDST} && cd .. && ./rebuild.sh e2"$OS"_package - find $CI_PROJECT_DIR -name ".git" -exec rm -r "{}" + tags: - arm test:debianarm: stage: test-publish image: debian:bookworm-slim dependencies: - package:debarm artifacts: expire_in: 60 minutes name: builddebian paths: - $CI_PROJECT_DIR variables: PACKPATH: "$CI_PROJECT_DIR" PACKDST: "$CI_PROJECT_DIR/scripts/debian_package/e2debian-arm64_package" CERT_DIR: "/etc/e2guardian/private/" OS: "debian-arm64" script: - source $CI_PROJECT_DIR/scripts/debian_package/variables - echo $VFULL - apt update - apt install -y openssl curl git lsb-release libevent-pthreads* libtommath* libpcre3 libssl3 - VERSION=`lsb_release -cs` - cd ${PACKDST} && cd .. && dpkg -i e2"$OS"_package.deb - sed -i "s/^#sslmitm.*$/sslmitm\ =\ on/" /etc/e2guardian/e2guardianf1.conf && sed -i "s/^#enablessl.*$/enablessl\ =\ on/" /etc/e2guardian/e2guardian.conf && sed -i "s/^RANDFILE\s*=\s*\\\$ENV/#RANDFILE\ =\ \$ENV/" /etc/ssl/openssl.cnf - mkdir -p ${CERT_DIR}generatedcerts - openssl genrsa 4096 > "${CERT_DIR}ca.key" && openssl req -subj "/C=US" -new -x509 -days 3650 -key "${CERT_DIR}ca.key" -out "${CERT_DIR}ca.pem" && openssl genrsa 4096 > "${CERT_DIR}cert.key" - chown --recursive e2guardian:e2guardian ${CERT_DIR} - echo "qwant.com" >> /etc/e2guardian/lists/example.group/bannedsitelist - e2guardian -N & - sleep 5 && export https_proxy=http://localhost:8080 - curl -k -o /tmp/test https://www.google.com - curl -k https://www.qwant.com | grep -i e2guardian - unset https_proxy - mkdir -p "${CI_COMMIT_BRANCH}" - mv e2"$OS"_package.deb ${CI_COMMIT_BRANCH}/e2"$OS"_"$VERSION"_V"$VFULL"_$(date +%Y%m%d).deb - md5sum ${CI_COMMIT_BRANCH}/e2"$OS"_"$VERSION"_V"$VFULL"_$(date +%Y%m%d).deb > ${CI_COMMIT_BRANCH}/e2"$OS"_"$VERSION"_V"$VFULL"_$(date +%Y%m%d)_md5.txt - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' - mkdir -p ~/.ssh - eval $(ssh-agent -s) - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' - ssh-add <(echo "$SSH_NOSTROMO_KEY") - scp -P 822 -r ${CI_COMMIT_BRANCH} e2git@e2guardian.numsys.eu:/datas/e2/html - find $CI_PROJECT_DIR -name ".git" -exec rm -r "{}" + tags: - arm e2guardian-5.5.8r/gitlabci/debianlatest.yml000066400000000000000000000120051477372360500207100ustar00rootroot00000000000000build:debian: stage: build-debian resource_group: builddeb artifacts: expire_in: 60 minutes paths: - $CI_PROJECT_DIR image: $CONTAINER_CLIENT_IMAGE variables: OS: "debian" script: - apt update - apt-get -y upgrade - apt install --no-install-recommends --no-install-suggests -y curl unzip base-files automake base-passwd bash coreutils dash debianutils diffutils dpkg e2fsprogs findutils grep gzip hostname ncurses-base libevent-pthreads-* libevent-dev ncurses-bin perl-base sed login sysvinit-utils tar bsdutils mount util-linux libc6-dev libc-dev gcc g++ make dpkg-dev autotools-dev debhelper dh-autoreconf libclamav-dev libpcre3-dev zlib1g-dev pkg-config libssl-dev git ca-certificates lsb-release - cd $CI_PROJECT_DIR && ./autogen.sh && ./configure '--prefix=/usr' '--enable-clamd=yes' '--with-proxyuser=e2guardian' '--with-proxygroup=e2guardian' '--sysconfdir=/etc' '--localstatedir=/var' '--enable-icap=yes' '--enable-commandline=yes' '--enable-email=yes' '--enable-ntlm=yes' '--mandir=${prefix}/share/man' '--infodir=${prefix}/share/info' '--enable-pcre=yes' '--enable-sslmitm=yes' 'CPPFLAGS=-mno-sse2 -g -O2' - make -j 4 - find $CI_PROJECT_DIR -name ".git" -exec rm -r "{}" + - chown -Rf 1161 $CI_BUILDS_DIR package:debian: stage: create-package-debian image: $CONTAINER_CLIENT_IMAGE artifacts: expire_in: 60 minutes paths: - $CI_PROJECT_DIR variables: PACKPATH: "$CI_PROJECT_DIR" PACKDST: "$CI_PROJECT_DIR/scripts/debian_package/e2debian_package" OS: "debian" script: - apt update && apt install --no-install-recommends --no-install-suggests -y curl git ca-certificates util-linux binutils libpcre3 - git clone https://github.com/fredbcode/scripts - cp ${PACKPATH}/src/e2guardian ${PACKDST}/data/usr/sbin/e2guardian - chmod +x ${PACKDST}/data/usr/sbin/e2guardian - cp -Rf ${PACKPATH}/configs ${PACKDST}/data/etc/e2guardian - mkdir -p ${PACKPATH}/share/e2guardian/languages - cp -Rf ${PACKPATH}/data/languages ${PACKDST}/data/usr/share/e2guardian/ - cp ${PACKPATH}/data/*.gif ${PACKDST}/data/usr/share/e2guardian/ - cp ${PACKPATH}/data/*swf ${PACKDST}/data/usr/share/e2guardian/ - cp ${PACKPATH}/data/scripts/e2guardian.service ${PACKDST}/data/lib/systemd/system - find ${PACKDST}/ -type f -name "Makefil*" -delete - find ${PACKDST}/ -type f -name "*.in" -delete - SIZE=`stat -c %s ${PACKDST}/data` && echo $SIZE && sed -i "s/Installed-Size:.*$/Installed-Size:\ $SIZE/g" ${PACKDST}/control/control - VFULL=`${PACKDST}/data/usr/sbin/e2guardian -v | sed -n 1p | cut -d ' ' -f 2` && sed -i "s/Version:.*$/Version:\ $VFULL/g" ${PACKDST}/control/control - cat ${PACKDST}/control/control - echo "export VFULL=$VFULL" >> $CI_PROJECT_DIR/scripts/debian_package/variables - cd ${PACKDST} && cd .. && ./rebuild.sh e2"$OS"_package - find $CI_PROJECT_DIR -name ".git" -exec rm -r "{}" + test:debian: stage: test-publish-debian image: $CONTAINER_CLIENT_IMAGE artifacts: name: builddebian expire_in: 60 minutes paths: - $CI_PROJECT_DIR variables: PACKPATH: "$CI_PROJECT_DIR" PACKDST: "$CI_PROJECT_DIR/scripts/debian_package" CERT_DIR: "/etc/e2guardian/private/" OS: "debian" script: - source $CI_PROJECT_DIR/scripts/debian_package/variables - echo $VFULL - apt update - apt install -y openssl curl git lsb-release libevent-pthreads* libtommath* libpcre3 libssl3 - VERSION=`lsb_release -cs` - cd ${PACKDST} && dpkg -i e2"$OS"_package.deb - sed -i "s/^#sslmitm.*$/sslmitm\ =\ on/" /etc/e2guardian/e2guardianf1.conf && sed -i "s/^#enablessl.*$/enablessl\ =\ on/" /etc/e2guardian/e2guardian.conf && sed -i "s/^RANDFILE\s*=\s*\\\$ENV/#RANDFILE\ =\ \$ENV/" /etc/ssl/openssl.cnf - mkdir -p ${CERT_DIR}generatedcerts - openssl genrsa 4096 > "${CERT_DIR}ca.key" && openssl req -subj "/C=US" -new -x509 -days 3650 -key "${CERT_DIR}ca.key" -out "${CERT_DIR}ca.pem" && openssl genrsa 4096 > "${CERT_DIR}cert.key" - chown --recursive e2guardian:e2guardian ${CERT_DIR} - echo "qwant.com" >> /etc/e2guardian/lists/example.group/bannedsitelist - e2guardian -N & - sleep 5 && export https_proxy=http://localhost:8080 - curl -k -o /tmp/test https://www.google.com - curl -k https://www.qwant.com | grep -i e2guardian - unset https_proxy - mkdir -p "${CI_COMMIT_BRANCH}" - mv e2"$OS"_package.deb ${CI_COMMIT_BRANCH}/e2"$OS"_"$VERSION"_V"$VFULL"_$(date +%Y%m%d).deb - md5sum ${CI_COMMIT_BRANCH}/e2"$OS"_"$VERSION"_V"$VFULL"_$(date +%Y%m%d).deb > ${CI_COMMIT_BRANCH}/e2"$OS"_"$VERSION"_V"$VFULL"_$(date +%Y%m%d)_md5.txt - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' - mkdir -p ~/.ssh - eval $(ssh-agent -s) - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' - ssh-add <(echo "$SSH_NOSTROMO_KEY") - scp -P 822 -r ${CI_COMMIT_BRANCH} e2git@e2guardian.numsys.eu:/datas/e2/html - find $CI_PROJECT_DIR -name ".git" -exec rm -r "{}" + e2guardian-5.5.8r/gitlabci/docker-ci/000077500000000000000000000000001477372360500173705ustar00rootroot00000000000000e2guardian-5.5.8r/gitlabci/docker-ci/Dockerfile000066400000000000000000000041361477372360500213660ustar00rootroot00000000000000FROM debian:bookworm-slim as builddocker ARG PROJECT_DIR COPY . /tmp/e2guardian FROM debian:bookworm-slim ARG PROJECT_DIR ENV DOCKER_BUILD=/tmp/e2guardian/scripts/debian_package/e2debian_package/data COPY --from=builddocker --chown=1161 /tmp/e2guardian/src/e2guardian /usr/sbin/e2guardian COPY --from=builddocker --chown=1161 $DOCKER_BUILD/etc/e2guardian /etc/e2guardian COPY --from=builddocker --chown=1161 $DOCKER_BUILD/usr/share/e2guardian/languages /usr/share/e2guardian/languages COPY --from=builddocker --chown=1161 $DOCKER_BUILD/usr/share/e2guardian/*swf /usr/share/e2guardian/ COPY --from=builddocker --chown=1161 $DOCKER_BUILD/usr/share/e2guardian/*gif /usr/share/e2guardian/ RUN apt-get update && apt-get install --no-upgrade --no-install-recommends --no-install-suggests -y rsync sed libevent-pthreads-* ca-certificates curl inotify-tools libpcre3 libssl3 \ && adduser --no-create-home --uid 1161 --group --system e2guardian \ && mkdir -p /run/e2guardian \ && mkdir -p /var/log/e2guardian && chown -R e2guardian /var/log/e2guardian && chown -R e2guardian /run/e2guardian \ && rm -Rf /tmp/* \ && sed -i "s/^#dockermode.*$/dockermode\ =\ on/" /etc/e2guardian/e2guardian.conf \ && sed -i "s/^#pidfilename.*$/pidfilename\ =\ \/run\/e2guardian\/e2.pid/" /etc/e2guardian/e2guardian.conf \ && sed -i "s/^#icapport.*$/icapport\ =\ 1344/" /etc/e2guardian/e2guardian.conf \ && echo "# Docker log #" >> /etc/e2guardian/e2guardian.conf \ && echo "set_error = 'stdout:LOG_ERR'" >> /etc/e2guardian/e2guardian.conf \ && echo "set_warning = 'stdout:LOG_WARNING'" >> /etc/e2guardian/e2guardian.conf \ && apt-get autoremove -y \ && apt-get clean \ && rm -Rf /var/lib/apt/lists/* HEALTHCHECK CMD curl --proxy "http://127.0.0.1:8080" http://internal.test.e2guardian.org | grep "e2guardian internal test OK" || exit 1 USER e2guardian EXPOSE 8080 COPY --from=builddocker --chown=1161 /tmp/e2guardian/gitlabci/docker-ci/run.sh / COPY --from=builddocker --chown=1161 /tmp/e2guardian/gitlabci/docker-ci/inotify.sh / RUN chmod +x /inotify.sh /run.sh USER e2guardian EXPOSE 8080 ENTRYPOINT ["/run.sh"] e2guardian-5.5.8r/gitlabci/docker-ci/inotify.sh000066400000000000000000000005771477372360500214160ustar00rootroot00000000000000#!/bin/bash LISTS="/etc/e2guardian" horodate=$(date +%d-%m-%Y_%H_%M) if [ -d $LISTS ] then while inotifywait -e modify,delete,create -r $LISTS --exclude \.swp do # wait all modifications sleep 10 horodate=$(date +%d-%m-%Y_%H_%M) /usr/sbin/e2guardian -r echo "RELOAD E2 $horodate: new files" >> /var/log/e2guardian/reloade2.log sleep 60 done fi e2guardian-5.5.8r/gitlabci/docker-ci/run.sh000066400000000000000000000002141477372360500205250ustar00rootroot00000000000000#!/bin/sh PID=$(grep pid /etc/e2guardian/e2guardian.conf | cut -d " " -f3) if [ -e $PID ];then rm $PID fi /inotify.sh & e2guardian -N e2guardian-5.5.8r/gitlabci/docker-hub-arm.yml000066400000000000000000000032611477372360500210550ustar00rootroot00000000000000variables: CONTAINER_PROD_IMAGE_ARM: $HUB_REGISTRY_IMAGE:$CI_COMMIT_BRANCH-arm CONTAINER_BUILD_NOPROD_NAME_ARM: $HUB_REGISTRY_IMAGE:build-noprod-arm docker-hub-build-arm: stage: Docker-hub-build image: docker:dind dependencies: - package:debarm variables: DOCKER_DST: "/tmp/e2guardian/scripts/debian_package/e2debian_package" PROJECT_DIR: "$CI_PROJECT_DIR" before_script: - docker login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_TOKEN" $DOCKER_HUB_REGISTRY script: - cd $PROJECT_DIR - docker build --build-arg E2_VERSION="$CI_COMMIT_BRANCH" --build-arg PROJECT_DIR="$PROJECT_DIR" -f gitlabci/docker-ci/Dockerfile --pull -t $CONTAINER_BUILD_NOPROD_NAME_ARM . - docker push $CONTAINER_BUILD_NOPROD_NAME_ARM tags: - arm docker-hub-test-arm: image: name: $CONTAINER_BUILD_NOPROD_NAME_ARM entrypoint: [""] stage: Docker-hub-test dependencies: [] script: - e2guardian -N & - sleep 5 && export https_proxy=http://localhost:8080 && curl -k https://www.google.fr tags: - arm # Working on amd64 only dive-arm: image: name: wagoodman/dive:latest entrypoint: [""] dependencies: [] stage: Docker-hub-test script: - docker pull $CONTAINER_BUILD_NOPROD_NAME_ARM - dive $CONTAINER_BUILD_NOPROD_NAME_ARM variables: CI: "true" push-docker-hub-arm: stage: Docker-hub-pushtag image: docker:dind dependencies: [] before_script: - docker login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_TOKEN" $DOCKER_HUB_REGISTRY script: - docker pull $CONTAINER_BUILD_NOPROD_NAME_ARM - docker tag $CONTAINER_BUILD_NOPROD_NAME_ARM $CONTAINER_PROD_IMAGE_ARM - docker push $CONTAINER_PROD_IMAGE_ARM tags: - arm e2guardian-5.5.8r/gitlabci/docker-hub.yml000066400000000000000000000031321477372360500202750ustar00rootroot00000000000000variables: CONTAINER_PROD_IMAGE: $HUB_REGISTRY_IMAGE:$CI_COMMIT_BRANCH-amd64 CONTAINER_BUILD_NOPROD_NAME_AMD64: $HUB_REGISTRY_IMAGE:build-noprod-amd64 docker-hub-build: stage: Docker-hub-build image: docker:dind dependencies: - package:debian variables: PACKDST: "$CI_PROJECT_DIR/scripts/debian_package/e2debian_package" PROJECT_DIR: "$CI_PROJECT_DIR" before_script: - docker login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_TOKEN" $DOCKER_HUB_REGISTRY script: - cd $PROJECT_DIR - docker build --build-arg E2_VERSION="$CI_COMMIT_BRANCH" --build-arg PROJECT_DIR="$PROJECT_DIR" -f gitlabci/docker-ci/Dockerfile --pull -t $CONTAINER_BUILD_NOPROD_NAME_AMD64 . - docker push $CONTAINER_BUILD_NOPROD_NAME_AMD64 docker-hub-test: image: name: $CONTAINER_BUILD_NOPROD_NAME_AMD64 entrypoint: [""] stage: Docker-hub-test dependencies: [] script: - e2guardian -N & - sleep 5 && export https_proxy=http://localhost:8080 && curl -k https://www.google.fr dive: image: name: wagoodman/dive:latest entrypoint: [""] dependencies: [] stage: Docker-hub-test script: - docker pull $CONTAINER_BUILD_NOPROD_NAME_AMD64 - dive $CONTAINER_BUILD_NOPROD_NAME_AMD64 variables: CI: "true" push-docker-hub: stage: Docker-hub-pushtag image: docker:dind dependencies: [] before_script: - docker login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_TOKEN" $DOCKER_HUB_REGISTRY script: - docker pull $CONTAINER_BUILD_NOPROD_NAME_AMD64 - docker tag $CONTAINER_BUILD_NOPROD_NAME_AMD64 $CONTAINER_PROD_IMAGE - docker push $CONTAINER_PROD_IMAGE e2guardian-5.5.8r/gitlabci/ubuntulatest.yml000066400000000000000000000122221477372360500210110ustar00rootroot00000000000000build:noble: stage: build image: amd64/ubuntu:24.04 resource_group: buildub24 artifacts: expire_in: 60 minutes paths: - $CI_PROJECT_DIR script: - source $CI_PROJECT_DIR/scripts/debian_package/variables - echo $VFULL - apt update - apt-get -y upgrade - apt install --no-install-recommends --no-install-suggests -y curl unzip base-files automake base-passwd bash coreutils dash debianutils diffutils dpkg e2fsprogs findutils grep gzip hostname ncurses-base libevent-pthreads-* libevent-dev ncurses-bin perl-base sed login sysvinit-utils tar bsdutils mount util-linux libc6-dev libc-dev gcc g++ make dpkg-dev autotools-dev debhelper dh-autoreconf libclamav-dev libpcre3-dev zlib1g-dev pkg-config libssl-dev libssl3t64 git ca-certificates lsb-release - cd $CI_PROJECT_DIR && make clean && ./autogen.sh && ./configure '--prefix=/usr' '--enable-clamd=yes' '--with-proxyuser=e2guardian' '--with-proxygroup=e2guardian' '--sysconfdir=/etc' '--localstatedir=/var' '--enable-icap=yes' '--enable-commandline=yes' '--enable-email=yes' '--enable-ntlm=yes' '--mandir=${prefix}/share/man' '--infodir=${prefix}/share/info' '--enable-pcre=yes' '--enable-sslmitm=yes' 'CPPFLAGS=-mno-sse2 -g -O2' - make -j 4 - find $CI_PROJECT_DIR -name ".git" -exec rm -r "{}" + package:noble: stage: create-package image: amd64/ubuntu:24.04 resource_group: buildub24 dependencies: - build:noble artifacts: expire_in: 60 minutes paths: - $CI_PROJECT_DIR variables: PACKPATH: "$CI_PROJECT_DIR" PACKDST: "$CI_PROJECT_DIR/scripts/debian_package/e2ubuntu_package" OS: "ubuntu" script: - source $CI_PROJECT_DIR/scripts/debian_package/variables - echo $VFULL - apt update && apt install --no-install-recommends --no-install-suggests -y curl git ca-certificates util-linux binutils - SIZE=`stat -c %s ${PACKDST}/data` && echo $SIZE && sed -i "s/Installed-Size:.*$/Installed-Size:\ $SIZE/g" ${PACKDST}/control/control - sed -i "s/libssl1.1/libssl3t64/g" ${PACKDST}/control/control - sed -i "s/libevent-pthreads-2.1-6/libevent-pthreads-2.1-7/g" ${PACKDST}/control/control - cp ${PACKPATH}/src/e2guardian ${PACKDST}/data/usr/sbin/e2guardian - chmod +x ${PACKDST}/data/usr/sbin/e2guardian - cp -Rf ${PACKPATH}/configs ${PACKDST}/data/etc/e2guardian - mkdir -p ${PACKPATH}/share/e2guardian/languages - cp -Rf ${PACKPATH}/data/languages ${PACKDST}/data/usr/share/e2guardian/ - cp ${PACKPATH}/data/*.gif ${PACKDST}/data/usr/share/e2guardian/ - cp ${PACKPATH}/data/*swf ${PACKDST}/data/usr/share/e2guardian/ - cp ${PACKPATH}/data/scripts/e2guardian.service ${PACKDST}/data/lib/systemd/system - find ${PACKDST}/ -type f -name "Makefil*" -delete - find ${PACKDST}/ -type f -name "*.in" -delete - sed -i "s/Version:.*$/Version:\ $VFULL/g" ${PACKDST}/control/control - cd ${PACKDST} && cd .. && ./rebuild.sh e2"$OS"_package - cat ${PACKDST}/control/control - rm -rf .git - find $CI_PROJECT_DIR -name ".git" -exec rm -r "{}" + test:noble: stage: test-publish image: amd64/ubuntu:24.04 dependencies: - package:noble artifacts: expire_in: 60 minutes paths: - $CI_PROJECT_DIR variables: PACKPATH: "$CI_PROJECT_DIR" PACKDST: "$CI_PROJECT_DIR/scripts/debian_package/e2ubuntu_package" CERT_DIR: "/etc/e2guardian/private/" OS: "ubuntu" script: - source $CI_PROJECT_DIR/scripts/debian_package/variables - echo $VFULL - apt update - apt install -y curl openssl git lsb-release libevent-pthreads* libtommath* libssl3t64 libpcre3 - VERSION=`lsb_release -cs` - cd ${PACKDST} && cd .. && dpkg -i e2"$OS"_package.deb - sed -i "s/^#sslmitm.*$/sslmitm\ =\ on/" /etc/e2guardian/e2guardianf1.conf && sed -i "s/^#enablessl.*$/enablessl\ =\ on/" /etc/e2guardian/e2guardian.conf && sed -i "s/^RANDFILE\s*=\s*\\\$ENV/#RANDFILE\ =\ \$ENV/" /etc/ssl/openssl.cnf - mkdir -p ${CERT_DIR}generatedcerts - openssl genrsa 4096 > "${CERT_DIR}ca.key" && openssl req -subj "/C=US" -new -x509 -days 3650 -key "${CERT_DIR}ca.key" -out "${CERT_DIR}ca.pem" && openssl genrsa 4096 > "${CERT_DIR}cert.key" - chown --recursive e2guardian:e2guardian ${CERT_DIR} - echo "qwant.com" >> /etc/e2guardian/lists/example.group/bannedsitelist - e2guardian -N & - sleep 5 && export https_proxy=http://localhost:8080 - curl -k -o /tmp/test https://www.google.com - curl -k https://www.qwant.com | grep -i e2guardian - unset https_proxy - mkdir -p "${CI_COMMIT_BRANCH}" - mv e2"$OS"_package.deb ${CI_COMMIT_BRANCH}/e2"$OS"_"$VERSION"_V"$VFULL"_$(date +%Y%m%d).deb - md5sum ${CI_COMMIT_BRANCH}/e2"$OS"_"$VERSION"_V"$VFULL"_$(date +%Y%m%d).deb > ${CI_COMMIT_BRANCH}/e2"$OS"_"$VERSION"_V"$VFULL"_$(date +%Y%m%d)_md5.txt - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' - mkdir -p ~/.ssh - eval $(ssh-agent -s) - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' - ssh-add <(echo "$SSH_NOSTROMO_KEY") - scp -P 822 -r ${CI_COMMIT_BRANCH} e2git@e2guardian.numsys.eu:/datas/e2/html - find $CI_PROJECT_DIR -name ".git" -exec rm -r "{}" + e2guardian-5.5.8r/m4/000077500000000000000000000000001477372360500142725ustar00rootroot00000000000000e2guardian-5.5.8r/m4/ac_finalize.m4000066400000000000000000000003401477372360500167750ustar00rootroot00000000000000AC_DEFUN([AC_FINALIZE_VAR], [ test "x$prefix" = xNONE && prefix="$ac_default_prefix" test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' ifelse($2, ,[:],$1=$2) $1=`eval echo [$]$1` $1=`eval echo [$]$1` ]) e2guardian-5.5.8r/m4/aci.m4000066400000000000000000000413051477372360500152730ustar00rootroot00000000000000dnl dnl ACI (autoconf interactif) mechanism. dnl for now, it's not interactif, but one day, the user will be able to dnl select which packages he wants from the available list dnl dnl Copyright (C) 2000,2001 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl This program is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A dnl PARTICULAR PURPOSE. dnl History: dnl one day, I'll do a public release. Until then, check the FAST changelogs. dnl ACI_D: echo something to the config.log AC_DEFUN([ACI_D],[echo $1>&5;])dnl >&5 dnl ACI_PACKAGE: defines a package (ie, an external package we should search) dnl SYNOPSIS: dnl 1=Package (name of the package) dnl 2=Description (human readable, for documentation) dnl 3=Function (the main function to search) dnl 4=Library list (where to search, space separated list of group of LDFLAGS dnl each LDFLAGS is ! separated. dnl example: "-lldap -lldap!-llber" dnl warning: can't contain tabs or newlines) dnl 5=Header to check dnl 6=what to do if found dnl 7=what to do if not found (default: AC_MSG_ERROR(blabla)) dnl It is possible to change any of the exported values in $6 or $7 dnl ACTIONS: dnl - try to find the function in each part of LDFLAGS and the header. dnl - AC_SUBST the following values: dnl HAVE_$1: 'yes' or 'no' dnl CFLAGS_$1: CFLAGS to acces to this package dnl LIBS_$1: LDFLAGS to acces to this package dnl - define the following arguments to ./configure: dnl --with-$1 root dir of install dnl --with-$1-includes where to search the header dnl --with-$1-libs where to search the libs dnl --with-$1-extra extra LDFLAGS to pass to the linker AC_DEFUN([ACI_PACKAGE],[ pushdef([aci_name], [$1]) pushdef([aci_help], [$2]) pushdef([aci_func], [$3]) pushdef([aci_paths], [$4]) pushdef([aci_header], [$5]) pushdef([aci_if_yes], [$6]) pushdef([aci_if_no], [$7]) ACI_D("[BEGINING OF ACI-PACKAGE($1,$2,$3,$4,$5,$6,$7]") aci_module_desc_$1="$2" aci_packages="$aci_packages $1" dnl dnl Specify the options dnl AC_ARG_WITH([$1], AC_HELP_STRING([--with-$1=DIR], [root directory of aci_help installation]), [with_$1=$withval good=yes if test "$withval" = yes ; then good=no; fi if test "$withval" = no ; then good=no; fi if test $good = no ; then AC_MSG_ERROR([You gave me --with-$1=$withval. I wait for a location[,] not yes or no (use --enable-[]aci_help or --disable-[]aci_help for that)]) fi CFLAGS_$1="-I$withval/include $CFLAGS_$1" LIBS_$1="-L$withval/lib $LIBS_$1" ]) AC_ARG_WITH([$1-includes], AC_HELP_STRING([--with-$1-includes=DIR], [specify exact include dir for aci_help headers]), [CFLAGS_$1="-I$withval $CFLAGS_$1"]) AC_ARG_WITH([$1-libdir], AC_HELP_STRING([--with-$1-libdir=DIR], [specify exact library dir for aci_help library] ),[LIBS_$1="-L$withval $LIBS_$1"]) AC_ARG_WITH([$1-extra], AC_HELP_STRING([--with-$1-extra=ARG], [specify extra args to pass to the linker to get the aci_help library] ),[LIBS_$1="$withval $LIBS_$1"]) dnl dnl Search the lib dnl OLD_LDFLAGS=$LDFLAGS OLD_CFLAGS=$CFLAGS OLD_CPPFLAGS=$CPPFLAGS dnl make sure there is no newline in paths i=`echo aci_paths|wc -l` if test $i != 1 && test $i != 0 ; then AC_MSG_ERROR([Badly formed args for function ACI-PACKAGE. please no newline and no tab.]) fi ac_func_search_save_LIBS="$LIBS" ac_func_search_save_this_LIBS="$LIBS_$1" aci_found="no" AC_MSG_CHECKING([for aci_name. Can I access aci_func without new libs]) AC_TRY_LINK_FUNC(aci_func, [AC_MSG_RESULT([yes]) aci_found="yes"], [AC_MSG_RESULT([no])] ) for i in aci_paths do if test "$aci_found" = "no"; then LIBS_$1=`echo "$i $ac_func_search_save_this_LIBS"|sed 's/!/ /g'` LIBS="$LIBS_$1 $ac_func_search_save_LIBS" AC_MSG_CHECKING([for aci_name. Can I access aci_func with $LIBS_$1]) AC_TRY_LINK_FUNC(aci_func,aci_found="yes",aci_found="no") AC_MSG_RESULT($aci_found) fi done LIBS="$ac_func_search_save_LIBS" dnl dnl search for the header (only if the lib was found) dnl if test "x$aci_found" = "xyes" && test "x"aci_header != "x" ; then CPPFLAGS="$CPPFLAGS $CFLAGS_$1" ACI_D("CPPFLAGS=$CPPFLAGS") AC_CHECK_HEADER(aci_header,good=yes,good=no) fi if test "x$aci_found" = "xno" ; then dnl Something where not found. dnl If the caller specified what to do in this case, do it. dnl Else, complain. ifelse(aci_if_no, , [AC_MSG_ERROR(Can't find the package $1. Please install it[,] or if it is installed[,] tell me where with the --with-$1 argument (see ./configure --help=short).)], aci_if_no) else dnl Anything where found HAVE_$1="yes" ifelse(aci_if_yes, , echo >/dev/null, aci_if_yes) fi if test "x$HAVE_$1" != "xyes" ; then CFLAGS_$1="" LIBS_$1="" fi dnl AC_SUBST what should be AC_SUBST(HAVE_$1) AC_SUBST(CFLAGS_$1) AC_SUBST(LIBS_$1) dnl restore the old settings LDFLAGS=$OLDLDFLAGS CPPFLAGS=$OLDCPPFLAGS CFLAGS=$OLDCFLAGS ]) dnl ACI_PACKAGE_SAVED: same as ACI_PACKAGE, but with an external cache dnl SYNOPSIS: dnl 1=Package (name of the package) dnl 2=Description (human readable, for documentation) dnl 3=Cache program.Should accept --libs and --cflags (like gnome-config does) dnl 4=Extra arg to pass to this prog (like a name of library) dnl 5=what to do if found dnl 6=what to do if not found (default AC_MSG_ERROR(blabla)) dnl ACTIONS: dnl - try to find the cache program dnl - AC_SUBST the following values: dnl HAVE_$1: 'yes' or 'no' dnl CFLAGS_$1: CFLAGS to acces to this package dnl LIBS_$1: LDFLAGS to acces to this package AC_DEFUN([ACI_PACKAGE_SAVED],[ AC_PATH_PROG(ACI_CACHE_PROG,$3,no) aci_module_desc_$1="$2" aci_packages="$aci_packages $1" if test x$ACI_CACHE_PROG = xno; then HAVE_$1=no CFLAGS_$1="" LIBS_$1="" ifelse($6, ,[AC_MSG_ERROR([Cannot find $1: Is $3 in your path?])],$6) else HAVE_$1=yes CFLAGS_$1=`$ACI_CACHE_PROG --cflags $4` LIBS_$1=`$ACI_CACHE_PROG --libs $4` ifelse($5, ,echo >/dev/null,$5) fi dnl AC_SUBST what should be AC_SUBST(HAVE_$1) AC_SUBST(CFLAGS_$1) AC_SUBST(LIBS_$1) ]) dnl ACI_MODULE: defines a module (ie, a part of the code we are building) dnl 1=abbrev dnl 2=human readable description dnl 3=dependences (space separated list of abbrev) dnl 4=submodules (space separated list of abbrev) dnl 5=default ([yes]/no) (if not given, yes is assumed) dnl 6=extra childs, which are listed under it in tree, without being a module AC_DEFUN([ACI_MODULE],[ aci_module_desc_$1="$2" aci_module_dep_$1="$3" aci_module_sub_$1="$4" aci_module_child_$1="$4 $6" ifelse(ifelse([$5], ,[yes],[$5]),[yes], dnl what to do if module is enabled by default [AC_ARG_ENABLE($1, AC_HELP_STRING([--disable-$1], [Disable the module $1, $2.]), [aci_modules_disabled="$aci_modules_disabled $1"], [aci_modules_possible="$aci_modules_possible $1"] )], dnl what to do if module is disabled by default [AC_ARG_ENABLE($1, AC_HELP_STRING([--enable-$1], [Enable the module $1, $2.]), [aci_modules_possible="$aci_modules_possible $1"], [aci_modules_disabled="$aci_modules_disabled $1"] )] ) ]) dnl ACI_MODULES_VERIFY: verify which module can be satisfied dnl (place for future interactivity) dnl export $1_MODULES to Makefiles, init'ed to the list of modules to build dnl 1: the name of this group of modules AC_DEFUN([ACI_MODULES_VERIFY],[ dnl Check the dependencies of everyone for aci_module in $aci_modules_possible do aci_mod_bad="" ACI_D("Look module $aci_module") for aci_mod_dep in `eval echo '$aci_module_dep_'"$aci_module"` do ACI_D($ECHO_N " it depends on $aci_mod_dep...") if test `eval echo 'x$'"HAVE_$aci_mod_dep"` != xyes; then ACI_D("Not satisfied !") aci_mod_bad=`echo "$aci_mod_bad $aci_mod_dep"|sed 's/^ *//'` else ACI_D("Satisfied !") fi done if test -z "$aci_mod_bad"; then ACI_D("Module $aci_module will be builded") ACI_D([]) aci_modules="$aci_modules $aci_module" eval "HAVE_$aci_module=\"yes\"" else ACI_D("Won't build $aci_module because it depends on $aci_mod_bad.") ACI_D([]) eval "aci_module_misdep_$aci_module=\"$aci_mod_bad\"" aci_modules_broken="$aci_modules_broken $aci_module" eval "HAVE_$aci_module=\"no\"" fi eval `echo "MODULES_$aci_module=\"$aci_mod_ok\""` done dnl Build the list of submodules to build ACI_D("Build the submodules lists") for aci_module in $aci_modules_possible do ACI_D($ECHO_N " Look for $aci_module: ") for aci_mod_sub in `eval echo '$aci_module_sub_'"$aci_module"` do if test `eval echo 'x$HAVE_'"$aci_mod_sub"` = xyes; then ACI_D($ECHO_N "$aci_mod_sub ") eval "MODULES_$aci_module=\"\$MODULES_$aci_module $aci_mod_sub\"" else ACI_D($ECHO_N "NOT $aci_mod_sub ") fi done ACI_D([]) done ]) dnl ACI_MODULES_SUMMARY: give a summary of what will be build to user dnl 1: Name of the root of modules (can be a space separated list) AC_DEFUN([ACI_MODULES_SUMMARY],[ if test "x$aci_modules" != "x"; then echo "The following modules will be builded: " for aci_module in $aci_modules do echo " - $aci_module: "`eval echo '$aci_module_desc_'"$aci_module"` if "`echo "\$MODULES_$aci_module"`" ; then echo " submodules: "`eval echo "\$MODULES_$aci_module"` fi done else echo "No module will be build." fi echo if test "x$aci_modules_disabled" != "x"; then echo "The following modules are disabled, either by you or by default." for aci_module in $aci_modules_disabled do echo " - $aci_module: "`eval echo '$aci_module_desc_'"$aci_module"` done echo fi if test "x$aci_modules_broken" != "x"; then echo "The following modules have BROKEN dependencies:" for aci_module in $aci_modules_broken do echo " - $aci_module: "`eval echo '$aci_module_desc_'"$aci_module"` echo " Depends on: "`eval echo '$aci_module_misdep_'"$aci_module"` done echo echo " If you think I'm a dumb script, and these dependencies can be" echo " satified, please try to use the --with-PACKAGE options. See:" echo " ./configure --help=short" echo echo "The other modules may be build even if these ones are not." echo fi ]) AC_DEFUN([ACI_ECHO],[echo $1;echo $1>&5]) dnl ACI_MODULES_SUMMARY_FANCY: same as ACI_MODULES_SUMMARY, in a fancy way. AC_DEFUN([ACI_MODULES_SUMMARY_FANCY],[ # the file conftests.nexts is a file containing the modules we still have to # handle. Each line have the form "level nb module", defining at which # is defined. It is the th at this level ### # Initializations ### ACI_ECHO([]) name="$1" ACI_ECHO(["Summary of the configuration for $name"]) aci_sum_more="no" # If there is more modules to handle rm -f conftest.nexts conftest.nexts.new # initialize aci_sum_nexts and aci_sum_more from the given argument aci_sum_nb=9 for aci_sum_tmp in $1 do echo "0 $aci_sum_nb $aci_sum_tmp" >> conftest.nexts aci_sum_more="yes" aci_sum_nb=`expr $aci_sum_nb - 1` done #cat conftest.nexts #echo "---" ### # Main loop ### while test "x$aci_sum_more" = "xyes" ; do # sort the list sort -r -k 1,3 conftest.nexts > conftest.nexts.new mv conftest.nexts.new conftest.nexts changequote(<<, >>)dnl because of the regexp [[:blank:]] # get the next elem to handle and its level, and remove it from the list aci_sum_this=`head -n 1 conftest.nexts|\ sed -e 's/^[0-9]*[[:blank:]]*[0-9]*[[:blank:]]*\([^[:blank:]]*$\)/\1/'` aci_sum_lvl=`head -n 1 conftest.nexts |\ sed -e 's/^\([0-9]*\)[[:blank:]].*$/\1/'` # aci_sum_this=`sed -e '1~1000s/^[0-9]*[[:blank:]]*[0-9]*[[:blank:]]*\([^[:blank:]]*$\)/\1/'\ # -e '2~1d'\ # conftest.nexts` # aci_sum_lvl=`sed -e '1~1000s/^\([0-9]*\)[[:blank:]].*$/\1/'\ # -e '2~1d'\ # conftest.nexts` tail +2l conftest.nexts > conftest.nexts.new mv conftest.nexts.new conftest.nexts #echo "aci_sum_this=$aci_sum_this" #cat conftest.nexts #echo "---" # stop if nothing else to do if test "x$aci_sum_this" = x ; then aci_sum_more="no" else # Add the sub modules of this on to the file aci_sum_sub=`expr $aci_sum_lvl + 1` aci_sum_nb=9 for aci_sum_tmp in `eval echo '$aci_module_child_'"$aci_sum_this"` do echo "$aci_sum_sub $aci_sum_nb $aci_sum_tmp" >> conftest.nexts aci_sum_nb=`expr $aci_sum_nb - 1` done # compute the status of this module if echo $aci_modules_disabled|grep -q $aci_sum_this ; then aci_sum_status="(DISABLED)" else if test `eval echo '"x$aci_module_misdep_'"$aci_sum_this\""` = x ; then aci_sum_status="(OK)" else aci_sum_status="(BROKEN)" fi fi # Outputs the title of this module aci_sum_header="" while test $aci_sum_lvl -ne 0 do aci_sum_header="$aci_sum_header " aci_sum_lvl=`expr $aci_sum_lvl - 1` done changequote([, ])dnl back to normality, there is no regexp afterward ACI_ECHO("$aci_sum_header"'>'" $aci_sum_this$aci_sum_status: "`eval echo '$aci_module_desc_'"$aci_sum_this"`) aci_sum_head_dep="$aci_sum_header " aci_sum_tmp=`eval echo '$'"MODULES_$aci_sum_this"| sed 's/[^[:blank:]] .*$//'` if test "x$aci_sum_tmp" != x ; then ACI_ECHO("$aci_sum_head_dep Optional submodules to build: "`eval echo '$'"MODULES_$aci_sum_this"`) fi # outputs the dependencies of this module aci_sum_deps="" aci_sum_dep_some="no" for aci_sum_dep in `eval echo '$aci_module_dep_'"$aci_sum_this"` do if echo $aci_packages|grep -q $aci_sum_dep ||\ echo `eval echo '$aci_module_misdep_'"$aci_sum_this"`|grep -q $aci_sum_dep; then aci_sum_deps="$aci_sum_deps $aci_sum_dep" aci_sum_dep_some="yes" fi done if test "x$aci_sum_dep_some" = xyes ; then ACI_ECHO("$aci_sum_head_dep Packages and modules it depends on:") for aci_sum_dep in $aci_sum_deps do # compute the status of this package if test `eval echo 'x$'"HAVE_$aci_sum_dep"` = xyes ; then aci_sum_pkg_status="(FOUND)" else aci_sum_pkg_status="(NOT FOUND)" fi # output the status of this package ACI_ECHO("$aci_sum_head_dep o $aci_sum_dep$aci_sum_pkg_status: "`eval echo '$aci_module_desc_'"$aci_sum_dep"`) aci_sum_tmp=`eval echo '$'"LIBS_$aci_sum_dep"| sed 's/[^[:blank:]] .*$//'` if test "x$aci_sum_tmp" != x ; then ACI_ECHO( "$aci_sum_head_dep LIBS: "`eval echo '$'"LIBS_$aci_sum_dep"`) fi aci_sum_tmp=`eval echo '$'"CFLAGS_$aci_sum_dep"| sed 's/[^[:blank:]] .*$//'` if test "x$aci_sum_tmp" != x ; then ACI_ECHO("$aci_sum_head_dep CFLAGS: "`eval echo '$'"CFLAGS_$aci_sum_dep"`) fi done fi fi ACI_ECHO("$aci_sum_header") done if test "x$aci_modules_disabled" != "x"; then ACI_ECHO( " Some modules are disabled[,] either by you or by default") ACI_ECHO( ) fi if test "x$aci_modules_broken" != "x"; then ACI_ECHO( " If you think I'm a dumb script[,] and these broken dependencies can") ACI_ECHO( " be satified[,] please try to use the --with-PACKAGE options. See:") ACI_ECHO( " ./configure --help=short") ACI_ECHO([]) ACI_ECHO( "The other modules may be build even if these ones are not.") ACI_ECHO([]) fi if test "x$aci_modules_disabled$aci_modules_broken" = "x"; then ACI_ECHO( " Everything went well.") fi ]) e2guardian-5.5.8r/notes/000077500000000000000000000000001477372360500151025ustar00rootroot00000000000000e2guardian-5.5.8r/notes/AuthPlugins000077700000000000000000000000001477372360500224312../doc/AuthPluginsustar00rootroot00000000000000e2guardian-5.5.8r/notes/HOWTO_Logger.md000066400000000000000000000151361477372360500176310ustar00rootroot00000000000000# Logger and LoggerConfigurator Starting with v5.5 we implemented a central logger. It can log messages from different sources and routes them to different destinations. It is be used for normal messages from the application to the user, to direct the output of logs as well as for debug messages. It is configured using the config file. Debug settings can also be made on the commandline using the -d option. ## Logger Currently the following are implemented: Working Messages - info : normal messages from the application, default output: stdout - error: error messages from the application, default output: stderr - warning: warning messages from the application, default output: stderr Working Logs - accesslog: access log , default output access.log in __LOGLOCATION - dstatslog: dynamic statisics log, default is no destination - requestlog: optional request log, default is no destination - alertlog: optional alert log, default is no destination Storyboard tracing - storytrace: storyboard tracing High Level Debugs - enabled when e2guardian is complied with '--with-debug_high' Note all debugs are disabled by default and can be enabled with the debuglevel option - proxy: logs flow of a user request (explicit/tranparent proxy) - thttps: logs flow of a user request on transparent https port - icap: logs flow of a request on icap port - auth: logs flow of authentication (including auth plugins) - avscan: logs flow of AV scan (including AV plugins) - dwload: logs flow of download managers (including download plugins) Low Level Debugs - enabled when e2guardian is complied with '--with-debug_low' - config: logs infos about reading the configuration files - trace: debugging messages about the program FLOW (entering/leaving functions) - story: debugging messages for handling the STORYBOARD - regexp: debugging messages for handling REGEXP (regular expressions) - network: debugging messages for handling the NETWORK - content: debugging messages for handling CONTENT CHECKING - debug: debugging messages about the program STATE (variables) Note: Production systems may be complied with --with-debug_high but should never be complied with --with-debug-low Log messages can be sent to the following DESTINATIONS: - stdout - stderr - syslog - file - udp - none (= disable logging) ## LoggerConfigurator With the LoggerConfigurator it is possible to configure the logger, either by using an option on the commandline or through the config file 'e2guardian.conf'. To configure the output for working messages, working logs or storytrace sources use the following syntax in e2guardian.conf: - `set_{source} = '{destination}[:[filename|sysloglevel|host:upd_port]'` e.g. - for sending error messages to stderr use set_error = 'stderr' - for sending error messages to syslog at level LOG_ERR use set_error = 'syslog:LOG_ERR' - for sending the access logs to a file use set_accesslog = 'file:/var/log/e2guardian/access.log' - for sending the access logs via udp use set_accesslog = 'udp:my_log_collector_host:upd_port' To configure debug use the following syntax in e2guardian.conf or after -d on invocking e2guardian: - debuglevel = 'type1,type2[:destination[:filename|sysloglevel|host:udp_port]] e.g (In e2guardian.conf) - to switch on proxy debug to default destination - debuglevel = 'proxy' - to switch on proxy and auth debug to default destination - debuglevel = 'proxy,auth' - to switch on ALL debugs, but not auth to default destination - debuglevel = 'ALL,-auth' - to specify destination debuglevel = 'proxy:stderr' debuglevel = 'proxy,auth:file:debug.log' etc. (On command line) Add flags above to -d option e.g. e2guardian ... -d proxy e2guardian ... -d proxy,auth e2guardian ... -d 'ALL,-auth' e2guardian ... -d proxy,auth:file:debug.log For more details on debuglevel please see example e2guardian.conf Debug output format A number of different format options can be deployed for debugs See the debugformat option in e2guardian.conf for details ## HOWTOs for Developers For each source there is a macro, which can be used to log messages, e.g.: Working Messages - E2LOGGER_error(...); - E2LOGGER_info(...); etc. Working Logs - E2LOGGER_accesslog(message); - E2LOGGER_storytrace(message); etc. Debugs - DEBUG_proxy(...); - DEBUG_debug(...); etc. All variadic macros will accept a list of variables of any type accepted by ostream and will convert these into output format. So, for example:- int x = 5; String s = "Test String"; std:string t = "string"; DEBUG_proxy("The value of int x is ", x, " and String s is ", s, " and std::string t is ",t); will output "The value of int x is 5 and String s is Test String and std::string t is string" Warning: Each macro generates an if statement like:- if(flag) vlog(.....) so always place within braces if it is the subject of a surrounding 'if' if(condition) { DEBUG_debug("Some message"); } else { DEBUG_debug("Another message"); }; is OK, BUT if(condition) DEBUG_debug("Some message"); else DEBUG_debug("Another message"); is NOT OK and is likely to generate at least a complier warning When a DEBUG_mmm(...) macro is not compliled then is mapped to blank by the preprocesor, effectively removing the line from the complied program. You may enable or disable a specific logger in code: - `e2logger.enable(LoggerSource::debug);` You may even enable/disable a specific logger in gdb: - `(gdb) call e2logger.disable("debug")` For adding a new logger source (tag, category) add: - to the Enum `LoggerSource` in Logger.hpp - to the vector `Sources` in Logger.cpp - to the macro definitions at the end of Logger.hpp ### Running in a Docker container when running e2guardian in a docker container, all output should be directed to stdout/stderr (see ), so that the docker daemon can collect it (and show it with `docker --logs ...') You can enable the dockermode in code with `e2logger.setDockerMode();` ## Obsoletes The new Logger obsoletes/replaces the following: - e2guardian.conf: - logsyslog : can be replaced with 'set_accesslog = syslog:LOG_INFO' - nologger: is replaced with 'set_accesslog = none' - loglocation: can be replaced with 'set_accesslog = file:loglocation' - debuglevelfile: is replaced with extended debuglevel options i.e. debuglevel = 'debugs_needed:file:filelocation' - Code - files: DebugManager.hpp/cpp - defines: NETDEBUG, CHUNKDEBUG, SBDEBUG, NEWDEBUG_OFF, E2DEBUG - vars in OptionContainer: no_logger, log_syslog ## TODOs - Logger Output to Email ?? - Logger Output sending to GrayLog, LogStash ?? // may covered by udp destination (last edit: 04.01.2023) e2guardian-5.5.8r/notes/ICAP_server_notes_for_implimenting_ICAP_client000066400000000000000000000033161477372360500261160ustar00rootroot00000000000000Communicating with the E2G ICAP Server - Brief notes for ICAP Client developers. Developers should comply with RFC 3507. Allow 204 is implimented in E2G. However, as of version v5.5, Preview and Allow 206 are not yet implimented. (These are likely to be implimented in a future version) In addition to the standard ICAP headers, clients must supply the following ICAP headers: x-client-ip: (The ip of the browser making the request) x-client-username: (the username of the user) These are used by E2G to determine the filter group (see notes/icap) When E2G receives a REQMOD request it will respond with an ICAP x-icap-e2g: header which should be included in any associated RESPMOD request. It consists of six comma-delimited fields, which provides context information to the E2G respmod process. The fields are: username, flag, - letter denoting current classification of filtering Filter_group_number, message_no, log_message_no, message_string If E2G reqmod responds with 204 then the client request should be passed to the target host without modification and the target host response sent directly to the client. (Note squid does not seem able to do this and so with squid implimentation every request and response has to be sent to E2G). Wher E2G reqmod responds with 200 and a response header/body (a block or status message) this should be sent to client to complete the request. If there is not a response header this should leave those requests which need content-scanning (indicated by 'G' in the second field of the x-icap-e2g) then any revised header should be used in the request to the target host and the target host response submitted to E2G respmod. Philip Pearce 13 Nov 2023 e2guardian-5.5.8r/notes/LogRequests000066400000000000000000000024421477372360500173040ustar00rootroot00000000000000This is a new feature (in v5.3.4 onwards) which can help in trouble shooting It would not normally be turned on on production systems, except when diagnosing any sudden-death symptoms. Enable by defining rqloglocation in e2guardian.conf This log will be in the same format as access.log. It shows all requests before any processing is performed. The filtergroup should be ignored as it will normally show the default group as the record is wriiten to the log before any authentication plug-ins are actioned. The following information is provided in the 'what' field:- thread_id - allows thread to be followed and also cross-matched to syslog and access.log TRANS, PROXY, THTTPS, MITM, REQMOD or RESPMOD showing the source of the request TRANS - transparent http PROXY - requests via the normal proxy port(s) (8080 etc) - includes transparent http THTTPS - requests via tranparent https port + flags for presence of TLS and SNI MITM - requests from within a MITM session (can be via PROXY or THTTPS) REQMOD - requests to ICAP REQMOD service RESPMOD - requests to ICAP RESPMOD service WARNING - this option also modifies the access.log! With this option active the thread_id is also added to the front of the 'what' field in the access.log to allow cross-checking between the logs. e2guardian-5.5.8r/notes/NEWIN_v4000066400000000000000000000046111477372360500163200ustar00rootroot00000000000000New in v4 (v4.1). The v4 is written in c++11 and so to compile it you will need gcc v5.4 or later. (or another complier that supports the c++11 std::thread library). Note that the target systems may also need an c++11 library update. REVISED PROCESS MODEL The parent children process model (which does not scale for very large numbers of connections) is replaced with a queue/threads based model. The main thread now only deals with set-up of the logging, listener, and worker threads, the input (and reinput) of the lists, signals and statistics. The treads communicate via fi-fo queues within memory and so there is no need for ipc pipes. A listener thread is set up for each ip/port combination. They listen for a connection, accept it and then push the new connection socket on the appropriate worker queue. The worker threads pop connections from the worker queue and deal with the connection. When a worker wants to log a request it pushes the logging data onto the log queue. The logging thread will pop the data from the queue, format it and write it to the log. Most of the above logic is in FatControler.cpp. The logic is now much simpler and has reduced the amount of code in FatControler by over 50%. Socket classes have been extensively modified to remove interrupt handling (for list reload etc) and all select calls are removed. So there is no longer a need to modify FD_SETSIZE. New LOptionContainer class has been written to hold list and filter group setings. On gentle restart a new LOptionContainer object is created and loaded with filter group and list settings. Once fully read in a global shared pointer is switched from the old list to the new, making actioning list changes immediate an with no interruption to service. TODO - for next release (v5). Make lists parameter based rather than hard coded. Create 'story-board' parameterised logic for handling filtering logic. Transparent https option. WISHLIST ICAP server mode. Re-implement mailing option. Per-user exceptions/blocks. NOTES FOR PREVIOUS VERSION - v4.0.1 All pics support has been removed Mail option not yet implemented. Url cache not implemented IP cache not implemented Auth plugins - tested and working Scan plugins - some tested New e2guardian.conf options httpworkers enablessl Please note the new format for the dstats log. See notes/dstat_format Philip Pearce 12 July 2017 e2guardian-5.5.8r/notes/NEWIN_v5000066400000000000000000000165241477372360500163270ustar00rootroot00000000000000This is the v5.5.3 stable version Note that large sections of the code has been re-written and there are significant changes to the configuration files since v4. V5 is written in c++11 and so to compile it you will need gcc v5.4 or later. (or another complier that supports the full c++11). Note that the target systems may also need an c++11 library update. See notes/NEWIN_v4 for details of the major process model changes incorporated from version 4. REVISED LIST and STORYBOARDING MODEL Version 5 has a completely revised model for defining and using lists. List definition is now separated from list application. Lists are no longer hard-coded, but mapped to a function using a storyboard. Filtering logic flow is simplified and made more consistent. Requests are analysed first and flags set (exception, grey, blocked etc) and once this checking is complete actions are taken. Large sections of duplicate logic has been removed from ConnectionHandler and large sections are now separate functions. Storyboarding is a simple scripting language that maps lists to functions and allows flags to be set. This means that new lists can be added without changing the code, by adding a new list definition and then applying it in a revised storyboard. A different storyboard can be applied to each filtergroup, so if required, each filtergroup can have a different logic flow. Please read notes/V5_list_definition.pdf & notes/V5_Storyboard.pdf for details. TRANSPARENT HTTPS Detects SNI and flags whether traffic is TLS. Currently limited to port 443 traffic on BSD. On Linux multiple ports can be redirected. ICAP SERVER REQMOD and RESPMOD mode supported. See notes/icap. DIRECT UPSTREAM ACCESS I.e. not via proxy. To implement globaly comment out 'proxyip =' in e2guardian.conf. The storyboard action setgodirect can be used within checkrequest functions to enable selected protocols/site/urls to godirect. e.g. to send all connect requests directly add if(connect) setgodirect to a requestchecks function. This can be also useful to by-pass squid for some requests (e.g. os update sites) when squid authentication methods are being used. STORYBOARD TRACING New option 'storyboardtrace' to enable tracing output - for storyboard bug-fixing READABLE THREAD_ID FOR LOGS & DEBUG Most debug and syslog messages are now prefixed with a thread ID as follows:- master: for master thread listen1_proxy: normal proxy listener listen1_thttps: tranparent https listener listen1_icap: icap listener where '1' is index hw10: for http_worker threads where '10' is the thread number log: for logging thread for access.log HTTP/1.1 Support for HTTP v1.1 completed - including Chunked encoding ANTI-VIRUS PLUGINS Anti-virus plugins implemented for proxy, trans and ICAP INCLUDE FILES IN e2guardianf1.conf Filtergroup configuration files may now include other files, enabling a more DRY approach to configuration. So configuration common to several filtergroups can be placed in a file which is included in the filtergroup config file. Syntax is same as list includes - .include OPTION processing Where single options and list defines with the same name are repeated only the last one read will be actioned. This differs from pre-v5 versions where the first single option was actioned and any repeats ignored. This allows the overwriting of single options and re-definition of lists in a structured way. LIST INPUT VIA STDIN This replaces the totalblocklist in previous versions allowing multiple lists to be loaded via stdin. See notes/lists_via_stdin. OPENSSL v1.1 SUPPORT Supports OpenSSL v1.1 Note: Support for pre v1.1 OpenSSL versions is withdrawn in v5.5. % OPTION FOR PHRASE LISTS New % weighting option for phrase lists Code amended so that -Wall compile option gives no warnings BETTER HANDLING OF TRANSPARENT HTTPS REQUESTS Requests without SNI and those not TLS no longer tried for MITM. New option to use original ip & port (currently Linux only) RELATIVE PATHS IN .Includes and list definition The directory of the current .conf (or list file) is taken as the prefix path. LIST DIRECTORY VARIABLE A list directory variable (LISTDIR) can be defined in config files. This works like an exported shell variable and the contents will replace __LISTDIR__ wherever it occurs in a .conf file or list file .Include. This feature will allow the development of templates and easier separation between boilerplate and user configuration. REORGANIZED e2guardian.conf files and list directories. Both example e2guardian.conf files are reorganized into sections and more usage notes added to make it easier for new users to understand what changes they will need. List directory reorganized into sub-directories and READMEs added to help users to know which list(s) to change. The example.group sub-directory should be copied in a groupn directory for each group. Common lists are now in the common sub-directory. As each group's lists are now in a different directory the e2guardianf1.conf files will be able to be copied into a new group .conf with minimal changes. V5.5 NEW LOGGER and DEBUG system for logging, messages and debugging. Thanks to KDGunermann for all his work on this! This replaces the legacy and 'NEWDEBUG' with a unified logging system. Note that the --with-e2debug configure option is replaced by two debug 'levels' --with-debug_high intended for high level debugging (similar to NEWDEBUG, but covering the whole product) and --with-debug_low for low level code debugging See notes/HOWTO_Logger.md and example e2guardian.conf for more details LOG ROTATION New command line option -t for log rotation See notes/rotating_logs for more details SECURE PROXY Secure (TLS) Proxy is now supported. See NETWORK section in e2guardian.conf for more information. OTHER enhancements Made sockets non-blocking so time-out works with MITM - completed but using blocking i/o Reduce unnecessary memory usage on in DataBuffer & DMplugins Remove broken mapauthtoports option/logic. New Bearer over basic token-based auth plugin. Integrate DMplugins with storyboards for plugins. Option for output file for storyboard trace (part of new logging system) Storyboard action by category added alert.log option to enable reporting of key category violations by external email alert and reporting systems 'Auto' MITM certificate generation implimented TESTING - --------- Please prefix all bug reports with "v5.5: " TO DO IN THIS RELEASE --------------------- TODO - for later releases. ----------------------- Create 'output-filter' plug-in system for content modification POST content filtering Implement a "learning" mode, test the rules effects without block Remove duplicate list/storyboard warnings with a DRY system. WISHLIST -------- Adding a 'control' port (something similar to REST API) to pass commands (eg: reload, update lists, etc) and check status Multi-tenancy option. Support for authenticaion helpers to allow ntlm, basic etc auth without using squid proxy. Per-user exceptions/blocks. Per-room blocks. Rate-limiting feature HTTP/2 support (v6) IPv6 support (v6) Philip Pearce 4th April 2025 e2guardian-5.5.8r/notes/New_log_fields_in_log_format7-8.pdf000066400000000000000000001001061477372360500236460ustar00rootroot00000000000000%PDF-1.4 %äüöß 2 0 obj <> stream xZ96W`("HkÙ1<$m(j87$NŞa64qzQϷǛ>~;)N/g!WyJ¬,,LцkYbYipu=sqhNÒ3?)`Y#RyRUiiKwE|F2pYu ,Ü:E*3^L^2V#ԺD \RpcqkV,?}1`ʠH#H\>V$\(͊L5I:%i(a^0q[fx4f{vjd4/aOAs3^N|:awgLWMN*Ma3B,riNXx rxg5qNRa2 N'BQ;*u{gZp7D&ɵpDԇ%RsOz%=FoFbBV ؘIYMH433mt猇fMG&l/#e)!jN{>) &L62ՌU2OK#F P/^`sL :7%JZnc``I] p"5jٱI>1Ihd[&!ELĪ]|Mn٩Ug̚Ng)0]ދJ݄~&8'}V^ Miƍ:*^0Aə KXxɰi$gXL9XOe픝hJiM0֊ 0fU줹j1"_ы+rK(*n2j?utxny+bE -FdS繁8wܻnP>Fml5~RpKhzt`|) Y;%W~*DYvgC#juP;af/uO: RhNMFR,Uj3JM2x/+ˑyENr~.\H| ϫ7 @ɳjyKNQuҭu43M<*.nYXDrc&z*nތk!)/D^1FgiNG#̋W2'7jq)1uC~OPNlը{ ͟ OmىiSƠAo~j3Vc v_ؗ=zyF?tKgQ½_;t?p;-v! "·C'xi؎]f FZ!V bN852*`C䴩G[rjC&Lv}F/:<`RiPLTFu:gb@[c)LGtt}F6|Mν T$H=Ab<YNw֜IpW{9z˾f0 endstream endobj 3 0 obj 2265 endobj 5 0 obj <> stream x33T(*T0BSKS=# C= Tp-<@y endstream endobj 6 0 obj 40 endobj 8 0 obj <> stream x|y`T9ٗ{gY2If&$H[@@ Z J#Z*vW)PYTn'_F_o`T޺ ! 1Tnz꺻PMz6j #F~v8oCu-LьLPNrl ÑhQ+'JG?r-?@3% ܸ4\ HF}s+Pf??pհ/ ^Fs{k ^ςy mw-,`t VңA+hw:PjZ5' B =~0\nU`ՐZ`E+}>_3gf^%p m-T #i^^],/+MKbbqQ4.,~{=naY-fzVV)rCSD᤽Cq|lyyy>Me\W r_Q\QΖLdsO|45 L< Si1zRvPS@Фh pdbv)d3uCa@]gj͵TsS (uEIcdkŸMLt̅64,27L*sIwD/Dޫz:ip9c \\Fh0:{5ЈF}n=V5t]pMѽilA=Z5ه\.\QR 3"iO 8 x( ?p$%IͼMAR,+"RQ )}RoDLruxժyFeAE7m\zU>B_^7U->w-s* Ҳ%MbF̼EUP8fs3?*$ 9 s@RyeUNVH5-+[_6 iF؝#}E###tDĉDœx hiVc:.>Go$b%+'҉i$^ _j4DhF4G2TVrń؝2*R%noH(ɯ $ 3Ʉ)@!CuGZ}ʯVu4/KcϠOTъg,;wMCƃ:pf<-Ϩ/64R~fDGUQZ*0=u3PT n\25&E SܡfNeC8nIK*u܄Z9WP =vENI*R0b *01Y؝HLňa.!SS NFp !aY|znl={R8klꁉ =ӿ]{*|ij!$R**eFLn,/kKղuv&ٰG0bmHQ0@U me SE[eb-Jm_VWx_TZQE5"Y0h,!` bJ3U=\j_:UU+|ghz4 类k0Ylw޼| W_زxW}=5U 6EdrA8#PT0 iI^UǴ8|%3YO[1|͹7%+>9Hn"x7fw[1JT.|Y<|9%6Rd GS)ce%J8e^؜ZtuZA{.3_dQ=NSjRmD[U4.jQ LbI^ƨPuV;0ލx0s`Dn q*I"j-5Lt|Pb)VrJ: WqaSc4噍f1<BP,L+yӄhZѸ/4AX:#̩nHdЍ0aƼLFd~j.Y Z+R/?~tgc:;el)*+~R__\W[ w>ڵᝇ*>E#2 :\W=UrYYWsײ]Ӎ4q-K˴B/Re,NJ9'z;t>4z_@sDeԜ(i0kSʿ%&gj.Øi7cH"yXiœ(|(D ES(xHҌG*H3| Ot9Ɇ'Sc&(z>_$#1sbp'uɜgUsGe/)=5_:Yv*}Hֵ zv젦 mٺmҾLVxB1 9 &BS{nC#9vzpeM a~N&?k0jY,·8-va@)pZ̟!-nia)JI $7kIYlނӅ9i+V.~]ȋ-cF-)@ÃQ+Q9J([0O򥷖[R&Ut_KgJb)dQX*mCr~QOG@uP0#Gfc1ۺYұ FLLnNlsq G5sCW/æ4yqfUdH3+-_i,lOCrUq;-QEhqT\okO/zf͔CO4 .2(~_g.ۋiBls7W^s:gpEt {UzvmM&Z[ f>mXPKKs~sZf88# %NژI/Kml1D`<&pޒN.M̦h=u.At&B2y 6}%~&_r/^=$ɽu}*dJ2>_ }c^leܜKzʜ+io@vQzQ+f>)StP+9vˆ\KǗd"̌F2\p^Q{G/ ~Cay]|+~KKFv"|o᝕Ux77e]x/h@URHI" @)Kn{*9ys޻b)Va"0($v-6 {]2?_>Z@-&=' 'ӫڽ#Ws n& 3q \NL׬qr"aFEEKeŏitDHSe+\#?#SYYCcJx[|ڂ*w8*LwZ}|Uu/Z$cOK ;li*]I!s -cZ|zlgPe!p); V#rs3P#œk$1WS`\/h+)J_K ZtҒZ9*_kiٜ"I"K$5yMg}n1NjP+ .߿qў?ܾ KEZsJ|x)ߍHE&#uȵ8C Yhr+Cޜ&W\!5LOU [9b8C e =#cDy\ vG@k 00/b؈lGl 12EE>yśS`@/V@=*>q19 D `b**UdGXXeY-9c<b,ɱ mUvGČJ*55m@U0\ xZy;r`G D+,_Trϒ /<أ-%*M ˯;:^ uEyuN'SK۬\NThW/H2ȇ(P%@yD|1HL,R3H!T*@;iaMOtcQj4六!Ġs@BA Lf1?_f{ xlxPtO;=O3|,ea^r'BnN!\ 8+ˁI2i %L uKT=.o*%/l+K\t6Z8R:@];~شu0Q`C%W~0huȯS5K>gtMwUY|5 kgɾ $w^O2@,G,G+.$3PΞtժd6 hn i AH"`̕YdRHD~|E_~MvRGCUMYWyLϫgKʥd4n\ٱap|W>/oj9uU8r߹eꚂtVStmU  ;VQ$Ed?rR1֝<`]Kt\#.#qJZΦf?d!.}qQiByBJ<ԶnO!臲y[if#GN5iQk'E؋xw;nAn䣀ܫ X  Ԃ@0B=8$d2k\ nURZqǺFU%:HMXŁ|',90ɻI<&>^ǯץ+nim`pIH2Y[NF1La>N݂vysղY-݂Y-sF{sqP&kʨ9N}{AqmɃoqK^ZlzTQW1|;/ͻm׳05u+лxe2#x;W}hCeOX2eT˺3{ )|P *p~]2|ibQ[$ӷ 53S3`0xIv~0{*{CB6((4qB֭6j P`Ƚo-;es;}7*3Mq@6tjGdjSA0i4E.Q:v@}kmQ@Z x"t. 7ှ8=v`ԅo)3# 0aPn|hzoȴd݉"cjܒR}6[Tww+KjoL~xo;pOfh3nCI{o "K^/)B(8e);lSꞞ" ^w>0uHOAql'ZW /-4xkp(+7Xش\ErkJKDG:V ?]`9"mު̾>H)z!LGzcEހ(f.*y]ހ5-B֒_a6Ӷ=M">jcT!K"i~#J!K\Ӷ*Ƅ{zSMJ@b;دʸ$tFp#op'PB;Vy䡻"E5 Bջ޾|,',J?R)Yvbn/}x~,BRz3P@G4;r7CH}9gч7z$s{>F(5Cfn5t )Ȝ!{kMJL=c,QVjĮb(V‹f_۰ vyMgwVn/uZ虮 Dqo69#Ûb?ɰ'@Q{Y"3 "X! [&H \\*sUTW\<_2?2$18t"rhVi3f2EV`,(CsWWVjN0ZR:ޏx ^^7Dh8ӣ՚Vn #5 YX!oDL2KJlS7h,m:2 :qP=_ޭV [U딬On)4j‚Bђ?L>(Q 4OE+Dʐ .ƇT\Rif|hܼ?k Bf*!v,$Px/PNЕSNP^Iu#50SΊ92e>,kw%y?h5ɭs T AlfQww2G$b 8١ԿO7VgbtJ V6]QMnն챼41!)@pW@G_C?(zIBPmq %vgbf_Dv%Q(VΥL{5:swi(ct`mEqṇj5ɓk_k$VV&~qK %^gGH ʷU<'( {Nnp(N4`h'{DvWy"\Qa(o4n2թ*?R8k1]AR+'kXT^@c]1Y0(g{$-]+oũ,i%-+9b%J6Ot]'fC yp Y͖3Wl:4\^$ 5v.窅ﭮnjOYU7ku+*c_hK߮ 9Kvˇ[ߐn̆p3eY]7-vt ӑ@Bf)*F-v̳م_[~SuEG=-QAN-jԥfJ]tA+GdOc,[_w3'(fy9e6$4\Ϧqx/eUtW0}#J;%}7K?[OEO5^ l,wH]h-lz#~Em# ZzOp !K&l{_b/wF1H:ֆbbÖI͌l=&Xg3鹳}SDdT\QʅmrĪŋn4 Mk_cyVuӿ#eL7+*J2BJ]< NC =XX3*N@ҝ ޱJZy) !9`H ;_GiC91=e'W23aJS<ЫWΰƛ9sE0}F (Z2Sy5S 984#5R6+/is@4d.Q4RUc+(O{ش;Nxz2LQ+QxkL._t2v*W jL%yO\75=vTZ>5}mƹ$T KZ#e${Fv2#W)r2ʝ4D s11=r+9༑C 1w~;ΣOe#$91&CH*+5~}x ?4x՚eŅNľ&I7+}kBᇡosؘ/vM˗~|M\'_}P3Bٽ  IT,B7ɹ? T}0v>;38#@ƫ B| w󂏱DH.ڂla#T8liS>by)KMID"r䱗Y_}U7Sf|q[,0;Ӭ;שMŹQ\ v6-tL?W[d^ʩ̓H c\եPC O*c6 i*49řKv<&wʀOh[ GSTIu oJ  ^uB5UDqrU5봨mU=#S{{qHQo-sQ]fCgk[,cBڰ5*UO[F.pOUm@a⯦Q2f>䪪FAƫx:jW2]Uʍ/cu8> `p<, ]+/k]AJRwY,kYj#"A]kɠEyA Ar> yEG[t׹@LM88d|҄&SP5/Ct5ɘuJJAw^>ϡ_@9.jC{x[n:;R*nHC wD*K5ʥd'\k\VD 2[\V^L|[ ӹzos^hx@O׶P:ˣ?%8l/a{z#ӿ]mϷWJ M& XgnGa )Htn MCaVJ&#=8ǝ#O3o0ݲI!@ 343>O(|¡k1duI *30%LHY|9Yߤ1 {z>O|#QF #hFco9=|T}vŲ<+)asBL.a?|x?go:qNiI\UiFPRUZ0@(0?^2zIn~뒥eZg m*?dn~}2ϢVH]) :%G66a[k|F (T|B} `j X"[;+5ɪJT;o`$`#z7)'"/E1v欬C~R1ܠbTPUu!Q;m7 kKL)n0?i9a}ml;i{11u *k=U^w Z,fˑFLXwo:g TQ PiRfP}\,{YXtV=da\YX^L RX`:V'$sV_eatu ӈVeaߛeK; QO|L& +y5 :L`>#S JT؄`2I`3Y̳VRF`鳏'O|I."V5+!pf1'3CB<+m0&4 o;64XۼY886*t l9_20}Po{gKʁhPV]STZ-))*K}葭ބQa`hlp`+l84:6}_?w0nS ha֡14~Xл_D  :}h`w;zBlX;w ʮ–Ž4Z݆cB02}{nPvR>ܿo bP>o~ZBf44juC goݼ[(*ǃ.M5sWDm݈09޴#f sU lX>?k޹Ekͬl;Fv ;p#WH(ڵxKf}[ı# #FE ` :06smCH~N 49O?I <cAC(l@c@f!^T3ԡ7{Q() ]Qtǣ7~}#؁F]VƯ$cG@PJI[>1ts\򒀮-} G ɺq& c ώ G+J« T"kw\RZ̮`xԊW4Dz5e{+؊ʋɜѻH]d&ntAY_w,cd=[Ȝu'csԄjsZFSȌ\"Cn!>A(0gyPv\䊹 TFق+[K6fyr4KӾYwEzde @֧Ÿs`ED,CMMJz%\T.r$Ћ_zmoem/{ye9Rꥶ_x}HE;r_Gno9r-s߃>8?oGcw?Ohq۔C㏢W7KS{yj)Zx')I5[Ν=GΏ 9\z#O,dg)8<8yq|ߟᾷH||<?xԽF~G=]' ?.<>GǞOuA9WoP&߹=o__ɫRS5'/Pđ wpbO?2>>N]w!o߳7Jq^^9 {`/-b}A!X@/X/w=:éXm:ɬSü歖0o2y.96̳A_<J-ȴRZ+z)^EyJ&YBkc]K'd1j%V)yRɃ %ߞVк~}Yd"zN :&IU̿DM X>tB7c57OՊsp|[\g,L.tM1pE-h'?g^gp|=xs|#8ǦI$3GԤ輻cxXd42#Y;v\5fF5Ʋ"k܁{? endstream endobj 9 0 obj 15539 endobj 10 0 obj <
> endobj 11 0 obj <> stream x]n0E .E qG;H %UZ Ù!=L7~=]Zo4S:~8U{9ú[p66>ܦ鏿a6YRצ筙7j㾋|%>7kKvujZUfՉ ǒ݄jcjOud![S8W \0Kq?1-xa\l-J֞;: n3v߁gK>Wſ_4,K͡h-+e;8[WOK ,w58,/pF3gg=kd#ϕ_蟣_?p.S1;BMg{ U+K endstream endobj 12 0 obj <
> endobj 13 0 obj <> stream x|i@[ǵ̽Wv IH662 80oYp;NIp&$Mʉyn_״Y/9]C__ҦF|gK}>I3眙9̙9gbGGzC4⻶w/B}!徹vý_S_A6Bo4fGz:Z1h $P53GwS?P@ C]S#P^sd+e vn<)BjhR9K#=5{aŤLьH, *Ej:hJ5Y6p?bbi$:1"n9J' UBO@|QwZFvƫp.QJh\qԋ:Q;vԁa#C[0څj 4POvqբc p6`kK76|߇n ա [%]>Q|zpq+ңaz osSԏЧPsѷcIwwѻ8KZK Cߗd\Ne[-iTѠi5:ETeRXF8Zv,Zv{ؗ,]Y.O^drU包rS c5*9TŰ>H/X=%+UwosV̕Pr?mS+=r_.:'WxX%($UrcZorUk[? Z2ez$!]1qeL":ĝ˽0yxE[:ngw&\'xѮVbjruq1np}ܤ*u]" ֶg)LUv}h]n44FU/"o W_ jj sd]$Wuv O L0kÖ'^I;dZؖla40 LsA6` 58 `AIyKjRM sHm1CD ~19i=it*FW#$b -iawApk~PQ$HL= YL1\]7 \'@(}/Ӑ䢃|wRN6T/I-ҀIERv5n}QNԓhZ gL6E2V%˗UD[썄1ŋ#|~/s `)X '荦 l2 PScw/@>vesQwÝn;ۗPFZp8N2Y_3u[o-75ѦId(9܊K mO,Wk[{ǹ=զT(*䜍[ 9GB.wdeMi=X p):E]QѪWVV2ZP bP տ]=N?ձY$l.7gIhi6X9AI9>?ۆ5IH\WGj}'gf:w`ie29Uf{fBhzF=ža>&س+K, xRKf.wW)9h\ zv`ZH˥qں [uUvX걱իuXg!4uiKtZhƆg 75t0Ιe}y9딅eb 3%/o4U jMk=x\XB# Ew2q@A9# ɐ/`@]CIeI]QA[_]mOw*&64{ҜR4mf-ӧl BnM5=쯜b:J"VF:%KltƘxDUIRha}8 s]:um|Zc]ofL J%WAİS\ e)HX](vhV $f$S)&s. YX$V5˲&^Ta3{Lw\s.'/d1BHB Sܻj:o@>_3=zS}MR1wP{vja" s [TuCn "&ļ=ss|BjR Ry70-kNoû9=>pl4۱ݞ0,,[u©Vb/b'dJ;L2IL*=Mr?UJXΔ10FG$bD w@ :.!#:K)zf4/ǡoi7ݵ[Նvd`ɉ#/K~(c[ߡF@F^){6 H̊)1 t0ϵx1&V'--c;1a`ըR)G$bD"fqfĴ!xK%M aFB+2,YuCMy*/1e$DH9IH-DBjrB.d/vM]fQo`{?i>rnj-Du99(VVůS"H0[0;,^h=EmϽO꡹iHLcxX`UjI?mTQ @fڜXf,UrTP'2"\HJaV.!](8 *&(Gb!>3=6I 8wC7ui4wy`K$~$bK7~csLzXy '6 ~CrKZ"$ O=<{D˖_mßI!֬ *Kc?˯%䱣Ɨ*VIaM"TfU} CKTYNEUD\)롋3?ϐ .M, Ƀݥㄫ-6SKO{-)v7l(l5NSQ_gJOo痵S6Q8`ˌk]Z!~UVq?Uh*1_¥2+~Ft-J漼@Mּ1߄hx'_ȏoxl]h+͙!X\;}bTS|233IWVΐ*RQ'E"ɓb0ޟ"R^; $7$Cԝ)'! )8|`d@~%Ts.2AP3Tu_`Ou{8TUuf.(04g4}c>U:Uu692ſ _P31rc\EyXA1jY@FdK}<Bce^OǞS2!4S^IM2>z2dP3",)BSkQ8_wbal=Ek2ntq_F) ihe[\:mn]׭4'ms*[ƾIi<㩙x/_))H ~'iR6E&3S&v10ybіGm"f MɔH*j"T?Qbs9ՌZC1B!rkfmw8Cm\}#]JW=MQQVۺfﳧGF\Vb/2 mBGMlsv#'nd 2Kb+*f5Xq_XWo,hSߙ OP fzs`sfNzHr"KY$~Nh;â/_2Xr$G+. (@;߸KD!ݫ.;qZ^%#ҺOG6pˁq~j܎{W5h5ʆZQg\ᶃp=Y\[(s wKk*Rz{ɠ?A>g9ie+/|Sz 2?VMdk8$ḨKd2-L=r+2zۘf2v ' CítN誕NZӯRSRMT}4dr-q~0†ZʩՅج|źSBtf&΃aƦQiOM\'- MT[xf3gT7;] ~6b/zwH),JStIdv<֨VZ9f+RT8{t]VdNOـf k<_eTseӿ@ɸeɡ:b6r]k,uF34O<Baf a׻_YcMQ4R ^Ѣ` D'JA^6Q7Q S Y{yS!,/9+ P"P 3. ;=ai3gNŋ6lɱRgNP:f<qz}*F}oAzuNKZXKAM_֯LQ]8S]Tl^_R1\+ͪ`#CeӨw7+1g g'.gge9Jf-.)1cl>X_ՂP8RVXmA-߇t.O"Ngvd3rVƫB;XTelp 0φ\pΧЉSSb+a%3Dd\uY{~ΞOml{g˛?nczkwuWS maj]Y;*.u]UE˪ƆZItٿlec˺N.) GN}mO>2y064H^m'xzazV.9_x=XPmB~7G& oL*K4`,ìK# '9d ^r{hHLgʽUSMJd"=WdS|=R[ g}[3wfN̜HXXșZq2'cCzyMe '/?G]ĖN\B|o #!RhiIɿ4CwGw#ӟS=ާ~#CG{Êݶ*#tlf&WfȆcOֈ4"tb( ګ} qlfsReT։`;2M dG"BV LwUX-.,ӰZrW}׋%w}׾:2?7nto lU ]dvJ)ؓR(^2&ޛ"|K̩Y"V9#';8@B!1"܅Fdv'WAģ6~.! ~ǝ;Fș2ȿ|-fPZeJ۝y?2fg7P%J4nP'T6L E]ȋdE&aSc~P]]G!R Gogs=ڟ_o[vnuMF^phʶKfSg^ǝ bJ+WAJ foV4׾Yi}RWXx5ٓ‘SEߐfvU:mr oVz&߬)+έԪPjQlMu4%Z*IxR%߭l.Kt. T+*rx8NTqrgT[[7^Ȟ4J1D`pӔ1iı3B.׏V4{<`vʵIR6K֦Ilߕ1'VlZܨ6/g_5{)AҲavCxS_\SH;M?d$?7'۝E:GPkEQhr;S-ТE{+4*]$gIWŷ+=UdiZNuyT&[yM5ʺ:Z: ;K+;)NJiXITwsUq Jk,P Y'?~|92<_cS1XkԮ!׸pG#*Td2(^sODE:̬S.>ES* ie*%)Hr?xH! ;-f5.>#A@('EkCc!I$\|%N޾}m_dp)i5uMT7-{7zd=VVxc7ʖmX [?p׮[W=׵P_:ѳvGşZ'/[N-޻q@9d*}XZ^=1[6E"*.EħE Eb.(v~L>;ْݟyho$c6ǂicfG, ,g>kϗNβgM/.Md= #aC}͢tdخ)f̥/ر-Y-]YElѯ7 +څNm5+gRVbEt* 7TCTOw]EE/s;r%BB'iB]ߛ߹UY xe$H'.IU0I&(9jſMS"h2w}|P'!Qi)ZGA#]HL}D6TˤJ7PFU f&J9hg=@G}14 WãZ0/:97Q%"Dž:"Hޥb0kTNo!Vɟò岯2Wũ~Rr{շ/h5wh٤KyrC~,&u%u&`$NbPiQ AJ4_Wh$ [[Б$,E*,O2MIX 3L)C 1$̠ i&Է0Ҩ bwIAF,$aj^I0t@n9 cB?I;_M4ZC' C% P3ߎ𫓰}'a)Ju%a-IXEMN$* A:c {kK rHMDNIdp Y( 3% 0#`? =eC'&0#Kc^v. 3(S 6 I„6~!  xNARUx:z^/MšBhȸ9_\ypZ]Y 3 9?"+F,љ"V7s:קnFH4(E:( 2&Gh(`h]P*_CDh#+) 6w ˜a%Nh</HȗV ) R˽E;S5h~;2,بzZLbWܯoLx`DŽHܻ0P$?BWNfzeCg^=]OkZmtnޤO&Ǵkce/RE $t "i?k7`v  }}Mv¦=I3U7&$Yhp>rh y2_/,Clۡl]ٶIxLxl0PﰩS' <1nD/3A{s[ߧr}j7%;GNtήΰXTs`4:J"̩CXǦf5J*EI7DaJidP61bZjnf$MP9jc,eRzFJmDjk1m=o]ax]OKњX['!`ciZci ʍmL?h8rrIB7=]-6p4=s3wwV?YuNF^'FޱjBy QRnwtf& endstream endobj 14 0 obj 11027 endobj 15 0 obj <
> endobj 16 0 obj <> stream x]n0E|"W"!E*O Sվn_ֽ+ Ec*alcneA,s\rƺt .cE!q"G*@9sDN6|| i#R#ϟ 3Y"_iϒp,fKOCdC EJ_"og$T }$dW͟6(O?ƌbϮ%n׉&[Zz7۷8a=ߋi! endstream endobj 17 0 obj <> endobj 18 0 obj <> endobj 19 0 obj <> endobj 1 0 obj <>/Contents 2 0 R>> endobj 4 0 obj <>/Contents 5 0 R>> endobj 7 0 obj <> endobj 20 0 obj <> endobj 21 0 obj < /Creator /Producer /CreationDate(D:20201116124721Z')>> endobj xref 0 22 0000000000 65535 f 0000031418 00000 n 0000000019 00000 n 0000002355 00000 n 0000031587 00000 n 0000002376 00000 n 0000002487 00000 n 0000031756 00000 n 0000002506 00000 n 0000018130 00000 n 0000018152 00000 n 0000018342 00000 n 0000018871 00000 n 0000019239 00000 n 0000030353 00000 n 0000030376 00000 n 0000030572 00000 n 0000031018 00000 n 0000031320 00000 n 0000031363 00000 n 0000031861 00000 n 0000031958 00000 n trailer < ] /DocChecksum /6AB819881978627023525DD136846EE4 >> startxref 32194 %%EOF e2guardian-5.5.8r/notes/Upgrading_to_V5000066400000000000000000000045111477372360500200220ustar00rootroot00000000000000Upgrading to V5 from e2guardian v4 V5 introduces a completely new model for definition and handling of lists, logic flow and support for two new modes - transparent(HTTP & HTTPS) and ICAP. There are changes to the configuration files, mainly in list definition, but also some other options have been replaced by storyboard options. The e2guardian.conf is still used for global settings and e2guardianfn.conf for settings specific to group 'n'. Both these files contain revised list definitions. For comptability with older versions the same list names and paths are retained, so you do not need to alter your list file structure, unless you want to take advantage of the new siteiplists. Details of revised list definition syntax is in notes/V5_list_definition.pdf. Note that blanket block is now defined in fn.story and dg style blanket blocks (*, *ip etc) in list files are ignored. A number of settings have been moved into new Storyboard config files. These are noted in the example e2guardian.conf and e2guardianf1.conf files. For details on Storyboard syntax see notes/V5_Storyboard.pdf The new Storyboard config files are:- common.story - a library of storyboard functions - you will not need to change this file, but you may need to refer to it if you need to make more complex changes to the logic flow. This provides a logic functionality as near as possible to v4 built-in logic. pre-auth.story - actions to be taken before user is put into a filter group. You may need to edit this. site.story - site deployment modification of Storyboard library - you may need to edit this in order to change the site-wide logic. fn.story - one per filtergroup - an examplef1.story is provided - you are likely to need to edit this. UPGRADE PROCEDURE Before upgrading make a backup of all your v4 configuration & lists. You will need to refer to these after the install. Install E2guardian V5 in a clean area. Edit e2guardian.conf to your requirements. If you are going to use transparent mode set the default group for transparent mode. Leave proxy_ip commented out unless you need to use an upstream proxy. Edit each e2guardianfn.conf to your requirements. Using examplef1.story as a template create a fn.story for each filtergroup, actioning any blanket block required. If you want to use tranparent https mode redirect port 443 to 8443. e2guardian-5.5.8r/notes/Upgrading_to_V5.4000066400000000000000000000144561477372360500201750ustar00rootroot00000000000000Upgrading to V5.4 from e2guardian v5.3 If you are upgrading from v4 please read notes/Upgrading_to_V5 first. There are changes to both the configuration files, mainly in the definition of auth plugins, Both the e2guardian.conf and e2guardianf1.conf contain some new list definitions. Some new list types are introduced, including maplist, ipmaplist and mapport all of which are used to map users/ip/ports to filter groups Details of the new list types definition are in notes/V5_list_definition.pdf. A number of settings have been moved into Storyboard config files. These are noted in the example e2guardian.conf and e2guardianf1.conf files. For details on Storyboard syntax see notes/V5_Storyboard.pdf UPGRADE PROCEDURE Before upgrading make a backup of all your existing configuration & lists. You will may need to refer to these after the install. Install E2guardian V5.4 in a clean area. Main configuration files. You should be able to use an edited versions of your v5.3 e2guardian.conf and e2guardianf1.conf files. Changes needed to e2guardian.conf --------------------------------- New logfile formats - see notes/New_log_fields_in_log_format7-8.pdf # 7 = Same as 5, but with searchterms and EXTFLAGS added # See notes/New_log_fileds_in_log_format7-8.pdf for details # 8 = Same as 7, but with server field blanked logfileformat = 8 New useoriginalip option. # This option only applies when request is transparent (http or https), # when no upstream proxy is used, and where it is possible to detect # the original destination ip & port # When enabled the upstream request will be directed at the original ip and port# and no DNS lookup will be performed. # This solves the 'snapchat' issue and also should increase speed of connection. # Currently this only works on linux systems. BSD developers, PLEASE HELP fix # this for BSD, pfsense etc! # default = off useoriginalip = on originalip option is no longer supported - will be ignored in v5.4 Filtergrouplist is replaced with maplist Replace this:- filtergroupslist = '@E2CONFDIR@/lists/filtergroupslist' with:- maplist = 'name=defaultusermap, path=@E2CONFDIR@/lists/filtergroupslist' Add defines for nomitm lists - these are lists of sites that should never be mitm (where mitm cannot work) e.g. Sites only used by apps that do not follow normal certificate rules etc. Sites in these lists will be treated as non-mitm. sitelist = 'name=nomitm,path=@E2CONFDIR@/lists/common/nomitmsitelist' ipsitelist = 'name=nomitm,path=@E2CONFDIR@/lists/common/nomitmsiteiplist' Add defines for browser list - it is used to determine whether to return a block page with https. Non-browser user-agents cannot understand block page and often refuse to be mitm. regexpboollist = 'name=browser,path=@E2CONFDIR@/lists/common/browserregexplist' Add maplists for any auth plugins you are using - Note you must also replace the authplugins/*.conf files with the new v5.4 examples. #ipmaplist = 'name=ipmap, path=@E2CONFDIR@/lists/authplugins/ipgroups' #maplist = 'name=portmap, path=@E2CONFDIR@/lists/authplugins/portgroups' New logclientnameandip option:- # Enable logging of client hostname and its IP # If off the hostname will be logged instead of IP # Applies only to log formats 5, 6, 7 & 8. # on|off (defaults to on) # Needs to be turned off if you are using sarg log analysis. logclientnameandip = on # use dash ('-') instead of blank fields in log # This is essential for space delimited log formats, and makes all log types easier to read # But can be turned off if this causes a problem with log analysis # on|off (defaults to on) usedashforblank = on Changes needed to e2guardianf1.conf ----------------------------------- Add bannedtimelist - times when no access is allowed # To activate a storyboard change is required - see examplef1.story timelist = 'name=bannedtimes,messageno=122,path=@E2CONFDIR@/lists/bannedtimelist' Add blankettimelist - times when blanket block is applied # To activate a storyboard change is required - see examplef1.story timelist = 'name=blankettimes,messageno=122,path=@E2CONFDIR@/lists/blankettimelist' Add response header list defines if required # Response HTTP header rules: # Optional lists for modification or removal of HTTP response headers. # Format for reponseheaderregexplist is one rule per line, similar to # content/URL modifications. # Headers are matched/replaced on a line-by-line basis, not as a contiguous # block. # Use for example, to remove protocol upgrade requests. regexpreplacelist = 'name=reponseheadermods,path=@E2CONFDIR@/lists/responseheaderregexplist' Add searchexceptionregexplist define # search terms exception (overide) list # Used to prevent urls such os completetion suggestion requests from being detected # as search requests regexpboollist = 'name=searchtermexceptions, path=@E2CONFDIR@/lists/common/searchexceptionregexplist' Storyboards ----------- Replace the old common.story with the new v5.4 version. Replace the old preauth.story with the new one. Note that group assignment is now storyboarded in this file allowing more flexible group assignment. Using the new v5.4 examplef1.story as a template create a new fn.story for each filtergroup, actioning any blanket block required and refering to your old fn.story file(s). Remove the line 'function(checknobypasslists)' from your site.story file. Lists ----- You may wish to reorganize your existing lists along along the same lines as suggested in the list/README, but this is optional. Copy the following list files (new or revised in v5.4) into your working directory(ies) and then edit them to your requirements:- common/nomitmsitelist common/nomitmsiteiplist common/browserregexplist example.group/bannedphraselist example.group/weightedphraselist example.group/exceptionphraselist example.group/oldbannedphraselist example.group/oldweightedphraselist example.group/oldexceptionphraselist example.group/bannedtimelist example.group/blankettimelist example.group/responseheaderregexplist common/nologurllist common/nologsitelist common/nologregexpurllist common/nologextensionlist common/searchexceptionregexplist Note that phrase list paths have changed so that they are organised by language. First run --------- On the initial test run look at the standard output of e2guardian. This will show any missing lists as well as those which are never used by the storyboarding. Revised 10 August 2020 e2guardian-5.5.8r/notes/V5_Storyboard.odt000066400000000000000000000513641477372360500203250ustar00rootroot00000000000000PKJKT^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKJKTmeta.xmlKo0+m1dWu(#%øeld$kv> e鵪%޺Gk@u~TC pj.Eŝx랍"%DK,#j z)Мps6qD- (20v0nb~xg0m3K%njVbwl|LԿ7/ QCAc?0Cp~ϭ"R={ R 8`):S6ĪٌQ,Bբ-\ %CF<_){z~ۿ>+bS0 CR%YR (`!F%O).!xhpׯ떖>+u#w*"rW+]V6ǥlnuq:ٱ0+˂`tGiEP֛ݣ) \_z?PKQ%^PKJKT settings.xml[QW:~_Ae{]vU8IO ;I ׋T:>)d&3eRο."qp%/JG+ ^F/>ɄUD ksKp|"Ѳ!YaAk<᭥WˇڸQ.g壳vuRj};Z]'y'$6姃U$VKCCn!r:.;%w|:7go|T\Z}c1~#.Zv^~>ƿnq\biezn+ 7fnn \Ws'9ZkIVՏnX~%{B%V=VE۽8UN*ElQ*ማ):Sږ. -UbJ$܄F,Zh)?J k v4^ ).#XD() {Nޒa AY&-^H4(O**ʶ%XQٛf8 6R**^-l|,EcCecfy]dLZubuDw>#Dbbҏ/PւOpuآp @`I="tr&t %#ᖳ 3%BؤZfXo2ARoX5w{ng7Hm}fg-[S@NDL?P;2M"xuLBd4eMHVP4O8]2UlWco  a6X,SG7ӘIӣ!d{W_si8Y樀6Ae 8Aќri8@ؾ4A=]cқ-K2ܡ]zzR,PCuSZ] Vd\fen̽a[E hӾck{*_[J##&rWV SóÈ+~gID1}DӱPZW*GeN5ojOT|L,\p9r_Z"3iP Udo莀jLe#MŦ:@wzJ@K>/ibkTZຂXB ;(}%,]U꧵"S>fszՈB9qH}ZQ"4M{K`LV {jdh+u,V#26'L*OS߅3qc !>oO-)> [ XJI*T0૾iv$lǣTeHp &,TV\[Vx)n'K~D10rTd=9-oЖL|a"۽Kxh(ikjE4Ҍ C)=BYnJ]!xM}λ?~?5ЅOlo؄4+[~xrPRw* zhQ,1B5EO٪.K{,EB>"RP )1کV2Ԋ,fM;Ll eFKtgq0wLpj^K/់>ߒ}zM/=EPK+2PKJKT styles.xmlr۸=_NF,;mM'N;; Ih(C_҃ BD]ͺI8~o$'rBӻ~0=F4&޴7orI"c(`|+Ivj#'54Rl(5!`x v1K$h94éƞK!%eצ nt@f]WB:KpDTM3̈%sJ=rh6k;uGڴ lUՒ)ȖbؚRAR&̆gC"] 5bG"rcJҸv6'd?xpFuUtK KTFq\wve5MVD %8* `J3Wk3DFit@;7CAcA )hBT@->QD21]'ޒzd PUgE?Z&QvSz$+koz&G.)%({쭄bEP Iv6&0oS,F7(` +e41fi %#<q8[zE'q:H(ߒě؅](og|QfbDEWwP&_yAq5g!$JF7ct uRI8[F~oI[ʣ1#YkӭL{w}06PUxg()#_H95D~Q!vuaN-%|DDIm;C I$.ACb@cPQ8!YY0 ၚHkD.kÍdpX r㰝I{=SKɉ6 H.TNc9Aq9tU TDPp&BJ` [nZ,A:DiWbqqVa_2w:]/QK蚸˗u6DQ4Q;PwPPek%(IeHȆ YFPp;1}/&6RfQ wuq߼a ]dxHFfEvQZm,ey-\KMYOu ʄ[ Ɨ!Â趁 ug3kq#\ !>{ƈdD.VOb+JaM*? 8E7B@ Dq_P9 AX|V(P&R#8Ieq-]Bz ~+Û"ސ^T->jJsRNк}?U;qorHYv[l%N{?ͤ/ʲdjE^:Dy}AN`W\vQtzdyE0tFQ-OՕl:D1O [Kq5K谊k@ m#n-'8цC|E "rk^*J/D ~T7}^44QwpLșq.#,(ƏL 1hرSSuuZ ǃ_^Ole:UɴR] ~ lĩ[|dcrFlɨL)^HQ>ثΈݐڑ862c[]D.hsҤ ʴ-%?# h6/l7AH|&'$9[/2G #x'(_M5١D8jr;W4CVQh2U,w8턅_:jG +ZXh!=9-Ful[niEOm-YFb{;iiJ 9|KqQDv$, L=Tgk.5}V}MMf7e~/j.GmVF)o|N!"w`>{/_7ի/g )M$Bc풒gNlI,35Fse' 6OJtݹ5w˃H6X?Iufp rpInq-vZ |oP>\Lnæ'H)ύ׾&4V ?mԀ i^V׫pҢp,9y7- e50~L- e5pZ5Qs!5 j`z5NӰ q4/9 XGO+ɬMQ`4o Zdq-~$B?s.>ZRMJ jO()8}V0$"?>iԑ:EgNL4n4h DSѴR뱻O>ͪ&I*YkI#&_EiI98yI䕠'fp ?oY߂TgΉH{8zxv 9^D7X :C{6Gwd9/sc gl`y𠗀=ʈ%:B@#ί5-ϲ }`<ΈWґ/k2l.P^ILۊ7mgH^耽$E~ ׼ z&kKcsړl' AK9dJ5Ń\TQ cQ} C^`ں>C{eVi7g21fɌBW;7_7(/ӲւʖnKRЂCf*{&(sO2^5zܝW `z,?t.N]s~2PљvQ(9@?̕,94v#\xklp%t>BLSIS3j}0|BD>BJ~W+T@S s>4Ӑ;sx`9t=KL4ǒJƥŗc4frJ.T=_-R9IJ)\񔷵 +sG< 03w枦s=󘺮L{2q]H)%'Hm<4qԝC8jC*^C`~|9q> ,'xHnԆs m5AXO6VZA ~CT[YVGh]T)"Y= \KgkTT|) iw#)=Jklu@mim:.`7zYX K<}t,9sp'^^b,ҏe~^Q֦4-t+"/ٴõ.僯\ =&S<o7$M^[lNRگA~gvK`[`k]}5~`wYߏrǴW6W{zz}}[>o|oHq tqϓOٖP-?[d yDn Yk 0*X,o-)0P"@C!Ƿkwʆ3NJ]8\ůXi՚BvcDv.KW?!(\lM7=q={9J4[b`7J U+F 97(h9SP@Z,lu"?Z/2@˸X̫aͺwnW~v]@j@~ãK:|󾿾k\1.>'ޅ/y#;L)w 6esPfyhN/M%rv׀k@@lC"t%DbU:woY'֊$}=rc.97Z>yDXjj* P#T^b}6̑2>c\(ۍ^0˶~%m%.Qs'V7nh$WϺXG§ݞX XDžeNh#dmNZF"gb91m AQ1"3ఴ8*>lj~FƜ0rSؐB1_4jB/@.6l3=_4]P` l4΀1hr8Cq bP5_Q:J8Ω2QfKmXk@ުT''J).Fn!Y0|ن?aO{'Fbvqȷm%vscYzvR!vP:a3.`W,p'Ñ?*>RˡUc,;GfWaGY!=>xonYmxIƩ~z4wy @ @ @ @ @ @ @7 aW}IENDB`PKJKTMETA-INF/manifest.xmlTj0 )#~.4a'@u?y9i3FGfY#683brbۏmh QN"Q8K`$h1ȨjRz92Wvo]b>CoX6 adZ? X,s/t +& ke;t5j$Tq|n,%Z+ҋ@Ƙ{ U9i$?BJ:/T~Xu_AD Wse5noKлKj7/S#068yG:BbgsPK(R'OPKJKT content.xml=8)^`>mw&Lfg/@A K[YIr:}$WERdɖH˖: l-Qd}X,6P>$VQHFnuľU^."w%a6q0*y޹ijd_gY|7><<>Q.IF>gzx^ZWUczNL>mbҷE?ҵ6p֙a9K_y_QMקu؋G{ XNď$OBgKһ̽b+\cOp_c['?Bc{>o+t f='\X7sL{p(&ؖ$J_W0 l[6@Dɦu t`1K=do{$s!XL7 HWhtڸ5 \ڨy,Jy$NeMGIfxUVt2ZwzAPsT;Oéi]PxJڭV(T \ *=THE S,'%GSئP~\'hpCˮ uvoA[]~tO#P10 >~ `v'L<)Qb2]Q7'L*7cMs|7ِH RD['6%>ICHq(k {prm$4I|(jga}T3(0=Jv(>iz&t˄vseVnt%@@5C&`;l4#Vq\tvHHWy2ƞbXa%}2Ae$I?x=uv#qZc(l1NKtM#L6lCȟs@[Ks *'$Tkj& YS8e) $@e]xlު *p%('YggJY+J=4 gQ*2\b5d%I+oq3%55Irr rY7rY[Ϻ$;..˭[*7U]R-jRڧ`5Ӕ4?ۋi~ hCU,'bg a!`4|BRtQ#ѥ̋J 'IgM\c&֟W$1zX X ?MlN٠@=:*6UK$&8ĉ#տҊ|T*RXUSglqSzS 59h#D]QW?"6MQt,͜tXQtjq$n{0:kۅE5t$3%Й;(:Xs]ڈ+:sahC-6Hy>ot-$xt%R ^eLACᝦ!] |wUxԞtIfkaU&@q9@ QGz/hBG ȄcH6MwUvycW%/s7m:«FO6QgosMȹ=MF{,*oO[ȶǩ*W(?ꬴE~?CYXlRyvwf]nԋ)Kvn*?%N  lowL04ofeY FK?`tίj1wE Ɖe<`4EՄC267E a\^4L5חD ]q'!tGcM拪"M=SeaViFFH'}ᵮш3kksM|nQW1u*7brۺ c9fq'Ÿ$y2n>@\i{^ŁD P$MJ=/>DBHwm.9$XJNeT;M7(bG>/r.sqis/(OcFD[Ix3Њ,#;7|F䩂?=Q4e&y;W'GV /D5GO.Ppi OHE.oɊ8%i */>9RjB;==-6tKD^L[6yj[ʋ>c]\-]a,NmKbC!)Y͖\;eE =׫8q~ zVT1{#㡁ܬ]/he]C|h9?tYHks˚Yq4WT~uuk5 TeM`m0X媶,kMjhw,T"]+ˌ7[֋K-WnY.3T)eRKe R|c ^_K˂`L '3zj:we{DW>R⦧3RAiݑg;RƲ㑖e Bm﹬< 0QdDܹ8Sy;#jʥ(z VK0Z'u1U2z?w5{PtyʪK_W_/؈vbWCvü,vCmU=zvR] 3\hQ* n ї hdO,:>#@hyK!ZcHr#~̀Rw-7Ȍ-{=SoZ얱ck$WaY^/3zyQxrŗJRwj_>MS]Zjε%'_/.1kzInlw A| nV=~V ^c_QۆTOyc#r:hJkGB@;7ȏ0=?q$ Ha.Y=ҋ;z K?f $f_RɚfoWQI~s<Ǐu2rm ސAoxѕX*(y\E0@BOZ܎ʹm& =Wk:ae'T >W%?`ݔp %yٯx$3 #`,㌽y]3ʘ5p`T:UN*^c2 }v{hN{'Hr>8zyE))Ilp] s3oBYJfXXexfXXe|fXXezfXX5kaUcaռUgVUv gVUV-Y5VijX}fV:֖W6b=xLC!?<4rՆȓP|&RcxG3Rs0q{?O?7rMO~gěo-Z \9Cb: 煩]=)a|d`<.;.wd퇴W Ocsn'v~B,pe;}UaS,AvXuYyC2‹#WbqdRڎoPmN۾R*j?8iUltUs[6-?Hj)N80A`M8Jm|WYу>.q(vΎ6s>iq+J&~Q`p7WRF*aYSAj $&v11<&X[LIK/;-tx#󉔆gHFx͸"  e_!`GWE|N!A*!L;D)|mnVGRPkvwR볫ZQ&9h7eeԧh%I%G_iTyNdzT2 6/6 z wJA= O6؜o(5iGD-W@f}@c%;7!ٝ8N"4 ͅ${E`[xg݊')dal  {  hwF"Tbh*L:R w%Z&#p߄ڧ]R8S~=QS!p=k٬|wL#8ًi *q yR<0ߡ3.ݣvL:W T.l+OG(χҭkބj1#L>Vd=n\@ `+x8H~R~i`? S Y5=0D8Mb)/Uq)+B& ?gVE50Q=mU֮(4>Nk>p[I1&&<>@wWZ«d<% \V?Hsd#-ޞ Gs@!+-Sz&,隁U@`#ז90Kq\}$!88pt'Jd|mX2`31ˡ;-S4l#>Z㽔#] pehd#pV$P`9P1(ݖ$ȏ܄u^k!pX>֋0踤{dÀ˒7](~3z\XQJ {tb{!dPw;)_UnG1JG7<%)/RBd!4Kn)Tv4l6XB8%Kw9aK89ewo!3B(^Qumit%4X [~r>y<,p@ Tk<2;-$Vqw# 6)"Ԥ#Gr%#=v8T6ˏh8dqaI X">UOXãܾٗƯ٩$zHkx~G jeYf5SQe082Z^j ? %,rYPq BwY}(N$ g4IA .9NtcXP8a|W1%dC>eF?}ScJˠ9Y Cxw#Nx-LKӍ!i0N@C#цB"S^^_*pʴ!8:m,?LiXQN G+] rb|g+;o(4뢮yiX)QF^To骪"\7c'j$#[ \aцn͆" ;0BdR €fiC$(<\UI,Y:`AS}}0]@ vd;WlM>$%G_>NH(iy1].2m\,W<[-N3/$nVKۃ^PQ {0 o-x9q}f' ر Q[79s`OJiѝ&-17sv3]Ħ6>L۟~oo{'$Y\]狡MBGr^?pۇLBX)jk&Oٹ0~x(с1뗟:ܣ01+SVZ+qi7m}1KA-%[ 4E7 n dGo?yr-0Ν߼'dCa<8;K0Q9 1T5txzckqZGF2@-Qy;eFFq ~GxߚyClaD:_jXz7MCw-ke$.ӏDyzh_JPelP4%b3O\dr>-򩜃8sLM"*,Si B=L4)p/җlcEHeaEq D EZ>}|e>a&(ޘ-gx/ŊEM2@oGznAd]%z8^5qU^#kWo/ϫxG=$ڇt+(~|!cҢqg3S&ٖ/A%z7<.eQ,vߣj|o.sTɠ /\ B& ز';2~@9o\}D|As~)9fiaOB>y ¸Hrn?`8`OcJ7-g(#wyd/1K{NwjJYJ_^6~Et])BPY݄ JC2,@Yoc`Jf6KJ'fpn-M4 G){Bi7sM+ i*o*O' ] K_:_Q8!؏Kɠ 1ͳ5뇗E92ٮʡB"|FVfێju3$2FX6~3g6ض(jAB ġYk?R1/ 8U}XiBݹu*b)+ڏGZ ~.|&؃49  ES5խ'pҼ-LjSY#0%[gXh8fZzB ҁgm'l7}*'-Sr[t| NAb`׳{[pQzݰ-Sؾa{<reD-*i ݃yرzͻ ƷeDq͋F8!Sq8x*!c\82}8?EguGM:F/n&\wj6p jt2|`ǻT٣j;0ZQ+CW5f"g-bs+!gK\7w>WnTq.E.wd|;ؐ Ր;ym ]z>B]I֝v03IxCjIdd݌C YǣĶD40Eigme*ك kpE{{p^g+e.CtM 'MM% fmz#d,}m,5][b6![i/;h 0q6 _u ^,,VU=xX*#ҶDioKFjZr-xe,e1.DBjS0=B&R7tC"=p}8֥HKWs3*>? v$W{plN[sLsP2ƎqDk3)\1rd\  imDk\PFo#+?=Jd,FJ^un+2(=J^2eWOn~ O @ˆ!ಲZeSx.?@\%& `Ut@hB@;4NK;:L > stream xYK0W\:3d ǡ@Ko}@3&fPD#3<3՟ *P@rXozGn2VP,4kqRM n;0Ҵ=vЂ-׾`-MOme;~ }MQ6!yVK_p4TBpe>&.slEdhp)fb!x/ȣ%:FٛjBsi (npaK4v4 a*@4@O!=ZI&䃖X-$uМv\!2#P^Rv\#JQM u4@+胻Csv ?)'1G]A_NZKTxi(p@ӠׯȈ "oOc 2m[9).QoHRgk`b/w[ endstream endobj 3 0 obj 906 endobj 5 0 obj <> stream xZK6ϯ9Ў,Ac>0C-!GRҳa7,=Tz̧_f&>kŧլ/?0 O_|}j*Y19}6\ &ٲ_+[W+ۘof+{e7澾j.wwOg p}]>~rxR,җX|e bW- 1β\V.Xbf\n|:ATz+蚄}qw^_exSL\Im?bm`('t p7nƛ.=-)\Xh U|ވD9lX  -/k((D/DtFd6?ײs6 nE_K]WTXXطE(Z i]pk0t."\_R_}+"",e Lyu%ҪYRDIK`fASMzLt#D?'/4CF1@aP;Od%=ʐ,Ǖw" R.EtNz1 QQ?C0݌mIB;cw^·yX-*9x}!ЀM" "!AӊLâ&OԋxJGdB&85Q5H@;Ri2%@`aLVDgEה3D-MgXCY 6 ,n3*`Hk陓m֋Ür;I_G"H Jdi.q~_$#p -z(K]ŨfH < _yG`W ;=fI!wK1kNCF,n"58'䖊f!Cn Y"椌!ȖE6]2s2~ DjSDhӢK]M [ZN6D꟬Ъ>Ƽu"gSr:N(RAIn2!xd HNրԈ}NEўX?3gRaǮWS←BJU ATce^iX7+7,`r P)t&m7tIUZٔ,. 6W"7 vlH*'l'\A!M0m-ɘ L]rœDNcke~r(0i2 y2yX?#G&+|׊nPFdXRn] I4dC{(I3w=yo9/`K,v䥹,zMO~6y^x p][*ɮo`O:E^hw,1'!?AfCϤLXGx nb]ZvǙ_[/0uWJby?6]9h}^'xu!3H0 dYRoP 2EPv$6Y|"- ǩɁQuj(#f9 N1u[o",݇k'O~QO%u|hycއ[Uʶq#tkWY_aXwi7!#Lr1L%Furj'쬿YR"ɆT;N#MX>a)e}rqB PD' %m8f$RN߳q‚D"k DIU6ij`i^X5K5#7~cizEyr!Rޝ|d$K)鼥lz=M6?5Um/Ѷ*V%ka4dF$l"2:@<<ĭjQhM(SU/dH,>[jͱyeo`mY pOX.#u_xV%l8zkr8(W.u>z耣NxJ3?l5¼ل #xO*2q |3 rt<cyԛog\/IM1DkXV~f7糯Oj ;iZ}e%{l endstream endobj 6 0 obj 2155 endobj 8 0 obj <> stream xV˪0 Wx]T;G]ttQ{$'8!8䣣c԰}ePC6ɑMhCcS0_ߙe~!&`Sə~dܾ}{ב%G? c%eǶNt᥷^]e@#>0G[eӛc }8\o'.:b%sǩ?j,6Y۸[j-Wq[hKgضM4xY1*!^j} ְ͖ThkrGL`]8 0\݅mA< gFE(m QI2#SGɏ 0g+HaU$,@0$~m5V}۴s0I^@Q}|>p*U> stream xX˪H +GRl0'^̮!0fvY4toGR벜dф!H:RU j;}u m)_7~ n@կ#VH`: ps e챃Mjs孧7` : *(fvlۈ?o:LY}!Km"Fcƕ՚z|k(we8 riQf"3$^7D5fIs$/7&C~H:&ƴ 4.l58  .c`/IUMLV+iԬMpLLf+`|9AύPCO"j--ծ̿v+$\E7 ~V|ђphA_ohmdv$di[tUCѝ-Jj%_Iఈjɮ\,JBd)5^BxKC=jXXld[mYfzJEP<@=A!H -8;9sMB>yLR =Y2}?@_$Yۍ=MG&W};cR(,$+kQ׈=n#UE ]7c Mŝ$j-bJ>^ʮw;-9Z;ҡTS`~6h" 4g\ WdqG,geUFsgMqvC֔ވvLd\O4PY3k"Pn"0>W5vQk!j%KMߞ4JY5K;0Oq\ȦD{ъި8*Mh$^96o }Hic&FnQz2*¢&U/*Hy͋Գe5K+[͐mMKyfB]qҙVOO-6ߦ'XO5.:7Ŝ ƌD( 'HIyԖ{'w%u =?wHAI(֠o1:fԎHB~NbKgZ*G 17cL(9II 6oj|408E=N~} 1E{e|n y3J'BΎ≮\ DWH1TB<iiLt 52J1Z!]!k d'd\!]j d+4 Y]!]!mj d'g[!]j d+4M52S Ltjx F=i1HTyW:ЩM"hG!BHQb$`2o!C;LL n2xVsgS%@gT9/:AiCog8oϧ%oNֹ*IPASA?Sc݉.)~%F,+h?Oy& 6ypY\i&T VҕOg `,S(ŜBAytmT ВdڠhSqI<}=t(dDEJ I, pζSFs?@O7Tn*wtbT݂q"[o Ҟt%B>/@o쾮A1I̷!rp Yi *I Ah 6:eᦛEd!tx¶$řN?UI8މPf}PGxB :݃WN55 ^;֛ins^r ٞToY(,[\7]y&^$\km+ᆔۯβpXRqݴ!rab3@Va Z]{)^U딜ujsHhWZ@^ܷ~LɭXLR)ӝșjM<4.Cdd74 C>"x㳕J@zk[^RgZwچvFa|9'W 'fZՅ+Np`MmƐ3\yb>~ZBQB@> rg*Ao`#&K>o _L)iR _K ´51gg!RFb(fN?nF)DEpU,RVfT{_d~"px+1He d_M`pIxNK#1IEB%߆]kkn`f2 32`QJ8,.(⮰|kB-'NӐjJHfKhްMODV2wY/64etb|fWc7y爸_CS)ok*'G޳ dByAn;D%b&I,gWDs"rb;Sb>G|A *˝85FR)?~Ip-;3̩/E^Au[ $/CmMۤ_u / B`tBgWwlĊ#"ؤ'rgdЧ8+ 0jLC,hXI+|yg#[H*N©͒`}i}ۋnTz?"Naߏww-_ȏ_L6.i1ZA,zfFwv%2ڱk"KFHwW&aXGA, 1>+_.zxF3/R;¹r{Ȭ,Jm}zרL((pt{MU}y<śi endstream endobj 15 0 obj 2044 endobj 17 0 obj <> stream x}VK@ W̹`W< 0&m,Bҿ_4OOz4 聒Iy~V <d<>5o 2Nv nÄ#DHzn!N0w+spSGj -V0d 7}>2zpJhJ#Ʃ\e61f1 dq٦74)OM !R0~*wCaFALMkބ\:gR0"G&_-;.OXHj4Jh<@7.{k^1qW/_ Co@U ,U.B?܁`;P@D̂&( WA^(h!̯ zH(+`VǪ~'zn>2e3N.n2Z8W,,Y$'`8e ʼn[w)PНԎS]Ì [ 3>+kWMz㍊R,bJ#Cr]rL X# w3Y\u:_$0OlKL/&.7Y:+y+Sи5ƞ3pTTu?M{n w`#WG8Y_ sRx8,e&%/@.Y>Oc,-P^źiﻮ6T]oE~E-SQcv:j(}ۆRmSIc7 8ZҁZt{JCIη/U*ø$sH4hU,{ ]Oo, endstream endobj 18 0 obj 808 endobj 20 0 obj <> stream xZK69Ў^e0wt`o ,@*UɖlIzjWOCݢ_?шV(8#[ge ]o?h}ty>uuM <|{TF!'= %0ӹEv腣a:E\6ޝ =Y)ENgERI [y 0`3|X;SX,;ałjr짳E`{ڡ+FA.-tYQ]y[f+<{U4&H [(U# D5S/Yo%MTG?QENKwKUPI9#lL*?IwF<<|hdU]clN7_N_~o~ sDV:!*ѭLxJt;$DP#}ߚ(,T:[c"^ /TкDIB 5"L DaJL DaJ$LjP#51DXY",TL",T % 5'$?ژ# rJA^"\>D4\G4-C>K6dʋ ulw%, tK/Ͼ<*'ekŚRt0 M_Iya6u`.k*ʅ)%zմOl=)ӑ1ѝo3׉iybb-̋-e?\]OALC4+^#ܢA;_[9ÄΥ?w4䱣Ӡ%dWG9jܪ5&sX \B4csf} E:8Kg6i^_9+PwlYm1/{ypq.j W Rn"LфJ 'O¶rk ;Y(dW5Ɔ*tVO܂e#&+?s_~dK=Q'^)4L߯-qԓIu Ltr6d@tG:ő8klӃڼ4Ef=lP5-z0 zh,`d"4*6yk+gE) 1dYe>z8 %QId9=o9Kwv9xP8RJXӃaY=-:/e2KS9:+l›Q/OpFEfBEM˭b_z#s^G|ITAs>q,!wpaz|b7f]PUiJMOWfS c こRxDr#RqGTn6>%s y\uAە6̫>Ӽ:7F6~l,D֘oޚyWe4_c WEcpX endstream endobj 21 0 obj 1800 endobj 23 0 obj <> stream xXϋ6 _saRKvlB`vwn+]W$leD>$G{S轅n}w }r? ݀Jwߺov?~zR2Mjढ़aRN8zQ|qΆeW~n hz5JQX Mz̿?7u~V,9g5jh3b+1c?L6߿u( ƞ뾏aUIWI;0tD"nJ%g*XB7ndMڝ#wiO (JMW?ŧ/:}==f,:S=dJQTBݏR4]-%<IT6D4M%:Y |$h)@$ASɩ<IT D44H`$h)@$ASə<IT<ITC$h*-q몊aj 릊Q G 5dXnauMj<׃Jz5ji<㥯&o e_jE7nEH{NM:<=ÓL:J# =xE qJXy)P~s +Xp3E{8 Ga3d``Q+o.%yO9t$;r~ѱM]Lrc&)zXR-j9΁,l/^c*nfȖ۝*hHKus[bgZ;1 B hg f0!Л?RHwB鿮^6lcER5em3r7\m>>[js2^ ףswOXC/z_݊~)>xҒhͧ!BOser}PEu#M} D59P wbgPfWPO3$$rM%9>lR_c,%<FNfQRSG= 7Aůex Tq%l~|V"{Yc[b/OHykD**u 梛E:-HWZ CcJiɏym54ćƮMJ_hcdzWsVYȟ, endstream endobj 24 0 obj 1281 endobj 26 0 obj <> stream xZY7~_ρjI CC𛓅<tH^{mWv,NObP~FiYYe樂h|t|ZE ==:}$']E("h7yNxzZzaSlcع'x!EYqeAx|z{f].D.PH,j)JĠ1@. Jrgd:5D g($ fs2[I0;jADv8>Z@OJ]$zbmCC+80<>hfx?/>W. N?azDv5Q\`XmKż6Dq%rv Q\Z Kt DZ`X"mZ K$ն@ȹ-"-Dڷ@H-i% I/[ KdU DZ`jH ,-i%"-pD`JQ!z\(f T =$GEB$U2d 2ׅL(( f8;VR5,DS"ye$yP/qdDYr:?ln' yAH.S*_,io缀,xx CkU ED1h5ȪH5X5y^ BvO*8 k fu` D# FMŁ!hЂ 1@ﲐ߾8#JF(;!ϹBLNISb@d`\!]fq"QjbP4:}QO}> NW@~6nP\*"+ɌG˹HD?J"t% iAyiR)pI(hsATsfmJ#0_j6#Dw8}V` O:Z[*ctP)L9#],3çH;Leo-e$dZ.7Sݰd%a#¦:[TtT@/дrbH:쾱qTadͣ$S{&6~7:*βAXKHk/3޸xe-C\7|=BlCXFZx9oR׵Hx\;7ƳR:}ٜϖ5'cj2xmmKć3&T+; ] j@ Z&}Q[LL7^') 5K<&]Lng'_Ľj^hy({=M gTYqP6Q7ҼG_|)]A7.x;4vu[D_ endstream endobj 27 0 obj 1767 endobj 29 0 obj <> stream xWK1 W\W{`0n޶ PzJ[^kٞLb;;+ɒ ٯO 8ǜB 2k.fOv^iG<}wpǴ܀dǏ v~r\Ziʋ!|sCDreؚ-:fgءn=,G,gD *{1!݀w,֢]DӉC\mՏR?WJ jaj'SplP[QuyM>q!J a4jɾ\ D\^&Cdi MWlh Mu"&C $Õ[%b24A֬1 m׉ MDL*; L\Fu I6&ft_To9LnN"I,IڵQֆ_v}nfqLR.0h!G&[3bN1wFp g~yF"(%;}EĚntXЋk>OC)te*UBt^ .xhѻkIP,?*X[nD}^vBtl[)kfv?{J>g8Jt٩ȵM dY* \HE81hITo`I endstream endobj 30 0 obj 800 endobj 45 0 obj <> stream x{k@ױ9VoZi $BxlFI . "‰Cq;m4io*RI>m&#mӛ/M6m sV Ə_$vw̜93s̙@rl"h?Qww5#%ooM~?s~EHC?sۏRADO_#BU #2[v͊?9eh'{Rl*,67ܳ;o;+FGƓ06aNcQо C%-rҦhF&W(UjVg Fbe'eP!B[3Duu}^@s(7zp/nqt=ލx-(VF<ډz6BEQ7jF*xa-5Հi>' q5)Le؂-nDωFoƷC>t EQڂ_DCo>~ 37߸O>ni[".E_F;P 3]݋^CQ8ZQU(/ `_+*,7u:r6l2X}NQ )8]9kWn;9S}ϝBKur^νnM!s'gQ/SȔ"`:ISnQϳ\]ARE=Qu u0f5,nXEjRH RFȵ+= _bHp$ S)y]J! =)t {E;>g;xtE76A#Wb@xs pu[Ad #X[^vԱS4\WPP]wgݼ(;2Ӿ0-9AHq\ m䁵UhJ wCR2/sF)͟ݥ #Flxa{g<ӳӣnz]x#pw R!k9qzhl PgkL/禹馾i @HLʍ MEE0׀8Èr a$a(ik+huH>VC@;  ٶ)$OT.QxVT0%!dvMmk #`(Ə"X4QDB_!TPϪ'ϖv:vL3V ]0sʄ. N]| .,~l( G*W2"la]蕸P^ ,.y}o^ &bZ{Q`]WsMN{s{6!n|^ӯ:xDq[-k8UVZ ҿfUq觡ʨ[?þb@vjS)i|ZWXtS]`ۢ}:Wt$kO甚8+Ǽޅ5(B2 l);* v ]xӨG鳘,CzkhWP }7kb-!Mv^b0=7S=@[[A 2v"fZ^[{6Qv ]0epX s )N_¼ ('~*Dy\-fRrYb,Oʰ$u7&-rOm5y*ߝ&l=_e-/ ,3NVcsWV&X:B4jB"48 c$\>qxE Eݸ͂MZS?/,\|ũ܆۞++.YCw8gI~ҍo]WXc4h[֬\ǎ3߼փh`GEl_Aa@Uh x(JirGiYm2kg}qPjGOT?8=B%YLކ\wh%m+ o?thb35rj婪DvX4{ysv!i~ۏsjuqYw-])>*Kym2HLM,(fOˌ\6ct7㽾Ik+ASï̶xA yR5%^ܤeky7Sԑ" >ځ>E8ζn۸B{-8ʖRDf*ЩSx ? `P97MՒxcTΖS5_8)SB_[UeUz<}yR2Ne!=d)8cycv (/XCUTsr2Et( ìxfeǛT|؃'jZWwt-d&"/Y$@,\Gy|7/ϡb1ZYU&|by጑eQcr+8Bh t0D4.dL"x&5.dLvL>Ƌy?ㆆXsw^V]ybZaYpv濫_|ScgOoZv[CO[>IA~5Qj9Pڼ6n}C㿌 شuk:Ԕvۍ{槽W$ }Ť.i ECLThXf3z=jGhN3]cR+gt*2@Fjb?ST2(!c7Ѓp gתG9Laa߇?ء:uC@?#֌^T-5#KJG399 caTԎڑdefGp߭ gI᲍MyyNs(羉'S _j$՜%_JhiZ1/Aؐ|Q{gTVPi[iw)]x@|I"bniLЙ$ rm뺵m?;|Ëm-֭}xBȜiU&r6^;;:opM ֥eU'v5.]ӺY ]}MBF :wg ѽٓȽYO7wr9]ZF͹)Ш՞viw:VhYդmSmt5{Ut4[E+̐C-܎fãa*d鶌ZhY3eFygHN@sM3f=V-ت<]q %dc;iT.<]̫qpdYMe&1 MKZ -lPz"cRB{ڐv*_H t'6_αֿKw~jn[fUTQصdV蚥5{5 UeUϫ2ÕUYkq3uhחBU&<=S"{2c[vKUןdsgqk:=9ȥ=ls3bY2v݅_ЯC]VNFˬj{( 46 C;DptD8:T eτ)w{zYߌ3b˻rt3?_7p#u/D#y\&S<.B:o4TWa#S -3jILJi_ek93?Ld,3DP3ey&}CP/ {ʃsg/S;uޚ<\= W6-*7f/P8riuV36ZzVZ,?5Yg,Ū'J ]Z6?]r 9SZfFuک&<lx&uM 5S_hͱZӽifn}kk_/4PA!'j}8̫am{[thQi-vmɡ50ǘӵAw'8VZ'_^ ,2vO-FĕYG6SP#-,6uFؑi9ݙr#zb3cԂ-cZ>ϡR {Km =$&etVHqcUc2p)ᐄ 82˝mz*A5~ m|Gq#̓j8p5FTSv =]0 8)ҷ$Na ̼.L+s*ɹ#+ekBN,l]qUǶwn!P}\y->5gKb|8/ߵʱZc*Ndl|מ+3:ꋵ Џzkm.AȘ71"d "$ !Bd*L ?}%i0ɊO}Xq!:2V0@7,WPCuSl*S3Rk.<9~ueJ8wΥ ܡd!l,$C fΒİgg`aU)2nيyߖ#RO{(v =x."DP-q"+K++($Q* +^å*&M6?(ϙvo-7w[tE%7k;Uцxѻh51o5jJߜgs[%X3+̏N^_g?u s4DF*HCKP?QJv-Q_j񛨍 rR ^6JQpL5%! __xO6h&ɿ FVP, ѲVOzҿc1Yy&E'WS?RotI^O*P] O /ڈZXŠj * -Z+Cz4&r* t3:,Jj V,l`n^ Bʢ>- ʣaGM}GC/~+ RaBMEX x-]( re"-6 (9_`mUA&`c^fP(G+*,:jFv\PT) ?(a Y fG =H0<'YgU2 f0K*$ED`jDDՔ>>U̠|շD*F _DN ~Od~$WEs>= K?,A'󫞕`_WE8(.X)Y`SaQO Z_I ^Sͭ $"ܺޱ$W726:2֓.V qw$ǹ u#C}x916P`2\Z, K/cHǹ`r 1pcXK%v}!% s 4<=83'qޑ`bg @pb]O{ 2>2M'@h?2zƹ$c8RlS*ѱ$b`#fp}C0na}\ѠKA:]5|UDq8f袋$YDadb7 7wxhg頠sOFSp٢3G&I/q`o $F/3h {^ޑBrαс}b\@(vBN$AN4-2jM`F"(}W3QN _I3g/繄$ |#^Ap$Չ}G;{aTUhhhb+p_'jZr] eoKcqGA(II:7WaĿ/^oP.$J$r"~LWBMXb3E>hF\fh4,"phE(KZ #K,PF"w GtJJ^'+/rEIh>xNQ/~QJRԇG~E!])V@e->QzE_ʹ o/8{HJƂ2c Jz%Y NVvA/3}tQz(iҜ.FmEl%[x Hgl^1ni=4!I ':њĢ9Dύ.t$ѣnIE a`G3j <yWr|C5އ>ܕLqiH96|@vUy.K(+z\g*bV2˘:&T1QfT_< _4\ XKrEJGa6b)EgWwK?M|9lG(񖊍~oI~"\?xW^Ri"/e迼F]DžS\~Hz"ד']w?=ǝ\G'sp4u>^/#TY??e2z6l43gTSȵG]u WO\=K #B̹h}=k_R~br ?㯙\_upONSmx4 6ip Ծ}"ס"]EixR*v}fcJǥ?:@]?#Sxjx^_oG[&9puONR7&ߠA}?ȧ/(=|繲\]=GgtFY.e2b.=kjuYZyrRK32-”֕'Q.969 Z}h>ASEmpcҥQj U)]e8elFS& ύSe9%ڐ Sml:8QG98GXu[O5S;f 捝x*L#qxr>̾I׏ oRgUD 7O,vxrbGH.hMpVD = vv endstream endobj 46 0 obj 9243 endobj 47 0 obj <> endobj 48 0 obj <> stream x]Kn0t FBH >T{H-cgV뛧fův86(-- `W)I%jSb_-TQc t(~7;owwc`XU0>OyGCն>ܲ% 1IM/Q$+@4 >{SOMl_yNk1GΈ

GO &L _('ې3Oȏ}p_Ȥ@!gx/_WN NJ{Ywk;( L endstream endobj 49 0 obj <> endobj 50 0 obj <> stream x{{\T׵y3 /ysPD@DE1 1&4T6M{4ɀi6/7M6co&iccs5m{! mn?_sX{ksݝ=>b"D8B qͻ}?K1vkǶ=xzkDm%!V_cXB웠cn+:3===uGoonFba"ڧy{G%hC?Qv6=%VB>wuJHwt:AVLuz1*:APgHM$pP ㉡B>Q䳕9h{(@_%_{7yurn!7bsܴMgSE1:.R M7'3]B_u$DoYJ3R"BH2MG?$zV~H i]– ;^ocgn$?DPt&UiOW^= [N*J=zYX&l~z=JO߰:y-/7r갿Ocd>zlT(HWIK}iyT0υ_*|l$*xԓ}ع u2~CFi\n2URz040\n_ A"h JoG &MRKN'.Yu/uCCw.X|0j!)dYK&m\ Ͽo]XYC,\QLci:5.ƪi7~>DK(,ql5ۆ^c'פ$yQlEƟ_z#baC8-<5\~:B🐹 E\FN]O5ة!OaGE o6:fUt .}㈜ p EOoгwElśV=ƞg/#IND7H_^,Ely쓿!?!X~Sԭwi'`h'?\ӿiAߎ&h+G$=@o|&Gғuv3?%et=Ȧ4:;F~8|3;LNJ'i+E>Z,l ]B $$" *SU8oW>I%;"l6t$<͂\yDRdi*2=݋.ɣ%n?l5[ޣȓRX=trPElvDa :"4|\{,q~ 4ȏhMK~CVJdܥt]Eș N,6YF~EEwBDp켅uR]NFN~l8[_t-*]nnnΡKYtS wgɉIzgm/<,d$O؟?6g7U5EyP(t1BwBC_ u#/^._~~m1|(fZ mro]թ^$v&4j=,+2VHSQݮёu|txI ~,֤MþxUjz׫nB+2>lLR8&QD1aި7,E$ ECwYlv8y>KTWif] ''g`ͪ6FO4}+` De!*@b׹@7 e?hP@L,%K5H@L╗:"JPL%K,r'=Ţ8EƬ5+`|R@[`qc%/,XK 3%+L%DEq~M7ge{+ 'yvYpS$3i> eSyNpDpJ?AxYӜpOURp3P;f+i8 &Av)n%K^oo`@S:{$);wـl8)w҃kUzvc`]˽e <\Xy]LlwlFS6@zy%uyDu%F%V[/)YmduxJFJ8Οcsز66\V˪\"<̿H Kx#(Uc6%.:e0<|, 'T&xA![ORuCm"7q]gv>L ͝7w zozgfN6Xzkcܒܬ˶8я>vE%3ӧ^9r4JN̒du]=Ga#aejb3%D_xBN]dƸBNI=ssKffӹGйaY~nY;(A}V^=𝚕Zm]aeO[1*ǪQjT.%5mjz]qd};X0@f(0%WGTsrBƞF{S28.(YTnɱBQgd )tFl MW0CM1Xc;>#5pՖI#Q::zGAJ?9܌xV;g[r"%E~=!B10;,۫YLX"ٜjcgE\7D6rXW|m &p( 8Wlg`s#S3°\.re9\OVx<+F5' 3)Kx/E]NeRڒnouÎOr~nI12oB^SFv2He!KRh͠jƁ v(f dqNQ` D #ZUP- # ;1;@x;\uU6Vx6ƤfOC!&ī"{|G:cW}zzN,s+uzo>ivBqY4gg~Ί+[voPރ8.oKH1:jJ"쬬t}EӑBSRf̠&f^*.2ʚF(ϱD$'$x: g'D[;$RSJ"'%%"{rmin voXߙ_W3Yʖh:at.%LFe8Dbi39*C'DDW(O-qMOwW?|-}!It)3~ob-.>!1)iʨ{&;gY}2~n(gnh gi;9<;F/NTG^Z)IȉpE(K)z:ldQ2'MYDlNUAO<|7ā2VߎD XG.p""j3]9pZ?wJ^~rճBg{о܂)9 ̡T!mJZNڼ47g,vK5&̈FcT3_Ѡsj2ɹy zⳏifW)X>M,EQT"po#ĵ!-yd5&ٛ| YDEc4q_ ?&E8AI&^k 'i L5~a)vx&#;zγ+rW89|(ֹNԤ F W PMluA76m]EKCč\}[}~{!eQja' 'ϲAoƾדVڙ}7R;NS&ٻG18CKz${,6U&v{\K>a2: [FbūY8]Na략A(Гğd`Ƈ?ƤeZL~0Y[ɫ*OGtw5 FMEXM1^Oyo5;!E)yI%YF~ ^"-ؙ4^G2I$xyh$"SR7#9\$_xJbt5YW)Щ,ujXt_x=}] YIޠQ$Vox3]σf*K5\26ц4^&Aѯ720xʨ&cd@Q5zk0jc[Þ>'?!/nX!o-35łO{jٮSKӹ=oj>HFI6ѫըcv'Wf ͝@آ"(d:f1'[wո~}k?3;2ԒqkMx-<؂;5X#֡Ն˺OCމ^Рݭ®6!=m2{j8r$q _݋GEW JvI]iֺ9nCy Pƈf_isfX#vEQDVeMЬsvMǼy 4a}];?{i:GylxWk] $n7\d-"6"xO7NOF,j7zLnnߘ.G+Vʿ߽ç;;mvem۶.e׹2TV[F'W@n_g(Eښ;]3>+~ܬka 3ںFŷſuR󕶝J7l(ݍ>Lw*~t*P5k2%{;[*_wwߣhܫtlV_玶nӴWV[YN4ws5mgs{O vvJj@3|;g)(g^ez ŷϺk$[vnS:}]Xu3_8x.MBa6tvpgtſggq<(n 9ӭv5L3+wm"9=}cT7fD_*x2RLJJK/J'PO&;AO|S?*>Um_\HD݊z7yOϤ>/Y)<5'\sGgD]Z.L>2 sځoN^"1k{'E?z&HvyP.eU^$W &u}EbEd{ΆdrW$Dv`W'_8j#)9wȎbpv2}礟 E2%nP>HT ~ > C#ڈإ0qA ΃tDBbb 쥕RSCt$cև:*o-pu0UCt0WH;UtAG: SFt t@} tzlMm)->O`': ΂ni+G:0St pHAqR\<( ,f`6\f-Ot{F^§@ z⣁壁u9GN ״ZVߦշjNnZ]ՋzVhuV,jE@휾ҟ8>v},}Q =}ht Ed%64- M@àS{QqJ*݂蹅$c}uf݇YLQZA (j@z7CyA*H7%I/-8Cl@ݍ0@ `Y4tNCi>٧1u%Ir^n)-C}9" *yY' < b2AI$ʰ2*%vi!ڼ.rE@K"dJВgVZ95 ihM(af9ᬟ&6QOq 84fVA󒃭q,HSaTX#7@ i Ĵ&Ma+]D ڥ3Pk} >DKY=v L 2&Ƅ}6=&&D qdBLt,6.Nd?6q2c}cGevDPbG؇П00ޣߢuvǰ72<#UjIV2L%GU*u ÚiͱZ+[J5-?eY+)M(U#^;OpGQ7,m%O΂$/7Y@S"31keo`/A*$7}B1:Iuv ~9Ph_s\? zzE꧰$ = ^P4 ;`mG +qC #tmAIɍzrҍZ^u}HY7Gtv:A]=6v5'0  ?0 ?0 ?0/0 X Z^u} #z:@@. VPh;/X/b/bb/b/f~4 bXnn[nn~7 ?(?A ABB ?(ȶ!ȚA- hAM*&fբg(zjAuub뷑 ?p/p?p?p ?p/pg {lOfP *ƷFP t56ZP])TE UH+R5T jT j UZ UH@H@R5R5jT p5Z@>V15DUM͠EP=QԂ@@Em#3n`n`[`nn`n`A`%0## 0# 0 0#(0# `G>,lYs?r(rr9Q )G cyȏ\I% Yp KdK[G.:a~x-},尸þ<ؙ {s`w˄XZ5%m TOF@GAP>() I=g3S"bϋ(&=\)JQSjט{ksCyiC"!񮚾|xM+K7ovW:/%QUgEy(KD2 I 8Xu_pT:7hB}8h 8F 8PmpFU6(GU#cPGU {~?C1Ns̴D7Dr^=/ғY7H97y }>o/7g7qF1h2FFQ62#1&J/RE Q##+8[DŽEl^ ^bUe*0LǵAfc@,*RUWJ VjldT)R֊@`)_*b?R!4滪Q5WS 05+~]izWCII--X V|fj?eWs vWբ)C_LN|>N4 Vlbb[̫S"䐜!7=r$3rl1UD.SeNtT,w8Fe*LDe qOB?s2_B& e\}|e!5uV@[e,AjMͭn Ҡӷ$йD_|xsI?]QW߿[-Xp6.i8V>Q~϶/P+p/ncsjZ`U쫩7MF{ eI֎":ǫc$09f++CH|>%ZJ޿Бv> Yms>K_%>_Fw'W-#gLZPL.jxs7`]] yU~EG_gkD-su@7TD:NJhWw$zH@%G!zz~ endstream endobj 51 0 obj 10299 endobj 52 0 obj <> endobj 53 0 obj <> stream x]n <I$R֒]Ta Հ0>RoȵyjtjނýT¤gw0H jś 29D&g9 "oVjkv6 FPSTUX@0F QmKl/y &8X@%.BĿXJߙO4?Vħy]bxE?ı)q|IoMh7g ĥRm*o=y endstream endobj 54 0 obj <> endobj 55 0 obj <> stream xzy|S虻heIWlXW/`a1`%laĶ% !8!Mi6YpvҬ4MRHB%)%is$C޵̙3g̜k x:|O!!`XV[1B+~w)BnBO]uM D9sCcr7֪Wk @}ߴ~ؿ7ᓯ]x5q>< 9OH}B@(LPPd<28 a?2`SC/NhH2̒%j)i Nŕ鞘CADld={s ӈ#dK;T/K)t E&_xC!xlȁ<{`!)qE~NXGa*y8!Tآ*r<!l g`=0IJw9F9Sqܫ^~wGr[6J'jH : Am{Fnέd%>[Cj@܎| V,'H ;y@iЫw 3;Hi(3&a P}U݈z=HR=s,475.j_xͫ[[3gv,OӧMpI%E9Yݙ.gU2I zV# <@@{MRZ+,PjRԸk#OKv1QڕH6|bvbz0TJ:[VaVlo*XbY' ;.R0JMvmPM;fg Dl&b+ܙ\nʹ%iq5Hâ֚9[X0/btaCd6c̎hKNnRvyLV:ݝއC| )?ɻx )pϩSvJYf25帿\/d&fGE/G-zh֭GW{h0AuVd1ou%40ArP.UM;EѾBV8Oq7µӑ}[ qo7EyM Utc=d^T:{JBh7d:o8AdQ SiNA *.?jͭlx|]Rvܰ9l3#`]*VMOS+#E##F)a~ ω@y>MF.WrChe5C]gKiu"/RC s02[in䞿hq1AUsw# 0)"J-6ՕhtxQ J Ric{jKZ]JCNy1 ?-Db6Ry#OIp"z :X|PԩRT6$%%tPgPF]%Nd3 D\$؀DM%!IԵyMFqțĭ(xQ3`E0O ۼI$P`ٲe*TK258m2z>اb+*^䲹bf;3zquq&\̉]OI:qC69=9l0׬%i!/IJR%T6MV1̄rwF5Je(h]SuO/n3|S P:w{|W<\ǎf}]3Lyzํp/qհR6^iwR.dR)6j:"b`ӎؖ'S v0N UULc6F\V:jweW(ovw:Au FKdfnΙms| `R I6)#}99Zh*y(6Bc N#PE^9{'bW(g)&,[۲e]DI'WYd *Ny\H[5r6 BE)DH-XOЬn[ј?wzuL<)Vm4~F]Fs&A#t஫x~ƹԞn@EB=LON 1ؓ, ^]Y4ؘR`W @q u3RUUU?ˤWfrev]M*`ERUTՊ>e5L:bۑdD S.̶uv3/xkYl7>~ŵ]G. $ye#|Fuȅ>8}nv@,ʪʘ2$KG y.UF (!x̺=ѥvzJ(6k3xQTW( *n{9 h@~,pzjj&O:ʅYY3. qs,}pJyRG*^HldgBKٞ+&ht:H^ SN*6++ʦlX+Uݖ6sW/Y?X&HD J7vx!o>Lc`18;( RgY3Y;S(8Q @BRO,xPSS\xYe\kKTSGÚ^[6r!A~kZ:unvͺh-S/{An<3}%_?ޥn6v%qD= ,xE0*O9heP$z(_d0ZH3V&g ]Yj[_Y~{;;Cƒ4j O<^T?P O ;cBo[""a"8E8!!1x^KXH3/_BRI3۞`DxG]f$QZIƺL`DPbGt^42;zEvv#J6;Db-$މ)kHq;uVLl`Eݯ^wm=t5P}qQldgbR24gC EBʹޛ,ˢvA%,n X PfԿ;ꦿ_yZV~IԺVYR0g)fM%t2i[L&Nke6'6xys>hq5Fdʪ-c5b3-dUVh`EƯ`E#ϋcߏ0IQ5:DQȃ'iRuYWˋDPGs i1OpGpiqqƀ)RHgՖ@ UoC[$a nmrIXWT4n߱͸SQdGqߨUO |ЛiMo[! ~clPTIعJfr@DbMQxф'e %VP'Gr2|@V_@N^}#}곏Իݟ83q֣/lB:gYd(5۱ԉ\x7ͅ\ Gr\'-Ι $¡\\̅v66vF;Cw*{ٸr6^`Mk/; orX[~O*4S~w6}lh|Und~Ȯ]}#9$h~lB\L!mthް-D(xxFяQJ;X+pWF$VdQ` f_zOO7&0Ef+?H$AIH$^lzζU2\&2e.C d _ oiG-2lϗa͗9s/2<,}2l'2Q%22Ő(i ex-Mw-aŝ.sd&)\(ijL!Yt&,,Cg$[%R4 $wmLB.]::UʆyV Dۼe `A_g->f:F0 Ԡ''lY\%#Uu~\~:5V7!t5p۾MWྲྀޕ /uJEbⴞ$ N-/hDfIkڼI:`0g^4I0[r $%$%xWW%xJ_JpJF. %a%I H 6F3Aj JEt|^`;%&qF H0)& pk (QV 4=N[ &O"[$ÌrG v0Q4ęZW; CuˣzΫPW|(h,ZlJcNq67UŻh6|c&K92ҨIH)ջXKrhvE_fsFyV]|c^N;S]Hfs`wPlBdf*i>!]O 7`J|3X3={Xmg4!ҀnuH):O6kif G>$HU%W]Ͻ 3WL]څګ]Z/}~Pf1ᶄ'%PmXlx$<)bcL )լD`?KĄ=Q k$Q"ƾL~kkd8aWkjcX;sϏlAD*xmm$i|%J{@2.NPbm @rH҅kcm vZrJx-֑\X[OŏcmQɅC,b$JkI9=+{=W;N_اtzVv܎NFMm?FOy//@ܨ . yqq{ u}/4cyn{g}P?YWOan=&$,w (R!"<9~6!g8o4/f] x8iz;vY|rIh;8⟏+Op~|,ѱΣ3>l |X!!-ų!>=x Ó^?? wNwvƱNjM {L Nϳyj=f˓ '0 Mp@*{={m'àx'OzC㓉ZӞ=nIN%Ux͓>ՓWUj]ixvZhGc|{ C Χi{*DiO7`z;$J窆J8^jsgOk+;wpovvkӴչ5u֭[[nvo9Tk]vLjn-ν0!SA}A; l:uRgh iJm^ι6ǹdŵɥwW([<*y > endobj 58 0 obj <> stream x]Mn0Ft DBH)EԴ 0H CܾJ]$z> endobj 60 0 obj <> stream x|y`Tg{{_o7#ݷlYd  I x ^zTU66 UڊU[zUkR_V%gf71Akvf>3sgm׳xke ԶOw鿎 | @>P(WݲX(x5~MGk5}GyO]0߀]m h~]ED6@;l]בȣ0p;AcXF-rxA&W(UjV7F>p$aW n<7-V?:ro x}140hY o`z!܃1+P7cML',ௐd<@ ""X n\7p\uG8pu G 6p7GaH>f,N!b"OE\?BE*ca䯣?^},'հ!"#v2\EF |RI+ #} VE8pg2O#6&, hmtu-PH(0.Cx.$H5CK~Np˸۹AY21ףڢTkЋ5p'샧{(6~i&-~y_ 020:ctDrWa"ݶ%p-<?4< M'm r pϹ_swOݣoDۣO? 9Vvfp=#x>)+ȉ g_IQzVd5YO$ב= w'prN\[ur<^}it\=+[O*T-V62emDoGm gn8Q(9Hez RzcQ1Ƈ7 f@"[%YCr.r' 2H#(=yW8s'y4+fp\->\#]m.Cܽ}ܓ!wGy^W|_k9|#>6~Dh:{Pz =';!YK'Z.\&OnBj\0n;&'_C8c99C~J. ɠ $;/ _a@.ȿνK>B! e* $ȳ(,y|\ Q Ur9߄\F.RiK{|ez(%sKqW!=/%Na7neh% ,~._Տ% G//?KjQ3owH=!WAN/m#G>_O^8JH(GE}QJ6K` (cƌ2T'xA0ueuR 5)Q7 AM^#u(]ZaZ'|En&[k7*xvDѫP`OL- 'b)09-854q .ϟU[>czٴ)Ey9YfFz/՛.grn&Z &hJ#Ui"i-!;kV6{[uBAKD¢8I1C,P 34ID ʲj9Z商Ȳ__m"'+vO&K>s q18D~*Jd$qggmv&H>ofMSsC!XȎ+B(7ZhbZcfyw?s-e &&T!Q[[QoӶaR.V_:^" |$բ$.GFmDW$}HDQd%D-bqV,Y|i򓦧qBWװ_=yQPPlGP<$,8=22_e2~7JMT"b5ϿՋ>}hXZ"|kEؿX<اL#$FƖ&2KLj]bkYWz6ȾnP"~Q.'l{ӈ8{Zv!`/PiZP|I^OP4BaM0hu "?MP[ҚOq*F:U\81jm_K4!ʈi4)9qqjbjYY>pQL 0"F#*1p1=\ nl5^u(cW11=xmR:\W2!^rzѲIW74SEeKESS6:[Oe(4z>ѳZ3LH_$FˀgWR<YJ{!Jgx L39X 2IIO̯,Rl2:b!]#96JkC柹kUJ:LYYDYJv;$b-G.d8D11 ;VD-Efm(n|Ԋ Xwj| lHYS'vrKl?yd)@E&xk"LIO Oo!HsS4y*ztW׎)FMaiՆIdu#F2fwc 0L:ڮTc~JCd.|20aO!౩l4U1[.L.ewnkʱϳ_`*n9ER<.gf?luQ.-vϲ{dr)UfRap{zxh4z2<)򔔰Ceo%Tް $&C-g A@dưJRLJJ^Ѩ.DTJY9o.m 8 Wgޞ|+ABpn(͂`)İ>S}5QcҤPnC8j:$DG1r<=ޜ+X,QɥjEspnscB}ʦnj_++Ne'sr.YTKspF?W~RZZD6%SRچ,b8krU0+\->S48jvW=xs[vd}:xy]PcLЪϸ&\TVWlkze_ݹ+hC녛!hHT.I-/Agz57f[EK7B%*LH@-B+DgsL8 jF՚UUMثF{J)OԕWraoq[*QyTn6@4uF1n-X,6yzü;8R&/9m2T%bELGO@Bx]TJßٖrʘr Z-,kknzyU7٧|5FQ,w%~h4YempG ZLY7K_.I4s[fDo'X,>CяBfTb얏,2NL0[bVMUeB̊ v!pFd.l6[82V6;3,f;DnD(nݹB" f٩6S2YjLk4 B9s9b>fvx{Q؇rx'6ܮr `y鲑Fw2e8ۨA<1'2Q3蝒p_9hLy%#3 bD_Q;Edឤ$eڋ7&ݪzT OdIE^m%7ˬW%o5oN|KoO֥|+eך]sdG0yauz]Lp9 N q0 %nNViSFR/!T r* &BRVpW)N+9by7olA74W+8Q(ޢVv;ewNQh]J 2$H𰑕Kcpҡ&R>_6=Kr=j5z9\ͻ_ܛt'yrk-kB[6 V̧ iLa_˷'TPXj`J,!KG.9/OMbzּF7SNO W -;r QY/+1%jeE 7uۺ]bTo^wP={[ayh z`v(X=[?6hVulTvxnu?'HQQAq1LWUqC;ąڪBJuUjQH 6$^U&kibUdxJTe*A1%%┲bQͥUٳUϞ]SN[]%<7\VjyR<?`F϶U f\$|&N_ygX0֔pf8/ MW˰Y~( Y$XCEӋbk(W$Z%kuUqM?h~D34o ~D %o]EVZ2>l(~ݬ| M|OzS<ڔ-V!.u]iק dE;ڎ;FszI*KW5ߪיĜOnz宣[A/5{ &1Z]EƥB",Cx*=zs`9ێ׼o J&p!ߗZn 鲷+AC t:AHZ wUIi-ӸiCs_O؆H֕ށS杦FǸmdl!憌l;5{g,SmʔS ɝ <@>=fZo/Uw;bel^ y s̓dYޭً$bR1)38JLsO=m/K Gaݗ?p~n$ ?zEw]Y]*O 7rݡ4ƒ0x ,rYj$';Sux]9H7-<ȉZ%9 =1{0˛NNW%YlsMfє;$ٹ$e T 9*-Gm2;FPxV:qQg]pOr8޽\H#vC &3Bs*޿4(@nA6c> ;] |~C8}2 'r*Z(d]x\ -WHbYµ\v[Rk4x.O{ߡ)эK^d|IjJEIJQEۜ*4疖‚LSO :Kjf2Jʷ4y9Y.)mpKHa#e'ݛlQdžhE ўa!QD n(v*ww1˾gB/&_}ML55aFܗ}t,u'%j.)C@ DM iG#SFҧ6f_U_^_Uw[ve.=VrgM~N:]h\.bĜsDnwm.ɨw x-3 /0F8]O2zy>\hb|;pB^/~p󓝜 DuoPJih ;zjo9iHF,?ZGg0iyZr)-i//W蹱3S:'s zR[泷V*(Ky\w_Tw禇F{v́9-&MfW9]xdg5-xl04>WW~tSTFoU+`䏌hU= *.g}%#&8 SD=}S3Jq/?6eGrE@kٺf]>EN8@\X % E i,\Yg_y`uqXT8e"8,G/ xa%qiqXܒ8nzƩT,a8́\67P$[0ɮ20nr hJqXz8#3xK) 2V|1XN2#Mr+hiy |YWaTF|:#Uq۪a'q:q鯎aXF0XMרqr8,@kDqX z,W+~Eڿ8/bp+q3L׮%zq-a7 Nd%qW1N1\&'10N8Lba,ckO9qg+0-fp6GN?S|/%[8LˏSX8S8##__IHЀz܁<` tJ LV,d9X3#B,[:0@M#&a#;Y1 wB7Fz6ҤVS=8ZlNpC(8=XӋa^.9G9C}=,pα:Z11c:L{B,3A{]z[NQK8>y=$ ،׳yu2{&0VCGC>|ֲX[L729:FU>6Kfֱf%k;j\\X۞ 5݌8J1Fl6}X723.Y}7Velxm:XLD)Ze`LL }*}{;i50x6οo_5y^&Ѐ$>6ޘdckmgAWWt$velTC%&~(ZN="J k:y]twH]=]=}]skJ ;Wvvlhϩ#cSg[YӱEy6ku!mf2օz1?υ? 翞=z"Dg{Dhqܯu|ë" 0wN_y5Mgr7qx W_k%8J!$JsSXI8//<$z"LǺ'cg{q'`йLƪ12Mnt?<3<|go0ŷ\11`pKĸ.WS BC ܶnA2 !;ag8e8u8w8<|3pk=1) ޣ%G /nTfKq%8Rtߖ Bڎn {Xi.'kZaPaf ` !+\L314c؅Y 2h mga 1p`{0à`5ږx l,.0DH#y/v='۝v`qп#(jE^L=KD)q&,vrmM6m6ݢ66M7D 9>Z[T7{|tx`.{n4F/Y<,Na4Ձj\f!j_كyYN;WNd/-{qH5eiN OJ)W S9L@b M1S H ҤzVV*r䔠4_eY.D.X`-ǴH_- umPR^""2o꠮RDFJuUyЄw!EW&py~I4뛚 qS4%Oxk ~˳H7yŏ=_ឥpOW<ńVk5Z V㊁Y銁"V6gdwݢȣΦHΦEr\W-J&M^kꐕ [kCb-V& ˫& \?YxN$dx8 {=cg8`8| 3Gq < ǣ8 IV ߫?՝TPT<&33nO:'@hmOrmDE {Ƕ-ab]*{fLZ@dīlۦyU"q*׋<잛Z&%Xۇbl> endobj 63 0 obj <> stream x]n0 6/xƓd P穳)֍ au%/XFIl; 3WH KVuQb% ?;REH<~ 6ysykުTT3X{r??~F͹0VZ$' NUtܕq endstream endobj 64 0 obj <> endobj 65 0 obj <> stream xY}pvW%Kmmav˶I\bh,l@ gHŒ%qn89DϖPnR&M掬B:]4䦽NgBfiB>fL=ﻯe\_$}ڵVP_ ڍe/q@b`^v~`<04v`~4@OQB-d6!x^VS32Ҧx8$2q8tW1ii)V~~vg"B>"'J¼a"3q@NJ3Rh%kQX`JM_5 K u}~@ VWeq?cM} `Byxty5_܏> * ^Ð8J%^B7 x67Û-RNw8 }!.W@B+H=O³*, O7 ! z7x~3 B0p_+c u?Zo_n]ۼP[jem r$.^ꨪeEv[IRd6 A=Ut3n 40*քeyJ9FKse3iPu:: teW•mH'|QCѢe=,;c)pu(E ) BD뺃fx07kncP "pu']dM?8[hCe:4cVHJQਔN?Cm]rع#}#4:9/"]HWW'ZGsڡ٧-rkPcݿui_ET"d:-u5T`i 3`;Rikb $`G4Ƅ't'|rHn׽wh>z=~Z 3XL%* vzw;HbPCNq %tIaI~#utxz[i5vt}r1bc  ].+L&]ݕtw FU+GZ׃p{2];m@͑#e`o ;aQOpbL w1k鑲n= :d:fЏ6ݯ;^Ǎ$d."If̃.]jgl%eH-Y@r/8x@P?[܈WӸ \ݭ}^tuo|)9R28P$>Jdq<8 of604C%Є (F{ٞrGvONHE;@CvwgpC,wm2 S`a>Fh'Ezd9.}\)-iKdjzFxpwkހc*@+ ?'@ J{껱~"LMNch!{g2= 71u\ӯyirӎ4,Lap0e,PMYn&\N7ˉy:1>'#UFrX?2 oeMssQ .j](/Л <}GmY}SA#|yv]ܯCx-bvF؏4^tp`޵rofDngX71ldlpF?p!K<ư RP1:.r @?F23a,3P; yȷ_2lB!Ņ/&|&f@R sP"a!.1M 2Ca#cfUO0l' fL E^X( !Xa/ȷ6dńy0NF濤b🥸 cw)^L?a{*F~J+"h;?0O1LOS,R &~[3'&gF} eG 1LtV7[}NjnjjEx*~@:D<RXqhH)iRqODC[P4IKd -ju=z_aMSCs!k4%)Q5$T)UI*&C2JD2<0!R4&iW,N5*))= N#15UR$jALRQ<*@|DI#)av1U w۵u#JH$HX%YF,[F/ >&q64GzRw'Kβ_Su/7K&^N8+L zyЮ݂hjQ3^pIM6ArbЈ0o v o"(KQJڇpQ3? 4ٲH~R,7]ЀYk݀G_-5JUbīDITR.遄tOFjISzm!Ũ/H CeUAT㑜TQ2!ԭG)fzhEcxS|v4\6Wڊs&gIG2F Sz/Fi0i];Vȿ<kz(fOܺ|Uh0֕4_V<].SOl&SlO33ק(?:@*kQi,z_y8+frd7B-Ih5ʌ9 Pv.8̑iG?gruTT8@kL1H}+̾>,t%ѹ~- rێz^_Er1nyz #ү 5ǰ7,_Jj ߘkPe0x  s5d ~;KbM\'8|-~޴ߛQ,r{>3kEb["gc>{m[|^{=sŕ-DR{A9+{^^%Ϊ5-̶WUOqgSOx%ol.?X[l//f g>?ß9-8])~th?v*ʼnS~탞{*>z޸ =I2.T+^\Beg?:[%4g{I|l5lfȽb{qtx4G<2i,4dxr.7\+ٓu_M7{ǗӮ6i1ƹwm\q.8r\/=~|H67%VjW-wHN2ıx[Kʋ ubeEdqXVZ'բV'심b5 oyی=Ffl=6CڠXom&Q$:ػJ]+][Κ`̽{<"Wd9 G> endobj 68 0 obj <> stream x]AO 96О&f&=?´؁L)VM<@x7kQ13.qe0HU5fߖsGcl_[2opzqwȁ&8_{'Hj[8=O6=u.;"_mKuw=.:dKƘۭUHw>,K1)t~ڀ[ITߓbک}Ymu endstream endobj 69 0 obj <> endobj 70 0 obj <> stream xy|TE8^Uwo/I: ݐYIHB dDA)"(8#\f3:8#<ЇS3.yu~~^nTթ٪Mz״"=ڈ8$j_|!BҲWܦ|.\j?DHӎ}ye+!;hm^*o1"T>ƭCO(!_Ua+ ?ycgi~UuWAVyU?y =&мNZٿq3w ec%W(UjV7MbmvxS|~) S#cw#>n !? nJdžq6 M\xe\<=4ZB?B;8 O#H_N͋0e hE@C'c'Wė;P-fPSqY-ٸ֢ h;ځџП1Gd.=<-@K;aNWEx"mIN¨ 4}C'W@SpËN|~?Ɵy㸛boŵøEʀ)AW~>e,\@$ü~(+OoP:Xt4fC/BGo@%kh!k`O/vONsQu~>-"8vuc`9_C0)x~ &7'I+vp.،تt-) 3ijoj>9+vbWq=n+p]|#Pq|o?( %-$'[z>>MzzRpJ =0XN&vmlK[H` h9GН!'`g_-8n[5{:|>nYW7⽸?O+U`}< 2Xd/#?\p\ !Ox_o_Rp'/+>U ʅI1w^#\;ڃf $?pY"Bj2  &E|G o, &~] Sr=d1o 0&"-BUM;G{TU @-^,bj;Q ?Q J?CY;ډ5Cwؗn< ?壛pPcZIAAI[ $M,C+@)ݶ,MAb;d_}{q}$,vp\]q ~]V~ #ئ 5oCwЯ<v+x%Ps6y*$JW@,t5&ВjI,45nZ =`n@7oB[A܇C'O@֒6.z{4]h6ankM-y@?)1I3e5[ލr\tℒEy9Y̌HzZj8 H~_v9vhѠi5jRsP]ԗǧLɦP34*hꓠn,NФ2`. SN`#XPYvT^ IxzՄAOgT\Q#&n͵M5~:TݪB:u9B38j''HmIC5}P AZۼoO АՇ[BKPhR)PP5OYݧbHmt5h?Z/ -m^T571Qq9,tn4muI4y&oѵ74@Ж5m]%hֆ>|+ )ѕU%%M+>MhRhM5}h@-EZiPjh9d5&;k`Nvє@H:,3 ] 'H0F%hsK OV}KaG4MS !i78 4ؒd2UQA0efRQUÞ+X8;ki u $@>4 h01 2 %8>O?s }֜ͣ5kF7O6kT&]6{AT)Iis%#uIZ]yH"S.Az} JKTjJV>iJ"nbK.5KNobtltL~90F]yvL]h͛BRKB|͝M;:?WOn%h}~~Ղ#o[MuӤl%^Bu?*TSCZG-;K=gtplP ۲P,*!?0́<(q'. tI }8? RWTY?KQc{_GOGIY`QDdٍqPQjcF !v*5jmpoܧ, {eCMuް6W#58|#̴Lf]YVFg>t-& a ߜ.KiXVz٘x3Y~d17Fl)RqPQ*ifK\s}C4~0?yM6duZ&;bu#N6|Ю[,^3N&!;ر9@u\^QM>'{f8<#K|6 p`:D)Pҵg35I)8$!eM`ԧTXukTxLD#xA), [&:)uϟDre+IzYeOKfwF.+TZyY}@WM =lϑ=?())i@]b#c/e 7WyaŌi|Ãk|hۜ&_;{=ۈJ&yTʓj_ٯTUET<$7W m`mVٜ%#Y֢ܼ`YGFd:Ѩ͑ kRT( #%P\_\\,(-Q,plM Hb1k5Z}ƅ]t_( ۓG\`[PQԀ :?X9OrW%NlUUj( {) dInx_#jm\RwdZ ֫mצ.K֭P]͝!dXRݛ}{ݏPlr8ܫ.d@Yn72T@Hg,U7~wg4٤ܓMi`g\ѓw&2CN5<r`sfc8)ȹfPWcI ݈F1ӆvM !qYrT\ jN찚Dc~%α fG/u>tS~w;<7~OO%:IbW1DwKQzE_`!hi}S{NXkx1㋺tJeU DLoDEK1;pXU1R.x;Xe1+K/"^w|x ;)'U9DaLL14nVskH'4;eF$&u +idjUDZT4R%,P ]4;*DYEH1NZ-!"_4` K2R]s,6등b_cyDKl>~c_>8ϞA4.r[Yrz>S ѪؕJ[mVtƺFHty{+jy5s|tz,:ҮL[mv[gҖEL%m%+K/;JE흥d:J˗ˊNQeO@Ey!>gɨ`\p^⃜s}7m\ JU&f:|]?7Ta T IrCGpV8e6fcB@h*+qAa\C S#DC '0`NX n.?"yS z&x "$+~pVOL)]ZVGb'Q"%" 歘{T,z?ǃ˼%#@A% TʪSA*5"cnHP$hp^Ha1aD')bž$Q-.JO %xP'%s6mQZw۾ſ{ jervuc]c-qap{ Z'BL̟ &YY:S/x"hV'jzl?q& X{ikHfA)8aPO<*S|M} " |:Eo+ <$歮\eɧNJ7pˡF `L΋R&tg"dcn;3"'2s4&u*\9'(;99-~GdyJ 'G,<|4 >{w8Ui=9ŧjr]5w5hT5evrz炇Rp<_'D)2wz 凰Qμ{;_Ŏ޽wi?z7`|⣗v/A Zą$ܦﲑ2!-Ew-C?7[DJ|S=Wy;<<[=|(LkG'WnI?pY.݃p'pPvR\Gql /O:VM? T鷋J A# }0vRik~We5 N!߉]ܹ* M.Y(=<<6*6@:)ƛJLMerl6j,iq,>dwjWUSxkUy*Ez|983O8"8fE>ɂgYް|iERgXLƠ-՟ A~_0u\^P($uyqu2-l=S*`vҗI**s\ꔙh8qRSmZQreq}/4O> +UVPՍ*W*\-II j{pŘ{r]?tq]\~A"NV+t|+.Ï#L?v]PQ*UZTwawd.|B9HL!e3V![K0ݾ$QiOmZ(陸pdv{Faβ:*fhr^f^Om]]m QMM~"ϭmzwUOXȌ!&{@(DMm {F{r5ݜK*Sӏ7@Hv]:#Y|JkSd>߯_cwwaן]wݙ_&ۓпٱh*re4!Bql<4b8e+52stQI'ͻHVⲸT[]ܓeՕrySY^0I] R\\cn@ KSi݅Y(L)3St) yٰy,<73;s|6z9>?KgzgzP{957'6[_c@O߼ӻ~r[kS݇bgbJ+RwN7NE w/H6TFCGH>@*STRzGuƭr͎č)g|MHi &($|re١ev 2>#cUxUk$'vkl>w*.AfBFA󿶅nGRz.?v2"C/&$PQ2';;G̙s;]w/^5rGc3ɵg=볥uC_bmxgifjWZ[ܧUMl YubYֈNM zcfdF8I :>P ؊D R[)x,z=ZKQ(MY=/Cʐ+kK FәL}JOB=/~`KGYEIy>c'&$䑇kjPk㸗7VansKN3mu!wXT MNA,0<`Rp)(q+=tO'x^ýIs83NA_}|F8 EOq5Q?K1bKw%)|~XowGkls\nL"Wc Ev9BxY0 ٠l)*1&J&l4aX =ڳ:l;xb LQ1gCop`06I'X`W]xaTI֬<6 ~ifk tDN+-56O;Ki]GSxg&w%λgƻq4Ѥ,6D9PIl!\*˘K !wY?M{z$!^a+W&,mW< ._b{2'wTh,tvG4ve~*tn2κy<֩/2kΕBL#N5" ݀<(_֘:u?RZa;r!t.˜tvk Ìq?F_%ƗΠahov;LF_,)?.JU18V}Œ Y@Z Po{˛ޙNv>_`ǕۓwCeB7"csm0'Yd ;ew~I{]T\OcZPdͽPZJ= ^Dh/<ՋA%U%y#9 ΦSLoK-r[c2fj X_g2}Q1 E}l2heGjr<n{0+5XspaƒVs} /D:`S e_|#0tV&A eC(%: +UH5DneNiO`vii 9:7@k͍cHc75:UZ5n{ҿr^򤕓NK($a"*%edMl 'ُ^k 3F aG~_6 ! ǬR( jhQ;sF>E ƒ,Z *Z-6 LGM[T+e+̜(^X#d γtXXް>Moa+ͥ8AI_ň3!d^^Bj*mE8g++(^Fy7ok?Mc.~ꀅhCG_#NO* 8b27dר1ޥ SٴEfk9Ơe絠0C@%\aQ( r4@^;.7~~tXBؕH;ċZd3$GHs.] דy^IHL:Vѫ*Bɥa LQ6+) c 4}rDTߧ=3z3>" 쳺*EQiTdtk#@2A0Vh. Aĉ,l-Dv sIti+% p?K6AZKO+.[O~UEoZCa8'h-9>:0y۞32a=oľٍŁ˯y ]vu\G< ~J@{ F‚bᩩ%H!p[ɽJ-~U?[Gs\Sv>p윿^[BZ iysڎ7$^H8H6>w3} >E&a9: TdϞl}eV],,bY4EQffV&$jGda!9c S@m2h1-03--#3%Eu#Tg2!.++ۯVpX,栗M}ѿϽj?t=nwVf&x4㶘>-,¡VF5sHNƕ润]}׃ݭ4H Yϗ34sv(@f8B@?(Cy s̛@nm8:=at1J(+!qQ]~N/9; H*þjƿT◨g'\3]po"-C?MM?Xl*F ;BN|/齶 (,?h_Gh!j/n8jN8Y <GPs~~Jн2tlCh< pߨxTqQqQ5W=GK:]w~f?4LŦJE >&Mkn{csk{y  (RG<+H~?rg+Za= C НIOP, +{ Ka 軜$l #4.%at W&H'ae+I$@&EKV"; PbcV#?p.$a@Ϙ`,j+(TKd Vf P~ju}IhJ@CmIh~0  G0PVjqג0P t[s'g剹Fa[O2Xd8qmlg_38=S(`?# S|C3<alIeXDa}|Z q$O @s tԁVCEQ'+\74n65U ΁о\+⥀I{X6V*cxˡzθDk7a E6P߁Ao??K$ZBW!o? tzB*kʴլDykeɑWCi.Ab}Hΰx5W?ױБf/Tm@m@mC\Lbl<#\$:O3ka~l\}8Nnxau6O2mhmx۶d_[鬴T'|OԔGt~IgwߴHi+@4VeWeW*2Ze]4Y弩49m5ӓߞkz)]}ЃzzJ1[ le:iM 5KK@kg3c7(5}@mz0TB7N2 B endstream endobj 71 0 obj 15162 endobj 72 0 obj <> endobj 73 0 obj <> stream x]n0E|"6 !%$HYCT 2dRLX'3͢Lcin&qkgEu_Azid!˂Mծ./!ۙX}g>z2AZTUOZk+x$_+VVJA&e.2ͿXr˥?+J+rki3Gkbo)s ~[f_c=̂{\\2MIxZ8(O_#_N_->=(Yzf?ك?Źkkp?^KKc0?#&꛵n@Du~g~Ft7" endstream endobj 74 0 obj <> endobj 75 0 obj <> stream xսy`T0~2vgޙ;3l$%+ !@H@6-$JM\UѢm5XAݺ`[ۯZuFkLs̄_$ss={s&[5Wml\^ aնa?\3v+ }g톝kdaXvg51`<~68<ο 3mXՓ n%X<ƞ{eܦNpUCJ#G A):&)Z"JZ1zdXmvP8 D,U^QYU=&-άVr?\.F0KBGo2%B'>?>@x ~^?O#x f`HlA'hO=`ARof;Lx\Ëe1Xց]i#8  ރcXn7J0tzI( tˁ>ċ.p+5Azq-u\vj&_#8jQUy$( Eh$*,˱۬h3:FR*2HD1[]i,zܱ1*|:9uͱc1`kAƀq0t8)wQu|Cnuw7gܣOu|]2* &k I Ģch@ucnp$xx̹ L?eyȘ1iݘ ߗ{!t[1`ewD_s\|Ӏ 4-Bxl@>np }AnzxfE]>&أKN`CN,~֋6x8 a]-|f)=lX<ԋ kC =k׍,Zځ_3הLw}7涎:`|O3;S-ݹА?ɡ' Vqc]Ѧ\UIZ\5& 0љ 3h<3XӚ?c16Us7{~XA9,D:))#ӫǬFIxc"c NC fo!߼`ipp -7EtI;c%Dcdݢʈ<iؿpiyu ss}(b9<帆zHA`I6ry;٫r\_j8xO/{;;+8hs.7:<`vZm/l`! $/@DY6pߖ{3/ C+;Ž19:`t;>.\@k\9GrmŧFyOG3Yc/GӳR#xSק`C F-)*;#f 2Y%j oDZ# &V~KpVhKOΣI=%44P?ZMVPr:IR2Wxu’7DJ4f$T|GTfhsya0 D7QaA?.M$I!7TTq!28lJP!t%' *G[ZCjR}I((y+"">(gR+-&C$+Or~ƳZ-sl[Ͷ~wb^JiȶW–V@S%tdEH%LHBO %xZjkzXЌhjQ a4&}RnB-' PC7="ٟ[.wmND _>O?3" H Jy<ψMR&`$'_jsl3ImEp: eb.vT.)J}chw+NOo j9ӦX~jLMvᄌ^%Cާ j@3=A:7 r>,Ϲp}4qn7"gP1K2͏"(#A2D?C@cSs7egPйRPT#m[^@"(Р4xKyyTù">UV#hHK4m5j\NX!fi%5jY s'GUJ%Y[KREO՘,C A>5yAA6$ $D%,E_1l1C>y) ,V7gm njLUw,QkNy<p/xËPFYNje!<~WT!lz9ixy8".ϥtK;MѠf %kXk,ԟc85WxוH$iȦJG"HuZAS+9Q G3%,Xt_eUÜ3N_װ7V:8˓[wT"\udo]aV]+$ F=]c}sXșj[SYE̋MoA&z"jwS #"1"#e^3vpG{7G!r$7Uhض9E X4 9-  .+q J-h%xvɛc9g8#dA3,q鶄Sd)#¿[HPP%yyBjpVĪ䫢mЩ&TYkvrH$ɇw[H /K9Xd[`؆*GxB) ܇FOω:1-ƽ]M,#0M!އiӾ|jQtD.uA[+gU:q޳d]2nk NȪ@r*Ieֈm<$-^/Jl*bVgqNLRN¹I֤R&JidY?N8<-o)TT4yxՈNɲn&BʺoTSaE+ʀڠFDR!MQd!R-! [ѤvRDimu홏BE67SGWyyf6ĊQbݬ`ɦ&Ry+3 O/kXYL&/U%Di m[@ҪHb ;#lY9VչA`¡p$t800Z-/F[D` b~$Wx4_@Z,& ݃>qQJHE Rlx b V+ڃbtqKbAw@!BOSRE{̹цC@.G<{E付 p(0% %쥲vI7xDIu"愸 oPn1%#~Q MɹmT*}IMW7:ןtpÓ@ D RA֐]B8|qh [ gKnj'$(VBRR'8 aogN5'H$_"hpZ Uڄԥ4Mp;7 (lUHdK&\pqqQŢMAF*=RD(c _SVUVXX^%a]H!ݭTjxDj> TQ4،(qv_Om !JEuV hA ~W`JGVt!eX+;8:Kf"K%TjJ2c51X$W|!Ҫ]`Ka-DZ+f>6cj/,]qs{õo\MiFYhБv=ux-LhTW6\q>vyL|\pXtJ8!EPGTFvRpX鸞EBV\Ue2RER]heΩ ;!0Y+J$NWT$r'_'SËٳ3g=3k yHf^uBQȰ8Z}G=EmOڎ1]a ȅ@+{MQ ȿ\o9٣F 3]Vr5(@A%pH*odȦFvF˟9B5Ϸv#u~S}vfnʚ湵3>)?>xRDa7om%9TceSSSBQ8xx*>,8K*RKex0:''?7bq;&N\ )·PB^rc%P ^MJM68(E~N@dQzk>mVý- 09%@‚045ܚ=feJBPWdC{ƈ{)XO̱,Dlm?O wfHیR8GEIYm#aI]UCqc5{ @pt1 ]PëEkx#c/8a TXBݔwM8 ]Z(.#ŎQFʽ`Jn8(OpguP!SGrKD'U6$FT$\Lt5u(UXpѣUr*|K%LYa^8+Ԇ+lKt8 kS#YIj654?,=";z")|8w x'΀Yꝥh2w(fn;.QP$7p&S::lJСqluEƧli?fa/l'ڙ:'7x}[G署yfio?Z0u4ȌFn^h̦yP?4_OP#zX6b %BeZA7A%2Q d4L1d=eqJ)"k,*ҁKYEЀ9c{ۍF6=YPpn9ʝʝA8QgromF+!pk?/W!7Q!kvNUzj|:.=kj, zV޹ݳ0䜾"dggQo^}ÜBgM\uuTLh\֐3-u<܋UygVA*Y^T)#A>NXSJ4vDQ1RrbDNѤ!pϕ\r t!c2pL'G8ʙDjJ +CX$fCWj ))vls\2#h O)ʀj^m}R d5\LfQ8ZS!h+kTn8egN6 Zg( tmO27?2%yNeL 07OHT.FɩpץKЧc.!m5ʘclۍջ n7]N9I;&'.(pa9q:#a(#+"#:Oaa3p " #I!ie1z/4y兕.0Mt Yl%{])7hUABӟMd6T+4!z%b^/q#“i;j&LD!gyH:++供(Օ*xJTU3%*.MZ ɡ\EM+ʳڗT}~S3e6y%sRɳLeyfK(1 œ '*B$H -"+rnP@ _~4;SU3m)_ =rsϞrM 4H̫VXXs(xbs[< HNCu(ZT8\zz?j> ݿ?BK ª) >*?)c)pϢ=.,\7qԘ Ńdn j1j󼫝]-]8q<ւIi[z@i|ݒ2xycg9 \̝Bk6||L柋/jRe9WdSK]g .z Ӥ9J].SPZ;e"mųϴ͜<5p毲uv?8svuBfxfeDYYxZ@\]Hnm@p R 7ē**J<ژ6 dnmkCz"mmB;![)}J:=/օ8fqPˎEp8vy `Xl"tJ}ӚCdf|D^~|F:mNQG,mϒoPRgsr)=7Q/URVP-auٺб{vZ &J6B`a7"̳1‚Ѝ.|THW$Z}v KP<6CblkOV^OnUMf #ҀeG .#EŅ8rM).הR)Х x&lf6ЅK4›%SuO7]SJi_vn'01S3*kD 'smK˗' ̫Wm(39߬׾hcd8:XxCWa'fUx;LLgY1baigcεS70M Jw.awgv eeJup蜠qWs#,0u>+y1GC,lb5RqHT#:с'ؑ@>!TlO-9"?֊ZqGv䦜F̈&кF+gT+ZqemK\S>>Ʈ|W4`ҷjj`JTj89!n LYWcs@zE*=THW4J2:3!kdPrΌ7_=R!heY,?ǟצwW\ysuo3B#NNKJ;S_U%\Ҡ֡z[;/8ȨC`ڳAhj4@TSph9@(k*Ņ ;"M# %+WK9 W=|9jƽ糯ۖ>.tK\&]c.k *mhx4T5_ MMA^nm2h=_PU@ 4Hja$I)U^ RJ^D %ӈ4G$A(!(8 #%XY!#~LHtEr$b4VT+*umHGTW羢ex={K>tO/L/ifĥ RK.!oq~, TY3W>SUy<@Y(EFZ<I0P.,]Y:F#Aݶ`x$6Tn`4Hfj 2ZBG# >)>,G\G#>5jP>RiUcZ|liß 8tƯk@9B] ȫꫪJ~NLg //l55+%}^]% ]3hk0Zݶe=j/?dc&/^ *Uݗ_f^O!f^*UjjH*$q,'"Iu9-( arzyЧIWT]zw_PT)E 5QhTWAɲi!ʭH QNЂaqR,E$7Wuc.LtJ OH_? ؤ$8)yocJ}C(J%K3e3G[m)fi8'p Y4$ԽtM̿.ّ"9~8P&m$LVڡLB`69"fK!HvA.QTCWGJ(K:bj䫓DuYStUhcR#M. iJmQI7ND.?6peհ5PhvО0O}3Ocf,gfLG@w! &#E!bKs> j%JGF^qoXN}O9xL _Q$+ ˴ ޸mwQ$m9ǍwO: 똺:b9xMGZ,8jkb\Td6 R_%oSTǹ&jEQeK.Cstjk2U\TU5֦O/gk4+hmc):\ J>Zϥ AЙ ?ܾ|yv&$I>F#89>j>zc:}&uy *†[IDTR8 }/$d%)H)m6^"d59\G^OHR@eYEGP(U8Bр<^ !Q]/E7Vd"| }[A}ӓ?ifBb?|U@FBm0R![ɊdbHlF*ٶ,%gm4Tvφ KKSAl9 ""[ S(]{+M:яؼ_o %Zӽ|Go?u7X{ddaHɃk9aZw_YRB-B8MCx}]BbpQxa}ẢA΢ւ`QYRLSGU'slW17#99B&#%&HK}w=U)%M1'YW-IﶟPOuc]r=_8u9<~OTzn(#9 AȻ\l*oCLEMWbtkM[{CK'mיXc'o{(]5h{ÌܣH'O찬Yv=E&R 2 u ;ڛ S:LFdMֽ1pwCQ'>K Nk&ނp[ΥW}* *7B?VjorX/ZWB ̬ !Nb9/mxETf{#$GtPᰙI[NFFi|  6b/@.G(/vRm՜"$'N_K2:(wLo/do+k]gcxW$¡$j2}aqV*^ n b NE[inhX D#('%LQei![zebeob7`\0F@aC:RSNy?kypy]/tLp]>ҤNK)$Jca)8D iH ^>(JbEahKhWQ(#Os>[]8vכb+N:cďbV|?y5[rN_(XCW\d/uE C$.ˡN_S "W~BVs te~96hFSN'Q;Lܸxzэ>[GKBn3A^?]^$p}`Qgըp+Xco)]1d' .CyL/ZAKjed@I`K8 '$Y|e7g#݅E6OXkjog<*2ﻁ!1O/ui`d,.e2,ΛQyg|>E+f8K+*% /}Eh9eh{ןlSjd18Lsp!d?zor4JAEẒ1zf)g V)BG/9#@# !.3ٓgmɷ$OJ j;'8~v=@ %H5 fNmja1 bw.Nl?F[+*Z+孭{~ޖ< నVx1GpX!@,@H9fLǎ*†z-Y0:w,RfbX,( vpyEne CO 57&21W\U?}uoCҥk7X?,OVRXi-Aqeo=yo`Uanʾu=O}MuwYڱj5Hj# ^_ߢxFܤPg+0ᰜx!/r%@1$ jez^1j.ڧ [;'v B_i?kQP (F4N(9cy+{-F?^ODL^|_oؕ'}eF1D)ũ`.W|E٢&_ZV#PE>RAH}r⫒W)+s䳕F`z (\.)EnPnSo|z74!-qnT4:¥kҊEQR,U-ҬSSTl1:+2+9)IBBL &h+(Ju\H^\4"&{x夕&rW/tcf5K2H |O$R={0Mx]̡Uo^$殗֭CʕD>MGR2'Μ?6(70á*;23hg?jWW$ojhg߀&byn[I _G+cY  bU\;R͞/>GJY3_;楗@\HDQ[7 ׫V B4IRfάnr8[%W?SGqQUhi5/v,긡FQQQ#qd\$QW)M_O55MF:ٚ"+h_g$tkX(p!ۇ_F2qD@S;¾6M@*>L:6)K%q"s~!t$u3or5j* ,a^ǻmٖJwSMDiV=fР(,n*…}1(ʵ wkK|RfV|ڱa%$h&L eK!֪R‚MJIT nԨM}7<Q; x^|."7W-j+Eh8]zvv4xz3%=X{˅ &QL*JE Φ5H AbUҪmV4 Wh״SRɯ@2aze\LRrcbFK]2 ^q-1l6:gV&˧ouR-~V› M#CKQ >nP řjA/E R ^&#$EvgGHpJMk! % A\ ʨ5n"OK'́6 "& Q4cekS?vŵ7otg$X~'GC-bXo{ۓeB]EVLZEraŮTccEٶjqApnb#- u©-f).>SNm+%\pfO7g.2*P)@֞IO_5A$6I^{Od00>f B?fGX<T$лrE[A#8}䇴i?PA?wW> C l:1  9$^D Ԡ~bj+-gJ2X1%O'+$'Q z`VS(h罗J>Rd?L+Zĩ bnt/XIAp Cg%ǥ2}]MG+E6}CW1w1\2>d*4dbYilk{>w9?v\w?y>az̗k'A_Kc #}[c>3Q|OlCgdԼ&2xD䮒L?s0 W` Ё9X ۯ2 ܘ@29X*k=kAEOFCj6 K ̐_ð2,!pr0TU&!<;S`$Ӱ,9X eS9X.z<kcEւE1D,U9ը]ǰn 㬰Y gOa83g Xm>x7Ֆ5\-=6pmkޡ-zW5lYֻv놞-*w+-wJűx,QsM\knq}[nK--={7lY 3|Cs88 xS0~pp׳i7X5uޡ-6\w^0<6vl6ӭ4 q[6WwjX)D2nVn_Ջn+aEsVj`0sp`햞ŐC z; 腂y+\9 &(z`j^4]9O=G}<y:qPyr0D ml&P̄o6ضsC{ampl{tZد_ +]} ^ߎ݇p|;@ yo ~pWǸGܢaNhoύVnVVã}ök+L'ڄBOÁ?v~1ιL 'Vx=Q?]i8#{Mx3{v~g'o>_wk(ypEC/~8G [o1W`t~U ٦_,d Ǵ.&zV\uġ_6|݆gi}K{5_ X͋OR|=+K%]K@moߦ[}Mx-Zk+^xMʾ*~Ug?eؗ_q?e/`=`ǏC0s~?~N%g>V ?wͱѢGNeڲ}{9R|N+cϏ*C9L|۟YBw=K3g=gTڲ#‡`y''a7?`ox>?''Jf%w#_"tcG^x~~?ϼf_dsp+g>uq^{^="eYgol{^w{]&nQsؑ>v(ǃ}2=T4 G!]@|> endobj 78 0 obj <> stream x]ˮ0<cL!"eы H ByR>g!wǾoМœ^}xLMHi52VIsOn2I=>3}ٴ9|JS/?8>=W~N6\bL^m|ט/9Pp&Lu :ϫt}8TISΗg=PCUd,,/NԘBƗw Fރ76+ZG`s]e`Xc/nǺ7CR{8_W`;s~ C<ߡhrO{/wKu/p//_Ku/пDMYoQ_oQ_o5Vk)ӿ`B{;YG_/[go_>k< tZ e1M]}2#W a endstream endobj 79 0 obj <> endobj 80 0 obj <> endobj 81 0 obj <> endobj 1 0 obj <>/Contents 2 0 R>> endobj 4 0 obj <>/Contents 5 0 R>> endobj 7 0 obj <>/Contents 8 0 R>> endobj 10 0 obj <>/Contents 11 0 R>> endobj 13 0 obj <>/Contents 14 0 R>> endobj 16 0 obj <>/Contents 17 0 R>> endobj 19 0 obj <>/Contents 20 0 R>> endobj 22 0 obj <>/Contents 23 0 R>> endobj 25 0 obj <>/Contents 26 0 R>> endobj 28 0 obj <>/Contents 29 0 R>> endobj 44 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <> endobj 35 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 40 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 43 0 obj <> endobj 82 0 obj <> endobj 83 0 obj < /Creator /Producer /CreationDate(D:20220621102647+01'00')>> endobj xref 0 84 0000000000 65535 f 0000102450 00000 n 0000000019 00000 n 0000000996 00000 n 0000102722 00000 n 0000001016 00000 n 0000003242 00000 n 0000102892 00000 n 0000003263 00000 n 0000004218 00000 n 0000103062 00000 n 0000004238 00000 n 0000005637 00000 n 0000103234 00000 n 0000005659 00000 n 0000007776 00000 n 0000103406 00000 n 0000007798 00000 n 0000008679 00000 n 0000103578 00000 n 0000008700 00000 n 0000010573 00000 n 0000103750 00000 n 0000010595 00000 n 0000011949 00000 n 0000103922 00000 n 0000011971 00000 n 0000013811 00000 n 0000104094 00000 n 0000013833 00000 n 0000014706 00000 n 0000104428 00000 n 0000104556 00000 n 0000104685 00000 n 0000104814 00000 n 0000104943 00000 n 0000105072 00000 n 0000105201 00000 n 0000105331 00000 n 0000105459 00000 n 0000105587 00000 n 0000105715 00000 n 0000105845 00000 n 0000105975 00000 n 0000104266 00000 n 0000014727 00000 n 0000024057 00000 n 0000024079 00000 n 0000024275 00000 n 0000024681 00000 n 0000024943 00000 n 0000035329 00000 n 0000035352 00000 n 0000035554 00000 n 0000035897 00000 n 0000036108 00000 n 0000045882 00000 n 0000045904 00000 n 0000046107 00000 n 0000046575 00000 n 0000046898 00000 n 0000058653 00000 n 0000058676 00000 n 0000058883 00000 n 0000059259 00000 n 0000059500 00000 n 0000063519 00000 n 0000063541 00000 n 0000063742 00000 n 0000064033 00000 n 0000064199 00000 n 0000079448 00000 n 0000079471 00000 n 0000079668 00000 n 0000080139 00000 n 0000080461 00000 n 0000101067 00000 n 0000101090 00000 n 0000101281 00000 n 0000101874 00000 n 0000102302 00000 n 0000102395 00000 n 0000106104 00000 n 0000106202 00000 n trailer < ] /DocChecksum /8196E0DA8F1DEE1679D033C1BF481044 >> startxref 106443 %%EOF e2guardian-5.5.8r/notes/V5_list_definition.odt000066400000000000000000000456561477372360500213670ustar00rootroot00000000000000PKLU^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKLUmeta.xmlTn0+W Y=4H@nMe2)Pt}@oRyrng2zha%j+0i gpc'ZPaֈbIx/Q/V3;11'iA˦\smټFl\l)6r{DAEHH}i( 4unbyxW;o8mUN3+M ,pgluRj{V@>ʩNCE1>Hp$/UREJiF4*XM4D+m#UgrH@^X?D_6./Ybߢ";4:X0 ~c>wxj ¡G$=gQ2SB9&K,^f+0B&C07Ϡ:zØ=^1 =(o*7雹+ryyet/7'Kل?BD:.ūwt:m>S*.-JKקey} #qJxh'V.* >l~󓋣-'jڃ&`V dvӖ UyFL8X|e3`z޲JÕ77VsZ Ur[\gjpÇvȮ \ACY&ΏDžbR\͉gP8,\%DUllFCaKOX`<Ӌ"M– f 1˕7 uC vPBt`M߬@p-;T(DGJ6 [J\0mRfJ9{QRY y%Eyl|,0ˆze$]$n) {]9(*Yke{.]\>Zu.0hzbՔ'n']'N4Xu2Yܒ䋘~X(+2Dޖ]&D8d D%VȳU4N81E6󛡳cXi1i{c ;@dF@?f-~>-ܛܹQRe(&h?1N[ gh7%dS7 uQPtᄁM#UVaWw}n$#͓aSE=io1oݖh O\|(Fh6u%'C># jcy,P b&9 HҎ =D>NGjˊC&Mt 27/Z'H`R<6ڳXjRŠ׏*gv;06ԋ 2]dݖCboUPL٤n\ʀY'I|eˍzPt$D_zPC&ns (H56Ɔ3w=LQ%I*T=0ಾn6ղs$u#_V(FFDq'ќԯq}5&&{ѝ_U@oqm(=ln!mDc,*kڧ(,Sg8~V3& +A*A=,F*ȵZŬ^Ż֓@fd~2fuoS bD{D%dRa;@[0 ;(DH84>Ѿm{'gT S0$ݷYc_m$Dz' :M;xcO;mQuŻ ls -{iZ~:L;)s sǓC S^pSS:j%ĥ<@ ov-i-S(GK0I^J( sFEI o5p?enqv7Ɋ$fnj-ӞP;Wsl e|- t@'! pܰT9{?[gA/k?aY^Z}PKc+Y"4PKLUConfigurations2/statusbar/PKLU'Configurations2/accelerator/current.xmlPKPKLUConfigurations2/floater/PKLUConfigurations2/menubar/PKLUConfigurations2/popupmenu/PKLUConfigurations2/images/Bitmaps/PKLUConfigurations2/toolbar/PKLUConfigurations2/progressbar/PKLUConfigurations2/toolpanel/PKLU manifest.rdf͓n0ci^,6G0F }{Ut1t0?#F2d邢mq(pW0BIeEٶookݢbӱ_5Zn+Q`$V#@?e>JRo{$-0X.P_y87`3[Wve 1%6F0TjHm[)2}  bG0pLq3m NlNȲ\!eE=(?(nBѷ']ARImѰەm׈J5Y8^0UP]`ۢ1BCeFGyZN9O@~jqT(!u Sx+EtB-׽~\w "%o"Ys}W(^$F[Eq|[FPHع: ݒde4([]18q vo,)"H<FexRd3ufk `bjtg 溅'EXq>4c;'{3lm4V*U' k Tˠ P[U0xpBv4HTi8_E+YhVLV] s\xO?EV"=3Af6ϔvΎ2?EEhz2RtؑbɄBI M;C 74.HʂyHdEI]dAbHTh \>n¼bٰ1ƘGX0rjA#YvoO,`ieA/)YH`%d4YPR.K!sDsN>CsfhKP.p*"( ƃş*R "XݚʃP35c&YCS?o4E o?NeL^J XW H>8l =Je IH(+N[7ႀ9Tm"z !MyBdI%*{IVQQy$ez~Mߋ DO$i{o L:&{Imvf\`Fv Na}4?^R݊,.0<"xZĀV+|8 ņpW'9t1b4 A/(6DPyҤ< KM>Qt Pu?$ %J_!"T-(١}'ZAv<༐Vt=wuJҵTt^O~ۜsVIiN_O(K67kPP\#)C * ϷHlK>@ ]UT&`Xy+!qy" H_Zg8m7&qDvun] p 3Z㝴gR^zac(GJ!v[Зp:Msi?]vD:6 d0%T!0ctRSS>b1 Z .]! - W6 ܊-dcZu%2j"ًVc#ݶSE&qwY'M&3mGA (7ѯl7AM6Ab} >},7\R 6Zۥ:MWu썖H:Ү_e^㧝0KNZ ~ kP9/*ݘ[~ڊ:z_e!NfNc+PYE{ƁdWwp);a}Cc&_ ^jg"QqWH_Tr?'҃|=L7;^XrAr9&KuktZ"~"?3CjjSBJ:IPtL]iBSqΡD_cT/jm>a0E<|zүwk] Eӻi&) .2OV1`H0zix$xnh dQn},F5h\$;{˽s gAMwRC6Aj]p$*R/~ѣ%,'1(fP9|`': aVK^3W_xG>_ Ra+qZ#`|\ d'm[@俊!P:G8tn ) uɿF=Qϱ?R`VVm k'0MA+gK;eB ļ10=hcz5ԡ+f۾=64|SILM3` bxܡ~ 9I0 ~p`OkF=(XI٪ݒuߢZU;M /O.Թ\htd/O%LIPs2PU-쐡&,wH*J5û㣅]8r~({;'0!OU-75NpTnݨdLl) v=ĭcl--aa$Y6U[Sw?PK2)qx NPKLU content.xml]ْzStѕ7px,َ4rjM#̒sHn @BrYs5ۿQ׮aY`_ g_#66spXEk9SG}ö-ϧvQQnZ_6 {;3;c]$׺ǽKnw뗶ìPޗBk .]v4R#y=4풹eWCzrfj@&as9@K۶[3v.c0ǚ6@T vWv`;2ɒfO)[H;&C,pO&+˃_QP4zDh1]Snޗ_ukM.sl-62/62(;^8-S@{魼5 =pAթ@/L̐m38"g8cȄoб5FA2z~r!;{YVA]toߡ)}(gjߔbn]9w$/ϱ`ԹQ;^?h@wEu5nzBZir95X;nxG?wO;$19讙z϶.upnwfC/$>Xdm: v-$ZWq\E>R^2WHݷ\d\Ms3eқpxu40Sљ6tk؝[ Ic*&0)f2H i sE7ѣ-2ljRY(zl4,D/z cmKlvufY$8+ҡL%B.m,ad+_4Lx @/ tlS_j"9)v+o9u;H~Jס.]ل K;◮/!IYAR^B b>]tb-Rݽ>UH) 7@6]c{XbbUٙ 3VSQ켖D=Lr>Ug ?s3km<,>U7t:#Tg?sƴTvp"WCnwl-(zy͞j:T+(-9EH?y8wݳ{&CmIޡs(9$(Gc#华:zڨg$dAÞ6xW5z*-UX&WԆ+DmOQZ{S/6W^@Y{Ӵ`ګמY{*h޻q#i>h?!pvB3@zf{Vf,ϕ4Kj^i1^AYQrZWrnO[ϤJj^g8yui@t/|rq04PK|C 0'è3JAyZ04PPG3hTCVx 1,DhSَz8\/_,VX^'|d7Xa^XFO8OiUZBfkZz!MB5d{NU^8pR@a!ܢJV'!2f2d+eĺI[((kK*BR[7큚/-4mY5M*X<tʥYz:f5N:KzY&ek,ʪK,ZGSJY:\<)UgiYlRR:kCkWg9]M%ĺ==S-my2n= Smxy2jOTuPץTuS96TuVr:ldOU8z{! 2Q:|N7(|zQh5# NfN ڧH }c&}y2)CT^zڧH 5mluSjV\#!_Uf-qr!? *l>nx*'Գ5Ug %x>Yx)ƪQsx+-]z ~7}"6/v*c>v //@E={73[$>vo쎙 lj|kOE6Gx^S!O2-L_n~f")S.k?5U_&䏿 {>yVwhA"?Vjе}j7&gN\  \T ¡x 4胖):L">-Gv(OǷdMfiVDlWb*)\,FY8W&g0)7PW0|dy1^'9&vrdrWM49U-9ԺO[&[lUI8K)m3|-"QMΧIrvsB沠uLȆzy g#qdCb[kBßn0"=˾'`ko(Ǐ\ǀ=ra#eНf.P]Y>a}B"}\0KJQ>A@ o<0M(x̭Q(Ht/x\3m eٓ3 =AV~ ][ЀqBJ HM (d%p#YQO>q.[lX .8YPE.h{o0;ree_MڅC)Yl <@dBaGrH Sb,/* 9-Iq|Zȇ]wΗPÐ u|hD_y '$=L4 `[$%>':q9c[c>8d1lN Q1 _2*B/tM#ǐ?I*lFeegRҴO 7lW8r!7d!ΌXP7 iDƲv5C}XXxb^z1S^u..oadPK?~Zk!Jiu(Dd-Z8~ЈxB<|$oc.!I1DcԊE kT #It%4*NK|^E_rֶ7/ą™.4L;Їwٴ#^H=Z>}%TJp_# $2}ÿ+C 7bvdc[vbOMc4[9.܀QJaҹҤZB)Dh%5 3r',-r<{~lsϦѰ1Lb/RI OIQ->- .ײ ) {K0L3YC؎Q˶+t7@*^0ƕ|Qe:X]ַo J.\n]Kk\ Cwk62ETZav#0ކ6"V7 !O1,S..w`vaaZ^w 0 0+! '! DA%\(YTMUNHT0,Ƥî(-Tn@t1=F]}C‚Ag2_(gWL(c$Z9ԜX2 , DXIR֏noܢadE9VQA&AnHm|mA4hsS@d c$B~0k!n ;p0gV%[\Bpv%ZoJ|&0 /5WǪš\W;xŬ: 1Kq$; Ȗ-C'(I, 백( I^_I`$ *iEΣZ. B6 /,u"ˡB!@p UCȉ*SP X7C,d,WeY :YX4P B=&_q!΂0*ֱUAD*~ܸTۏ7o~%w2S5@YK*ĥ9V/b& `®|ۓSc[l=hqҪN.LTy/>RZ.YU"Vxl wֹCzv/V]Q3GmUI׾soy3ǃ)Hrδ"R^0xAg.2x &4s%&0/>m!/`J^]%lqy ܑ٪m^ϯ%ĸ]e=~Z,6bmۖyf>s Sn]<z}_FvA.VpfZef1k6ָm= S-Ă:ܟYqZ۳Z:y1s@qr闎O˼Z3u۹lS&.swe_LZ7\PƘi I_2Mkh"룡ޖՉz(P4m٭oLf~˦bnYo%*o8ZO^uKOSR$e:g~a}j/,m}5]j1r`7'Iڼ&mB]SB,8,Wmi]ǼMbS7*wCF{`]:i恛/0v7f[S_ƥ]KEuK|9O;|9A:*-E~ۡS\8si 4FmmsV9T,&6%꧒_5>Ix.>Ǖ66ɵs_TX6`tJ:O铼{3A p!@@@@@@@ׯ>/Oi ǣrz3OQA8M)J{Vk8'ġe}]?{=sN0e1>}BețZYog-oK~I^9EUVWv>]+]h:ۚRzvNX'ҥ#jOˁ3iMӶLTq\c%=29?6ўe}Jx^ k\©b;tuPNMӒss09q"R0qEqv}a5eC5JJlnՅҟhl0OUy$Sہ퇀Ri>B*55 SE^K=]eYxh`n#;*Y{=]%㰝WR~[|^GZz'U{TP%UqIMuGMUХnDݽ:XbF~ek+m:M0URIUR<j*$AݎG5TZ*/#P;`9=,Ee=M>!KS~w.cTڨݘ^GPU]{EW~ZR"1P ) J:LVDd"ɌbjϙIc5A2}0MhnCh VSdR5?jX%HjPwcj9uGƮwN>ը68 |3r@mǞYr]: r߶pxteeel>T|!|00;!"ySے{ǜkZFCu!#C&JfahN>٪75,-@\ʁ@VFWJҡ!EVgt-?.*T[_OJTGco(h0v:1R ef`'ݽLMv\Um ,ˌ|}Mx[ 6li>;n/OęwxZ'm& qnmJ8R6|L5>j%4@͢JF̂I.Br Ɍ&[RcMB>'בJr.LCb)_Z"؆}K$-g Ѝ.D1vdi(ٜMUfJ>d R)"2(BKIQ 3I5ebzʠwW{w>^y;}߻DNRA{D0Q{>(|8yp~xK[<P*{홯x{Qozo^BJ9Z1eTeyߗ+W^/j^KE0W@| _<|ܟk oY_A>ՓԼ[&}Ut": ǑZy:w^vLmju_RlriS;`V0jmԖeɻt^.p~PPfL.uapMf̊yE])oGG!7'15WEQsa7ĥJL VQQr!!яQ9ҏnاi%oH/Cڳ([;&-k4O~J{n<]?|Ukfso?6j3x 7*ӛVnzgE.2~}ϔ>nOoz,=4P(_vcKy߱zNN>hH*7T0GbA4QIۘC))A/5ҕgH7U-NnC|%WB!|;1 .~ _u@|>`Cه ~۪?ӦExE㥖FWOws.h\1cs ~.;&|UxWǑ{1/$SoٖbP8-NQ!oa619G!w)Gmh敏t@Ь?Asev=[(ӫ΍Zr/]yhrP8s%4o}@ ;L=?j0.@iAV{nB aSKL(cI'M4"hHg }ڎLzAy*ufƧSIQ!=;M&"oS/evz‡|S[)_RJo|]iJ(tJ#R1G5?dR6);$$gg2E*vQQmяY瞺OA%䛤&*Yzv/}eBMTԧ2*])}97% Lw\%\wKKRҧ:s떸ywޙlQ^MQ~܏?k+/WJ\̷KI>r2S7@@@@@@@@@                     @@@@@@@@@(@7lUqIENDB`PKLUMETA-INF/manifest.xmlTn +,KP_~ŋ %_lʼnUƲz{:@aC#k5v ؽׯdY@ &>lHȝ:r"O;:-`t&^88#r[[p'fU]WP@]*@ecj/Ҿ!lVhCCFA;`Ktu –h-Kpo/R5͡] )@)]`2{0?S{ 4k* c9Ъ3*[sn' m"KzfH_XǷPKK7M?PKLU^2 ''mimetypePKLU.P [Mmeta.xmlPKLUc+Y"4 settings.xmlPKLU# Configurations2/statusbar/PKLU'[ Configurations2/accelerator/current.xmlPKLU Configurations2/floater/PKLU Configurations2/menubar/PKLU Configurations2/popupmenu/PKLUV Configurations2/images/Bitmaps/PKLU Configurations2/toolbar/PKLU Configurations2/progressbar/PKLU Configurations2/toolpanel/PKLUh ; manifest.rdfPKLU2)qx N z styles.xmlPKLU, *content.xmlPKLUrށ0Thumbnails/thumbnail.pngPKLUK7M?EMETA-INF/manifest.xmlPKp(Ge2guardian-5.5.8r/notes/V5_list_definition.pdf000066400000000000000000002472021477372360500213410ustar00rootroot00000000000000%PDF-1.6 %äüöß 2 0 obj <> stream xXM0 W\+G2;Co Joz(^WR2oZ hyd)~ҳh{,P2C{Kћm4&)L >Ȁe pЁP0CTژgM_ZV pv2_:ѝJ &)$tAZӬNKK:i/wijnR-dB r(3vS {'#>ڸ:OC2 +WwuʺCMƳ Ljg!X< 0"z08*ײ ^eo`P_p8bw>KKԥԜi2i^}PE+YcN-.n8*K7G)ߩMMɺfTo%ݯ,J ص&w oWV5UjBj^L>'=Id)[X|e٣bk#]gb3d"+>Ts9QgG9]Wj.vω * d *x= Bi#4ҁҷˋK2!nnӠJ[=Xb_l]ôԯk[ź endstream endobj 3 0 obj 588 endobj 5 0 obj <> stream xXI69UIe0 @C!,C sOm_f%<^w[TZh>ոƵS&>6=]ΠϿ.SDKO:;zo#&wq[#?\*M̅[y?YdKLn@G1OԕQ=kXf|czLg *`~N3aT69gTyè3 ЋJ. xR<(j% m6ojovvڑj5C7NI+'}-t]%3zz И*044=HDV[hc&1x P.<fo&u^%DC5NHv ]?@;lѝf(z8^f8eFXtb:MQaZ'Cɵ4--s室j-X^G C1߀\RKT@q~G. 4i.?ږ;42]o9LUcUI/]ZMOڿP<?R_]6Uad*VBK9c CLL]3G4L _w@DW!.@+G*c۸> h0}ѾB j*^hÎyc~ ˚8u UbΗ$*)i&ʞe E}%oiΒVIlӴ.*2)t;8>߻eH?Z+^&zl:ݨ1 [> stream xuUˊ0Wݒ,smC-!,uUuuI#?@c9{~r]/q [7tHnV_C(S rR #I&T &A -(HXX2vFgO8pj_џkMlB_5daޝSrLSGS#vmHjs5iO'!Hy_RhHzl:*މ8tS&rCj;,~YJyG( ,CQBWI|6mnoB~ F Olu.{zTYyitT">(dD7M®mmӺυcCkEcef!3,KTb۩ptv%- Sb] JSm ?M@v_RAA/F2i렝:0DpmgH8Żɪs8(D#ܘt ;+/K\ȏ䳵0Q~'6W1o4 endstream endobj 9 0 obj 599 endobj 11 0 obj <> stream xXˎ6 Wx]`\$Ytw(m. nC#ސQ9P 3.zNt2{؛BL샰> zWs!g>QbƁ7O3~"&{zyeyU;1Pw6Ɉ2- Rèq՚`;]k5fJ,4dZx82 ET)E:gܘ,E BOD~bB L(X`:|q1(M,.3fv)YQ_CQ{8p%ߎDR g}w-w!I=[dS Sn_U{ ꕂAgsX=ZQdfUj+h5uƞFG~PM!AP+DŽ)<7}9Gh/\6a>;U6dR &KapJ-#f//ZXxg_nwy\Oht\Jv Ha҆AO-[}黒rѐ⣁(X]MK*k],w':s9y~d3< lcts!~48%-)uaόBpRy^+?lN3E#YՆ`Yp0&J4PFϧFvOS\Ww? )ׁ/xY!Y8Ae r9/ endstream endobj 12 0 obj 1632 endobj 14 0 obj <> stream xYIFWwv`n D $zSNY\\DZ6d'}M<..f4PTJJ\Q,.hvY4M.2kBh !Ej,Sxu /q,̜.~mJOzX”>%47VI/i8Oe,'c?-si]1۪]c/m=R< ڕEC4㰦#Cc߀t%}9||b։Q_4s7,U` KeE"loiu!⡼[ z_Z6,׋/Dkk7eQ Љ3JLxy~LwHy)zmzJ;K}LT-!7(xk-UOQb [lsdiV6KIRuEyAC`j<'׆f֋Y~ׅnP–/V>t 0PK?Q9</I"='YJTpeQ 4 !>~sހ~TPeEx%mbvbR/_,{|ƗvI鲕GӲ>JۈZOJ澷<> FՆu7&̿_uV}sV~)Mw4@^2iS}>*665SU&⻽ALfP4ϊ#1_uEڍ0Nl4=~0y6Cic'* z-} H endstream endobj 15 0 obj 1569 endobj 21 0 obj <> stream x|y`Tlơ̒L&$ld# da aIPLB $p(ZZkjڀTi֥6j|('ZMw뼹{۹s}nz١"5o/dzPgoC5붭7~ ]{gmg[ǵ aekȠ?|}]6YrbU?I.!@ =<֞M}vLq<͈#ЏA5sRk:Qo0̒ }0UHQ;;#:/QBF8A{rx yC^ @UK:a&!<ޅ|L4)p̉΅WHD(`+.|x (=̂q"!,}/܇=6B+\D.[&nN| jͅDH #B/< /oI'jlw, G/O<2Ъ ˅޸n;YsIŸ4-k1x@I9mln#. 7ƻ-p98| #DO$䐇Bn)ϯ|aZHeywg$NnG` Wn#?d*ˑfX "f~#z{J%",ĚěqEP a!o-\ ߻$DI Y@V^#Vpwpe#¯Fp%ǰ89R+t&|~xѼRB7J~H'?KSFFg0ۻB9&Xmȷp \O8 ?Q'v:9J΅|Ǹq>?G_Ot$$;&s!Bi킭a7w x>Sl&:}i@Y5d\O9SsgRe*'s>ę?ǿʿ)U..DxK*HCȁіѻp6e0R<LEq>;m8 85N¸E׭%;.r9}d"ϒoɛ-|{t.er%\97g>k:e/^>~qC|y^>S"M;=M9B),܃ҳ_xWuqtX>Qs&(rQp9&v:)H.:Ig9^rI.H%չ|9W \sGȇ( עL̅*?@q(qy}G<_0GŰ;~`&GB~G.Q"?E\"pϑ\ù<Sz*dLwM_(qjI6_Ws #oɒы &sqḡ_oBn9+_>sU;F//ą#Ztr>S%P&FP=s el'P8 /7,]BYL^P7ΉA$:.0 5_]| &n~ 8ᯣpqQ(#_I#9J\;W 5Ol5 9T0[]1!*w,k\dqClѼfV̘)VZR\4 ?/75%3L~7-v9Sv"MF^i5jsrkr<5.dˣ`"& Z2j'VF&O"3(I8% ˕kriS0)P Ǹa99T.4U8Y,_e+#ɘ 5O/@tU*w-nJeX8JK8ђ]c%[(17fRf8Idy`MrMۺIdyxmUM|*@\*JQ'&C\W$cHEQd"ƥyɸY eIZ%)ÌOOϘ4nlj<][1 ҡđƸacv,kz-5Ќl ɂyp7PvDVbmd">K/VCqc}}UH_|$S [mPooJZ`<118x]jzWZ2!sŃQrM-ilڇ9ylfR8wV%޾Rb7ڇnhZObs^d|tjh5e%sN+Kw ni-.6Fc3j}H8rCmpjvcYCRrA Jɕ̑"#ɕ+:`?TS<܌`^ ك<1)hY8V˲.Ź<Ӳ̀6Qr22XNuyy`ngSR[\pD. C7؝ԂLsH:c(k돍qiB:V9rjS~؄\EXɳ*30%-q SlcKqMS`|}t@Bk-y\P-ߪjoi~ehgrɵwRpg)[R_%.PG^q cEMrXs3<}K<~*' ȅt"s6ʪ"UTu]^M*c_&ir(IF\s/(*-KN1`H="i.<&zA~ϴ,?f٭>vBn2ج Vm1ubj d"?W$* yʭfu'i!D iOx\㪣[0"!4]amRoߕq]ED qWV}0QXbDRh~$\a\"A["" dp6p3 ]_zҴI_81sV1h$?kEɔxVm|>~v#Ssq7}Ar $N2<1@ZMy[<.}wuawN/>wB-)(4Hqy}IIqXMF>/NLFgz}!PMf@;RL^HhvfpOb>{O{|۫ z^w+ ^`1\Q/}ZA 3NZ1(8_Mu3T0KoR05>%fy!WV5=qX[f]2f#JiJ+9MI!KRgR+9iw-c5.bDOϐ1 v &"ȭO=7v{i֧F{$m|#]Gv?{+W{/z\TFDGy{ڂP08zDhcšdՅ? #ViNOs 9Ё NKY I"3 LΚ s3xث dlUZ+|"U\7 ǒum[C>eI)Z uj䚆ȵ@/ T>O\b)-)GRws\LԏgeS9D ` "+vA4_Q>~̍N0̆⫐58x0=`HO-T5Eu{\׻d"mG`r0c)ePk|?x79MAG?aH]/o+M s:/]offJS[|=xWi{22,|g4f(ڵtQE YFEwUǤi߰llmXD-)AaM꽤F"_O1qsΐ>2LO#L}>e,) x3nswv6Υ ]Zݲ+[naι tologn88W8"Gë́\M]L˹q*0͔<?QYy5N۴ש}i yB8"ٙ bf<#CdbyC Ե۳*Eb\Y.tFJ'[ 9CEՅFG5N%Š%2UOXh`ҢD9Ol2 1SP|W[OnS4Zmsz 3H6¶s[f;.ZqטӃ<y;bkT+kW^ /PѠN5hM:!OWI5z?f]lV W̴F \D[30 [ByiL 酄ߤhQF%4&XfHl:4]IsTI@EjFaJV_e5j$_i%B}3+,4t:b'Q#L*αͽlƂUz;w Py0cJԹ+ҿP\ YynuK̴)kF~Dz7mBHk|?G_2g@!2 /pCduoRz8tNQweRRu$"bC҃"Dꢋst&lf':xFeɐrteZ]g^պ՞d)?zGB*ёr0R_CH'A`ކ`ql:3눎wLEFcբ9kfD.&rqaqK=0SItb;M7;rOE&yOCzc<-q {:@~[r4_[=YZ[7NĽߓթvLR ίrt~ɽhM:n!K@m*YF.6\,}Ǿ9V--럷|eJIo/]֬jRp^h_ݾ5N-=fP\U۝W_"O]M;5P)\#J<xw8V>yO~ʀGg8RRI$m xLEԋ+ܴf0&s”$Ml_8;G?ر僝?Kxˉy4^V=>9ZދKӺ#]:R EYeN-O}"99#B.)ZIt!Z22F]"gK39jG4noȹSE:B Tא5Ss-w #Z\kBw;Y>e}*lV\!i`rc ٝtpV(#HA<[Tfڠ5(7v_0 fb0A$"$Xjz,5M fe%cLҐV x aIAg0W AG\Н+ q*/7*+׋q n+h(.vegk+Oٻ̤<juz9E4S \[E+5>.^Τ/mK8d-izdlrf`l1W|i/k_=һruN-;~h{~'XRx3,Ax}p}wa*A`HoST?٫}M|C0|xӍ捬o,q .a!s| X̳jѿ,H<X@WG\ Rx* LAw;q. L@U JU`Vݮh2U(UX ._u`ROS`#W`D/& SsU Sk VSh(0ŗ1X+W3XGW`#NT`.En#u?P`#u//U`%E:Gm s4zۍ/*ƷleX,&kMsXٴmtclcR`1tWkJfN,18/P`lǼ2M~T/%Aۑۑ*{GۑnS`@bke)=[/-c60z2Kq],! /eh}܉i=t } Taa!QclX K&Ĵ`܁͘bXa#}:,uIzdٌd6)M/l°eV9K0.:dX yģ}u1.ac@zL{BёA[ZL[.Q8>F۩Va;6.F/d[lhuHه:C3k_f݆f&'Q'FI=z6٬bufZs];}ZLrh+78lu:m7K3H2WBV'>2sJAYX/S*qmJߞK[`-A\/[ZtM׌ <3IΥ7&\;lЙw3&96)}Hդf9\߸FErN{Cw߶N{C<{:yIך}%:{tvWuoun׶} %rhMؒ\_X(gwvo^ݗUs&POH!yJ?kum޶mݫ: k܇e6tuvK:򆎂^KzMgkd^ֶo]gosfy}6yNl{CܶI]GYuZll,e}t@[vPӮ 6w`վnkS:G:7Xmhmm!1 kM8v: 3v)m`#^:Sfva[7n)-9T8c7l;:twRzΘj͊ق61B'd '54u߂~ $c~?<$x"D'ـ~㴧Fu~ ݔ&1wV<&SW8F/n=\&Q J rl5&}+>_;)_ F?I 0T- :[T5voϿϿgxϾfyWbaq+ 1W#q%=<5ůޯ3E[:e@;pppplO)þappxx/HYԢ\r%Í]R~9vvvݷspxAQbp7 [/A-Òe8Vz0pX 1D14`+مa=|c Vr|`L|% {0NKCMWuoM8CL b7›U5D;7#?Ru endstream endobj 22 0 obj 11255 endobj 23 0 obj <> endobj 24 0 obj <> stream x]n ;O;T_"ui#-tH Aøۤ} dUsj ٫U jx 2smT_ cYm)~, ŵ)/z^o/>6ۛs_0 \S瞻lt\6a^ƔXR)j0NX!Dɋ.XoM)ҫPCVetN>WɻzM^7% r7}Fh>%|BWyD{h)(ϕ6It F?}lkzO줱nto% endstream endobj 25 0 obj <> endobj 26 0 obj <> stream xzyxTU=o-/$+bVb lAh ĤHUH0IĖ&`kwk13LFn=H .@= t7=b^~z.}o s=s9sn  (]Mv B^#]#U#;/;ECu}[7rl C~=_e߃zwcVOʠ-jP_+xɺ WFؿ@?>BX9xt0%-~9 q#!}D`4Iffp8]̬lϴ3G|M|l'g}A62)yNS {C/'S^ oH>fCqr]RQV0(9B^"G~ ]8ߏUa 65l V6:eAY`Cn'PK1> ^"w۹C |1q@/ηsD$wk`Gg%6%y<, I9=HԠ_=qaV ;%,5HnC ~+JyD}RC.'{I3GWĢ=2t"i㖉/tۅ)Gch%u3ZmR^hokmi^xE+_ai}]mMj .?%Esfy36Ŝf2:Q9 r:|l._ߙr!P`B; RvC$)IJʕNjZَZ@N|,N:v|>`ZQmDNFL5LC(19ܜ1iquPie{] ,Kl0 ]MBDʽTur| [~"_Ȼx&<(%͓,?3%$_r}z6&at9/\M}G[ZlJpMQS7y7XЖ^߼} mbi1+CYi$\(@ E&'qD]e蒖":cGؕt(I9֬YCIfUNdw\>%+>!~&wr_"n?{\ykbS2,RDJgLH'Œzb$ X$; YIYf;FbU,y?SY e.,}ΥO0e~S_cӸ[~[{X0h}7}æ?&m:B%/ QzĎNf;MH# πeʊS9KɜU*|6J|rXasc^uG;_RWo8'oPO?eF}'g^<#kΜ&ݦK/_x0o{ߞR7ݡ3鄞ۜ -oض hUhD,Vr .!Lw)`H/eL؛ PI:TUUOeپ,[̕taNۈȃě<|_myF2ȾB'eoV'xCuj"y=c18o'Rvc"Yusgt25%.P_TujGffk' ~&x7B Dl";s:;MЍQw:u|^K0E҄ #66#tkF8l ?ddf<tlB+|N@G K#d 㯿~~YBe=E2 YV*Y Ĺ;velHdCfÉ쿳|6+s3ɯx{G͛ }u;Ɵ.d[17`~7h9v@$蔠X#b_UVUƌa'S$.w6|_fze+nzsKw$sbBo)5LA2 WN>##; XټwDr}?TSww]À A v[~tk>3\g Z h28>M]! J*AJ`@ 1vI\HK"5I[:$=,Ǥ%nסdS*q ޒKA vI%:٢HRԈ9nH",\4$%II쐀HVIR!"AUMNH(E$7r$ɝ}y*a|͚3y/K~RȜr/ PB޳ů@.TjoN0AN9f dJ7 ˝Uzc3tbk U3YeʜeNV`Y{С*ߢ ;>ύq$6 ʕфqdXs}%'kp`Q+ `m-nxP}_BpΧcߪO?$nWTG?l2&ܸj=*xm`OV8Fep6an2B!#MiFtUEpeR"g1VӌE#Ÿ< ?yaFa9grQlh5#τFx#L#x[7$tNz&ӆo$ ŕ{A'6/ '{Nw ?VV*tŒƓ?Ӂa54'/7np%k_VU:c7#}]|Fy5v!g+mܼq(SO._e[smumؾ{@2.ԾJ!QBM"("$D-6:EpB"`!W+v) \SffeZ8h,)#`JXp 7sb-uxedޟNv[*q=Т>L}H 7_~{W_-a+/,]o'2}ߋt+u4~RŖ4d8MwLc/L;:?0gdjei2:^tL&.]2S}RE+iv'9KWM}mr0H.'p8yv"+qi((Ng',e*&VЃj;Q ›|tʵ =Xg9)e"͔a4v3K\nɒ% z7F; n g7sa7s7 5nݐ7_npËps&7t l t[nx F)ʷQ@ ]3%4Y;Pl8+ 8=N8r[(qw-3ynY٘L8'Mhggs7d&\U\d%ϫ+wXYWe<UV{`΢%!WmIW B*8dxVg&O7Hi5[L?34?]j=?{y:GD=gn<;n'53Pi__ SbbIs:,_oW)VT| 3,Op /V/&5g~~>[+C%d#Ku H!نp%?܀p+`{8Vbӄ=@([?qk#R<1!\I^i>J빗y]̏t}1r͆׌zŦ=ڪdR#VRL.#o%kd@,K a I6`8ȵ,3 ֑ G*_ s5HP& N#Ӹg&I ^f_`16 2$̡̓ra dZE-\:+ܣzrJq47FiD.3Nl&ƃzWCr(]u=qyNW\Z2D^ 5hd0 j%+QDC0^ /*Zѻ6[H_hI+< ʅ9teJ}Ix0FEsB:>7&`0^!GVE ÃۊZ`<<!u+̐]x#TxX+\ÛEx< Tc8jd?R o7cr(]7kg8ĵ D6MԻ{0X'ǨYbnM ޮ`_t]Yע6{ZYѤrot0W p`(72z.4Z+ƌ6ºht3VҐHߦpQáuD؇L8q_$r]Jwd { #kDBf4Tkc?uZ8R.5h_0RcE=xtaq͛W)E([aTJ FZe+(ڧ59sjS{XQ(2~ń8>Wa a u-dQ V&sm))!sRxx(D>A&7BH]5WZ)B͚ e߅V Z*W&- 1R>\pR LieR~<)F(ESR>O/;F+!k̇1 ^IېQ51Nj8mQ~ˌ8c7w1(lI{4 'WcHIRkǷGH n"~L[WfK|أ،y{d 1niZ={5ޠ mRKSٻclCF8-1^淩ZbAfq4h߇[]׏IκVW.\;f2ϞE2Z(6p鞲^!?̴PIb"<<gڦVE52L!c@zX䥘#V|ĤFdMngd0mC ,fJ+&͢,iVfkFF!K9QȼErAf߈:WR~+zXEB,)Q;WĢo^vJs7+,8uW Nҏ:^8eצ<тgQ-~5H{܌9e̳W^Ǚ>1f"u8ވ3HYU`iZ[ ^X H,D|lq)|8(%n56c[}@@<ğ9llsf2Z?DZ%M4qx~9 ibt[|\ (>q"rGO8.{?:c;{tmJ>>xI̠F[G839YB7y|#ydבGx$8vD8葬>vĔ^ob*O-W9~ |Jd "c0%+;Gٵ1$}qnPb~<\oױ+T,RO#><7YʜܼzU{+X'Gz?ɇ>Д}> endobj 29 0 obj <> stream x]Mo0 CE*!>n?&Cz/&z⼶ ~ _ݨϰ<ޜqko e[_> Ƣ77/nw3^>_۫~}M `!:_穝B5o˺)uZ=VkBRu]`ͿxKtu^T$-=GqsG8͐+K#bk~uP$\s+T?G?'ěeYSlSMP/(~(璆>؅9GNfS-i0oܳ endstream endobj 30 0 obj <> endobj 31 0 obj <> stream xսy`ű0=3{3HV۶lKd˶lɲl˗%bKa0@^Lr$&#yݻ+ˆ}[TTWUWUWFj0h mb~/ƾ#-~ ڰga'@y qc:~FVڨ؈ Fdԣ|`֑]Z7}e(q`_oȻXUc[{w P~m[nUz6QmzPDC)S4# JlpQ$'d"]YU]S;#Ř) L)W $} lSyO}~ ΁^":?_CG x< fu`DlW.տl=`x`"%-*9hnPPI+2gPR6=qAtߢQFzP ?C%Snj5>wџ 3S×Rj=؅ZjrpzQĚ%5TD(NcHQ8 ~z=naY-fѠi5jR!I% MAu%yq:7Ur]q犼w:`[&)80C8OImzzMoPw}Jl88T!P TwlZOQ@IǍq*،MM%ttʙs7ͼmȔิq\F 0w*~Mg`mOL_׻ a`<6g#7ΠIB%\FѼ| Qq9׸ VT .P}Cp.yՇӮ.;f5kԀ^.$w#`]&M͛CI_o"} U7j:Լo^׻!z㸸 "5u\iݶhy# tIO4.rsP\7-QJWC}x|]qqIPsa?%iP7Сk9sZNjAOX:3axM]S@ˢUv",y?!,%}B].^"J4yaWN1|:Z[DWzj4bOOI)CxHRZ3 A@T1 :B,H.ˀemArZO!ѠHkBD p)`h@5n6P&@)_j(;JBQMd (f=}GaMˢQ3b{S+L%pBm2ԻO@tR(=?.L& )!0VU !6*X%t& vĺ!+:Xn( agѩƠ!@WҩK%f S\q+Zb涙ײ7ouYYI[ÛFsbߖ`h_ Ȯn42]IK5f5of76=)5T c1vqh})os0P?Y2uɊkۖQBÃK EEܳ#GC5[O}mήԜ"L ޔP(1ωs#bJQX]R!lXKvw[5%QbY/:`@+BQLަ1d`&S5 KLV3Sz=p YL ]:,(46R=&+LC&-$hPTC'r=J c4n@p9Ldr8SJ ,CTy1]@SaȐ/łp9OIAmHeehm@:6X2ߍ. zӝWDv[p^UQesy(5UT];x")2%_% O#wPڠ,܈qʭ<:5䙩D=AU%cb(+ UPFCT!T aK y ;il4/+FL}(cF"iʌA9-7q(']H/z>;_O7(+_f5BECV&&y>Ahx'Vqg(y4֝/$BƂd|nXC .'YlBوg-?Z 5o8Ǣ@JTFv)ScIʥ.ޟ=t,^غ%h4X̞wp[ՊWV94s}4k0/խ%>w }b/8Jnqf!7BB HSbb"H@p4E~Vܡ[bzdॠd=iz<;%Z0 B6?-DVČ8Gt??3*i_@4r'?x?}оlrQt1.t#۪0txsd^\ozli4 }:Qe> ol~g}v[~g2*-{Ν::9J9n굩$KWOQ5YʹIJB1@&A)[ELKxջ;avFuJKRsuOPD4(JZjGJ1CEY<PY KtL̤JUA4Vfs萟CՕteR@tcKNX cḅιvܘŷf?.Q\ nm>y"~_O%MТ4$\= Q jmenW6ۜp~zeƢɄnR=J_`J⢈]h0jDdWcSv0lPDc#9 Fj)r3N1їDDt4`f ik-JTļ %QPIP R{&:M a.AӥB)dQ)m/]S:Z*5nTT T72!`H#e ]F5Hc=gKK@7QQ(0-`ͰgѹaYQ6cg[R8n\JrrY9_=xo>-5/zl2Je(KU޲'\&>p@Xt AA)^pHRJ*h2[@/7T a [XYÕĴ! .)Ь%`Hu DþrN2]{=k5,(*}dN5bV"972H46}Ο3w7?D/yS.3C K{L,90B_Ͼi_il~be;4vE_l+Z)\ ӟjb-h}[昍*F*苿[M9SEjȝvU@U,PQ1io JVF L7/._[> F!Pt41\iOll(#\E1?FF^ݣF2Z#Gcd&Ӑ9Ȅ4f@ 4ƿ1^-[ TdbT>1TUcu5ikf);d"( t3P`5tKy!H9{3Gϝ knS6˂M=}F+zw6[l9njllXtcuz,1]0:f >~}ox-eg6_Tyf#p#KiFD:JTN42*Tp"+C1%ba c61/#ƼTȰ1-[) DB3UU)Ly# ):hz~dɗ?}Tbsls=[z ;W 9x hUqtꌲ5nO@$'Hc@t+֦tŗp*ůكQ8yfX4Rt NMNɒiϛq:!)(}9<-xM ɩ[e?m Ov2ɪ=7mޜ]شm5uU}%*&`BR]ٷ~|;g_ٲcs.`>ؔؿ̈PI)+~ulw F(|hl%_r^UHH!iR{8#@*8 BlYX"1(xdp`ư(oE(ZXc=axEpdul\;GRd`F=+8weDL)Hl*(92yjٙչyF}=GQܲR𷺐61oηJVS Z6c8J2g$| Ylg$7jA> HkJw,j1'$4vP1Nt:] o!b r_,9Ǽ)'f/L7/yEk׳b`Mmɨ3?WNb6>WV~lxJo߹#C={}{h.*ZGoz¨6&B 29REThp]:xIrQZ bGxQtG]A_7[jYXfh|w+|-*\44Ecϗ3ރAQI{oAKWMU;61-g`|RBQhdh1~i4?g='n{~xWM뜹m ^}O.X*J~vvлMo4A- :j/CԐm!:$ "=Z Ѥ.8|))*)fIKJ>`1|,]˜ j^ Z>MrysYSW.A)NJ,}ܠoGؚXjy!F LBuPl'rzx_+%kE z;5JuI_J'7}اpҳأ!ut-l`k*N 5Ji؀tdP8:1>ARb\fUu5iabsRD"H{dMDGÍr4iE(oNU)V\tI$~jfdV^uaahBJGu5|tIN[#%"nW)6l|rnûk5VI'Wdjqnr_dwL^X?^v6ɫ0`@|'Djf5eR>3hFX8r-f[f t^z |x#SN)E~HZJҜgsPʅxF" Véhk y[9J1JRZ\n Q0ʎKEUX]w#Rb0p*9UUSY'N *]~k:`"Eַ²D8-hoLiݳ i['aX7ieuG[O"QZR띗ٗazM:oѦI54d(giav|iPV4N8JGOR%IK`H$$F|Hb]+rItr8i2/}}~0X9[=\ivDΡ_G?P,k+3ݬZs5P"`v2Fӧ^EǸ@6X&]T i Ighǫ6qb3(pEOOYN" .ҙt^De\d蛙xDPKw)FS{K^&J̉D]tjN%PF#Q"uC4M--:u %7Uc$ZmDKH0Z:d۫AXsJ/ sf 3p|^) 'h%"!t+6av]KL+% iXV\ۼkW- mE ?Hf&]usi^IݺnSݙu7ݓzbZ"}5:8OXDB8BK1 1mVi-D\Ϣ̯?^EL+·(t RU8('qx8%2 .A.A?)Dogci?1e0tL_ψi]ݽ:g q!iXcaκm$.Upd}WV-㫈6_809ɠQK{H0BA!:@{og/^nw:]Uswt-Хj>!,Gּ`H䉂 RUK*DnK,RðH} jCD{DGQVHt_ :yO4B>Qdx結4`Bi&AJ M5OXiDbeEJwe}J/9fC@:ڐs3.ƫ.k04 jquY_/RFhq4Y܆9|=hVkCMC*AP jAtUәu8K H`'s¶3 RPϪTR<\=EihMR[H$Jo8UV2ʔܒڕfKFXԲFبܨ^ܭF{NY7JɱLC*B&7MBT^| Z ( iBr0I%E:mƛuig-'AA*0sčz MbWu|XW/M)8=! 'D{ɮ1WonIWD|[(^0i8YP默P!A8ulmΘV"GHܐ__HC؍Н 7j |~כ~ \V]ϱZAKied7P`ngr3aTsBU̳{Tc[SMg'?{d[DۇsWOIH Dw@|5@'=]w$E.O@gݙRDVB ʕ:D\RnseC顚Ɍm*j*ZjZ:kd5uVùMj?HЕD$r/b1JiJ.Q5sA7'l蘌ŐyY ^[y >59-se%G|,_'b~ͱBGc Ƭ]fU۹bCКǾb i'n59_Դ|GCΌGޔ.-jF~vـM EK#3X 蠥Xv$ < c*՜dNx=,1ɋ2˕{}ud,Ygzt7 B<^Rtu$Q4 ;kꚝmuΕu;)jA~S'kb:Fik%Z2sF9xbm?ЙmHk=iE6Ĉb %YC4fAȑs H%tC^O$W.( iBlm"cV:eʖtUU٣MyvpyhqWVX+0#of^E+W{Z$J"[Ymҫzlݴk *%c2ӁȮ-c..g@l -4GS k,yxY$p\d\]/3є?ÑUUJzieG/3#r 8_D1S@Qzt2q(Id%ZQ|^5C8*Xt 7`ؑ=C}_rh@ӓ%;T\r%NW o!тgٝ"iE$wA" z~/ٟ44fb>*;ݗ dy#aҏ̓WK( YC]P~0?9OT^Q "<.]IyѠ g˳44$1/ p>/d]$>h#QrsO S |F8c༴&сUNl$WZ'si1)hmA1;Ěq`)x2ѮXT*^!͝rÜqR qYlFfLU1wwk e8 IG,g6kL;9Wr^4Aq8nkx{0U@~ԉ/T+ 6H,ςL,8Ɏ*F剛5h51`j1+tqbq Vhs=xˑc)Hаʀ?"l0bxtt1 |[p傮HO29]i?֝m+kyAb2o)-Wٵ-1}yC7iu,~ilm-;-rǙ_Pp5FjW/a6 ERByѢ@43LUIScb_Q$ihSqOmPtwUMpL(ʔ2lHL)9cpd0G_8C^s s!4=XB+џ-n[`Yfa0hķf ʿL֜ ON9_Xlέ vcW加 A\@ťOnQgs#+&{̬uP;WQZ|u;O=c^=rܺ~RG&RVezdzCζѢN!-e~N}YuF| q{b40~"K)Jwl/{m}m}mM/ڞL\/6ϥ=?_sSPA̝e^-`\/\jsQ㿲/.}[0oVVe]JJFE0;X!R3.Mi&GA1_DP6CJO$Ri1ƽ)v(Gf b҃Q#e$X@;!q+{ 70)J GJ$lPVSBamgrPw&9\%ϛ_WB&eZѵi㪕xt>E+ޓE[L&:|VGu}[ʄtjw.Y]й'5?.Dg_&}"jZ3)I*jL5[EҢcct*VVV8Vermcr%t:RZDTdyܤש*(uV'0L:TЙfi$m$LoƛG-s-+,IVhV,U ;SFar!1(sHP*=:nAnrErӰF9VLcfeAj3#zӖg+Vlزf?2UN?y7ʓenofGl녖{u}k7n|ǝKl[o}wuN ďy#xSؚ`z,*+K+KY31[0 U؁(yuzorVh.G475cbbۛi% ѥVKYoLD6hd,g!C',ĵ]HM} hEX B`ޒ]sԐslmKchvo\aWm袺/Q4nON ǡ;~L\07@^nóJ+ .Ҳbm[^:u$tmn\B"4_ly̳b# ƴZ(}AK7pʆj;}fT޻ {X 4|K]a8EƝ!U9=Tz5h ՞>iyVP@=uJ?}S/#xl3(ρRtV9cTgij$L#^z嫪Q[ڵڋO K 3^c߳tXGmwu|evuAiϧj{;o_ 0;ۂo ] '82 8) b-TP-c?d(w@UAyA<,Ear<Z8:D~3FUSX`=uKI7 ,#OXNXZZG&Ck09yeyAa RT<,ҿrdap3yXC(,O`%oV~!ո\n q?0FyfR' ig)<7*HQ{H>Y O8A'1,'}?C\cX+OGdII7oqpޑm\-ܢ GEw+nܾawQ[zs5\gatW^\R+QV--M$e:WȗO0?0;m00<ҿ7w]훹A|eFvwơfFGzGm y@m#{p!#\kpw7cz~p; o:0^} Fzt/ HI{Qbt `#6Lr܏jD:TP^r},]~I?ܿ]0*#5pK_%(Mm\^R< Ñ)!cem% NW0{{Á(7@s.?B3ޠvGv1zHF(߃m(?jR~fi#ow/ҟa'8@{-N!zݻ}ƕ!u)}.&C?7u #A#ׇZYmSo!P+Bhf+z“W>=.!O1훦/~KTtyjf`I]F Ͻ+W7$ose8䕔: w$o?#`nT#P_1|VL܊ꌠ7oja7*-0\^qn z[Pf_"I[P3)eژVfJ.{$#gzT=/s,%}Kx_&*ٌ Vr~.1‹_\R /0}y'+t/ϛf~-5{iRd^Trcavtq'Ǟ#٭g>/8q*(:|6>T!~OwǾKsO4%>UžCggɮ-B|[i;͞'3O쩷l[}C,C[owc8 >zQկ dzz;N|~:}'CtO|~=|H`oDǡ{p=6{= U̾}T)רxl7{ ׏u޹^?go` {p/O1mQD kAr>պX6dȤQaVK5,CGX.ZU$4#QHYX)i]F׮c25NCӎqY],)8nlmKM7bmg`x26X7 t:x%3:W\~:_ bumyƓE~Nl8ϩTz_N:o*oMh=@5|Hl86#[=v\j]!UPitHwZOF2 endstream endobj 32 0 obj 18372 endobj 33 0 obj <> endobj 34 0 obj <> stream x]ˎ0E|=Ʀ[yHYC ऑ:YǷgFEб)W|s~c{ :C7x۠N6%^)㶄a8UnPOn5 *V]8<_[s z>tu<)(#kMvmj07%dj0tGNcEQ:l_KJb`}__ȯW6Myu%1l m{l"u_)Lt߃o%KK']Yӿx;AӿZ;D4=j[x;a{15c ~ gC#7N_I58_H ,K|#K667KFⷆod1)L#_9H`fjL[ OS §} endstream endobj 35 0 obj <> endobj 36 0 obj <> stream xy|E0^Us_=3=Gsf :KC!@$2 AE]uoteW<0 *^.늢BfoLB>NW}]՝+[A .|!:B2Nq"[EHue 9OҴ!h[жj~‘h^,H KJ+TV Eo!1s̑Ily7!Bɹ3 mr8h:WA2AaOC5hd}R -̤Fi+t?&U-Ԋ6&-m(ZnGtZLdA^cfSwjf=!OAάl܃$}!SYWMA-h%zH-ٟ4 =^I-Ȃ&Aߠ;,:CbM8a=>9'3'ӎFh"Z^ d:3yyG3>h{2].GF Gbhd2yQ-hn>=^EGX,{НE6ߌ6Ø>DћO0 pO35&|+~?' 0W/Of<u#Qf /S$I7.͌ʬμyPԭEǡUj׾>CQb[`,D“x%q %vJFa&ɼNeݙߤ3m 4 4eC]p17=Cx,<|IM$ Sld^e8-) 45AۿѼ= 35vb. 4܂v /Ǘ+`T;>MX$6$K~M6 ycs>3Y\lbv2o2_bql LҮ~9w.HO_^~.N.?)R"cZ}<&tǣǏ0X0X]cߓfL)aJ:f-s<Ģ,aml1{>}Y()nWlVTR)gxy<1mh HKgRENᇈ?w2T#/Aj2 q3S(G@oL'ג ~".f [,f3{ [Eឈ5:r|IE9XB `-?8H0Z&4C|P{TTen cȇ׆66qaѕx9FZLnEA_cP)0X@ä fMl!J%h=^R@Qne=->}ogF**a`4{H!`5HAREf=ENoҀ&VķgRMUjQx2Q`#iBfbNf2,1 Fg4p@K؎/2d.@ȓGS(, pF3:|`Gzb׳װ++@6y-ݍiV03,QJP9<]\(.~\r>Z= q!\7-P+ע܎D"H.&eFjt> <fݐy Gne@g<{{~r aT_W[S=rHyYiIqQaA~*p(>Z̜hJ`QjDhdmbѣi:42fh!k:]b\M\SϪ)ekJ51'֠8"$v1<$M& 2l8 ΅."yu#ZCsuPc6?kurmǎ:,1bvT+4|DN{DF̞5i#TnӅBúLI joӥlRɷѧAnМ~^hӺM$wxc3Ih8mR7nsH֭7m`iMM\K"#[֍[8|Fiօ[ISe54,4aԸuI.'sF&O ݡ=ynҪ$ KS9sv`M9@oɐ\Bc',= %'BL4hDVB55akȢ.Mc:n(ͧw)"\H\= jp\2}(HՠJ& "FSc.O]Cq"D0|h"즡0=5M˦E4ݍdSi%JlShɚ[B;e֥8u¡]o[c=o4qĺ؎<(-/A]i fR@ʙibRFy=*5`ő]\lؤ Ëz2'Urt\7&uOQ2vu봃FZndHe̚9!  Htݲ-}3ړٻ5&xx(`+AöumuOsӺAilԔ$+v*4rRՃ;*X 0HTaKy1.w&s'k֌~֠zHX"sത@a 5eMi~ad/>| |/ ')#1s36wy=0dZ^S_4Dal*6`' *9b5v~??³HrF}{#vM]_+5 f3W2_} +jHV`6[fX*Sč0h4_hc%ϗ=hR#'3Ƞli4[C\0@@YIp".1.)kX,b Z-ՁjU\]RU'U7VWUWKP^AA^}zQɍBzc݋6)DQ;BwYzLB,oWk.mBYl/V!' T?Li1A,;愩j6W p\ǝ1I3r 9;+P-HAv%|??atkZk )U\ .$e|{=JJ8y.`*+| 1"KXiIc՛}:K=DYkwԙfK]cRi8c4hLW+3AP :l%; ƞ_! j 'CهÀ>v@{e(Ub,A_Fd٢l~Pp@2DૢOw{n\7>W [S a7!IK`q:-VJR`W}:Z-@NlӪO <)N huzLeG2Oه"͑yME'a囬.PsqKĵZ&w6 ɀ]gU{G 3VF?R| *4[G7oprL ǝ<8e`GL͖7WVndb:06ʫy2Xc^Qub 'o;ym3;Z){9pZ=w.˯a=(?}%;F;qT_5UYB p+iU^dė'V^Pоz_Aş)?ժ0/W<( 6bXLƠ-`/lmj@Tca@&Ve4`Rc4ϔcEW(p0SG%^8:58Vq`/&ho$ϜܙD7 *e{^2kl`9,[ Wi9GJ$V^t3LG4\̽oywqs׌VJgJ+)8V[-Sˢ7RނRU)|ʍ nl>}K{Lx6Ɨt5V%O ljU563/ AKf_u8)A_:#J5b4pѳ̳ߣtG~qgH,akY$\:kWIF[ӹ4~u%q򪫀(rhA- ދ_r)/ s'6͔B9tztΜR Mu+I!V4 q :ADI\Si<4(i EPolA"( z-,4BM{[GTPdO5DL3ʭ d =7xj9}CjuȁGK;kvUm/uʋT]^]lh3Z;Զc hF́&^AvX2ʻSZ{Zi (%NrRpt syyƠGPzP}4 rwP7̾~*D2R!󜻞 W!Pk!P򿒼 US>޲cz'O`؜>oz{m|[M*neYZ"sUfJrI S[r&Fl#)-BfREYGFdƑZQ cA:"$23Xe 4,.//)TW*"ϕbYъ#z7E[HP5jdHvޓ7EYl€p(DGͮvlw{jZ&Jn׏)sIwc~.v7/#,onnrt>no4~OvSS˄(U~L t䯗Y2w2fqszVkz@QP = r.jB?&>6w[_W;w>w뱲/,qEgUua~ns[lI7:rQ*n^{S.?9ORmMi2l*TVRb *LF=HQۃw2b.KhE US||>Y<9T$%8 #U2 .\L8y=&㈲fzP\V4PUTs)2"z"ֈG*?"B`I{ӏκڡWuM3E$Elӣe,1dg5_<92_ᙯ!T%k yH×YzlL&{#H?z +?V<8&dNFw){j|H|Pf ב%(oʬwq.kw"pV~E[vy [$| Rsۀݱ<4$g&CHyV8)UQύRB^r ŴӇ1nd/}TPj`%.3O3.Nwr"+>}XۃW6;UZW љwb~ P)I#e<(VȩɰsK/3\_^zC|MKŷs`B K;E!͜ѷ8 j)RW%VWĢVih4H&hfi(4ЖPW:::bCBYb l׌㎃ul 9 ,Cpr ʜNKz2?vԠNhח|{aO&j4>̫OpP{ RZb<2W(a粙d1׾Son5<,ua x{Nw(=toEݿMk]vcڤC}Zר&Dnh:qx`x1Pg35՜I("S]P̢ zwFW$eWV /YG8P,VVXYY|֞ skn:A6v0$,OrFlq9!sc\.//.6ٸFV.qJM'-~: f-1J3a. *Z`@@i1pa>0!0+p_:tl(ەYwD*!}gH '~&Nf7u/l]2'sJΪn}0UBn#UGT92'wX~oyNe"Ak@SU}-x␜#N9}J10ӞzG; k.j88u/ċտ9/YdUw~nw݈UZ'C&W޹ߛ Փ]8uJ4v:s M0fvez{*}NbaI:bI,C܅9cf{v%1}HUf׈kn;]H` |ÎdbM \S'DBpS"il=7z;j!%,@1;Bp?oT }C:P$|OVnn>WV#?+[>h} ` }?p$!՘*USI25Fh,Q}~; Lxx:UE3B53E(RP_E c^Cj4R0FèTTD"6(:쒿ȾN짣^(Cb}enݡ:NEY֘s$k>7m9+ռ%JHLgSfNtSY.gX?)~Իd3L)CQHp2>V e*W'ӞtC,7[m2z]8]dӪgq/.jk:b,7/-0R'jOh0*燙1bNEQ!M 8M p`L&`$JџQth7AC6zw"-Zg52jƁ~È:uG z+ $A;a]`P~TpYЗ|ѽgXVٱ)t2ғ+Z18Tj]\Ͳgyuֱ֡UׄYw?}` JTFSXZŹUUeE;?~LWLl{4QؠZ\TH R`cUɄ¡4Ũ<+a4Wyce-Jp 1Q`ϐ7P U>\ʚVZVͽ(ǰ,~mIʙ&Q :Q؄Ek?&PTP$SCfR/OKt8bȓP\@2*esL^lfID3;{{ھ(098-/ 0zzK3Ω9d[ٮ9w|>pNSSn<_1͸pcMqp)3v용@LY\!qV>hi*n z|*ĨY̮h@#05j!Fy&ҕMFzӭ.&?3#ۂQ__#pǜccYSnMXtEQ bb?;xM}H}LlgqV׈'](UͺyxM\ܷoஈ(Ddܞ7yA[i nܰMޱq=X箏68`o? TK?>gH+`G-#@K*|Mް2JP WRBPb/MlL3?wS ^՝g|J2_HpU|bB!F}.I4 k k IBPƴZ!-yP҆kMrǪ3aTvFNsy}WqtF+X7%4 c: hZTxi  W]/Ԝrܯme)Qez}TNeO5VE7ͼU.Y+}._Q /ut{nݽ7}'^XfΊK?Oڦ7^C3ߥ2kW*ޢL><:&VVa& `Q+z$X8DxE JZ :$n 6ɶ +Q'˾3VSC=r\5x)yݓH= OʏxVtZ[=8kruI!Wn~K֖ڜz¥eB2ld^|]mXmJ8iNfuR^\aj-vR9V[U1Nn1% ? @]!g11yP`^ Xf2"6ү(iy4Oo^T%X<)s?{>yU5 +1?}6!@K{(vNji .ֽx\_*c;MKyGt~ۑΤx?@o`z7Ԧ{۠f4Ru*J~qZN[eԉqm҇;nXU[.{*/I[>I}t6~g[чp5y1XL&/#Tf2cR1{ZQtjgGiz "f6'Hlm4;6OM؅‚쥶;;$؇m[>xJx*eLJÎُ9:۞$I{ұVX{̾%# ǏG9+ݍ\xm&sP-pl~ʁK"WZzDe?$b ^M|iLN&IC r 14Fcbw)$[jA x8d;~ fK<-<-Dђ=s@񵊂WAgCWI'RX50ܼ|O~Iu3|f?ޮZPܙ.xE@0y̠⡐Zj\pO'>E׳a1ݙWbx@@1m /MWw৥z'rZ!( qA::&/4_j4x3ֽ%sUdl0plq}k|wW P; =NIWRPP\LX5R#/ȁV]֒D.+ CD(h-)CA> -aY@Ōڧh6Xv.W%Z2\(Lj|}daU6QSe B(MMˏ5/>[ܩ9Nҿꂤ"7;i>~/ɝ][j5ΐۡ ~Iee?o<OuO./jYxd,l&Y2U^EMihj5-(nsW+B!N9^UWX,^!dT%,gAJ")xyX33˳̳G[v$;zk0t*rjpjSf> ?U u}e/%8} /A/mQ{2?T"-'Z&Y͒Mܳ=2%KnL5_%"3YRhzU 䏒-03d,oN oO|)vƬ^ۖ_U 쏧f6.Pj od>a0Gn}O'-OP\@+'7N: Ceةulaa?x?muf : %*JV#$=:m#@/GYK/ko}AqbSX`/>h%pOD ≲XyU>R/[/[qZa%I~Be_E*;9UZ8|- B(yG׿sG^މ7s48?3B2,*YVTTZ/+nF s;67ar+[SQ?*ǣ`*by_^^B< yEӥo[[NVRDWm1#u5&ZnдpHxJ^E ǰlMˊZʘ2UՅE2 4H*{њHds)H#N6SmNxSel6k_Mlȗn> ڄ*~upx¤ `S7;\2bP qn\etPP+ &j#"} } g;^J} /?= "nMo,nv7B4:}8nS_8kqN7quPT'Ȅ2taXHRpʃA4T/"Eɲ2o>Ȉ *#,-i6{h Ź8'#o* e("ZpT#(GtuR*Qd{MQ =,rqk `uθH"nC9)5 3)o1͇϶O/C(Jr0rr=YvMIc+g<6}^Fz@50W@k'>dI[meB{M~A&ƏH Wȫtywy_wAY<%3?tJS!f嚇: F7-+zOꎂG,0o m8RƁ[Lol^ʟIe>\yj GOhSPƂFhI{T{Bh]ʼnF=??gBQFNQڄy=v>;vdzwvx7*vS=dO/g}s}}4|^w{~dONb٣yazW4}Fz ]37:to  #6l:mNbw(,f1z [TeAbHIr ZC.}y틂ÃIY0  Q/WTW%,L7N{Ιg_Udllm.X.qϯoؐq Z)7]Iysy'"Zik=bdd+Q茬m뜷w6ݗ'|._W7I*@Ta|0t flwձX%bA]Dr0@1Y1IǕZVi%.sZcWm},$KR8UJ }LEnƪ}QB8@*%"ET~3 |u[䍨?6%LFv2KEvC ) L!x} m4Fl4oІ=2嘐b8d"5H.-G/Z<318<4nw%9[W~/}>mCeUYwк9m=lڔVoݺtGҁ K # ƉW  v9eTu߂v1i^R&,[omon[ߐb8n4w F+[T8r[J劸 &nO!>5 GM&l2&br|>, t6yg${O\(AT&&6`қt&VEbxUZ-(6\E[GL rn̽b"2ء[ 3 t'; +S<>ɂ qk:]#o:ŋ4 aKUZw]GyE8a_T/ !?uY.i.$>u-aȌMnb`."Qs~Բ!s~Ucfߘ3f uwh߱IC_͂9L_G*l_ebouQRZ =ݎ}r?j-,_a,w7/S~˦/7<\KK`Q#[ˎC~4C*fCj FP;f3T*GEzD50QΒHvJJ9'E/-|6dGi;);)LrV {/[[[? ?ڤJXnxuԉhV(Z.չ]D;\!@;E>j +je̟Y1Lr`o B; lԝwOA)<+eɼh+"VW4%^LN4&YJ+4FiOx+dNQN M*6u%*8 MQsJ #>п?vLg0H<8kuMGCn܇{2GX} GJsJK/_k_}C]ô#ixxێ;TVg5WR4)xKBLAYlBiԭg E K1n"!>H>"GCQ=:!~_ɸ".w/x`G7Ir;/r9ܷGݸ&m HGۡ3\,);nV[ majg/|xuFKOQtf|.N^~r'_avɤR[w׮/E_hmgsF7R4 Y|4n dJ@]w8f/^Y̨COwM+;SX@CѷoGo5J \ƿ.E'q[gIo_GA&9A^mK=ɀ2 bV$ $S}AE J 2OHJ#`p6Z! |3x"Ȃ.LfÅ pB嘶n[yaX13 o{,2TЪ&*׋ssv?E2̇Pnr+MgH4/RJ2N緜Y,ç}{QelkO=*Ѧ &5{ٱ*')/Xзc{5 wgOK ~+}'1hHV p C2>_p[7OA/|lEfOjq D ~ۋP_7 !8Y4 չ!n˷0P|NrB^ 9LSns.@8 s3 s\sjAi0'g{Ԅ=20F6l9zu=Ыo f !~ sM{M\ Z䈟2t70S'p 3$pKWp p `rh!39Ϝnjxi<9G1\wB<Y Ì@z8ģ!MQeeGCC ќ pn3 pkbf8p40 pmHP"!ਇZ[ XjN50V5r LO LO R15L9*Ss"-p*\~)&O#>4d'g \Cvv+,ѺpNs'Tl#@&0,`w|GMMVdc7]%L).ǡqxԾNy#pσȃȃ̃ZJ7pfdu~8 BscT AWpns,(#sPF BX/C&L5xa8y#ۍC%B(p*@sȃ#G"0fo# 0I2|uʟ,_GfBZ$-ہ%ZjWGX4q9\!:$LñWfimaL3 3 =xH>L6& $ ICyP4ē3nS=+DrS H4݌y;W? uG ѯ٨fSٜh6 a4?T8)Tf$U@T!_ū-jNmTZZTjFjޏ>^^HҐaА.`D1ʌ%cv~8?ԃMR.X4v0gאUfRWerlfi1 R]&Ou=57sqSfvxc_\﬷ԙF%uu=m*@4F=T QӴ=5r$]3L=$B=(@#i=$UGzezAF թ`pr:LN`@QTGRԉl:g>m{ m hk ]kԈh!ڃ j ]1e#&O~:{4fDhf'tn=1gMK{zgţzk4Jj4qf6AtZ@wi[V'csuy{/Hl҇uE 9sEdCÐsĢёÿ ;.젱ѹNSt4enLyz8oy4ԙ}/c%uLJhw ]?y3'ʞ\J,t`(fd`N endstream endobj 37 0 obj 20961 endobj 38 0 obj <> endobj 39 0 obj <> stream x]O0>n8a% ġT $F*Io_yn+@<~3y0KmcX̹9s ~Ȭoo{m,m p,n0Ox ܅.4 0,6Ӆs󹙾4אkx) )ѵ%J;v65muQlzda+fVEQMԢ^A;txK芞z~~_2~S-ZswZsODm ցǒ%e_-+e/w4,+Ē.O%u_7h{H?%_;Q?,蕐_f׳_M_j.kI~ w譤#/pG~),_٥~8%~9/KݑkM{ч^08f>qltPu^0)4No endstream endobj 40 0 obj <> endobj 41 0 obj <> stream x{k`ו3mȲdI~ز1~m N,lElK2< !N <I 4.S5$v4YJMif[wn~;cH~߿bf=s{46JCxe% սܷڷ~_nokm?qeBciG}y3Y3Ag7I`Ğ_I0Nk "YcXķH6Q0Px098xl@z|8~ ZÇ(&cfDbT&Wg(YZա~i.:b>D<)'0";Z~ևMj 4>{q==x:ԍV4Y,CC5a5x@̛Z ڍ4Q_S,zS x#~Oj@P?۸'!kȄ>oa Ң8\-!u?̜ABW"/0fot/QQQ`yDz yYi)vEy\{fgY&cAj*Vˤ)\8Y5i86-Ʀ[IޖD[K&Y-IMkjIT^iXDdiI`4ivK#˞Tk5ۅ& o|׭<@ORHv%$kG2p{-H&ejIC⚤_&$:M.?>Ţ=δ>{_.\tD#A$WO?d@83 sv`0x@j>elNLzwL3KZߵj#P(  wS2=nW&}=;Ț;Dnpq^5 w\>{/ܷ&%&cso 6$0<6dK9qCW Q5¾09AHr\m2r,C䱅0j9+)rvn({ӿ0bGD:{]xOjXdcx<uKɺCI/ߓ UmBö!R_/. O]8$ b,(l=5h<<@[a<+5V(qB?e$P!.C]lYD3Нޕ (4>!/`0B 0Z)KOm3׻U4LOzgHŴ|XUey8ܦ Sil\Zb~ˇ7w+s-"&6q&Խ턭^{Ngz4*V+mYӤ(4\fdпgeUk&0Y Ռ<ITRWzĊR0SRi0g-KLpRh؞W^/-S/29n̶h'G֏_hĒ4iz=/MAM40@bP3-Ϩ eKYJ[nlnl4syG-%u3D"9k ~{JviP9] @-˗Rp)*J5blx % _,nIoPֹ={[Bp?}y2][o]%rnv.ţnueE-t59W~D6^H1AȀZ^& SV(l`J%ju6 ctvL.\d?())@&;^Y쩞,NG&VKa+t6tNs'.L ΜM=q6=<0["-1;Z(Ekl6Z>§1t;m;:tFёpvyz <)p^wb!;u [[NbWDǖswxLypGgE5&OǬ͢{?nySʈbn+{LL?>[˜o@U3ή@cB-݅6ghȜ[YDF_y<ͬ6+t'A+ ҳuel%m${ *C%;s.eegC5 QGWRR<{pq<\BTtJ,TE|mi*%Pnu&If|a>}={V6Sllph֮/5k}㷳's(%K2zw_|oje4ZR9Dj?ܙVk̦#|{gP{2p[h4QT$ΟP,(:.s4RYP xeH,ͽ+KT#}JqF=`=UVS/gRRRJ |P=͇Wi) 0|Qo D5:k/Δs=lŔic/6+/!{H1j;O39{ʅ?vv`DYK&BJZV+mo; 6[:AbbFmrie6V'x>=,B=,=JsV vC?N7#T735z`m{}[1ъ\:x1;Xhݸan}]7_otxڮ}=F ^Q"˅M ҅BieuN>{j:#|OgR)HZ}=>:5/l4)I-DZ}6&m_۶=Ӡ-bg~afD+4BDu̜'gW${܏IۋG}bZįtĠ%-/^ Vf+"-Hc? -iG CU,PY(f /fNC{bc{';)kZai8k:JIwt'Gv7 {C_$ӓxݖ`gƬU+OD=qCGV=6{w}j&n߁~pgVwMC_Om_[w%Kg5J;p̙mv{(hѭ7R^[Sw%MqUފ].vqcZa >|7L(UnXfq?WxH̓'jWh`s}ҹNfpɠdh9TAmTd06kLJcw4 jp2l& gFdߔ0[t%Qr*,9&AƤ+~&j '<ДV8h$3|D|V;g=$QzRYkcZ%Շ}yFwodi_fX0.y|܀P咨}֮vntsFmo(Z;M&Ǝo(-flH֪Ls>2Wl߾u]eҤGv*MPwUl.D^tͿ䲚O lJ"mu7\Գ3G ZMV')KʞQL(NC#Bh4:GȜ_?va$o$7E#l^%K%kƘP ] KE`B/&BQh >{*jA$#Z)9~_<#DD?HIץe{dkh;P/Y# 0ALBpR0\Ra!t,`NM!I4cKBԳ̠l%^N}[dŀS`RZ" ʤ<,|/ Х<,-V(9_`mAA&c`f^%C&*Rd YDp:5!:)Ca9tLҧxXxtR#;N#zJo0& 23x%rd rdbV닎`P fE"Cbn~^ ZEI7E 3Q#/:<9,Zw W $"ċQX/{h ^(Ni .[pfl4Mp}] ėԟHė{np/Idir~Q޺<`+a>fE> s œ;SE  6|'uK.dY: >hSY Q)]! sgofѭzU-$eK_o>}[3Ζŧ!'f&jFD]59s8>9Bh9y :O1[Y1?sx`p;oc$#f~bOQ]C0߉cRy|P5>9S`)=5oD85b+&FZʗ®xdo#ϭ6<{ی;,Be*a2&dʥ3MJmLa|K7)nC4nb1g;M_w"]nr ?SJGݶw)k(2oV+ΛR yC𿑂x3+OZOzN=IgSY}Nj}Kgm<> O3֧=O'zRj}yNuY5|wwV%W\cſhz/btqE*0/WB3?fLn=v1kp{4ϝ"f|'W~*)[Y((Tg:9 Φ̶f-YFSNoHWkq՘Yd늬u ȪdUiii%4!LY=ٔU=jqV*=je[i>ESTZ!sԪ\.ҕR+Z&⤺5vIj0<$KSR!YlLڶ"IFIu-]SHc49bd(rf%ۻPDzB ?##q~ pm{O8{(ַa &хiI yY > 2nsd~pY/F^ endstream endobj 42 0 obj 7914 endobj 43 0 obj <> endobj 44 0 obj <> stream x]Mn0ta$BJIXG=ːgRoog:WW?&vx pla:=-u߸(> endobj 46 0 obj <> stream xWkxn6ܸ"@ IE0bv1$dad XK$H""E#*%P-ZEPRh I$;~|;9gƠ(#2K*݁B~D"dqHT(|͡Yæromo"+lzp:Z0Fd1VTV/}S4=v_⾌nL{ ۮܕFIEg}6V%Α@3&-إ54A>zD,V+S0-TLgD*V;Q5RimQ[ޠf\sQiB3hJm"EITj-LQ&exffis䤝j%'bZ`Y-HV" Eoo-(OC{)D[RD)yQC.q4AOkhb8!>ʫhG4M˰'-.JP.:E ?#y/k,jFF`mPSX8 y m6mBYo$Ƃk7`yih eRl]X!mrBYBRm #^l"<&3Pk/FƁ:bnvK'Jg-3Yq^&[yzj㬓Mhkڝ\ykF4NϦ%ԈőN5w1px'EFfڬEU!%yzi(~HMa6=D޵zsGGӒB$'oΓ)NYrs̰r 9AznNj36f8 qYtlgJb#Rt=ד`S pEyh*Й z^.Eb|hpFENpt%tQqz8Bf.PTJR//p $2s\ :X Ip\(ջ+So 4sFνGĄ` 2Bjh[IP]CCBat i fQW$GI钃+>:\[ʴtm4 n! n}5bkMKv9,TNE:\n@a ybT\}1|uy"Iy"C(Σ,QOxS32@k@MQO;TOupWr[hxUac>vTZcD}LZԴia_'leyڀyJOy=nx^u lwи)zQA>?t&zݤƘ֗U^}FBWmW)2#.7B33^j0Hz|^w01è*w/Ij &bc'˸}Aga.A}>G7XEuk茆T 43gVUKK:.x7htkΆ,>Ç ܻu+v|žFh_jMl|Gwjgg=Gn~js{*OyEFܳTϕDLp}x_%3iN}v{vq+ݸRK%re20/,^6R>C ×tK xcFQz/.(J +]*@uɷ7 5]J43c1vǦ=YD::8-*Ґ: FHy0~ 9Ưb;㗌cxq?>ƽ1giqKEjݍ% l_Vhң3IE?NZAjVXsrMv5DϠE w=oŰ&-63@<$s ] |fe3#'zR̉o{Y~nm#1n/W׳r/o9:ns-{&*qY;yNj; _"UqvVT]/oe\ְ,R,e\¸{]~lrQ3TTZDE `ˑpn~lC97T03%soax3>~3[9Os'obqcT!ƙ3x=c>㴎?~OebLa~ dIf@mkN W2eØM4G124H#3^AІ y2P&Q2x#$2̘^0r鋏dї>p-ƛx~%WQQ֍+ތQ#6bwU(3TF-# z?j0lD~i%ҿ~ endstream endobj 47 0 obj 3077 endobj 48 0 obj <> endobj 49 0 obj <> stream x]j0D= ۷1iK~,AkZuA̓fOkK>'8-B'UVMʻLTZnN- oNŅ~gi-1pBJPs1L3ul>GA5"TYUlp8Gc h>{5,RU\~QXm.$Ϟ+l{!nT^?m endstream endobj 50 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 1 0 obj <>/Contents 2 0 R>> endobj 4 0 obj <>/Contents 5 0 R>> endobj 7 0 obj <>/Contents 8 0 R>> endobj 10 0 obj <>/Contents 11 0 R>> endobj 13 0 obj <>/Contents 14 0 R>> endobj 20 0 obj <> endobj 16 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 19 0 obj <> endobj 53 0 obj <> endobj 54 0 obj < /Creator /Producer /CreationDate(D:20220823104058+01'00')>> endobj xref 0 55 0000000000 65535 f 0000082432 00000 n 0000000019 00000 n 0000000678 00000 n 0000082641 00000 n 0000000698 00000 n 0000002263 00000 n 0000082811 00000 n 0000002284 00000 n 0000002954 00000 n 0000082981 00000 n 0000002974 00000 n 0000004679 00000 n 0000083153 00000 n 0000004701 00000 n 0000006343 00000 n 0000083477 00000 n 0000083605 00000 n 0000083734 00000 n 0000083863 00000 n 0000083325 00000 n 0000006365 00000 n 0000017707 00000 n 0000017730 00000 n 0000017937 00000 n 0000018310 00000 n 0000018551 00000 n 0000026815 00000 n 0000026837 00000 n 0000027040 00000 n 0000027473 00000 n 0000027764 00000 n 0000046223 00000 n 0000046246 00000 n 0000046437 00000 n 0000047002 00000 n 0000047406 00000 n 0000068454 00000 n 0000068477 00000 n 0000068674 00000 n 0000069226 00000 n 0000069620 00000 n 0000077621 00000 n 0000077643 00000 n 0000077839 00000 n 0000078227 00000 n 0000078472 00000 n 0000081635 00000 n 0000081657 00000 n 0000081845 00000 n 0000082137 00000 n 0000082294 00000 n 0000082377 00000 n 0000083991 00000 n 0000084089 00000 n trailer < ] /DocChecksum /0CA17A85F72B9B53F2D7AA2AA9DB9540 >> startxref 84330 %%EOF e2guardian-5.5.8r/notes/V5_mode_comparison.odt000066400000000000000000000504231477372360500213460ustar00rootroot00000000000000PKnvL^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKnvL E&E&Thumbnails/thumbnail.pngPNG  IHDRPLTE"$*6$*.$,1.001))09,896-VB8JHIJMTMPNLUVTKLPOQZQJYVWNVcX[bR^p^`[ZdhZhueXMcZWe^bjc]re[zp\hggfltmpnkrvtkhtmtwslsssst{uyttz{zts{tz{yt{|{jxx{z}nst||~}tw{}uj{y|p|{y~¤ȪêȭíɶýŵʼĻʾ̺ԾٻĮƸ¶»ɽý»ƺ9#IDATx} T׽gwg}I6_T/_4=D#̈%!`\ݡU,n2#Ḋ09y.CqKE HU{AIq7qjU&ȤůHsvҌd# hǝ{|k<>:Mu4iTNS:MuHe^sK^a\G/~_œʾO;9w^rXM%flCZ_[D1Juyיg4ve8Nb## ϏVA\:;w{ud.Uf\Bٙ& A; Ogwfr1܅D"gwx~Qz`y`w-m&fb9Vw/*l)V .ܩ1E޶zC]/FH0hb܄ +ŠoU ՗Zk&,:Mpc<fًͯfd' ^Zc]= )8_ ΁Y a (ڎoB2T˜ͭeJ/>qotp~\'لǚTTo& In_3EL&7Ɔkeٞ|7gDP~5.6juj*o(L*!v:y^oXB=vy|1j h5Ng>ߣ3_p^k<,1>pзCBeh%`Owo ֣Q(eby 3CYߗIYA[IBj } B[˨1p}bhdby-AsL\>x|,wq1dБЙC@_Bn<3w75WT?g!I K:3dx]!_zxiTy65޳X qړ4k 3}աܺ %ݐ)ڴ1{"1VBnoM{TowV̗GpK;'e ,^Tr? ?$;)vHJrmG`[Dn!#![gb[;n ata|~N7he_"G&o;wCWc$D W>@#d>%g~\ef] |p+Gy;dka֨noxe9z 0oʌ2̘8m Ų*;h4xAAzNcȥB8RQƶ{ꑜG~gl_ |4 VЇ!;i !]rõƃ7{NsT^'x+ǂwid D7\*aG"\ >YmUnS]/@&x8'u1 6U~ U2BRMaWoh`3jOcRTձHC5&Cweӡ:(BG >u"5,-ts#Cxv28^O-y=Nf;E=^ޕGT'ƶ]:yGͅ>I",IrpfڷSu,wR7unEk1qNn%"dWIFa@b`Gnݣ?H>amPÃpes}nU)lVc蕛T4̈5Ւb->Q5 MCbJ赗m] ahi8b kd9n?Y1D+E$ dlSܕe_׊H"72S FvXC`τܛqP['񸺔|ČTӶC_X+y9ǔ+ϺFI>l-e6&沙Ɯ֑YFwƝV]4y־qP47&a#T{'g .( њ*!P(9}9D(L%PN!N=M jE+ f=F-|Y܄cm+pd(fuM;tŒu!hZF[>:' 8vXML0,x?i4rck b3pu "/bOk6?Tlbf2e WkΓ@nazɲ 'VK!͓]_vzҘ_ӧ=Y_9%wJ#>9u"nZWӘU%n2#K¼e|xȵUېF I ů{*`) }E ܠ'?l4)SL0j4oFM'7-э. ZDճHt^1ҀYJNHxژ^QcG"~͐ﭰ xlMS:xY,9xG7b8|oaF*GN,/֠fōM7n6>w7:9fd[q뒚cf(GDY>PW&[b:g6xv7&)UZ׊ڸB:Y D<~FMi\k.Vw:C}DZj5Q^Kj\nV7D03r^!(;,xɪIC5o#bu#b#&b!1 Yj4qFf{Bq?q)b_Gbre qt{*0A w"0 .2Aq1e\ Yeaf0,sv#.z0R{7xrE5ϰ1ŵvZ>Q0q8>MzZwk ~ǫg࣬=(HC_$g/3`mv۰:, m:,}3mB:zDT9#7h“G `“*oմO~렪Ru*T]ŲedXBa +d9Wm84/J8B]ޱ;.C̉F7 bٌcokbc 2iMmqƠAu4 # |%h&I>rCz>Я5aJ-1gn p H aȯSie\K6K$W`DuT'NKDB(hSo*Pɼ& z2-LdK(F:YJwhzz(BJ mg|BV(Zn t?'3Y V{ks̓i=.$1Zdys^GiWc##m)Pm:r5(* 8rRK 'y ϖ .W *QU5Ta`u /ccOC:i_}`ݏ_c׍ɳ14an`׋Ja#uZx$tZŒ&D4aG_mQZUZs!F7%%{2כ'ЫɏFqEߺhok| !Ktjq>n*o#Z@B[-'˽$fW;(N0cfg&], R;1qCjAd=B#JA5.v` ևse _7h[{~oEX媄lra٭]ѧ9+3Qb9Twx}t#پ⭽|=4/Kh9Ə=a|eq2Q賡y09^&g%DuC糙LW ;␟r ˈQ{GMV ]e8e0&#}~+e!^ÃFJ6[P}db|TN cR?ڔڹUkxP@Tj5h * @L^fN$B!Μ"P:_7RI^7Zvf~p,`dXAIIX+65L DKf+ځbe9YQK%l)FAF(N+s8)dCΘ(g4?\:1K 3,NvYLD$3^k2"K_Kėxz44W=ƵBJjT —1'JftGRVhxYqZT+KUO߭+PKu1w|FvBveEݑ!ERHZl=sƙvCށ+b!4ch=wަ[}u)@JY'[wcB4AKFXk$~ aV^b+ꕐV#Ezȣor=nG\u1 զ#ugTJQGY,WAR O٥o55 m/6_ t1ыԔsF|h7fIA7RoM1QƲk z% b+a$e[- 0c',͘iUQpϯ!ϫoe I VE7nMM+I4T _yIkS?A jאԍ<1لt# f^ (, O1n(dRHJXe *U]X](*/2"8Ԁ~k~2|҇Pi)m|Xi$SRYz,G|P-g˟=6i n<c_s7 eqtj;8n41c܂˻L}\}r)=55^t_q^x|NF?NcWJ)׍sSt*~#woMgK2-JʔWv6tmvZFL?wy@ԇ'40$굳tBoRÐ T K$ ?hJ \+fʞF+U`2:¤ Z$#+=Q y]7YfkQ0*W(k 2_OO/Ȥ5Zu{UJ% )q(R^:{i\.yTچzB kmy/[y_}[u_ۜ([ߔ?ny,rsw߻r=f!s|]7ia`[g ]vOiFkkkp}eqw{諹}2IjFuuPT~agj9]`c$"vC=>Ʒd Ggog6_"3L[?3ZbYKǖvh;[H)71jܒgs[^r; ݪ 2RAsbHQNnW˒cv r3GTT)t-^TVk!ND*>C1C8>7 Xר6s %w|׵;:8Ρ1;p ( Auܴ0z,S&X] Re6S1K}tS[P>qCNDV4gp3+ Q G|,xRر|P!:)I3$|FKEՏ][jgc(+'f1"CoELC꒏5t%fd=꼐Q¢ƲRZV.~l{Z+!,7s铴bH+X=L3:~ȭzTaX4x2"0T0$2zۃiΤug2d<+C /M?p(^~ǝX!K1ja)R$r'Pd߀Qo1bOÎ^{*q?% r;z-k6\|1گǤ> T\ *yZF ie=rU>͝4.a/UDÀðZ gܽJAYAJ0$b OYAi  jXk'm (g:Wplf{DxXG%C;6.}ZLK['!d0RSK4j v͸H0SIdۄ  k kӝHLVL̈́kxȕ{Ԑ Zf3;(2S ]ULW&Sr\6 3|kDkKC U i=+PW! W58L[WUU|Z[Jq0ZFU/%˜fG)f KOl]y$5HZ\1 #ëbs+eRkcJ>-W^H79uw5G]|Z7r17׮[&yYQ}=7ϫWc%$=6K! \ AL9w5h=P!ږMG6F7MZ}Ltס6np)y][JQLI3U>S#Ez[N" ohnFT1b" " qs;f\2xDds:F:\kd98VǠ#gcM+?MpY-usC㨟r9oܾl\MbttƐ4MUdE f2f"H#'F^r%&1Xdais$2woClhg635^o?M;1ⳑk$v's*񘲸NKlh$7%z40 >&g!~dJR|e/3,z|ߞ .ߴHfW[v0` 3zdHNh3@^5Ŵ\`D }$PF] FSKdס9ڪP<,TYnN]Y%Y+o'D1O0R@ArFWP~ R#'Ѡ`B/^TTB%@s@jŵ3Da4 T6,tvɵUI,_4H`50Մk>(sHNgsMT ZwJH4T_HbNhmճU5 6Y ЈO_E͘A* ';##fvBh N +wԲ:׸*;@;&4L&>Hx7rPQ : yL,'+`X*ye2z QD)79f~Ş>YO &hKp EO?f|', To2[k- ,>t {+?`&{Jf-OZ"+VRy!K;"Y>Sxw-Yq='%`qӁ PpNSn[QI`i>+@v(LޝPP T BA)E/*/]T_*q*PҰ{ G3!DJ!j=U#?1E:b4`;Ω1Ȝ[L9N9ms9Gң1Mu4iTNS:Mu4iTNS:Mu4iTNS:Mu4iIENDB`PKnvL content.xml]]8}_Ayf2mݞltgkߦdmmIׯ؍?0XfJyHbt%{й E8ktC{(5>j4~-N vtqaZz׈I8Ā": A鐹CpYk&G({KWO|ae+ ۵`R8_#`QOq/7D /> 5EVkX4&&Vg0`weO<}(:NZmRHJS(U<+="g;q瀔zOo 8'7ϼ0󧷱@} 5\nTQ!hjZb Ik]1mqN[Y DVZ2Φӣ;x3FMR7f8 ;=naVLL~9 rK񼭦Nťυg>#ƀH*Q>niJ|y2[CS >ye—$T>d6wZX ˈrik3 G\/tō@Re-QfOYOj~0Y@Ah+whs \hz)/1nӐ*1]7ȻE\o 9|#0$M€f|`~bS[9X%`6婄iAvvի>):(}ؚQCfJJ.5`)pԡ@x % [S08PLx)C£5:(Pr)5cMʄe(XxؚQe@J.5`)pԡ@i+ikcAQub⛳#5H́H7%FjlG!ԉ윿\ؚ#5B#>95 B*tΏ}8plY+t^u) ]خ_:S$yq}w6WRT0Xj.éErǧ-oLDn[iC3n(ˊ&{MfEOف)&Xd$A)vc\H 5=H,4uF1 ]\&J䐇hW,qՇOHIscHBLUiDMQq,5ZFfk.?#+Vcb?UoQR-RqZZz}6p׋-v2z7@Cog[ CFђoFܨ9,ۆe6r& @:q$ȤCCU׍c4.t#.o*([2Q܋'½4u#)00!7!&GZOIS?w?_CgHCWoNJ0b7e|d!jp!FUQDRb5_!`1 L;:SI7{l}g$!fmbj;y|!.ŔD$b|Nd44И"AQSF `ƪs .:tN^xjBm\Zg`I Mi }`gG79&'cdkE#*q}`R:zT(ikc+嗀3!zgBdԚPqքχHSߍ>Q(-ۯ7o!C}uԚd&5LP MyC侟<)uAR;7 s{KVMh2ULa ds1r52?`xh)3=S5∃ 0"KI, ,2YP@2yVdJKs(%l![0x} )`#iU&;mUE,Ȝ"SWl9 #LX*lԘTbi۾i,a#!эO R$'jռ,'/5"B L,&ҖOZ…Ƨ&YAcRiViViԖa8W6\+ckAL1 /yV5ksm L Sle˭ў@~x<9x7g J|sQ% ߭p/$$֎GvDBnP΍Y{W!eIʄh/\pTBpRH˂gg , }1+; 3kFe'YjO;K1 YO=`,XZ;ꎴw~4i-HSC> e% `~*o92JrCN v9}P0Ì@("P"gqE]> V_mk"e(S O]I7 G#'32!ҷGC0 W[,v,r CeeAoCڐ!hN+F3€ )e@ `df.͆$A`JW 5B'mI"GyX;DTSQtCzNzm~V*e6Olx_ n})sòQ1?u/n8YXc+hV9:za5ɟx5a Y&<^x!$\Ndp҇&,7Yw-^,^>הē t<>%<-jH]RyrC{BsoT] =XJA+=g,ʐ\|P$.>I 4]+^'bQ߅tLRBnhLٔ6ZKБ/?OFu 0'"(I7^}8=q9־זԠ*ֵcl]l+ʁd*x,S"دbCڏsv׾'P?*G } *.q2>49` ]~Jd!Ya 5^^HѲ#ςUejLܨVGTa~qqQM./QNx-TvK(D\INjj(9\8ZZbi:ttd?@d twU!tvy/SFWg"3eX^V_/]ʯ3ݸiV;)x8S ֔Qk# 0" z'\ kXx#%ZdlN(n}`þ[^zV[^]'j|,`~ae;h1@|~"h2Oq$cNM7 Q=耳mxҧ`ϦG=H2'3kYxINiv*!b]k>1*=5 GBBYMB\WCyRIM!10+O^V͜D)V]lHF5}Fu(AFC0ɺ.0E: ʋc3BwLڨ"fFxDcH`{%(L#6dN t $0Q)%ڀx$/ #&# ƄݍbT-c7*(mFE=Fi0Bzc-g)2\a7\h:2$CY!yBO;S->(ЍL{7(L;cF2@52bwY ;D{OPyZ*>XL5LJ;I^犼aimKFG@xҠ$ui_ HkdB댁H"ۧztjP:6)- 2lS;3_0dGwMݕ#x'MH]m*+T#]zqх'p+C;Pg}K id?I[5az~Aލypz@Sۇ*Kqg p~ jUv=(JZKm/fTtj_J],`Y;+z"Chobvk 2Z ?.s#H̛Dڠ ٦ӕMx6u]¤7ߤe 2]Xۊ̘qSD1998q,?81}~"ي~e_ndKv6ѷMegCPK`P4i6,PKnvLmeta.xmlM0 #`SWiSioc&-q/%[G;cCvW0j( ʝK܇|,B`媆{3iȆ)~Yt jNak F孒νX[3ڶצD$MS4Dg WL5P@PAߡA'hf{kgזKM(!3]>c9{/38u37gnY72)3f5bpx 11 > Ac:cwy24DO>rr|Ic:+XE!&c34>$)B"( vY!M49}GO.HctOxBߔEy"Iԧ~zEE34́4"6,RNt ‚Y `ۋ\t>'.3~xgPKfXPKnvLMETA-INF/manifest.xmlTj0+RSs P#VFZ+涏ٙaڝ-NX7 @[}>;5)4DsiR@U4Qr%iI˫=زfS<:c@wrPt#yFtfjъ2LW|5PB>`?8ՃTG=^Żؙ>RV(BN}:^)&-d2xhz.o(?; PKȋ>PKnvL^2 ''mimetypePKnvL E&E&MThumbnails/thumbnail.pngPKnvL# < &content.xmlPKnvL4Configurations2/toolpanel/PKnvL4Configurations2/popupmenu/PKnvL4Configurations2/images/Bitmaps/PKnvL'05Configurations2/accelerator/current.xmlPKnvL5Configurations2/floater/PKnvL5Configurations2/menubar/PKnvL5Configurations2/statusbar/PKnvL+6Configurations2/toolbar/PKnvLa6Configurations2/progressbar/PKnvLh 6manifest.rdfPKnvL= z; 7styles.xmlPKnvL`P4i6, OBsettings.xmlPKnvLfXHmeta.xmlPKnvLȋ>/KMETA-INF/manifest.xmlPKpLe2guardian-5.5.8r/notes/V5_mode_comparison.pdf000066400000000000000000001041611477372360500213300ustar00rootroot00000000000000%PDF-1.4 %äüöß 2 0 obj <> stream x\I, ϯshhгܜ [o(RmM-_Ҍybܿ^o1~yz.L{ߟn.f3}8,fF3-5|u7o}v_|.f51eY_X7뗫Yd;@>=/cQqW;DvRyn8[E-Xϯ@{aa][7VҞ1vmM܃hy-!|ۋ0_/(o/<++o115(10΂+i^;0Ryg̕t}E\\I34bsT?a *qDJ:\A'P+*sK +ifP̕4b.&|ɠK +ifP̕4b.6 E\\I34bsT7NBRHeP 2W :\I'P^(JAes%͠B`3((JAes%͠ dR!OL%(a*HL%1) (1s%P2W fHEPnʐg8=q2P)AL1 ̕4C\A3J!1@iG *qDJ:\A'P+*shSDJ".`T A1W ¨K +ifP̕4b./y` ^/ x lH ĥ+if8̕4Cb.FT"s%@ ̕tj0" %2HE_eb`U(VXeQb`̈UN!V] "VE €TgV9I1Ub`2@urŰHʮŰʼ@Qu*b+PY״K5tqR&J(3c)ΛU2iS̞tB2Ny_dei :>1IJJ]}j]ߺ`U娮U]ղ@UcY),Ytj]ДI,dF|9u \EFZfd:MTE'N~#)xNTGyͥUGg9eBwTTA6r^ʺZenUi,T*k0]d p(j2EJuv_f:n h|霯l~ꡩ^6Uҭ{NeE5ʆ¨΃n-՗un2e 貐,ڲE*tWV8OYEIkkQvNEFoZt`Mb mtkEO6FRtD.ktDߪZ F{;VhT] ~41 QID7Ѱeys F ϥk5dURQmj.ʗ:wAcॎ(rKoZzSn_ Ś4p]\CrḪ Pʯo/ f>j\4r[k\PT=LD1ΚƸG{b=7E>d+|n5:"5n/|ָ^+9jٮ5m'?yz.bX4Zosͭq,elcu2q:ZO]Gk\pyZz]c=nhxܴcXv8ڳcwŽCN(; p`{{_+9kĊ~ L<>xfínāKtHtz@&(k+n}R Qm_;\*xk =/=,گ/6uOzvWj<2oJ~c.3c.sw>8H¿4Lx|m*R;ߜOVc1+>7 ߞr0(p$8$L7! AN98l$صny ~r)Mqs'2TdnC}}$DtoA΀zìm׆pY,ۦk9BggWg<O7ׇܙ_t5tΪuY/ٹG4m*$&Ko≵[ڕe ӯxA=HˇJ |6u/k!Yh œN ,aۓ^:'_PuBAOzgf f@<ƼP)ۻfsL%XK:PD=lGWw#P8A/ q;&V9Aώjՙ;>?e4tձoMx<@APWFD lǹ#<4!Đ jl1{۪&Jϑ4r{ꖎ41( ̈; YA): L2}"a*- -ݑ(۞|}&DO^=;u嵦0 q7d}&3 p+yCXhB"60 p0ISEC@6Tv-J =Ctnϩ9 n?F7R:lQt=[&s!{I ,fA5O}>K%(%cbe5̖(uo=1*S5T%C?'#NB'~'YwJj#Ʈ-Fm@:j~Ys />)B&XʗObhgǶIG$^<$'CۮRIQM$uw?FcNTJ\bS-4tm ~98m=UOq睜E jHDntPd[]*#W%5ӡT2>Z<3e:s9?;u3x1d6sOLJ7jhxkϣb8,'ff<+uYsFwgcL)a(7$? *PV7!uԳ081zmb՞~~0Pcߋ?8ijHE *V&'-v^@t?}KTa *2"Ă.o^ݤ|52# _)EWSZz'KHromVDp,:GNeyѣCOw['_DonMdSƫWw?˿!~eSI `~ZB endstream endobj 3 0 obj 3239 endobj 5 0 obj <> stream x{k`ו3d$K֌$˲'XO 65IC1!!i҄IKEJ&]mw&atinҏ4n/MwSdȘG'Y3s{D )>DhΞûvlC!;ͺm~߁Ͽl|GH7C{/d $?[:˝?EtD9;{v˾z934ۃFM@)h/ٳgtHM@f{v6;N@:BjxRGb?l+`oa3q?DZ&r'gg+z76$?)!zEo7I䉖D á`zܹ2ll2:Vg L*(ȇYLnwvtiO ML[:YoigҶ-[(3Q笮~@L nkN}; Su_w7qlÀ ?Y}\ c j/HZoprn50Pt7( W^JB- RN~^v0Iߕgi۫slhX} `vəvuG@xuغ:;ivocz`;bӝXuMD;-`ơq;a;`$0m5~m Dk:Akp+yW"%}Y[@RHD- 9=j9/-ӠОEWnA`t+"* f?"kIv v쬁r|EFO2RLP^}U 5v .>'BZع2/ awղ*QԆ`)=^qz u >TS9et[j"eE/-?v-sڲ@\ F@why-y-.^) GJB"&`aQH{1kr]l}ڱM.c@$*̦m.O6G`Ukwf1o͢~M<ɟ]Ƹ7LfX:%!eBk!R:K~)3vð3|ZhP\=ΈF0N)1lh&Ab/!"Ąhs،8y[&X ]\` o` UqyhYU SFPt%6;E9a `Iv]0{zں[ln>{~iPǽl2.S+۷♋]c^wh*u+8!u|֋)rS9~^h@&¦*IkPAPrk& 79@frBn u.KiBobB8gB -7Us]1%u84WA.]}tpޚ%7!%wŎO~fʌ{]fI[舘2>;Se19wHV fujaqD4J)~(e@ R$Ak#>uUjД6w ۦ=3UL[LCϚ&Sk>BFؙb rBep(g@\RkZެxyݣLY}y- S \n㽔{C,.@*Jǥy)sPIΈ)G-.T׋U9-s9v[k`r*2ВVXVѾ3Zů 2U`W;]z^68]+6;Y^o6>*smPiX3f78>k!JJ+źoAnC~!3V.r$)P^9f|hV P  ݪ7[H@!$vɥB,H D-5eVl[br``6B-Җj]d~UN ',P)Ը..LIp6/#+hYbS:HdzÄ(ZRE۹ B-/GG#W#r18?>NЖ.7qp[<4|YLXyZ]WLLWMVTsKU}ϵ.>$y˸L-RR8m*鬹-.66ڳL#4&O^̚/9\{jΞ5耍3“ޟe7< m3d9.7|*II`mQԆ;63-S\=SF-]Xy=B,mDfѹ-S~V4cd̈́5* px˳TDEj,>W@_ke8}yuQk]}kiɟf?V@L~ ʆo5Uht1=]DWqZ<7r׎beNړg ti )32J(j uH`*5+Kj;]3SSK7G%[W=6u47~|ѠZ~QOgXVVn w^^]c[]]s9)*=bQժe|tUՂ;AvyкhIYFM~{|yg4͎>!6!d?`zŧ|n1P^AT2 7B1]EP(S/JtVU?Ojkk^Ŋ䖧הjMy e\w{}wmPQSYc+?/| 6kŠ5pPI8(ҔӦI)h&>3v#I)%Y;hG0 G;P>cHgҵ,.97k61n%Wco!R5wQDTK ,缮)eD9Z*'יӖ7s螈6ü'8K$ ڒ,eP:Ŝ/Ur%ʀKcg"~t[uS#^Zi>O{Rj' LM:H,+YZ򋭯8`tFWdS4w7~Go; -z5+=Si T#DƇ"?'wU:ǩID|D\L?{\ŜPP#L1@ƜS3}6!P+4,S'; v-ʔKlVkQңnv@,=9GaVZwXԦ[) ҈nlIZ;1qǵ5:0M]1T8rYȁFfCe5uxaWu*u:OqwEb|gHH52uBA$OFc ҠV6ԍ&M)U&,USrRq8 @/oW UseeZ^pvqj.]HGS'/Z5n]?s>06b{bځhKuyǐ[2*!%}v!34gi&Y^/yI4}/+EK"pfg)ލ?fnb/dwܦ=!2pX\bqOpxu7F [wMtt{Ǹ{vK[1_N:Lj$Rؿ?xڛc5h60-_a iKki-rh|9y {Bڃvn;HAGI{~TsZM\v7rZ! Dxr9: a)Q\)hys=dc+[,RJ/2ʍ+W7'X -=ly!TXf u Jw4xl&_)_&פQr3VC#1-nwGf\NNs.߃V*`9Hr6TqI8GJ8 ;&a XRs.yϤYϥB[A]ܙtw#sW6*75wG['t|{yb+'eM5g=w1_i[6)4^KRfNˀl7W6۞f%kpLI9cH83NƩѝzZ=t/ wc@C77E;(.ҖDrM!!O؎mgN򍲎7NHlֿʍuoŦHYNI_̿4~E%˹3(Nߌ\:v5y=~)wNO9K36e ؈)g˝5Uls#ngƝZ=\g0 C%T(Q$e33cy ˇRd8O"~Ĝx"ot8ktY3F뿏#%{ |Nҳ+}]V~.-IC@/9`u{GTmH4.K:9wʥOyd Qhg4?LvFxlu34#R@MP8PL8(LN;O{^*ZzO ɰp_"6g_i}a .{SK97Ia%jr[dQue8#4I~ſ_2:U3nWR~FHY V] }?)3:r6n\{,dژn(LyrMa(qWNdʹMWQ=W=^7&k,Ք+. ZRelyhXQwlk׬&dRyQ"ވُ9}UkI(6[L_k,.Srw+,CEk$IxeO Ɇ?凄6ذ#PDUV o"h R΀㨞| U6*.BEDR D{2j%T/,|_BQꡅDw\ATs[MwE"qxLR})-1v;T=QVVA2y .~*qUE}Ëc$zQLIBJ+Bj4&b` `)RaP6 7ag 2'B6yEȉ` a1Ŀ 0 RR""<,t 0d!@>U1 .8`%NaL,B6*=kX>X̢^/'*bFtJ3P4r) = kB_󰒓Sl9<- 0XinY8zq 0S6Ù<)yd)yϔ]` Ⱦ&_`[_y7p6'|<ήF{V 8b = 9pR[xll0>c{رXx<6cc=}=caG8ʒ;+0 ~x0{qg/L c=c0  ǚ{@ͦ#Ξx 鶏 ٞqv46s0΍m/?S@c#}qNK}pwhSk @45 Gyll6ӍwgKaΛH ofvrVYFv,dII &[4D|t"q<[GݻwLYA@|HX7xCq'FՏB BnP6z [Ǐ*C7 D{Q8G8|H,WNAQPV!xh-V 1ྛWh FܙZNa~,N /7:b=@CA%BkDUVz߸P5F>uxVvC0;Lm,9^ýR+.MTST5J(jnqGO7(uw1m4!໕~G(Z,5ltݟa?k?S_hm$˻2:+V3y>]0@e)8tD}98gN>y))yg=LO>xҌ99`N<< 9v{0O>:拧95~dp?Dmm_x&O:&O7?2?4| E"zk/[&^{cO1M1p?4%eNy/Ls80d~ gԤ\_g0ExO}$oOMj7#/_q ֮1k*dV FV=)F0j5J*CX"UH0dlq@\%nju@]nUSUd+II"P+8e2r)CIT*e qBۈW&tV& R66&dmSqqhuhzYlS?4M9b@ Pg7;׸#1ݙsNx|^Oy]x''f/A0 g'XڕXz'n<>i7\+⸽…g_ endstream endobj 6 0 obj 10080 endobj 7 0 obj <> endobj 8 0 obj <> stream x]n0 +?82'Kc{3^ne4N_ endstream endobj 9 0 obj <> endobj 10 0 obj <> stream xݽy`Tս8~2r;k2[dL627; !@H@BHHش,JQVѢ", hminֵֶ  G!^(*.)MH(9П>WO"5pg||t=S:yCC+pE< ~w90! m ցn0<vYI k~A8 n54KOx,`5 xw]؇5:p@GcBI\pς;-׉WBr% {#p|aʅeҒDa\(E#P~^0y=2Fgxzb|ś_d@ĘnL=c:zqY;\1xP =Ѱs|paChG_=KaccL cXlt%:ȣ{c;_cggxv;kX] _*Ģw"{5z4Άx71 0=S| }=}ą./QWߙ6Wh|ӓAvs[Gޑ!TMwV4.h`c|/?:|i:ey<]cݾwH?}7{OZYS[;]':ks * 4u]gؚ+RTu ߬ b,Q::O^1d2H,˦S=ĝ΋`,ߑ)` Hntbi+wen|iMcÚ c_\\3uP3 AUc#!^򍱑1IEGU'@[ko?">؟JȺd }ľEb߂%Xh[[Iuݵ`Q ;HJ)Ӊq?;a==&`%f(vA"֣7vE("KF 1_Ǽ4 L/ :Dܿ?SAٿ߱G|\WWيrՊ/yLDg=|2Hܼ$"OOJI/Caܣ@V@ Z0*h HQi4i+d9d-QH+M"@찟q r'tHq=jUJS'Ƞ_Tj~q: ~Ōw$<&U*<%gl{?y䛢 U+<| b=Q0R(VGϥK.]˅ȸHa|VuI/& *GSRM$]q0+|T@D]Q^I]1-n݀$*g+} o3Kf*=csfNL \K1ݾdgm 8_WЯ%5P{XlJ?2rL*ƚgM Mg C BqAl@,e|5M!psG*īIY\KJ}ݮ;Ӑ]r M  D*ђX8.?=,a"<AHW`*WRy27a2J}`I1BUQZVR@bGwmpS"y`ͨKfJU'JʒǗx-/#N1 վ}SQ+*JʞkւY5yΛGyQ%l!D‚Va;hof5ޅqn*xF N$D1qOTNpLJ#|WdZn?~+Gp L6W' Ց3LZej4dm35^T$@>@D"5ഋr4b^,)'Kd!wZg{%g9f:Q';}l [*LՅWyzFmMuS;ns ߳'דIJf[:ĕ˖C ʾP}m/j^ܾsexu`^So#ɿ҃[ A8"P"5IQ$UB?z%O^1SqV^#={h>Q5.Mԏ:z4Bo4n" P")T1W|k}ŏө;_A_Wvb^I0HשdH}u{Ǡ4 `.s= '7kI'/ hW2Qim@3j9g%~]c8h,8,ξ[_ߝW*H%[k2~4*A I2*JTA(?izy~3uN3NN9D3$N3);;LWkT AU&*e#pZ\X Nn(v3#T g4NU)dm-IK1?Ud Γ]R5ȃ֒HA<1鋨rnIvB/~{Dξ(e T J E{{P:0JoUd UH9j8L&L:D*bE!ɲf  ՄKe0u.'ȃa+,Ű2EE%39W\+Xuݎ0HP8: @7#bxxyx8L1?+>ԭ_@V,& ݅>QQJHE4Tӫ()I +fE –; z&=MK1~CG@>6.b ">W]HdD)H8[rO3xL*k?LWRfoN"{vH-"18vc=įO^/ UݺF;>t[I'r=UT?#)@qKCSt'ȴp~UB.VŖCy*R]Q5PUJXbG~p?obO>*ARV¾HM@ ]W-DHo [эnsRRTi p"2UBAm=aHVzFũ9T-*IWWgq9riڛ!^mu%_%= VYLl+vws1ig"]x~ij^vrK%bA/:VWP ZY积4 4E}S6lԵkM+Sy>'|qR'L+?ApRVXKHzHszd5 @ +0+')0@o'rLjlةj5]oWWt^NM0Ď fə_(dStCKQ;܆e%4мvsyy;v~:[J{TEuGguYoQg x?SQf䉾<4S"K$˥Ւ!iznXZ eUʃCAZ;(3 yԧيz3GNߋ2Q!–+=e8=?#Y\M$2XGdל7xu.3eI~iVTϨ,xpH ?o4,ҷ7߼p̋k/nZ\_Yvުke"B(_hI{mN7*sxxl#r|OwS^KnR& $;۪zV8p0DG㺨p\>. n8T%fI!b?΄2xLttd*&AѿI%-R|uߖtf'"P}Qϩ 9%O߲e#wmP~~n޽/틺Zg! ڟ=nqWD/Ӌyt[`|v:Uu_i`旨 ~s[ds8{RVz!qb9^!,w {xh?Y@K *h?_9b*$BB[D@#r3Z> so1ʍY -W]^Dx[UKbsG%ʬU;BP/+*5\#Ǻr.G f)7圿 a[JsZ˛ U0Dj{U\|P<ƎJHU3طt,49PhNȪQheTЁ rKAy2U^>C 3/h36+Łu])egdLږq{i]&/e7=Vm! UgX,[ٰ!8=m*g6!ݻ)}|F9ЀjR%ynif%(A9RCt 3]ED@ǂIJX|&~D]sϥ{?G/Iw;*(@KQvRzmxLvfU FbGsqFU(ZXN?cqBE)T xlb ec&='',Q;8(:$,AX,VReVoPReBkXւ&%5*=ʕӇiB\%Qz>™j53ͷ/yͼ^kwpu~̡y5õí4{9XJ4 J(LGE(<'|y75`3/5Q}Cr_%u6 / fmglC5mkk =N1ꛤIJeƑ?q8.˳љn!k"/}iAd${B%e&|H]vlF*eQ G -mn^[\gr15/yodqN&ӿH-A~IV3hu}"#GھӦz|W8P9'QV*f '#\++ ]{}t{^{H4 0ݞQDO(Sظ6C _ְث~i6^&E3YG,;R3v2TO ' oϪ٤ 8"KL&#-b%'T I%TpzϹ嵖U<ƺYv Mͳ,fuɍzBD'kQQ?o%yfyueG=#jM8/,DgT8J<3MbqԤUج7!=&*$X8$72 fcs>R&6cquL7#9T1&#TzS/ngښTnzu[Nsԃs_ܰ7Mf7{y̟ۖ䚴=B? td [E{pAHWXD(D#"hV`$ _/l蒂F.g(2J"%ٮD=ZmruvbdX6 [6#ћ$7n3DS 1bhO: ?J٬I=vnd0>i$5ZRGjZoH^g 3$VJH&<(ADL){H~D D\@]5a(Ϊz8ݕ~s?|{ y ߩAh]C[BtdtbyN"gqX,H&E+L2;ۈG?8,,lch<87S{̶_mdO0^1R / S˩E7O=.t t Y{ Qhfp26 l(7"mlDʯ+0m lneMog"D.xr](} eOCNd/j8 PEr"dkV<׽[Ψ}ŷP|9|mf"$|,%*OgŢA{Y7'+ӡߨhmy" Y6ha:+aNr~g!?G;^wxqpr ;$/ʬA9<]%P@D=;Oa"N w`&WIs뜐n1G-)@OFOb(ڄlOE3K_&@F@~BSc@HSJa.tjвTjlJ!QÀfO.THCj fXEf7 QSі(Wt" #|\g)߫ՠN*dHIdU+gislrG1fX3FĽD) !ץzۨ{7_?Vj +ӷ^`9"7]Ok!~ˈG> NJi]nhRGTީ|))C>c`:f"WR)v|_@+2ZI)Ռ}no_,nn˨ fb QBa>[nj(hm`qJn@t !hD+ TEKm@-pj\YKDl{z&Gek__.8S)S\# zƫL߽а$FȽP! zd!b{u˅xW(hΟי~"e50:ǥqbOdeN7}`x5Y1X-,MXs=k6'ߐL@|, ,Ѭ\epPa3㱞 M̒mg?NңËe,#DvY. O~yp`cw]MbKWc#'S*d*ʡJy̿q,n d<^<0"{С O4(_y S)(zٟ($d́^G?]_X ]1F\:{!pz+Z[oz6F8[ M yyη'롯a6[ LܹRp+ZN]|{its8tlsBσ+ҶZw?RQG9UH_UQ$QUGEf,) +H^WbmB[vyE9Qݏ) (SUC9\\uL{4vMggLہp [8Q+'r:;DY蜁f! o~n!Ra ZKg' c; 6~c#ޝhejT5}Uz;'; =+8tOT5І$<lPfFsolfoGD̾=FbgGxZA6$EP6wo| â?WϾqeZ8NצZZJmhx0$o1 ϛjg*Z*5OWdѐ6J`0Ad6S]duG<<߁#crZ=]Z:oJðp@݀ &LFеQPR8Hg)OHd* \ʦ'-OX3r|jFƕO CϫX$j&tVefk KcT rJ9֜kM[miۖ/il -О%x@4,ő'=B}0xFa UU84ɏ鸧ųCɧ\3r"Emyeֶklk2W W!j_0JG"9qc6K]N]>rO)kb4X R]lS (@,7O2zo=޷ᓂ&&rΧFȰI#<88dxU=Жh 5ՕO v.P|q(P= gg7->51*[nLdO}ٲ( eTsDM&A_i/=ER FQ#}Q:į7]};0^{>/=}Kys={^6UOa;m޲e$ <}DL'Nt~%\Nׯt8͂%2NbFįbC)NWi׼: &Z=EN+2@?>8(<& re|};;SB|z X<(?߉煀S&/ur6EEbA&sl&LXᰉ y'L<)Ab"N("2yHPܦ)&ʠ"$'N[Eӛӌӧ(wLcgˏd+;&̶M8ӛG | eCNxӣ&X'pH,j+,)S(]}hH$J㒸#>,.}|A"+ SW9A ҸSh)ŝ !6Ea2 ,0rD+ iI^2y mMSLlp$,iP~l+"ddbV((6 }1BLm\H(C{Efj9|U@A=fwY))l|N;lܰ8xb^kGK BnA^;. X%}QR86.՟~+U1,"^&{I9G PB/V"vf2|x;B{2K!PVEjz^Y@ˆeLOpVw nfj|`|GS1˔[eՒ kY/VY&\/.Dzi#(|E[HEt*2*mSqS^>\ ufjוG8bvgiҍZҤy\נt+.(͍z1(ʵ {KK|W̤4?uE4;'%rGD;Eg"RR*hR$vQb9}HYK D?<%[~껙hP̈́KaYֵ(6$V+D0<*> O$(D{,(hNfޯp(wxJaFxJa->Y&;Kr¥/+ͦ=3E2z$sP>{xaw#Lt~MO]%FmG)feFW2MTob|?GE#=/!Z 橵46-1Q&:v{AHA):B"q gRLJ7¡B*o;fep^dfC%ғ|ˊz8nOPoaQ]1'u/Zwo8>e< 1YѬ⩘p˹wtJ?AX@DX1s4S2]BG)J9U.d¼q&LvG!ma:&;M-Mm7Sż=ͬ#Mt[IiUMs?r#9o{Oqu1h]i'cά^Ze[~+gΦ?^ha*iRђM-u7t_ۖQg,m\Һul@`pL?#=_}o2TAʢ~SӾ땸_+JR`0h9h IvAhtJ[A#)y25(ojEXgu0M"*o`o`60,#g88l=`XA='- ZϨ`&JM &?g)`ۥd>衯m@ A#*I#ބLES^JVȬc(QV)T]^ퟙ#>;pqi]QkmeÎyלn%x^|3ߘgOf1^VcF"Xwo:t5N6N%1E#`%Y~ faea `,,far,ZB5– L|0,ư ?a9ǰG& @KUfa³0fR0 RX,,Rr`_ dz<"Qda-X(J4f?0BVzZ 0̢qC6@X/Ob؈do,°gfnىan' >) 0|rv0;i1|vn(Tvml7V{FGQ+'5,SnąC}zG( Lԇ^k^  za|CnC|]5}WAL7Etl_x)7§ mYvgC{2#(BЦM|_~fU/40::\![l)XYA:atЪ=C`#XzZ z7|mP>n4 @=E}< vo)A Y Am{` C5Vxm }eXZg ˯4z  z~|BvCޠ6hDu|hE0;<{ ܲ7ϛ03n%egG81?>3^ͽS|92nve>^xfʨm/Mj c>9p2Л_Z ûBg=F =qt>Ӫ,Odi;1_qUNz̻xyW 7œos8՜: mwno?j+Ysl3 *ðm6#J){F/+ڭc.>6ۦi7]H7M x, h~U/q3 5~cv{ kSy("ܛ›J8V .Kx3Z+Pz%Z@s/ /^nyyC/KR/D,‹v^4J>}} ޞz{^ؽ$ ۝Cy$;r&q']+v2]}~nt!v_TB[ӻ^zR2]޿ v^.l}Г8G0s$8K:<Җ-\ǽ;@c:Dz?ᑡG=B H>];K0?~H^|~?gqqpp M#=~q;w/"e7Y{O+Xkϡ={v8Jܰ ڵ~ln/}G@)At*cm~ iiNjkLfFo0jd0g9)aB˄9թLhs n4%mR j)ulSsL(sEĘ4/3v(|^1E1ry,'I_diy†.B]{tPgg5׼cl9@!W'O+~NNd3j1~/ c1{/ EFF""e#`iQLTE> endobj 13 0 obj <> stream x]n0E .E qG;uDjIs!53<paw5LѯϿՏɒ1?:K{f=yX!_cFtmMmvβ}[Q1t~K!4 ,N8W4P.-W/W+Q \-Y~gpfdxKβ_c//`po/w:7gZ%zh_ WL B Q_B\s/p ֡#/Շ%9+\b;胣\pqcsg\Lw_0*::#awfd7z endstream endobj 14 0 obj <> endobj 15 0 obj <> endobj 16 0 obj <> endobj 1 0 obj <>/Contents 2 0 R>> endobj 4 0 obj <> endobj 17 0 obj <> endobj 18 0 obj < /Creator /Producer /CreationDate(D:20180613155140+01'00')>> endobj xref 0 19 0000000000 65535 f 0000033739 00000 n 0000000019 00000 n 0000003329 00000 n 0000033908 00000 n 0000003350 00000 n 0000013515 00000 n 0000013537 00000 n 0000013731 00000 n 0000014151 00000 n 0000014426 00000 n 0000032494 00000 n 0000032517 00000 n 0000032708 00000 n 0000033258 00000 n 0000033642 00000 n 0000033684 00000 n 0000034007 00000 n 0000034104 00000 n trailer < <9FD778ECF801E60C9FD1371052D01910> ] /DocChecksum /D642BF15AA83EB97908DF83459BE4B26 >> startxref 34345 %%EOF e2guardian-5.5.8r/notes/cgi_bypass000066400000000000000000000064761477372360500171650ustar00rootroot00000000000000CGI Bypass - New features in bypass v2 Bypass v2 improves security especially with cgi bypass. The older version is now refered to as version 1. In normal (non-CGI) bypass mode, e2g creates a hash of the url, bypasskey, client_ip, username and this is checked by e2g against current values when the bypass link is used. This ensures that the bypass link can only be used for the save url, by the same user and IP. However, in version 1, when in cgi mode the bypass hash is generated by the cgi, based on the parameters passed to it by the client browser, in a redirect from e2g. In this process it is possible for the user to modify or forge the parameters (url, ip etc) and get assess to a different site than that redirected from e2g. In version 2 CGI mode, e2g gernerates a hash in the same way as non-CGI use, and the CGI validates this by adding the cgikey to the e2ghash and then creates a new hash with the result. When e2g receives this it duplicates the whole hashing process, first the hash it would have generated and then resulting hash signed by the cgi. Only if this matches the received hash is the bypass allowed. Also in order for the cgi to check if details have been altered a separate 'CHECK' hash is generated by e2g - format md5(url + client_ip + user + cgikey). Configuration - all in e2guardianfN.conf For URL Bypass. Set:- accessdeniedaddress = 'http://YOURSERVER.YOURDOMAIN/cgi-bin/e2guardian.pl' bypassversion = 2 cgikey = 'your cgi key' cgibypass = 'on' bypasskey = '' # allow e2g to generate random key at start-up bypass = 900 # number of seconds bypass is valid For Infection URL bypass If not already set by URL bypass:- accessdeniedaddress = 'http://YOURSERVER.YOURDOMAIN/cgi-bin/e2guardian.pl' bypassversion = 2 cgikey = 'your cgi key' Then set:- cgiinfectionbypass = 'on' infectionbypasskey = '' # allow e2g to generate random key at start-up infectionbypass = 900 # number of seconds bypass is valid V2 CGI processing Your cgi program will be passed a number of parameters from e2g, giving DENIEDURL, REASON, USER, IP, FILTERGROUP, CATEGORIES, REASON, CHECK and either GBYPASS (url bypass) or GIBYPASS (infection bypass). Note that all parameters are sent URLencoded - if these are not decoded by your httpd server you will need to URLdecode these before processing. CHECK is a md5 hash of (DENIEDURL + IP + USER + cgikey) and will be the same as a similar hash generated withing the cgi, if the parameters match those generated by e2g. This can be used by the cgi to check that a user has not changed or forged any patameters. If the request is approved by the cgi, then the GBYPASS or GIBYPASS should be modified as follows and then a redirect sent back to the browser to DENIEDURL + the modified G[I]BYPASS parameter. Make sure you check for any existing parameters in DENIEDURL so that you add it prefixed with '?' or '&' as needed. Take the first 32 bytes of the G[I]BYPASS and make a new md5 hash based on this plus the cgikey. Replace the first 32 bytes with the new hash. First draft 2nd Jan 2019 - requires checking - Philip Pearce e2guardian-5.5.8r/notes/dstats_format000066400000000000000000000032321477372360500176770ustar00rootroot00000000000000e2guardian v4/v5 dstats.log columns - Note the format differs from v3.x 1. Time - in unix format 2. httpw - Number of httpworker threads running 3. busy - Number of httpworker threads busy handling connections at current time 4. httpwQ - Number of connections waiting in queue for a worker thread. In normal use this will be 0 (and on occasion 1 or 2) but will increase once all worker threads are busy. Lowish numbers are OK for transitory peaks. High numbers indicate an overloaded system. If there is still memory and cpu available, then httpworkers may be increased. 5. logQ - Number of messages waiting to be logged to disk. In normal use 0. An increase in number may indicate system overloading, but should not otherwise affect performance. 6. conx - number of connections handled since last stats line. 7. conx/s - average connections per sec over period since last stats line. 8. reqs - indication of number of requests handled since last stats line. Only an indication as requests made over https tunnels (i.e. non-MITM) cannot be counted, so each https tunnel is counted as one request. 9. reqs/s - average requests per sec over period since last stats line. 10. maxfd - Indication of the maximum number file descriptor used. 11. LCcnt - Number of List Option Containers in use. A new List Option Container is created at start up and on gentle restart. Old containers are deleted once all connections still using it have finished. Normally figure will be one, but may rise after a gentle restart(s) and should return to one after a period of time. Philip Pearce 25th Jan 2017 (revised 21st January 2020) e2guardian-5.5.8r/notes/e2guardian.release000077700000000000000000000000001477372360500242562../e2guardian.releaseustar00rootroot00000000000000e2guardian-5.5.8r/notes/extended_rooms000066400000000000000000000022621477372360500200460ustar00rootroot00000000000000Extended Room functions in e2guardian Hosts in a room can have a override list of urls with or without a blanket block function. File format:- 1st line must contain a '#' followed by the room name The following lines define the IP ranges, one IP or range per lines e.g. 172.167.58.3 172.167.59.1-172.167.59.65 177.167.60.0/255.255.255.0 177.167.61.0/24 End the list with: #ENDLIST Then optional whitelists can be added by using #SITELIST or #URLLIST Each list must be ended with #ENDLIST To block all access (other than listed in one of the above lists) use #BLOCK. If #BLOCK is absent than the trusted sites listed will overide any block in the users filter group. So there are 4 cases:- 1. #BLOCK and no (#SITELIST or #URLLIST) - block all access 2. #SITELIST and/or #URLLIST and no #BLOCK - trust these urls and allow normal access. 3. #SITELIST and/or #URLLIST and #BLOCK - only allow these urls. 4. #SITELIST, #URLLIST, #BLOCK all missing - Legacy format file - block all access. e.g. #MyRoom 172.167.58.3 172.167.59.1-172.167.59.65 177.167.60.0/255.255.255.0 #ENDLIST #SITELIST site1.com site2.net #ENDLIST #URLLIST site3.com/url site4.net/url #ENDLIST #BLOCK end_of_example e2guardian-5.5.8r/notes/icap000066400000000000000000000022341477372360500157420ustar00rootroot00000000000000ICAP - Brief notes ICAP REQMOD and RESPMOD now enabled REQMOD service does url checking and any url modification and logs blocked requests. RESPMOD service does content checking where REQMOD has not identified request as an exception, and logs request. Possible modes are:- REQMOD only REQMOD and RESPMOD (recommended) RESPMOD only is not supported REQMOD service communicates user, filtergroup and classification of request via X-ICAP-E2G ICAP header To enable uncomment icapport in e2guardian.conf Default filter group can be set for ICAP using: defaulticapfiltergroup If user is passed from Squid, the user's filtergroup is now looked up in the filtergroupslist. If no match or no user passed, will check any ip-based auth plugin enabled to get the group. Suggest these lines in squid.conf for testing:- icap_enable on icap_service service_req reqmod_precache bypass=0 icap://127.0.0.1:1344/request icap_service service_resp respmod_precache bypass=0 icap://127.0.0.1:1344/response adaptation_access service_req allow all adaptation_access service_resp allow all icap_send_client_ip on icap_send_client_username on adaptation_masterx_shared_names X-ICAP-E2G e2guardian-5.5.8r/notes/lists_via_stdin000066400000000000000000000021461477372360500202260ustar00rootroot00000000000000Lists via stdin This option replaces the totalblocklist feature in previous versions. It allows lists to be read via stdin on e2guardian start-up. To activate use the -i option. List syntax is as normal but with added directives to define lists and indicate the end of a list. Only sitelist and urllist types are currently supported. To define a site list use the #SITELIST: directive followed by the list definition, then a list of sites, terminated with an #ENDLIST line. #SITELIST:name=list_name, path=memory:psuedo_path_name [ other_modifiers ] site1 site2 siteN #ENDLIST To define a url list use the #URLLIST: directive followed by the list definition, then a list of urls, terminated with an #ENDLIST line. #SITELIST:name=list_name, path=memory:psuedo_path_name [ other_modifiers ] url1 url2 urlN #ENDLIST Where list_name is a label as defined in V5_list_definition, psuedo_path_name is a unique label for this list and other_modifiers are any optional parameters of a list definition. These lists are added to any lists defined in e2guardian.conf and can used in pre-auth.story storyboard. e2guardian-5.5.8r/notes/port_auth000066400000000000000000000026311477372360500170340ustar00rootroot00000000000000The port authentication plug-in is used to determine the filter group based on the listening port. With this enabled you are able to direct a user's browser to the appropreate filter level by setting the proxy port number in their browser settings. The user name in the logs and block pages will be set to the port number. The port auth plugin is enabled by default on all e2guardian complilations. There is no corresponding configure flag. To enable the port auth plugin: Make sure both mapportstoips and mapauthtoports are set to 'off' in e2guardian.conf. Add multiple filterports to e2guardian.conf: e.g. filterports = 8080 filterports = 8081 filterports = 8082 Add authplugin line: authplugin = /usr/local/etc/e2guardian/authplugins/port.conf Create a portgroups file: Note that ports MUST be entered in ascending order e.g. 8082 = filter3 8083 = filter2 Edit the portgroups entry in port.conf to point at your portgroups file. If you do not list all your ports in the portgroups file it is also possible to run other auth methods on the remaining port(s), but the port.conf authplugin line must appear first in the e2guardian.conf with the other authplugin lines following. You can also use the IP auth plugin either before or after the port plugin - if used before the port will only be checked if no IP match is found - if used after port will have priority. e2guardian-5.5.8r/notes/rotating_logs000066400000000000000000000014141477372360500177000ustar00rootroot00000000000000Rotating Logs - new in v5.5 A new -t option for e2guardian will now rotate the logs (access, request and dstat). The existing log will be linked to logname.old. If this is successful then the existing logfilename will be unlinked, the log file closed and then reopened. Note that in order to avoid any thread conflict this is performed at the next entry to be output to the log. It can be a period of time before this occured ( 5 mins or more for dstat ). The logname.old should be moved by the calling script as any further rotate call will fail if the .old file is still present. So script logic is:- cd $LOGDIR e2guardian -t sleep 320 // wait for next dstats entry - can be over 5 mins mv access.log.old $LOGSTOREDIR // or gzip etc mv dstats.log.old $LOGSTOREDIR e2guardian-5.5.8r/notes/ssl_mitm000066400000000000000000000052111477372360500166530ustar00rootroot00000000000000The SSL MITM option allows https sites to be content checked. The request from the client is intercepted, a spoofed certificate supplied for the target site and an encrypted connection made back to the client. A separate encrypted connection to the target server is set up. The resulting http dencrypted stream is then filtered as normal. Sites in the exception lists are treated in the same way as before (i.e. NOT unencrypted) so sites dealing with sensistive information that should not be intercepted (e.g. bank finance, account logins, etc) and other sites deemed 'safe' can be made exempt from being examined. Advantages are that the user gets a proper block page and full https urls are logged, https urls can be whitelisted when site as a whole is banned and content can be content checked. Disavantages are that in order for MITM to be usable a private root CA certificate has to be installed on each browser and that more processing power is needed on the server in order to encrypt and unencrypt the traffic. Note that full HTTPS MITM requires a lot more CPU power. To set up MITM: Configure and compile e2guardian with the --enable-sslmitm flag set Before generating your certs and keys check that your openssl.conf file has the following in the ca section:- basicConstraints=critical,CA:TRUE Generate your certicates and keys:- Generate a key for the rootCA openssl genrsa 4096 > private_root.pem Generate the root CA certificate openssl req -new -x509 -days 3650 -sha256 -key private_root.pem \ -out my_rootCA.crt Create a DER format version of root certificate openssl x509 -in my_rootCA.crt -outform DER -out my_rootCA.der Generate a key for use with upstream SSL conections openssl genrsa 4096 > private_cert.pem Copy the my_rootCA.crt and my_rootCA.der to a web server so that your users can download them. Create a directory call generatedcerts to store the generated certificates:- This directory must be writable by the e2guardian user Assign cert and key paths in e2guardian.conf:- cacertificatepath = '/usr/local/etc/e2guardian/my_rootCA.crt' caprivatekeypath = '/usr/local/etc/e2guardian/private_root.pem' certprivatekeypath = '/usr/local/etc/e2guardian/private_cert.pem' generatedcertpath = '/usr/local/etc/e2guardian/generatedcerts' Set enablessl = on in e2guardian.conf Set sslmitm = on in e2guardianfx.conf Load my_rootCA certificate on each browser. On the browser Set both http proxy and https proxy to the same port defined in the option filterports, if you use e2guardian as direct proxy. The transparenthttpsport is only for transparent https proxying. e2guardian-5.5.8r/src/000077500000000000000000000000001477372360500145415ustar00rootroot00000000000000e2guardian-5.5.8r/src/Auth.cpp000066400000000000000000000123671477372360500161570ustar00rootroot00000000000000// AuthPlugin class - interface for plugins for retrieving client usernames // and filter group membership // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "Auth.hpp" #include "OptionContainer.hpp" #include "LOptionContainer.hpp" #include "Logger.hpp" #include // GLOBALS extern OptionContainer o; extern authcreate_t identcreate; extern authcreate_t ipcreate; extern authcreate_t portcreate; extern authcreate_t headercreate; extern authcreate_t PF_basic_create; extern authcreate_t bearer_basic_create; #ifdef PRT_DNSAUTH extern authcreate_t dnsauthcreate; #endif // IMPLEMENTATION AuthPlugin::AuthPlugin(ConfigVar &definition) : is_connection_based(false) { cv = definition; pluginName = cv["plugname"]; } int AuthPlugin::init(void *args) { read_def_fg(); read_ports(); return 0; } int AuthPlugin::quit() { return 0; } String AuthPlugin::getPluginName() { return pluginName; } // determine what filter group the given username is in // return -1 when user not found int AuthPlugin::determineGroup(std::string &user, int &fg, StoryBoard & story, NaughtyFilter &cm ) { if (user.length() < 1 || user == "-") { return E2AUTH_NOMATCH; } String u(user); String lastcategory; u.toLower(); // since the filtergroupslist is read in in lowercase, we should do this. user = u.toCharArray(); // also pass back to ConnectionHandler, so appears lowercase in logs // String ue(u); // ue += "="; //char *i = ldl->filter_groups_list.findStartsWithPartial(ue.toCharArray(), lastcategory); // char *i = uglc.findStartsWithPartial(ue.toCharArray(), lastcategory); cm.user = user; if (!story.runFunctEntry(story_entry,cm)) { int t = get_default(!cm.request_header->isProxyRequest); if (t > 0) { fg = --t; cm.authrec->group_source = "pdef"; return E2AUTH_OK; } DEBUG_auth("User not in filter groups list for: ", pluginName); return E2AUTH_NOGROUP; } DEBUG_auth("Group found for: ", user, " in ", pluginName); fg = cm.filtergroup; return E2AUTH_OK; } // take in a configuration file, find the AuthPlugin class associated with the plugname variable, and return an instance AuthPlugin *auth_plugin_load(const char *pluginConfigPath) { ConfigVar cv; if (cv.readVar(pluginConfigPath, "=") > 0) { E2LOGGER_error("Unable to load plugin config: ", pluginConfigPath); return NULL; } String plugname(cv["plugname"]); if (plugname.length() < 1) { E2LOGGER_error("Unable read plugin config plugname variable: ", pluginConfigPath); return NULL; } DEBUG_auth("plugname :", plugname); if (plugname == "ident") { DEBUG_auth("Enabling ident server auth plugin"); return identcreate(cv); } if (plugname == "ip") { DEBUG_auth("Enabling IP-based auth plugin"); return ipcreate(cv); } if (plugname == "port") { DEBUG_auth("Enabling port-based auth plugin"); return portcreate(cv); } if (plugname == "proxy-header") { DEBUG_auth("Enabling proxy-header auth plugin"); return headercreate(cv); } if (plugname == "pf-basic") { DEBUG_auth("Enabling proxy-header auth plugin"); return PF_basic_create(cv); } if (plugname == "bearer-basic") { DEBUG_auth("Enabling bearer-basic auth plugin"); return bearer_basic_create(cv); } #ifdef PRT_DNSAUTH if (plugname == "dnsauth") { DEBUG_auth("Enabling DNS-based auth plugin"); return dnsauthcreate(cv); } #endif E2LOGGER_error("Unable to load plugin: ", pluginConfigPath); return NULL; } int AuthPlugin::get_default(bool is_transparent) { if (is_transparent && tran_default_fg > 0) { // syslog(LOG_ERR, "%spa default set as %d", thread_id.c_str(), tran_default_fg); return tran_default_fg; } else if (default_fg > 0) { //syslog(LOG_ERR, "%spa default set as %d", thread_id.c_str(), default_fg); return default_fg; } return 0; } void AuthPlugin::read_def_fg() { String t = cv["defaultfiltergroup"]; int i = t.toInteger(); if(i > 0 && i <= o.filter.filter_groups) { default_fg = i; } t = cv["defaulttransparentfiltergroup"]; i = t.toInteger(); if(i > 0 && i <= o.filter.filter_groups) { tran_default_fg = i; } } void AuthPlugin::read_ports() { String t = cv["ports"]; // DEBUG_auth("ports = ",t); if (t.empty()) return; while (t.contains(",")) { String p = t.before(","); int pn = p.toInteger(); portlist.push_back(pn); t = t.after(","); } int pn = t.toInteger(); portlist.push_back(pn); //DEBUG_auth("portlist pushto = ",pn); } bool AuthPlugin::port_matched(int &port) { if (portlist.empty()) { DEBUG_auth("`Port list empty - true"); return true; } for (auto rec : portlist) { DEBUG_auth(" port is ", port, " list is ", rec); if (rec == port) { return true; } } return false; } e2guardian-5.5.8r/src/Auth.hpp000066400000000000000000000076301477372360500161610ustar00rootroot00000000000000// AuthPlugin class - interface for plugins for retrieving client usernames // and filter group membership // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_AUTH #define __HPP_AUTH // INCLUDES #include "Plugin.hpp" #include "ConfigVar.hpp" #include "HTTPHeader.hpp" #include "ListContainer.hpp" #include "LOptionContainer.hpp" #include // DEFINES // success #define E2AUTH_OK 0 // auth info required for this method not found (continue querying other plugins) #define E2AUTH_NOMATCH 2 // auth info found, but no such user in filtergroupslist (stop querying plugins - use this code with caution!) #define E2AUTH_NOUSER 3 // auth info found, but no such user in group for this plugin stop querying plugins - use this code with caution!) #define E2AUTH_NOGROUP 3 // redirect the user to a login page #define E2AUTH_REDIRECT 4 // allow access to http[s] based auth system, but with no persitance #define E2AUTH_OK_NOPERSIST 9 // auth plugin found a partial or incomplet answer (Eg NTLM): just break ident loop for this request #define E2AUTH_NOIDENTPART 5 // identify returns this if it has determined both user and group (and added group id to authrec) #define E2AUTH_OK_GOT_GROUP 10 // This means plugin has sent 407 to client, so no further processing of this request #define E2AUTH_407_SENT 11 // identify returns this if it has determined both user and group (and added group name to authrec) #define E2AUTH_OK_GOT_GROUP_NAME 12 // any < 0 return code signifies error // auth_rec structure for use by storyboarding/extended logging struct auth_rec { String user_name; bool is_authed; int filter_group; String fg_name; bool is_proxy = false; bool is_transparent = false; bool is_icap = false; bool is_tlsproxy = false; String user_source; String group_source; }; // DECLARATIONS class AuthPlugin : public Plugin { public: AuthPlugin(ConfigVar &definition); virtual int init(void *args); virtual int quit(); // determine the username // return one of these codes: // OK - success, username in string // REDIRECT - redirect user to URL in string // NOMATCH - did not find the necessary info in the request (query remaining plugins) // any < 0 - error virtual int identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user, auth_rec &authrec,NaughtyFilter &cm) = 0; // determine what filter group the given username is in // queries the standard filtergroupslist // return one of these codes: // OK - success, group no. in fg // NOMATCH - did not find a group for this user (query remaining plugins) // NOUSER - did not find a group for this user (do not query remaining plugins) // any < 0 - error virtual int determineGroup(std::string &user, int &fg,StoryBoard &story,NaughtyFilter &cm); // is this a connection-based auth type, i.e. assume all subsequent requests on the pconn are from the same user? bool is_connection_based; std::deque portlist; // holds list of ports this plugin is valid for void read_ports(); bool port_matched(int &port); int default_fg = 0; int tran_default_fg = 0; int get_default(bool is_transparent); void read_def_fg(); bool client_ip_based; int story_entry = 0; String getPluginName(); virtual bool isTransparent() { return false; }; virtual bool isSSL() { return false; }; protected: ConfigVar cv; String bearer_secret; String bearer_realm; private: String pluginName; }; // class factory functions for Auth plugins typedef AuthPlugin *authcreate_t(ConfigVar &); // Return an instance of the plugin defined in the given configuration file AuthPlugin *auth_plugin_load(const char *pluginConfigPath); #endif e2guardian-5.5.8r/src/BackedStore.cpp000066400000000000000000000215121477372360500174340ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "BackedStore.hpp" #include "Logger.hpp" // IMPLEMENTATION BackedStore::BackedStore(size_t _ramsize, size_t _disksize, const char *_tempdir) : fd(-1), length(0), filename(NULL), ramsize(_ramsize), disksize(_disksize), tempdir(_tempdir), map(MAP_FAILED) { } BackedStore::~BackedStore() { if (map != MAP_FAILED) munmap(map, length); if (fd >= 0) { DEBUG_debug("BackedStore: closing & deleting temp file ", filename, " BAE2ERS!"); int rc = 0; do { rc = close(fd); } while (rc < 0 && errno == EINTR); if (rc < 0) DEBUG_debug("BackedStore: cannot close temp file fd: ", strerror(errno)); rc = unlink(filename); if (rc < 0) DEBUG_debug("BackedStore: cannot delete temp file: ", strerror(errno)); free(filename); } } bool BackedStore::append(const char *data, size_t len) { if (fd < 0) { DEBUG_debug("BackedStore: appending to RAM"); // Temp file not yet opened - try to write to RAM if (rambuf.size() + len > ramsize) { // Would exceed RAM threshold if (rambuf.size() + len > disksize) { // Would also exceed disk threshold // - give up DEBUG_debug("BackedStore: data would exceed both RAM and disk thresholds"); return false; } DEBUG_debug("BackedStore: data would exceed RAM threshold; dumping RAM to disk"); // Open temp file, dump current data in there, // leave code below this if{} to write current // data to the file as well std::string filename_str = tempdir + "/__dgbsXXXXXX"; filename = const_cast(filename_str.c_str()); DEBUG_debug("BackedStore: filename template: ", filename); // mode_t mask = umask(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); // this mask is reversed umask(0007); // only allow access to e2g user and group if ((fd = mkstemp(filename)) < 0) { std::ostringstream ss; ss << thread_id << "BackedStore could not create temp file: " << strerror(errno); free(filename); throw std::runtime_error(ss.str().c_str()); } DEBUG_debug("BackedStore: filename: ", filename); free(filename); size_t bytes_written = 0; ssize_t rc = 0; do { rc = write(fd, &(rambuf.front()) + bytes_written, rambuf.size() - bytes_written); if (rc > 0) bytes_written += rc; } while (bytes_written < rambuf.size() && (rc > 0 || errno == EINTR)); if (rc < 0 && errno != EINTR) { std::ostringstream ss; ss << thread_id << "BackedStore could not dump RAM buffer to temp file: " << strerror(errno); throw std::runtime_error(ss.str().c_str()); } length = rambuf.size(); rambuf.clear(); } else rambuf.insert(rambuf.end(), data, data + len); } if (fd >= 0) { DEBUG_debug("BackedStore: appending to disk"); // Temp file opened - try to write to disk if (map != MAP_FAILED) throw std::runtime_error("BackedStore could not append to temp file: store already finalised"); if (len + length > disksize) { DEBUG_debug("BackedStore: data would exceed disk threshold"); return false; } size_t bytes_written = 0; ssize_t rc = 0; do { rc = write(fd, data + bytes_written, len - bytes_written); if (rc > 0) bytes_written += rc; } while (bytes_written < len && (rc > 0 || errno == EINTR)); if (rc < 0 && errno != EINTR) { std::ostringstream ss; ss << thread_id << "BackedStore could not dump RAM buffer to temp file: " << strerror(errno); throw std::runtime_error(ss.str().c_str()); } length += len; } DEBUG_debug("BackedStore: finished appending"); return true; } size_t BackedStore::getLength() const { if (fd >= 0) return length; else return rambuf.size(); } void BackedStore::finalise() { if (fd < 0) // No temp file - nothing to finalise return; lseek(fd, 0, SEEK_SET); map = mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0); if (map == MAP_FAILED) { std::ostringstream ss; ss << thread_id << "BackedStore could not mmap() temp file: " << strerror(errno); throw std::runtime_error(ss.str().c_str()); } } const char *BackedStore::getData() const { if (fd < 0) { DEBUG_debug("BackedStore: returning pointer to RAM"); return &(rambuf.front()); } else { DEBUG_debug("BackedStore: returning pointer to mmap-ed file"); if (map == MAP_FAILED) throw std::runtime_error("BackedStore could not return data pointer: store not finalised"); return (const char *)map; } } std::string BackedStore::store(const char *prefix) { if (fd >= 0) { // We already have a temp file on disk // Try creating a hardlink with the new name and see what happens std::ostringstream storedname; storedname << prefix; timeval tv; // Use time of day (in microsecond resolution) to try and generate // a "random" name for the hardlink - tempnam doesn't allow arbitrary // prefixes (POSIX says up to 5 chars). gettimeofday(&tv, NULL); storedname << '-' << tv.tv_sec << tv.tv_usec << std::flush; char *name = strrchr(filename, '/'); // DEBUG_debug("BackedStore: creating hard link: " << (char)storedname ); std::string storedname_str(storedname.str()); int rc = link(name, storedname_str.c_str()); if (rc >= 0) // Success! Return new filename return storedname_str; else if (errno != EXDEV) { // Failure - but ignore EXDEV, as we can "recover" // from that by taking a different approach std::ostringstream ss; ss << thread_id << "BackedStore could not create link to existing temp file: " << strerror(errno); throw std::runtime_error(ss.str().c_str()); } } // We don't already have a temp file, // or a simple link wasn't sufficient (EXDEV) // Generate a new filename in the given directory, with the given name prefix // Include timestamp in the name for added uniqueness std::ostringstream timedprefix; timedprefix << prefix << '-' << time(NULL) << '-' << std::flush; std::string storedname_str(timedprefix.str() + "XXXXXX"); char *storedname = const_cast(storedname_str.c_str()); DEBUG_debug("BackedStore: storedname template: ", storedname); int storefd; umask(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if ((storefd = mkstemp(storedname)) < 0) { std::ostringstream ss; ss << thread_id << "BackedStore could not create stored file: " << strerror(errno); throw std::runtime_error(ss.str().c_str()); } DEBUG_debug("BackedStore: storedname: ", storedname); // Dump the RAM buffer/mmap-ed file contents to disk in the new location if (fd >= 0 && map == MAP_FAILED) throw std::runtime_error("BackedStore could not copy existing temp file: store not finalised"); size_t bytes_written = 0; ssize_t rc = 0; if (fd >= 0) { do { rc = write(storefd, (const char *)map + bytes_written, length - bytes_written); if (rc > 0) bytes_written += rc; } while (bytes_written < length && (rc > 0 || errno == EINTR)); } else { do { rc = write(storefd, &(rambuf.front()) + bytes_written, rambuf.size() - bytes_written); if (rc > 0) bytes_written += rc; } while (bytes_written < rambuf.size() && (rc > 0 || errno == EINTR)); } if (rc < 0 && errno != EINTR) { std::ostringstream ss; ss << thread_id << "BackedStore could not dump RAM buffer to temp file: " << strerror(errno); do { rc = close(storefd); } while (rc < 0 && errno == EINTR); throw std::runtime_error(ss.str().c_str()); } do { rc = close(storefd); } while (rc < 0 && errno == EINTR); return storedname; } e2guardian-5.5.8r/src/BackedStore.hpp000066400000000000000000000032571477372360500174470ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_BACKEDSTORE #define __HPP_BACKEDSTORE // Class into which data can be liberally shoved into RAM up to threshold // A, then automagically stored on disk instead up to threshold B, then // start failing (but with sensible errors). class BackedStore { public: // Constructor - pass in RAM & disk thresholds // and a directory path for temp files BackedStore(size_t _ramsize, size_t _disksize, const char *_tempdir = "/tmp"); ~BackedStore(); // Add data to the store - returns false if // disksize would be exceeded or store has // been finalised bool append(const char *data, size_t len); // Finalise the store - cannot append any more // data after this. Needed because if we are // writing to a temp file, mmap is used to access // the data, which requires a known file length. void finalise(); // Data access const char *getData() const; // Get length of buffer size_t getLength() const; // Store the contents of the buffer using the given // prefix to generate a unique filename. Return the filename. std::string store(const char *prefix); private: // Buffer & file descriptor for in-memory/on-disk storage std::vector rambuf; int fd; // Size of buffer/file size_t length; // Temp file name char *filename; // Thresholds size_t ramsize; size_t disksize; // Temp directory path std::string tempdir; // Pointer to mmapped file contents void *map; }; #endif e2guardian-5.5.8r/src/BaseSocket.cpp000066400000000000000000000277671477372360500173130ustar00rootroot00000000000000// Base socket class - inherit this to implement UNIX/INET domain sockets // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include #include #include #include #include #include #include #include #include #include "BaseSocket.hpp" #include "Logger.hpp" // GLOBALS extern bool reloadconfig; // DEFINITIONS // IMPLEMENTATION // This class contains client and server socket init and handling // code as well as functions for testing and working with the socket FDs. // constructor - override this if desired to create an actual socket at startup BaseSocket::BaseSocket() { infds[0].fd = -1; outfds[0].fd = -1; infds[0].events = POLLIN; outfds[0].events = POLLOUT; isclosing = false; timedout = false; sockerr = false; ishup = false; s_errno = 0; } // create socket from FD - must be overridden to clear the relevant address structs BaseSocket::BaseSocket(int fd) { sck = fd; infds[0].fd = fd; outfds[0].fd = fd; infds[0].events = POLLIN; outfds[0].events = POLLOUT; isclosing = false; timedout = false; sockerr = false; ishup = false; s_errno = 0; } // destructor - close socket BaseSocket::~BaseSocket() { // close fd if socket not used if (sck > -1) { ::close(sck); } } // reset - close socket & reset timeout. // call this in derived classes' reset() method, which should also clear address structs void BaseSocket::baseReset() { if (sck > -1) { ::close(sck); sck = -1; infds[0].fd = -1; outfds[0].fd = -1; } // timeout = 5000; // commented out so that timeout can be set before a connect call buffstart = 0; bufflen = 0; isclosing = false; timedout = false; sockerr = false; ishup = false; s_errno = 0; } // mark a socket as a listening server socket int BaseSocket::listen(int queue) { return ::listen(sck, queue); } // "template adaptor" for accept - basically, let G++ do the hard work of // figuring out the type of the third parameter ;) template inline int local_accept_adaptor(int (*accept_func)(int, struct sockaddr *, T), int sck, struct sockaddr *acc_adr, socklen_t *acc_adr_length) { return accept_func(sck, acc_adr, (T)acc_adr_length); } // receive an incoming connection & return FD // call this in accept methods of derived classes, which should pass in empty sockaddr & socklen_t to be filled out int BaseSocket::baseAccept(struct sockaddr *acc_adr, socklen_t *acc_adr_length) { return local_accept_adaptor(::accept, sck, acc_adr, acc_adr_length); } // return socket's FD - please use sparingly and DO NOT do manual data transfer using it int BaseSocket::getFD() { return sck; } // close the socket void BaseSocket::close() { if (sck > -1) { ::close(sck); sck = -1; infds[0].fd = -1; outfds[0].fd = -1; } buffstart = 0; bufflen = 0; isclosing = false; timedout = false; sockerr = false; ishup = false; } // set the socket-wide timeout void BaseSocket::setTimeout(int t) { timeout = t; } int BaseSocket::getErrno() { return s_errno; } // return timeout int BaseSocket::getTimeout() { return timeout; } bool BaseSocket::isOpen() { return (sck > -1); } bool BaseSocket::isClosing() { return isclosing; } bool BaseSocket::sockError() { return sockerr; } bool BaseSocket::isTimedout() { return timedout; } bool BaseSocket::isHup() { return ishup; } bool BaseSocket::isNoOpp() { return (timedout || sockerr || (ishup && !isclosing) || (sck < 0)); } bool BaseSocket::isNoRead() { return ( sockerr || (sck < 0)); } bool BaseSocket::isNoWrite() { return ( sockerr || ishup || (sck < 0)); } // blocking check to see if there is data waiting on socket bool BaseSocket::checkForInput(int timeout) { if ((bufflen - buffstart) > 0) return true; // is data left in buffer if (isNoRead()) return false; if(timeout == 0) { // no poll wanted as done by calling function return false; } int rc; s_errno = 0; errno = 0; rc = poll(infds, 1, timeout); if (rc == 0) { timedout = true; return false; //timeout } timedout = false; if (rc < 0) { s_errno = errno; sockerr = true; return false; } if (infds[0].revents & POLLHUP) { ishup = true; } if ((infds[0].revents & (POLLHUP | POLLIN))) { return true; } sockerr = true; return false; // must be POLLERR or POLLNVAL } bool BaseSocket::readyForOutput(int timeout) { if (isNoWrite()) return false; int rc; s_errno = 0; errno = 0; rc = poll(outfds, 1, timeout); if (rc == 0) { timedout = true; return false; } timedout = false; if (rc < 0) { s_errno = errno; sockerr = true; return false; } if (outfds[0].revents & POLLOUT) return true; if (outfds[0].revents & POLLHUP) ishup = true; return false; } // read a line from the socket int BaseSocket::getLine(char *buff, int size, int timeout, bool *chopped, bool *truncated) { try { // first, return what's left from the previous buffer read, if anything int i = 0; if ((bufflen - buffstart) > 0) { DEBUG_network("data already in buffer; bufflen: ", bufflen, " buffstart: ", buffstart); //work out the maximum size we want to read from our internal buffer int tocopy = size - 1; if ((bufflen - buffstart) < tocopy) tocopy = bufflen - buffstart; //copy the data to output buffer char *result = (char *) memccpy(buff, buffer + buffstart, '\n', tocopy); //if the result was < max size //if the result WAS null this indicates a full buffer copy if (result != NULL) { // indicate that a newline was chopped off, if desired if (chopped) *chopped = true; //make the last char a null *(--result) = '\0'; buffstart += (result - buff) + 1; return result - buff; } else { i += tocopy; buffstart += tocopy; } } while (i < (size - 1)) { buffstart = 0; bufflen = 0; s_errno = 0; errno = 0; if (!isNoBlock) { if(!checkForInput(timeout)) return -1; } bufflen = recv(sck, buffer, SCK_READ_BUFF_SIZE, 0); if (bufflen < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { if(isNoBlock) { if (checkForInput(timeout)) continue;// now got some input else return -1;// timed out } } s_errno = errno; return -1; } //if socket closed... if (bufflen == 0) { buff[i] = '\0'; // ...terminate string & return what read DEBUG_network("getLine terminate string !SSL: ", i ); if (truncated) *truncated = true; return i; } int tocopy = bufflen; if ((i + bufflen) > (size - 1)) tocopy = (size - 1) - i; char *result = (char *) memccpy(buff + i, buffer, '\n', tocopy); if (result != NULL) { DEBUG_network("getLine result1 !SSL: ", result, i ); // indicate that a newline was chopped off, if desired if (chopped) *chopped = true; *(--result) = '\0'; buffstart += (result - (buff + i)) + 1; DEBUG_network("getLine result2 !SSL: ", result ); return i + (result - (buff + i)); } i += tocopy; } // oh dear - buffer end reached before we found a newline buff[i] = '\0'; if (truncated) *truncated = true; if (truncated) DEBUG_network("Getline(SSL) truncated buffer end reached before we found a newline: ", buff ); return i; } catch (...) { return -1; } } // write line to socket bool BaseSocket::writeString(const char *line) { int l = strlen(line); return writeToSocket(line, l, 0, timeout); } bool BaseSocket::writeToSocket(const char *buff, int len, unsigned int flags, int timeout) { int actuallysent = 0; int sent; while (actuallysent < len) { sent = 0; s_errno = 0; errno = 0; if(isNoWrite()) return false; if((!isNoBlock) && doCheck) { if( !readyForOutput(timeout)) { s_errno = errno; return false; } } sent = send(sck, buff + actuallysent, len - actuallysent, 0); if (sent < 1) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { if(isNoBlock) if (readyForOutput(timeout)) continue; // now able to send } s_errno = errno; return false; // other end is closed } actuallysent += sent; } return true; } // write data to socket - returns no of bytes written or 0 if would block and -1 on error int BaseSocket::writeToSocketNB(const char *buff, int len, unsigned int flags) { int actuallysent = 0; int sent; while (actuallysent < len) { sent = 0; s_errno = 0; errno = 0; if(isNoWrite()) return -1; if((!isNoBlock) && doCheck) { if (!readyForOutput(0)) return 0; } sent = send(sck, buff + actuallysent, len - actuallysent, 0); if (sent < 1) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { s_errno = errno; return 0; } s_errno = errno; return -1; // other end is closed } actuallysent += sent; return sent; } return -1; //should never get here } // read a specified expected amount and return what actually read int BaseSocket::readFromSocket(char *buff, int len, unsigned int flags, int timeout, bool ret_part) { int cnt, rc; cnt = len; // first, return what's left from the previous buffer read, if anything if ((bufflen - buffstart) > 0) { DEBUG_network("data already in buffer; bufflen: ", bufflen, " buffstart: ", buffstart); int tocopy = len; if ((bufflen - buffstart) < len) tocopy = bufflen - buffstart; memcpy(buff, buffer + buffstart, tocopy); cnt -= tocopy; buffstart += tocopy; buff += tocopy; if (ret_part) return tocopy; if (cnt == 0) return len; } while (cnt > 0) { s_errno = 0; errno = 0; if((!isNoBlock) && doCheck) { if(!checkForInput(timeout)) return -1; } rc = recv(sck, buff, cnt, flags); if (rc < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { if (checkForInput(timeout)) continue;// now got some input else { timedout = true; return -1;// timed out } } s_errno = errno; return -1; } if (rc == 0) { // eof ishup = true; return len - cnt; } cnt -= rc; if(ret_part) return len - cnt; buff += rc; } return len; } short int BaseSocket::get_wait_flag(bool write_flag) { if (timedout) timedout = false; if (write_flag) { return POLLOUT; } return POLLIN; } e2guardian-5.5.8r/src/BaseSocket.hpp000066400000000000000000000063701477372360500173030ustar00rootroot00000000000000// BaseSocket class - inherit & implement to make UNIX/INET domain sockets // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_BASESOCKET #define __HPP_BASESOCKET // INCLUDES #include #include #include #include #include #include #include #include #include "openssl/ssl.h" #define SCK_READ_BUFF_SIZE 4096 class BaseSocket { public: // create socket from FD - must be overridden to clear the relevant address structs in derived classes BaseSocket(int fd); // make a socket a listening server socket int listen(int queue); // grab socket's FD, // use sparingly, and DO NOT do manual data transfer with it int getFD(); // close socket void close(); // set socket-wide timeout void setTimeout(int t); int getTimeout(); int getErrno(); bool isClosing(); bool isHup(); bool sockError(); bool isTimedout(); bool isNoOpp(); bool isNoRead(); bool isNoWrite(); bool isOpen(); bool ishup; // close & reset the connection - these must clear address structures & call baseReset/baseAccept virtual void reset() = 0; virtual BaseSocket *accept() = 0; // non-blocking check for input data bool checkForInput(int timeout = 20); // non-blocking check for writable socket //bool readyForOutput(); // blocking check bool readyForOutput(int timeout); // get a line from the socket int getLine(char *buff, int size, int timeout, bool *chopped = NULL, bool *truncated = NULL); // write string to socket bool writeString(const char *line); // write buff to socket - blocking bool writeToSocket(const char *buff, int len, unsigned int flags, int timeout); // write buff to socket - returns number of bytes written or 0 if would block or -1 on error int writeToSocketNB(const char *buff, int len, unsigned int flags); // read from socket, returning number of bytes read int readFromSocket(char *buff, int len, unsigned int flags, int timeout, bool ret_part = false); short int get_wait_flag(bool write_flag); bool timedout = false; bool isNoBlock = false; bool doCheck = true; int buffstart = 0; int bufflen = 0; char buffer[SCK_READ_BUFF_SIZE]; protected: // socket-wide timeout int timeout = 5000; int s_errno = 0; // length of address of other end of socket (e.g. size of sockaddr_in or sockaddr_un) socklen_t peer_adr_length; // socket FD int sck = -1; bool isclosing = false; //bool ishup; bool sockerr; //bool timedout; // internal buffer struct pollfd infds[1]; struct pollfd outfds[1]; // constructor - sets default values. override this if you actually wish to create a default socket. BaseSocket(); // destructor - closes socket virtual ~BaseSocket(); // performs accept(). call from derived classes' accept method int baseAccept(struct sockaddr *acc_adr, socklen_t *acc_adr_length); // closes socket & resets timeout to default - call from derived classes' reset method void baseReset(); }; #endif e2guardian-5.5.8r/src/CertificateAuthority.cpp000066400000000000000000000427371477372360500214150ustar00rootroot00000000000000#ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "openssl/bio.h" #include "openssl/ssl.h" #include "openssl/err.h" #include "CertificateAuthority.hpp" #include "OptionContainer.hpp" #include "Logger.hpp" extern OptionContainer o; void log_ssl_errors(const char *mess, const char *site) { //E2LOGGER_debugnet(mess, site); if( o.logger.log_ssl_errors ) { E2LOGGER_error("SSL Error: ", mess, " at: ", site); unsigned long e; char buff[512]; while ((e = ERR_get_error())) { ERR_error_string(e, &buff[0]); E2LOGGER_error("SSL Error: ", buff, " at: ", site); } } } CertificateAuthority::CertificateAuthority(const char *caCert, const char *caPrivKey, const char *certPrivKey, const char *certPath, time_t &caStart, time_t &caEnd) { FILE *fp; char pem[7200]; DEBUG_thttps("in new CertAuth gen_cert_start is ",caStart, " gen_cert_end is ",caEnd); //load the ca cert fp = fopen(caCert, "r"); if (fp == NULL) { E2LOGGER_error("Couldn't open ca certificate file ", caCert); exit(1); } _caCert = PEM_read_X509(fp, NULL, NULL, NULL); if (_caCert == NULL) { log_ssl_errors("Couldn't load ca certificate from ", caCert); exit(1); } fclose(fp); // Now check date validity of cert { auto rc = X509_cmp_current_time(X509_get_notBefore(_caCert)); if (rc == 1) { E2LOGGER_error("Certificate notBefore is in the future"); exit(1); } if (rc == 0) { E2LOGGER_error("Error in checking certificate notBefore"); exit(1); } rc = X509_cmp_current_time(X509_get_notAfter(_caCert)); if (rc == -1) { E2LOGGER_error("CA root Certificate has expiredi - I need a new certificate"); exit(1); } if (rc == 0) { E2LOGGER_error("Error in checking certificate notAfter"); exit(1); } notBefore = X509_get_notBefore(_caCert); notAfter = X509_get_notAfter(_caCert); int ex_day = 0; int ex_secs = 0; auto now_time = ASN1_TIME_set(nullptr,time(nullptr)); ASN1_TIME_diff(&ex_day,&ex_secs,now_time,notAfter); //Add logic here if (ex_day < 90 ) { E2LOGGER_warning("Root CA Certificate expires in ",ex_day," days - install new cert on clients NOW"); } #ifdef DEBUG_HIGH else { DEBUG_thttps("Root CA Certificate expires in ",ex_day," days "); } #endif if (!checkValidNotBefore(caStart)) { E2LOGGER_warning( "generated_cert_start is too early for CA root cert - adjusted to match CA Root cert notBefore ", caStart); } if (!checkValidNotAfter(caEnd)) { E2LOGGER_warning( "generated_cert_end is too late for CA root cert - adjusted to match CA Root cert notAfter ", caEnd); } } // open again to get raw PEM for hash fp = fopen(caCert, "r"); for (int i = 0;i < 7200;i++) { pem[i] = '\0'; } int rc = 0; rc = fread(pem,72,99,fp); if(rc < 1) { E2LOGGER_error("Unable to re-read ca certificate file"); exit(1); } fclose(fp); //load the ca priv key fp = fopen(caPrivKey, "r"); if (fp == NULL) { E2LOGGER_error("Couldn't open ca private key"); exit(1); } _caPrivKey = PEM_read_PrivateKey(fp, NULL, NULL, NULL); if (_caPrivKey == NULL) { E2LOGGER_error("Couldn't load ca private key"); exit(1); } fclose(fp); //load the priv key to use with generated certificates fp = fopen(certPrivKey, "r"); if (fp == NULL) { E2LOGGER_error("Couldn't open certificate private key"); exit(1); } _certPrivKey = PEM_read_PrivateKey(fp, NULL, NULL, NULL); if (_certPrivKey == NULL) { E2LOGGER_error("Couldn't load certificate private key"); exit(1); } fclose(fp); //TODO should check this is a writable dir _certPath = certPath; _certPathLen = sizeof(certPath); _certLinks = certPath; // temp to check if this works _ca_start = caStart; _ca_end = caEnd; // Generate hash to be added to cert server name to produce serial unique rootCA_PEM/start_date/stop_date // ASN1_TIME *not_before = X509_get_notBefore(_caCert); // ASN1_TIME *not_after = X509_get_notAfter(_caCert); // Make hash String tem = pem; String tem2((long)caStart), tem3((long)caEnd); tem += tem2; tem += tem3; cert_start_stop_hash = tem.md5(); DEBUG_config("modifing string to be hashed (rootCA.pem + cartstart + certend is: ", tem); DEBUG_config("modifing hash is: ", cert_start_stop_hash); } void CertificateAuthority::ans1_time_to_time_t(ASN1_TIME *a_time, time_t *t_time) { // Sets t_time to 0 on failure int days = 0, secs = 0; ASN1_TIME *aepoch = ASN1_TIME_set(nullptr,0); if(ASN1_TIME_diff(&days,&secs,aepoch,a_time) == 1) { if ( days > 0) { *t_time = (days * 60 * 60 * 24) + secs; return; } }; *t_time = (time_t) 0; return; } bool CertificateAuthority::checkValidNotAfter(time_t &end) { time_t notAfter_t = 0; ans1_time_to_time_t(X509_get_notAfter(_caCert), ¬After_t); if ((notAfter_t > 0) && (notAfter_t < end)) { end = notAfter_t; return false; } return true; } bool CertificateAuthority::checkValidNotBefore(time_t &start) { time_t notBefore_t = 0; ans1_time_to_time_t(X509_get_notBefore(_caCert), ¬Before_t); if ((notBefore_t > 0) && (notBefore_t > start)) { start = notBefore_t; return false; } return true; } bool CertificateAuthority::getSerial(const char *commonname, struct ca_serial *caser) { //generate hash of hostname char cnhash[EVP_MAX_MD_SIZE]; unsigned int cnhashlen; std::string sname(commonname ); sname += cert_start_stop_hash; DEBUG_debug("Generating serial no for ", commonname ); EVP_MD_CTX *mdctx; #if OPENSSL_VERSION_NUMBER < 0x10100000L #error "openssl version 1.1 or greater is required" #endif mdctx = EVP_MD_CTX_new(); const EVP_MD *md = EVP_md5(); EVP_MD_CTX_init(mdctx); bool failed = false; if (!failed && EVP_DigestInit_ex(mdctx, md, NULL) < 1) { failed = true; } if (!failed && EVP_DigestUpdate(mdctx, sname.c_str(), strlen(sname.c_str())) < 1) { failed = true; } if (!failed && EVP_DigestFinal_ex(mdctx, (unsigned char *)cnhash, &cnhashlen) < 1) { failed = true; } EVP_MD_CTX_free(mdctx); if (failed) { return false; } //convert to asn1 to use as serial BIGNUM *bn = BN_bin2bn((const unsigned char *)cnhash, cnhashlen, NULL); if (bn == NULL) { return false; } char *dbg = BN_bn2hex(bn); if (dbg != NULL) { DEBUG_debug("Serial no is ", dbg); } else { DEBUG_debug("bn2hex returned null instead of serial number"); } caser->charhex = dbg; caser->asn = BN_to_ASN1_INTEGER(bn, NULL); BN_free(bn); return true; } //write a certificate to disk being careful to avoid race conditions. //returns true if it already existed or false on error //common name (sh/c)ould be derived from the certificate but that would add to the complexity of the code bool CertificateAuthority::writeCertificate(const char *commonname, X509 *newCert, struct ca_serial *caser) { std::string path(caser->filename); std::string dirpath(caser->filepath); // make directory path int rc = mkpath(dirpath.c_str(), 0700); // only want e2g to have access to these dir if (rc != 0) { E2LOGGER_error("error creating certificate sub-directory: ", dirpath); return false; } //open file struct flock fl; fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; fl.l_pid = getpid(); DEBUG_debug("certificate file is ",path); int fd = open(path.c_str(), O_RDWR | O_CREAT, S_IWUSR | S_IRUSR); //only e2g has access if (fd < 0) { E2LOGGER_error("error opening new certificate"); exit(1); } //lock file with blocking lock and see if its bigger than 0 bytes if (fcntl(fd, F_SETLKW, &fl) < 0) { close(fd); return false; } off_t pos = lseek(fd, 0, SEEK_END); //check if someone else created the file before we did (avoid the race condition) if (pos < 0) { DEBUG_debug("error seeking to find certificate size "); fl.l_type = F_UNLCK; fcntl(fd, F_SETLK, &fl); close(fd); return false; } else if (pos > 0) { //didnt get first lock so cert should be there now DEBUG_debug("didnt get first lock pos was ", pos); fl.l_type = F_UNLCK; fcntl(fd, F_SETLK, &fl); close(fd); return true; } //looks like we got the first lock so write the certificate //write the cert to a file DEBUG_debug("got first lock "); FILE *fp = fdopen(fd, "w"); if (fp == NULL) { return false; } if (PEM_write_X509(fp, newCert) < 1) { fclose(fp); return false; } if (fflush(fp) == EOF) { fclose(fp); return false; } if (fsync(fd) < 0) { fclose(fp); return false; } //unlock the file fl.l_type = F_UNLCK; fcntl(fd, F_SETLK, &fl); fclose(fp); close(fd); return true; } //generate a certificate for a given hostname X509 *CertificateAuthority::generateCertificate(const char *commonname, struct ca_serial *cser, bool is_ip) { //create a blank cert ERR_clear_error(); X509 *newCert = X509_new(); if (newCert == NULL) { log_ssl_errors("new blank cert failed for %s", commonname); return NULL; } ERR_clear_error(); if (X509_set_version(newCert, 2) < 1) { log_ssl_errors("set_version on cert failed for %s", commonname); X509_free(newCert); return NULL; } //set a serial on the cert ERR_clear_error(); if (X509_set_serialNumber(newCert, (cser->asn)) < 1) { log_ssl_errors("set_serialNumber on cert failed for %s", commonname); X509_free(newCert); return NULL; } //set valid from and expires dates // now from fixed date - should ensure regenerated certs are same and that servers in loadbalanced arrary give same cert if (!ASN1_TIME_set(X509_get_notBefore(newCert), _ca_start)) { DEBUG_debug("get_notBefore on cert failed for ", commonname ); X509_free(newCert); return NULL; } if (!ASN1_TIME_set(X509_get_notAfter(newCert), _ca_end)) { DEBUG_debug("get_notAfter on cert failed for ", commonname); X509_free(newCert); return NULL; } //set the public key of the new cert //the private key data type also contains the pub key which is used below. ERR_clear_error(); if (X509_set_pubkey(newCert, _certPrivKey) < 1) { log_ssl_errors("set_pubkey on cert failed for %s", commonname); X509_free(newCert); return NULL; } //create a name section ERR_clear_error(); X509_NAME *name = X509_get_subject_name(newCert); if (name == NULL) { log_ssl_errors("get_subject_name on cert failed for %s", commonname); X509_free(newCert); return NULL; } //add the cn of the site we want a cert for the destination ERR_clear_error(); int rc = X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)commonname, -1, -1, 0); if (rc < 1) { log_ssl_errors("NAME_add_entry_by_txt on cert failed for %s", commonname); // X509_NAME_free(name); X509_free(newCert); return NULL; } //set the issuer name of the cert to the cn of the ca ERR_clear_error(); X509_NAME *subjectName = X509_get_subject_name(_caCert); if (subjectName == NULL) { log_ssl_errors("get_subject_name on ca_cert failed for %s", commonname); X509_free(newCert); return NULL; } ERR_clear_error(); if (X509_set_issuer_name(newCert, subjectName) < 1) { log_ssl_errors("set_issuer_name on cert failed for %s", commonname); // X509_NAME_free(subjectName); X509_free(newCert); return NULL; } String temp1; // E2LOGGER_error("common name is ",commonname); String temp2 = commonname; if(temp2.contains(":")) { bool not_first_one = false; temp2 += ":"; // E2LOGGER_error("temp2 is ",temp2); while (!temp2.empty()) { String temp3 = temp2.before(":"); temp2 = temp2.after(":"); if (not_first_one) { temp1 += ", "; } not_first_one = true; if (temp3.isIp()) temp1 += "IP:"; else temp1 += "DNS:"; temp1 += temp3; } // E2LOGGER_error("alt_name string is ",temp1); char *value = (char*) temp1.toCharArray(); if( !addExtension(newCert, NID_subject_alt_name, value)) log_ssl_errors("Error adding subjectAltName to the request", commonname); } else { if (is_ip) temp1 = "IP:"; else temp1 = "DNS:"; temp1 += temp2; // E2LOGGER_error("alt_name string is ",temp1); char *value = (char*) temp1.toCharArray(); if( !addExtension(newCert, NID_subject_alt_name, value)) log_ssl_errors("Error adding subjectAltName to the request", commonname); } //sign it using the ca ERR_clear_error(); if (!X509_sign(newCert, _caPrivKey, EVP_sha256())) { log_ssl_errors("X509_sign on cert failed for %s", commonname); X509_free(newCert); return NULL; } DEBUG_debug("certificate create ", name ); return newCert; } //sets cert to the certificate for commonname //returns true if the cert was loaded from cache / false if it was generated bool CertificateAuthority::getServerCertificate(const char *commonname, X509 **cert, struct ca_serial *caser, bool is_ip) { getSerial(commonname, caser); std::string filename(caser->charhex); // Generate directory path std::string subpath(filename.substr(0, 2) + '/' + filename.substr(2, 2) + '/' + filename.substr(4, 2) + '/'); std::string filepath(_certLinks + subpath); std::string path(_certLinks + subpath + filename.substr(6)); caser->filepath = strdup(filepath.c_str()); caser->filename = strdup(path.c_str()); DEBUG_debug("looking for cert ", path); //check to see if there is a symlink to the file // std::string path(_certLinks + filename); FILE *link = fopen(path.c_str(), "r"); if (link != NULL) { DEBUG_debug("Certificate found"); //if there was then the certificate has already been created *cert = PEM_read_X509(link, NULL, NULL, NULL); fclose(link); //dont need to check the return as this returns null if it couldnt load a cert return true; } else { DEBUG_debug("Certificate not found. Creating one"); //generate a certificate *cert = generateCertificate(commonname, caser, is_ip); return false; } } EVP_PKEY *CertificateAuthority::getServerPkey() { EVP_PKEY_up_ref(_certPrivKey); return _certPrivKey; } int CertificateAuthority::do_mkdir(const char *path, mode_t mode) { struct stat st; int status = 0; if (stat(path, &st) != 0) { /* Directory does not exist. EEXIST for race condition */ if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1; } else if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR; status = -1; } return (status); } // mkpath - ensure all sub-directories in path exist int CertificateAuthority::mkpath(const char *path, mode_t mode) { char *pp; char *sp; int status; char *copypath = strdup(path); status = 0; pp = copypath + _certPathLen; //start checking within generated cert directory while (status == 0 && (sp = strchr(pp, '/')) != 0) { if (sp != pp) { *sp = '\0'; status = do_mkdir(copypath, mode); *sp = '/'; } pp = sp + 1; } if (status == 0) status = do_mkdir(path, mode); free(copypath); return (status); } bool CertificateAuthority::free_ca_serial(struct ca_serial *cs) { if (cs->asn != NULL) ASN1_INTEGER_free(cs->asn); if (cs->charhex != NULL) OPENSSL_free(cs->charhex); // free(cs->charhex); if (cs->filepath != NULL) free(cs->filepath); if (cs->filename != NULL) free(cs->filename); return true; } CertificateAuthority::~CertificateAuthority() { if (_caCert) X509_free(_caCert); if (_caPrivKey) EVP_PKEY_free(_caPrivKey); if (_certPrivKey) EVP_PKEY_free(_certPrivKey); } bool CertificateAuthority::addExtension(X509 *cert, int nid, char *value) { X509_EXTENSION *ex = NULL; ex = X509V3_EXT_conf_nid(NULL,NULL , nid, value); int result = X509_add_ext(cert, ex, -1); X509_EXTENSION_free(ex); return (result > 0) ? true : false; } //String CertificateAuthority::ASN1TIME2String(ASN1_TIME *atime) //{ //}; e2guardian-5.5.8r/src/CertificateAuthority.hpp000066400000000000000000000031521477372360500214060ustar00rootroot00000000000000#ifndef __HPP_CERTIFICATEAUTHORITY #define __HPP_CERTIFICATEAUTHORITY #include "String.hpp" struct ca_serial { ASN1_INTEGER *asn; char *charhex; char *filepath; char *filename; }; void log_ssl_errors( const char *mess, const char *site); class CertificateAuthority { protected: EVP_PKEY *_caPrivKey; EVP_PKEY *_certPrivKey; X509 *_caCert; std::string _certPath; int _certPathLen; std::string _certLinks; time_t _ca_start; time_t _ca_end; static int do_mkdir(const char *path, mode_t mode); int mkpath(const char *path, mode_t mode); bool addExtension(X509 *cert, int nid, char *value); public: CertificateAuthority(const char *caCert, const char *caPrivKey, const char *certPrivKey, const char *certPath, time_t &caStart, time_t &caEnd); ~CertificateAuthority(); X509 *generateCertificate(const char *commonname, struct ca_serial *cser, bool is_ip = false); ASN1_TIME *notAfter, *notBefore; bool getSerial(const char *commonname, struct ca_serial *cser); bool getServerCertificate(const char *commonname, X509 **cert, struct ca_serial *cser, bool is_ip = false); bool writeCertificate(const char *hostname, X509 *newCert, struct ca_serial *cser); EVP_PKEY *getServerPkey(); bool free_ca_serial(struct ca_serial *cs); String cert_start_stop_hash; bool checkValidNotBefore(time_t &start); bool checkValidNotAfter(time_t &end); void ans1_time_to_time_t(ASN1_TIME *a_time, time_t *t_time); //String ASN1TIME2String(ASN1_TIME *atime); }; #endif //__HPP_CERTIFICATEAUTHORITY e2guardian-5.5.8r/src/ConfigReader.cpp000066400000000000000000000137131477372360500176020ustar00rootroot00000000000000// Implements the ConfigReader class // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "ConfigReader.hpp" #include #include #include "Logger.hpp" #include "String.hpp" // IMPLEMENTATION const char* char_equals = "="; // delimites option and value e.g option = value const char* delimiter = ":"; // delimits multiple values e.g. value1 : value2 : value3 class ConfigReaderImpl { public: void addOption(String line); std::deque * getOption(std::string optionName); private: std::map > options; std::deque empty; }; void ConfigReaderImpl::addOption(String line) { String name = line.before(char_equals); String value = line.after(char_equals); while (name.endsWith(" ")) { // get rid of tailing spaces before = name.chop(); } if ( name.empty() ) return; while (value.startsWith(" ")) { // get rid of heading spaces value.lop(); } if (value.startsWith("'")) { // inverted commas value.lop(); } while (value.endsWith(" ")) { // get rid of tailing spaces value.chop(); } if (value.endsWith("'")) { // inverted commas value.chop(); } std::deque entry = options[name.c_str()]; entry.push_back(value.c_str()); options[name.c_str()] = entry; } std::deque * ConfigReaderImpl::getOption(std::string optionName) { if (options.count(optionName) > 0) { return & options[optionName]; } else { return & empty; } } // PUBLIC // constructor ConfigReader::ConfigReader() : pImpl( new ConfigReaderImpl ) { } ConfigReader::~ConfigReader() { delete pImpl; } // construct & read in the given config file ConfigReader::ConfigReader(const Path &filename, const Path &list_pwd) { readConfig(filename, list_pwd ); } // method readConfig bool ConfigReader::readConfig(const Path &filename, const Path &list_pwd) { Path base_dir = filename.baseDir(); std::string now_pwd = list_pwd.fullPath(); std::string linebuffer; std::ifstream conffile(filename.fullPath(), std::ios::in); // e2guardian.conf if (!conffile.good()) { E2LOGGER_error("Error reading ", filename.fullPath()); return false; } while (!conffile.eof()) { getline(conffile, linebuffer); if (!conffile.fail() && linebuffer.length() != 0) { String temp = (char *) linebuffer.c_str(); if (linebuffer[0] == '#') { continue; // skip comment line } else if (linebuffer[0] == '.') { String filename = temp.after(".Include<").before(">"); if (filename.length() > 0) { if (!readConfig( base_dir.combine(filename), now_pwd)) { conffile.close(); return false; } } temp = temp.after(".Define LISTDIR <").before(">"); if (temp.length() > 0) { now_pwd = temp; if(now_pwd.back() != '/') now_pwd += "/"; } } else { if (temp.contains("#")) { temp = temp.before("#"); // remove trailing comment } temp.removeWhiteSpace(); // get rid of spaces at end of line if (temp.contains("__LISTDIR__")) { temp.replaceall("__LISTDIR__", now_pwd.c_str()); } // append ,listdir=now_pwd if line contains a file path - so that now_pwd can be passed // to list file handler so that it can honour __LISTDIR__ in Included listfiles if (temp.contains("path=") && !temp.contains("listdir=")) { temp += ",listdir="; temp += now_pwd; } } DEBUG_config("read:", temp); pImpl->addOption(temp); // store option } } conffile.close(); return true; } // findoptionM returns all instances of an option std::deque* ConfigReader::findoptionM(const char *option) { return pImpl->getOption(option); } // findoptionMD returns all instances of an option & allows multiple entries on a line separated by delim std::deque ConfigReader::findoptionMD(const char *option, const char *delimiter) { std::deque* values = findoptionM(option); std::deque results; String temp; for (std::deque::iterator i = values->begin(); i != values->end(); i++) { temp = (*i).c_str(); if (delimiter != nullptr) { while (temp.contains(delimiter)) { String t = temp.before(delimiter); results.push_back(t); temp = temp.after(delimiter); } } results.push_back(temp); } return results; } std::string ConfigReader::findoptionS(const char *option) { std::deque* values = findoptionM(option); if ( values && values->size() > 0 ) return values->back(); else return ""; } bool ConfigReader::findoptionB(const char *option) { std::string ok = findoptionS(option); if (ok=="on" || ok == "yes") return true; return false; } long int ConfigReader::findoptionI(const char *option) { std::string number = findoptionS(option); if ( number != "") return std::stol(number); else return 0; } // findoptionIWithDefault gets an option value, checks for minl and maxl bounds and defaults to defaultl if no value was found long int ConfigReader::findoptionIWithDefault(const char * option, long int minl, long int maxl, long int defaultl) { std::string s = findoptionS(option); if ( s == "" ) return defaultl; long int value = std::stol(s); if ((value < minl) || ((maxl > 0) && (value > maxl))) { E2LOGGER_error("Config problem; check allowed values for ", option, "( ", value , " should be >= ", minl, " <=", maxl, ")", "we are using default value:", defaultl); return defaultl; } return value; } e2guardian-5.5.8r/src/ConfigReader.hpp000066400000000000000000000036221477372360500176050ustar00rootroot00000000000000// Defines the ConfigReader class, which implements reading options from a file. // Options are declared with: // // OptionName = OptionValue // // OptionName can not contain spaces // some OptionNames may be declared multiple time // else the last declaration of the option will be used // // OptionValue may be : // - a string (may be limited with single quote ' ) // - a number (long) // - a bool (on/off) // a string OptionValue may contain the macro __LISTDIR__ which gets replaced by the current __LISTDIR__ value // // Lines starting with # are comment lines // Lines starting with . are special lines: // .Define LISTDIR : sets the value of __LISTDIR__ to dirname // .Include : includes the content of filename // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_CONFIGREADER #define __HPP_CONFIGREADER // INCLUDES #include #include #include "String.hpp" #include "Utils/Path.hpp" // DECLARATIONS class ConfigReaderImpl; class ConfigReader { public: ConfigReader(); ~ConfigReader(); // read the given file, storing option/value for later retrival // Params : list_pwd : base directory when looking for lists definitions ConfigReader(const Path &filename, const Path &list_pwd); bool readConfig(const Path &filename, const Path &list_pwd); std::deque* findoptionM(const char *option); std::deque findoptionMD(const char *option, const char *delimiter); std::string findoptionS(const char *option); bool findoptionB(const char *option); long int findoptionI(const char *option); long int findoptionIWithDefault(const char * option, long int minl, long int maxl, long int defaultl); private: ConfigReaderImpl * pImpl; String parseLine(std::string line); }; #endif e2guardian-5.5.8r/src/ConfigVar.cpp000066400000000000000000000033461477372360500171310ustar00rootroot00000000000000//Implements the ConfigVar class // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "ConfigVar.hpp" #include // IMPLEMENTATION // constructor ConfigVar::ConfigVar() { } // construct & read in the given config file ConfigVar::ConfigVar(const char *filename, const char *delimiter) { readVar(filename, delimiter); } // return the value for the named option String ConfigVar::entry(const char *reference) { return params[reference]; } // same as above, but in handy operator form String ConfigVar::operator[](const char *reference) { return params[reference]; } // read in options from the given file, splitting option/value at delimiter int ConfigVar::readVar(const char *filename, const char *delimiter) { std::ifstream input(filename); char buffer[2048]; params.clear(); if (!input) return 1; while (input.getline(buffer, sizeof(buffer))) { char *command = strtok(buffer, delimiter); if (!command) continue; char *parameter = strtok(NULL, delimiter); if (!parameter) continue; // strip delimiters while (*parameter == '"' || *parameter == '\'' || *parameter == ' ') parameter++; int offset = strlen(parameter) - 1; while (parameter[offset] == '"' || parameter[offset] == '\'') parameter[offset--] = '\0'; offset = strlen(command) - 1; while (command[offset] == ' ') command[offset--] = '\0'; params[command] = parameter; } input.close(); return 0; } e2guardian-5.5.8r/src/ConfigVar.hpp000066400000000000000000000022101477372360500171230ustar00rootroot00000000000000//Defines the ConfigVar class, which implements reading options from a file //into a map // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_CONFIGVAR #define __HPP_CONFIGVAR // INCLUDES #include #include #include "String.hpp" // DECLARATIONS class ConfigVar { public: ConfigVar(); // read the given file, splitting option/value at the given delimiter ConfigVar(const char *filename, const char *delimiter = "="); int readVar(const char *filename, const char *delimiter = "="); // return the value for the named option String entry(const char *reference); String operator[](const char *reference); private: // comparison operator (maps are sorted) - true if s1 comes before s2 struct ltstr { bool operator()(String s1, String s2) const { return strcmp(s1.toCharArray(), s2.toCharArray()) < 0; } }; // the map itself - key type, value type, key comparison operator std::map params; }; #endif e2guardian-5.5.8r/src/ConnectionHandler.cpp000066400000000000000000005315651477372360500206610ustar00rootroot00000000000000// Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif //#include "NaughtyFilter.hpp" //#include "StoryBoard.hpp" #include "ConnectionHandler.hpp" #include "DataBuffer.hpp" #include "UDSocket.hpp" //#include "Auth.hpp" #include "FDTunnel.hpp" #include "BackedStore.hpp" #include "Queue.hpp" #include "ImageContainer.hpp" #include "Logger.hpp" #include #include #include #include "CertificateAuthority.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_ORIG_IP #include #include #endif #include "openssl/ssl.h" #include "openssl/x509v3.h" #include "String.hpp" // GLOBALS extern OptionContainer o; extern std::atomic ttg; // IMPLEMENTATION ConnectionHandler::ConnectionHandler() : clienthost(NULL) { // initialise SBauth structure SBauth.filter_group = 0; SBauth.is_authed = false; SBauth.user_name = ""; } // Custom exception class for POST filtering errors class postfilter_exception : public std::runtime_error { public: postfilter_exception(const char *const &msg) : std::runtime_error(msg) {}; }; // // URL cache funcs // // check the URL cache to see if we've already flagged an address as clean bool wasClean(HTTPHeader &header, String &url, const int fg) { return false; // this function needs rewriting always return false } // add a known clean URL to the cache void addToClean(String &url, const int fg) { return; // this function needs rewriting } // // ConnectionHandler class // void ConnectionHandler::peerDiag(const char *message, Socket &peersock) { if (o.conn.logconerror) { //int peerport = peersock.getPeerSourcePort(); std::string peer_ip = peersock.getPeerIP(); int err = peersock.getErrno(); if (peersock.isTimedout()) { E2LOGGER_info(message, " Client at ", peer_ip, " Connection timedout - errno: ", err); } else if (peersock.isHup()) { E2LOGGER_info(message, " Client at ", peer_ip, " has disconnected - errno: ", err); } else if (peersock.sockError()) { E2LOGGER_info(message, " Client at ", peer_ip, " Connection socket error - errno: ", err); } else if (peersock.isNoRead()) { E2LOGGER_info(message, " cant read Client Connection at ", peer_ip, " - errno: ", err); } else if (peersock.isNoWrite()) { E2LOGGER_info(message, " cant write Client Connection at ", peer_ip, " - errno: ", err); } else if (peersock.isNoOpp()) { E2LOGGER_info(message, " Client Connection is no-op - errno: ", err); } else { E2LOGGER_info(message, " Client Connection at ", peer_ip, " problem - errno: ", err); } } } void ConnectionHandler::upstreamDiag(const char *message, Socket &proxysock) { if (o.conn.logconerror) { int err = proxysock.getErrno(); if (proxysock.isTimedout()) { E2LOGGER_info(message, " upstream timedout - errno: ", err); } else if (proxysock.isHup()) { E2LOGGER_info(message, " upstream has disconnected - errno: ", err); } else if (proxysock.sockError()) { E2LOGGER_info(message, " upstream socket error - errno: ", err); } else if (proxysock.isNoRead()) { E2LOGGER_info(message, " cant read upstream Connection - errno: ", err); } else if (proxysock.isNoWrite()) { E2LOGGER_info(message, " cant write upstream Connection - errno: ", err); } else if (proxysock.isNoOpp()) { E2LOGGER_info(message, " upstream Connection is no-op - errno: ", err); } else { E2LOGGER_info(message, " upstream Connection problem - errno: ", err); } } if (proxysock.isNoOpp()) proxysock.close(); } // perform URL encoding on a string std::string ConnectionHandler::miniURLEncode(const char *s) { std::string encoded; char *buf = new char[3]; unsigned char c; for (int i = 0; i < (signed) strlen(s); i++) { c = s[i]; // allowed characters in a url that have non special meaning if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) { encoded += c; continue; } // all other characters get encoded sprintf(buf, "%02x", c); encoded += "%"; encoded += buf; } delete[] buf; return encoded; } // create a temporary bypass URL for the banned page String ConnectionHandler::hashedURL(String *url, int filtergroup, std::string *clientip, bool infectionbypass, std::string *user) { // filter/virus bypass hashes last for a certain time only String timecode(time(NULL) + (infectionbypass ? (*ldl->fg[filtergroup]).infection_bypass_mode : (*ldl->fg[filtergroup]).bypass_mode)); // use the standard key in normal bypass mode, and the infection key in infection bypass mode String magic(infectionbypass ? ldl->fg[filtergroup]->imagic.c_str() : ldl->fg[filtergroup]->magic.c_str()); magic += clientip->c_str(); if(ldl->fg[filtergroup]->bypass_v2) magic += user->c_str(); magic += timecode; String res(infectionbypass ? "GIBYPASS=" : "GBYPASS="); if (!url->after("://").contains("/")) { String newurl((*url)); newurl += "/"; res += newurl.md5(magic.toCharArray()); } else { res += url->md5(magic.toCharArray()); } res += timecode; DEBUG_debug(" -generate Bypass hashedurl data ", clientip, " ", *url, " ", clientuser, " ", timecode, " result ", res); return res; } // create temporary bypass cookie String ConnectionHandler::hashedCookie(String *url, const char *magic, std::string *clientip, int bypasstimestamp) { String timecode(bypasstimestamp); String data(magic); data += clientip->c_str(); data += clientuser; data += timecode; DEBUG_debug(" -generate Bypass hashedCookie data ", clientip, " ", *url, " ", clientuser, " ", timecode); String res(url->md5(data.toCharArray())); res += timecode; DEBUG_debug(" -Bypass hashedCookie="+ res); return res; } // is this a temporary filter bypass URL? int ConnectionHandler::isBypassURL(String url, const char *magic, const char *clientip, std::string btype, std::string &user) { //if ((url).length() <= 45) // return 0; // Too short, can't be a bypass // check to see if this is a bypass URL of the btype type if(!(url).contains(btype.c_str())) return 0; DEBUG_debug("URL ", btype, " found checking..."); String url_left((url).before(btype.c_str())); url_left.chop(); // remove the ? or & String url_right((url).after(btype.c_str())); String url_hash(url_right.subString(0, 32)); String url_time(url_right.after(url_hash.toCharArray())); DEBUG_debug("URL: ", url_left, ", HASH: ", url_hash, ", TIME: ", url_time); String mymagic(magic); mymagic += clientip; if(ldl->fg[filtergroup]->bypass_v2) mymagic += user; mymagic += url_time; String hashed(url_left.md5(mymagic.toCharArray())); if(ldl->fg[filtergroup]->cgi_bypass_v2) { mymagic = hashed; hashed = mymagic.md5(ldl->fg[filtergroup]->cgi_magic.c_str()); } if (hashed != url_hash) { DEBUG_debug("URL ", btype, " hash mismatch"); return 0; } time_t timen = time(NULL); time_t timeu = url_time.toLong(); if (timeu < 1) { DEBUG_debug("URL ", btype, " bad time value"); return 1; // bad time value } if (timeu < timen) { // expired key DEBUG_debug("URL ", btype, " expired"); return 1; // denotes expired but there } DEBUG_debug("URL ", btype, " not expired"); return (int)timeu; } // is this a scan bypass URL? i.e. a "magic" URL for retrieving a previously scanned file bool ConnectionHandler::isScanBypassURL(String url, const char *magic, const char *clientip) { // if ((url).length() <= 45) // return false; // Too short, can't be a bypass if (!(url).contains("GSBYPASS=")) { // If this is not a bypass url return false; } DEBUG_debug("URL GSBYPASS found checking..."); String url_left((url).before("GSBYPASS=")); url_left.chop(); // remove the ? or & String url_right((url).after("GSBYPASS=")); String url_hash(url_right.subString(0, 32)); DEBUG_debug("URL: ", url_left, ", HASH: ", url_hash); // format is: // GSBYPASS=hash(ip+url+tempfilename+mime+disposition+secret) // &N=tempfilename&M=mimetype&D=dispos String tempfilename(url_right.after("&N=")); String tempfilemime(tempfilename.after("&M=")); String tempfiledis(tempfilemime.after("&D=")); tempfilemime = tempfilemime.before("&D="); tempfilename = tempfilename.before("&M="); String tohash(clientip + url_left + tempfilename + tempfilemime + tempfiledis + magic); String hashed(tohash.md5()); if(ldl->fg[filtergroup]->cgi_bypass_v2) { tohash = hashed; hashed = tohash.md5(ldl->fg[filtergroup]->cgi_magic.c_str()); } DEBUG_debug("checking hash: ", clientip, " ", url_left, " ", tempfilename, " ", tempfilemime, " ", tempfiledis, " ", magic, " ", hashed); if (hashed == url_hash) { return true; } DEBUG_debug("URL GSBYPASS HASH mismatch"); return false; } // send a file to the client - used during bypass of blocked downloads off_t ConnectionHandler::sendFile(Socket *peerconn, NaughtyFilter &cm, String &url, bool is_icap, ICAPHeader *icap_head) { String filedis = cm.tempfiledis; int fd = open(cm.tempfilename.toCharArray(), O_RDONLY); if (fd < 0) { // file access error E2LOGGER_error("Error reading file to send: ", cm.tempfilename); String fnf(o.language_list.getTranslation(1230)); String head("HTTP/1.1 404 " + fnf + "\r\nContent-Type: text/html\r\n\r\n"); String body("" + fnf + "

" + fnf + "

\r\n"); if (is_icap) { icap_head->out_res_header = head; icap_head->out_res_body = body; icap_head->out_res_hdr_flag = true; icap_head->out_res_body_flag = true; icap_head->respond(*peerconn); } else { peerconn->writeString(head.toCharArray()); peerconn->writeString(body.toCharArray()); } return 0; } off_t filesize = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); String head("HTTP/1.1 200 OK\r\nContent-Type: " + cm.tempfilemime + "\r\nContent-Length: " + String(filesize)); if (filedis.length() == 0) { filedis = url.before("?"); while (filedis.contains("/")) filedis = filedis.after("/"); } head += "\r\nContent-disposition: attachment; filename=" + filedis; head += "\r\n\r\n"; if (is_icap) { icap_head->out_res_header = head; icap_head->out_res_hdr_flag = true; icap_head->out_res_body_flag = true; icap_head->respond(*peerconn); } else { if (!peerconn->writeString(head.toCharArray())) { close(fd); return 0; } } // perform the actual sending off_t sent = 0; int rc; //char *buffer = new char[250000]; char *buffer = new char[64000]; while (sent < filesize) { rc = read(fd, buffer, 64000); DEBUG_debug(" -reading send file rc:", String(rc)); if (rc < 0) { E2LOGGER_error(" -error reading send file so aborting"); delete[] buffer; // throw std::exception/(); //cleanThrow("error reading send file", *peerconn); return 0; } if (rc == 0) { E2LOGGER_error(" -got zero bytes reading send file"); break; // should never happen } if (is_icap) { if (!peerconn->writeChunk(buffer, rc, 100000)) { delete[] buffer; peerDiag("Error sending file to client", *peerconn); return 0; } } else { // as it's cached to disk the buffer must be reasonably big if (!peerconn->writeToSocket(buffer, rc, 0, 100000)) { delete[] buffer; peerDiag("Error sending file to client", *peerconn); return 0; // throw std::exception(); } } sent += rc; DEBUG_debug(" -total sent from temp: ", String(sent)); } if (is_icap) { String n; peerconn->writeChunkTrailer(n); } delete[] buffer; close(fd); return sent; } int ConnectionHandler::connectUpstream(Socket &sock, NaughtyFilter &cm, int port = 0) // connects to to proxy or directly { if (port == 0) port = cm.request_header->port; String sport(port); int lerr_mess = 0; int retry = -1; bool may_be_loop = false; for (auto it = o.net.check_ports.begin(); it != o.net.check_ports.end(); it++) { if (*it == sport) { may_be_loop = true; break; } } DEBUG_debug("May_be_loop = ", may_be_loop, " ", " port ", port); sock.setTimeout(o.net.connect_timeout); while (++retry < o.net.connect_retries) { lerr_mess = 0; if (retry > 0) { if (o.conn.logconerror) E2LOGGER_info("retry ", retry, " to connect to ", cm.urldomain); if (!sock.isTimedout()) usleep(1000); // don't hammer upstream } cm.upfailure = false; if (cm.isdirect) { String des_ip; if (cm.isiphost) des_ip = cm.urldomain; if(o.conn.use_original_ip_port && cm.got_orig_ip && (cm.connect_site == cm.urldomain)) des_ip = cm.orig_ip; if(des_ip.length() > 0) { if (may_be_loop) { // check check_ip list bool do_break = false; if (o.net.check_ip.size() > 0) { for (auto it = o.net.check_ip.begin(); it != o.net.check_ip.end(); it++) { if (*it == des_ip) { do_break = true; lerr_mess = 212; break; } } } if (do_break) break; may_be_loop = false; } DEBUG_debug("Connecting to IP ", des_ip, " port ", String(port)); int rc = sock.connect(des_ip, port); if (rc < 0) { lerr_mess = 203; continue; } return rc; } else { //dns lookup struct addrinfo hints, *infoptr; memset(&hints, 0, sizeof(addrinfo)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; int rc = getaddrinfo(cm.connect_site.toCharArray(), NULL, &hints, &infoptr); if (rc) // problem { DEBUG_debug("connectUpstream: getaddrinfo returned ", String(rc), " for ", cm.connect_site, " ", gai_strerror(rc) ); bool rt = false; switch (rc) { case EAI_NONAME: lerr_mess = 207; break; #ifdef EAI_NODATA case EAI_NODATA: lerr_mess = 208; break; #endif case EAI_AGAIN: lerr_mess = 209; rt = true; break; case EAI_FAIL: lerr_mess = 210; break; default: lerr_mess = 210; //TODO this should have it's own message?? break; } sock.close(); if (rt) continue; else break; } char t[256]; struct addrinfo *p; for (p = infoptr; p != NULL; p = p->ai_next) { getnameinfo(p->ai_addr, p->ai_addrlen, t, sizeof(t), NULL, 0, NI_NUMERICHOST); if (may_be_loop) { // check check_ip list bool do_break = false; if (o.net.check_ip.size() > 0) { for (auto it = o.net.check_ip.begin(); it != o.net.check_ip.end(); it++) { if (*it == t) { do_break = true; lerr_mess = 212; break; } } } if (do_break) break; may_be_loop = false; } DEBUG_debug("Connecting to IP ", t, " port ", String(port)); int rc = sock.connect(t, port); if (rc == 0) { freeaddrinfo(infoptr); DEBUG_debug("Got connection upfailure is ", String(cm.upfailure) ); return 0; } } freeaddrinfo(infoptr); if (may_be_loop) break; lerr_mess = 203; continue; } } else { //is via proxy sock.setTimeout(o.net.proxy_timeout); int rc = sock.connect(o.net.proxy_ip, o.net.proxy_port); if (rc < 0) { if (sock.isTimedout()) lerr_mess = 201; else lerr_mess = 202; continue; } return rc; } } // only get here if failed cm.upfailure = true; cm.message_no = lerr_mess; cm.whatIsNaughty = o.language_list.getTranslation(lerr_mess); cm.whatIsNaughtyLog = cm.whatIsNaughty; cm.whatIsNaughtyCategories = ""; cm.whatIsNaughtyDisplayCategories = ""; cm.isItNaughty = true; cm.blocktype = 3; cm.isexception = false; cm.isbypass = false; return -1; } // pass data between proxy and client, filtering as we go. // this is the only public function of ConnectionHandler int ConnectionHandler::handlePeer(Socket &peerconn, String &ip, stat_rec *&dystat, unsigned int lc_type) { persistent_authed = false; is_real_user = false; int rc = 0; Socket proxysock; // also used for direct connection switch (lc_type) { case CT_PROXY: SBauth.is_proxy = true; rc = handleConnection(peerconn, ip, false, proxysock, dystat); break; case CT_THTTPS: SBauth.is_transparent = true; rc = handleTHTTPSConnection(peerconn, ip, proxysock, dystat); break; case CT_PROXY_TLS: SBauth.is_proxy = true; rc = handleProxyTLSConnection(peerconn, ip, proxysock, dystat); break; case CT_ICAP: SBauth.is_icap = true; rc = handleICAPConnection(peerconn, ip, proxysock, dystat); break; } //if ( ldl->reload_id != load_id) // rc = -1; return rc; } int ConnectionHandler::handleConnection(Socket &peerconn, String &ip, bool ismitm, Socket &proxysock, stat_rec *&dystat) { struct timeval thestart; gettimeofday(&thestart, NULL); //peerconn.setTimeout(o.proxy_timeout); peerconn.setTimeout(o.net.pcon_timeout); DEBUG_proxy("down_stream thread ",peerconn.down_thread_id); // ldl = o.currentLists(); HTTPHeader docheader(__HEADER_RESPONSE); // to hold the returned page header from proxy HTTPHeader header(__HEADER_REQUEST); // to hold the incoming client request headeri(ldl) // set a timeout as we don't want blocking 4 eva // this also sets how long a peerconn will wait for other requests header.setTimeout(o.net.pcon_timeout); docheader.setTimeout(o.net.exchange_timeout); //int bypasstimestamp = 0; // Content scanning plugins to use for request (POST) & response data std::deque requestscanners; std::deque responsescanners; std::string clientip(ip.toCharArray()); // hold the clients ip header.setClientIP(ip); if (clienthost) delete clienthost; clienthost = NULL; // and the hostname, if available matchedip = false; // clear list of parameters extracted from URL urlparams.clear(); // clear out info about POST data postparts.clear(); // debug stuff surprisingly enough DEBUG_proxy(" -got peer connection from ", clientip ); try { //int rc; #ifdef DEBUG_HIGH int pcount = 0; #else #ifdef DEBUG_LOW int pcount = 0; #endif #endif // assume all requests over the one persistent connection are from // the same user. means we only need to query the auth plugin until // we get credentials, then assume they are valid for all reqs. on // the persistent connection. std::string oldclientuser; std::string room; //int oldfg = 0; bool authed = false; //bool isbanneduser = false; //bool isscanbypass = false; //bool isbypass = false; //bool isvirusbypass = false; //int bypasstimestamp = 0; //bool iscookiebypass = false; AuthPlugin *auth_plugin = NULL; // RFC states that connections are persistent bool persistOutgoing = true; bool persistPeer = true; bool persistProxy = true; String last_domain_port; bool last_isdirect = false; bool firsttime = true; if (!header.in(&peerconn, true)) { // get header from client, allowing persistency if (o.conn.logconerror) { if (peerconn.getFD() > -1) { int err = peerconn.getErrno(); //int pport = peerconn.getPeerSourcePort(); std::string peerIP = peerconn.getPeerIP(); E2LOGGER_info("No header recd from client at ", peerIP, " - errno: ", err); } else { E2LOGGER_info("Client connection closed early - no request header received"); } } firsttime = false; persistPeer = false; } else { ++dystat->reqs; } // // End of set-up section // // Start of main loop // // maintain a persistent connection while ((firsttime || persistPeer) && !ttg) // while ((firsttime || persistPeer) && !reloadconfig) { DEBUG_proxy(" firsttime =", firsttime, " ismitm =", ismitm, " clientuser =", clientuser, " group = ", filtergroup); ldl = o.currentLists(); NaughtyFilter checkme(header, docheader, SBauth); checkme.listen_port = peerconn.getPort(); DataBuffer docbody; docbody.setTimeout(o.net.exchange_timeout); FDTunnel fdt; if (firsttime) { // reset flags & objects next time round the loop firsttime = false; gettimeofday(&thestart, NULL); checkme.thestart = thestart; // quick trick for the very first connection :-) if (!ismitm) persistProxy = false; } else { // another round... DEBUG_proxy(" -persisting (count ", ++pcount, ") - ", clientip); header.reset(); if (!header.in(&peerconn, true)) { DEBUG_proxy(" -Persistent connection closed"); break; } ++dystat->reqs; // we will actually need to do *lots* of resetting of flags etc. here for pconns to work gettimeofday(&thestart, NULL); checkme.thestart = thestart; checkme.bypasstimestamp = 0; authed = false; requestscanners.clear(); responsescanners.clear(); matchedip = false; urlparams.clear(); postparts.clear(); checkme.mimetype = "-"; room = ""; // CHECK THIS - surely room is persistant????? // reset docheader & docbody // headers *should* take care of themselves on the next in() // actually not entirely true for docheader - we may read // certain properties of it (in denyAccess) before we've // actually performed the next in(), so make sure we do a full // reset now. docheader.reset(); docbody.reset(); peerconn.resetChunk(); proxysock.resetChunk(); } // // do this normalisation etc just the once at the start. checkme.setURL(ismitm); if(o.log.log_requests) { //if (e2logger.isEnabled(LoggerSource::requestlog)) { std::string fnt; if(ismitm) fnt = "MITM"; else if(header.isProxyRequest) { fnt = "PROXY"; } else fnt = "TRANS"; doRQLog(clientuser, clientip, checkme, fnt); } if(!header.isProxyRequest) // is transparent http proxy get_original_ip_port(peerconn,checkme); //If proxy connection is not persistent..// do this later after checking if direct or via proxy DEBUG_proxy("Start URL ", checkme.url, "is_ssl=", checkme.is_ssl, "ismitm=", ismitm); // checks for bad URLs to prevent security holes/domain obfuscation. if (header.malformedURL(checkme.url)) { // The requested URL is malformed. writeback_error(checkme, peerconn, 200, 0, "400 Bad Request"); proxysock.close(); // close connection to proxy break; } // TODO this needs moving is proxy operation is still to be tested if (checkme.urldomain == o.conn.internal_test_url) { peerconn.writeString( "HTTP/1.1 200 \nContent-Type: text/html\n\ne2guardian internal test

e2guardian internal test OK

"); peerconn.writeString("\n"); proxysock.close(); // close connection to proxy break; } // total block list checking now done in pre-auth story // don't let the client connection persist if the client doesn't want it to. persistOutgoing = header.isPersistent(); // now check if in input proxy mode and direct upstream if upstream needs closing if (persistProxy && last_isdirect && ((last_domain_port != checkme.urldomainport)|| !o.net.no_proxy)) { proxysock.close(); persistProxy = false; } last_domain_port = checkme.urldomainport; last_isdirect = checkme.isdirect; // // // Now check if machine is banned and room-based checking // // // is this user banned? //isbanneduser = false; #ifdef NOTDEF if(!ismitm) { // pretend to use xforwarded for clientip = "192.6.6.6"; ip = clientip; } #endif if (!ismitm && o.use_xforwardedfor) { bool use_xforwardedfor; if (o.net.xforwardedfor_filter_ip.size() > 0) { use_xforwardedfor = false; for (unsigned int i = 0; i < o.net.xforwardedfor_filter_ip.size(); i++) { if (strcmp(clientip.c_str(), o.net.xforwardedfor_filter_ip[i].c_str()) == 0) { use_xforwardedfor = true; break; } } } else { use_xforwardedfor = true; } if (use_xforwardedfor) { std::string xforwardip(header.getXForwardedForIP()); if (xforwardip.length() > 6) { clientip = xforwardip; ip = clientip; header.setClientIP(ip); } DEBUG_proxy(" -using x-forwardedfor:", clientip); } } checkme.clientip = clientip; // Look up reverse DNS name of client if needed if (o.conn.reverse_client_ip_lookups) { getClientFromIP(clientip.c_str(),checkme.clienthost); // std::unique_ptr > hostnames; // hostnames.reset(ipToHostname(clientip.c_str())); // checkme.clienthost = std::string(hostnames->front().toCharArray()); } //CALL SB pre-authcheck DEBUG_trace("Run StoryA pre-authcheck"); ldl->StoryA.runFunctEntry(ENT_STORYA_PRE_AUTH, checkme); DEBUG_proxy("After StoryA pre-authcheck", " isexception ", String(checkme.isexception), " isBlocked ", String(checkme.isBlocked), " message_no ", String(checkme.message_no)); checkme.isItNaughty = checkme.isBlocked; bool isbannedip = checkme.isBlocked; bool part_banned; if (isbannedip) { // matchedip = clienthost == NULL; DEBUG_proxy("IP is banned!"); } else { if (ldl->inRoom(clientip, room, &(checkme.clienthost), &isbannedip, &part_banned, &checkme.isexception, checkme.urld)) { DEBUG_proxy(" isbannedip = ", String(isbannedip), " ispart_banned = ", String(part_banned), " isexception = ", String(checkme.isexception)); if (isbannedip) { // matchedip = clienthost == NULL; checkme.isBlocked = checkme.isItNaughty = true; } if (checkme.isexception) { // do reason codes etc checkme.whatIsNaughty = o.language_list.getTranslation(630); checkme.whatIsNaughty.append(room); checkme.whatIsNaughty.append(o.language_list.getTranslation(631)); checkme.whatIsNaughtyLog = checkme.whatIsNaughty; checkme.message_no = 632; } } } // // // Start of Authentication Checks // // // don't have credentials for this connection yet? get some! DEBUG_trace("start Authentication"); overide_persist = false; if (!persistent_authed) { bool only_ip_auth; if (header.isProxyRequest) { filtergroup = o.filter.default_fg; SBauth.is_proxy = true; if(!peerconn.down_thread_id.empty()) SBauth.is_tlsproxy = true; only_ip_auth = false; } else { filtergroup = o.filter.default_trans_fg; SBauth.is_transparent = true; only_ip_auth = true; } SBauth.group_source = "def"; DEBUG_proxy("isProxyRequest is ", String(header.isProxyRequest), " only_ip_auth is ", String(only_ip_auth) ); if (!doAuth(checkme.auth_result, authed, filtergroup, auth_plugin, peerconn, proxysock, header, checkme, only_ip_auth, checkme.isconnect)) { if (checkme.auth_result == E2AUTH_407_SENT) { continue; } if ((checkme.auth_result == E2AUTH_REDIRECT) && checkme.isconnect && ldl->fg[filtergroup]->ssl_mitm) { checkme.gomitm = true; checkme.isdone = true; } else { break; } } } else { DEBUG_proxy(" -Already got credentials for this connection - not querying auth plugins"); authed = true; } checkme.filtergroup = filtergroup; docbody.set_current_config(ldl->fg[filtergroup]); DEBUG_proxy(" -username: " + clientuser + " -filtergroup: " + String(filtergroup)); // // End of Authentication Checking // // // Set if candidate for MITM // (Exceptions will not go MITM) DEBUG_trace("ismitmcandidate"); checkme.ismitmcandidate = checkme.isconnect && (!checkme.nomitm) && ldl->fg[filtergroup]->ssl_mitm && (header.port == 443); if (checkme.ismitmcandidate ) { if(!ldl->fg[filtergroup]->automitm) checkme.automitm = false; } else { checkme.nomitm = true; checkme.automitm = false; } if (checkme.urldomain == o.conn.internal_status_url) { peerconn.writeString( "HTTP/1.1 200 \nContent-Type: text/html\n\ne2guardian internal status

e2guardian internal status OK

"); String temp = "User: "; temp += clientuser; temp += "
"; temp += "IP: "; temp += ip; temp += "
"; temp += "Filtergroup: "; temp += ldl->fg[filtergroup]->name; temp += "
"; temp += "Flags: "; temp += checkme.getFlags(); temp += "
"; temp += "e2g version: "; temp += PACKAGE_VERSION; temp += "
"; temp += "Server: "; temp += o.net.server_name; temp += "
"; peerconn.writeString(temp); peerconn.writeString("\n"); proxysock.close(); // close connection to proxy break; } // // Start of by pass // DEBUG_trace("checkByPass"); if (!checkme.isdone && checkByPass(checkme, ldl, header, proxysock, peerconn, clientip) && sendScanFile(peerconn, checkme)) { persistProxy = false; break; } // // End by pass // // virus checking candidate? // checkme.noviruscheck defaults to true if (!(checkme.isdone || checkme.isconnect || checkme.ishead) // can't scan connect or head or not yet authed && !(checkme.isBlocked) // or already blocked && authed // and is authed && (o.plugins.csplugins.size() > 0) // and we have scan plugins && !ldl->fg[filtergroup]->disable_content_scan // and is not disabled && !(checkme.isexception && !ldl->fg[filtergroup]->content_scan_exceptions) // and not exception unless scan exceptions enabled ) checkme.noviruscheck = false; // note this may be reset by Storyboard to enable exceptions // // Start of Storyboard checking // // if (!(checkme.isBlocked || checkme.isbypass)) if (!(checkme.isBlocked || checkme.isdone) && authed) { // Main checking is now done in Storyboard function(s) // String funct = "checkrequest"; // ldl->fg[filtergroup]->StoryB.runFunct(funct, checkme); DEBUG_trace("Check StoryB checkrequest"); ldl->fg[filtergroup]->StoryB.runFunctEntry(ENT_STORYB_PROXY_REQUEST, checkme); DEBUG_proxy("After StoryB checkrequest", " isexception ", String(checkme.isexception ), " isblocked ", String(checkme.isBlocked ), " gomitm ", String(checkme.gomitm), " mess_no ", String(checkme.message_no) ); if (ldl->fg[filtergroup]->reporting_level != -1){ checkme.isItNaughty = checkme.isBlocked; } else { checkme.isItNaughty = false; checkme.isBlocked = false; } } if (checkme.isdirect) { header.setDirect(); last_isdirect = true; if(!o.net.no_proxy) { // we are in mixed mode proxy and direct if(persistProxy) { // if upstream socket is open close it proxysock.close(); persistProxy = false; } } } if (checkme.isbypass && !(checkme.iscookiebypass || checkme.isvirusbypass)) { DEBUG_proxy("Setting GBYPASS cookie; bypasstimestamp = ", checkme.bypasstimestamp); String ud(checkme.urldomain); if (ud.startsWith("www.")) { ud = ud.after("www."); } // redirect user to URL with GBYPASS parameter no longer appended String outhead = "HTTP/1.1 302 Redirect\r\n"; outhead += "Set-Cookie: GBYPASS="; outhead += header.hashedCookie(&ud, ldl->fg[filtergroup]->cookie_magic.c_str(), &clientip, checkme.bypasstimestamp, clientuser).toCharArray(); // outhead += hashedCookie(&ud, ldl->fg[filtergroup]->cookie_magic.c_str(), &clientip, // checkme.bypasstimestamp).toCharArray(); outhead += "; path=/; domain=."; outhead += ud; outhead += "\r\n"; outhead += "Location: "; outhead += checkme.logurl.before("GBYPASS="); outhead.chop(); outhead += "\r\n"; outhead += "\r\n"; peerconn.writeString(outhead.c_str()); return 0; } //check for redirect // URL regexp search and edirect if (checkme.urlredirect) { checkme.url = header.redirecturl(); proxysock.close(); String writestring("HTTP/1.1 302 Redirect\nLocation: "); writestring += checkme.url; writestring += "\n\n"; peerconn.writeString(writestring.toCharArray()); break; } //if is a search - content check search terms if (!checkme.isdone) { if (checkme.isGrey && checkme.isSearch) check_search_terms(checkme); } // will set isItNaughty if needed // TODO V5 call POST scanning code New NaughtyFilter function???? // don't run willScanRequest if content scanning is disabled, or on exceptions if contentscanexceptions is off, // or on SSL (CONNECT) requests, or on HEAD requests, or if in AV bypass mode //if not naughty now send upstream and get response if (checkme.isItNaughty) { if (checkme.isconnect && checkme.automitm) { checkme.gomitm = true; // so that we can deliver a status message to user over half MITM if (checkme.isdirect) { // send connection estabilished to client std::string msg = "HTTP/1.1 200 Connection established\r\n\r\n"; if (!peerconn.writeString(msg.c_str())) { peerDiag("Unable to send 200 connection established to client ", peerconn); break; } } } else { checkme.gomitm = false; // if not automitm } } else { if (!persistProxy) // open upstream connection { int out_port = header.port; if (o.conn.use_original_ip_port && checkme.got_orig_ip && !header.isProxyRequest) out_port = checkme.orig_port; if (connectUpstream(proxysock, checkme, out_port) < 0) { if (checkme.isconnect && checkme.automitm && checkme.upfailure) { checkme.gomitm = true; // so that we can deliver a status message to user over half MITM // to give error - depending on answer // timeout -etc } else { // checkme.gomitm = false; // if not automitm } } } if (!checkme.upfailure) { if (!proxysock.readyForOutput(o.net.proxy_timeout)) { upstreamDiag("Unable to write upstream", proxysock); break; } } if (checkme.isdirect && checkme.isconnect) { // send connection estabilished to client std::string msg = "HTTP/1.1 200 Connection established\r\n\r\n"; if (!peerconn.writeString(msg.c_str())) { peerDiag("Unable to send 200 connection established to client ", peerconn); break; } } else if (!checkme.upfailure) // in all other cases send header upstream and get response { // this is the case when there is no request body or rather when there is no expect_100 // if(!(header.chunked || header.contentLength() > 0)) if(!header.expects_100) // if has body is sent as part of header.out { if (!(header.out(&peerconn, &proxysock, __E2HEADER_SENDALL, true) // send proxy the request && (docheader.in_handle_100(&proxysock, persistOutgoing, false)))) { if (proxysock.isTimedout()) { // writeback_error(checkme, peerconn, 203, 204, "408 Request Time-out"); writeback_error(checkme, peerconn, 0, 0, "408 Request Time-out"); } else { if (!ismitm) { writeback_error(checkme, peerconn, 0, 0, "408 Request Time-out"); //writeback_error(checkme, peerconn, 205, 206, "502 Gateway Error"); } } persistPeer = false; persistProxy = false; break; } } else { // has expects_100 if (docheader.in_handle_100(&proxysock,persistOutgoing,header.expects_100)&& docheader.returncode == 100) { docheader.out(&proxysock, &peerconn, __E2HEADER_SENDALL, true); FDTunnel tpost; tpost.tunnel(peerconn, proxysock, false, header.contentLength(), false, header.chunked); if (docheader.in_handle_100(&proxysock, persistOutgoing, false) && docheader.returncode == 100) { // get response header DEBUG_proxy( "somethig whent wroung with expoect 100 logic"); // should be now proper return header!!! } } } persistProxy = docheader.isPersistent(); persistPeer = persistOutgoing && docheader.wasPersistent(); DEBUG_proxy(" -persistPeer: ", String(persistPeer)); //check response code if ((!checkme.isItNaughty) && (!checkme.upfailure)) { int rcode = docheader.returnCode(); if (rcode == 407) { // proxy auth required // tunnel thru - may be content checkme.tunnel_rest = true; checkme.tunnel_2way = false; // treat connect like normal get checkme.isconnect = false; checkme.isexception = true; } if (checkme.isconnect) { if (rcode == 200) { persistProxy = false; persistPeer = false; } else { // some sort of problem checkme.ismitmcandidate = false; // only applies to connect checkme.tunnel_rest = true; checkme.tunnel_2way = false; } } if (docheader.contentLength() == 0) // no content checkme.tunnel_rest = true; } } if (checkme.isconnect && checkme.isGrey) { // allow legacy SSL behavour when mitm not enabled checkme.tunnel_2way = true; checkme.tunnel_rest = false; } } if ((checkme.isexception || checkme.logcategory) && !checkme.upfailure) { if (checkme.isconnect) { checkme.tunnel_2way = true; checkme.tunnel_rest = false; persistPeer = false; persistProxy = false; } else { if (!checkme.noviruscheck && !ldl->fg[filtergroup]->content_scan_exceptions) checkme.noviruscheck = true; if (checkme.noviruscheck) { checkme.tunnel_2way = false; checkme.tunnel_rest = true; } } } //if ismitm - GO MITM // ssl_grey is covered in storyboard if (!checkme.tunnel_rest && checkme.isconnect && checkme.gomitm) { DEBUG_proxy("Going MITM ...."); if(!ldl->fg[filtergroup]->mitm_check_cert) checkme.nocheckcert = true; goMITM(checkme, proxysock, peerconn, persistProxy, authed, persistent_authed, ip, dystat, clientip,checkme.isdirect); persistPeer = false; persistProxy = false; //if (!checkme.isItNaughty) // surely we should just break here whatever? - No we need to log error break; } //CALL SB checkresponse if ((!checkme.isItNaughty) && (!checkme.upfailure) && (!checkme.isconnect) && (!checkme.logcategory) && !checkme.tunnel_rest) { ldl->fg[filtergroup]->StoryB.runFunctEntry(ENT_STORYB_PROXY_RESPONSE, checkme); DEBUG_proxy("After StoryB checkresponse ", " IsException ", String(checkme.isexception), " IsBlocked ", String(checkme.isBlocked), " mess_no ", String(checkme.message_no) ); if (ldl->fg[filtergroup]->reporting_level != -1){ checkme.isItNaughty = checkme.isBlocked; } else { checkme.isItNaughty = false; checkme.isBlocked = false; checkme.isGrey = true; } if (checkme.ishead || (docheader.contentLength() == 0 && !docheader.chunked)) checkme.tunnel_rest = true; if (checkme.isexception && !ldl->fg[filtergroup]->content_scan_exceptions) { checkme.tunnel_rest = true; checkme.noviruscheck = true; } if (!checkme.noviruscheck) { for (std::deque::iterator i = o.plugins.csplugins_begin; i != o.plugins.csplugins_end; ++i) { int csrc = ((CSPlugin *) (*i))->willScanRequest(header.getUrl(), clientuser.c_str(), ldl->fg[filtergroup], clientip.c_str(), false, false, checkme.isexception, checkme.isbypass); if (csrc > 0) responsescanners.push_back((CSPlugin *) (*i)); else if (csrc < 0) E2LOGGER_error("willScanRequest returned error: ", csrc); } DEBUG_proxy(" -Content scanners interested in response data: ", responsescanners.size()); } //- if grey content check // can't do content filtering on HEAD or redirections (no content) // actually, redirections CAN have content if ((checkme.isGrey || !checkme.noviruscheck) && !checkme.tunnel_rest) { check_content(checkme, docbody, proxysock, peerconn, responsescanners); } } //send response header to client(yments if ((!checkme.isItNaughty) && (!checkme.upfailure) && !(checkme.isconnect && checkme.isdirect)) { if (!docheader.out(NULL, &peerconn, __E2HEADER_SENDALL, false)) { peerDiag("Unable to send return header to client", peerconn); break; } if ((!checkme.isItNaughty) && checkme.waschecked) { if (docbody.dontsendbody && docbody.tempfilefd > -1) { //#ifdef NOTDEF // must have been a 'fancy' // download manager so we need to send a special link which // will get recognised and cause DG to send the temp file to // the browser. The link will be the original URL with some // magic appended to it like the bypass system. // format is: // GSBYPASS=hash(ip+url+tempfilename+mime+disposition+secret) // &N=tempfilename&M=mimetype&D=dispos String ip(clientip); String tempfilename(docbody.tempfilepath.after("/tf")); String tempfilemime(docheader.getContentType()); String tempfiledis(miniURLEncode(docheader.disposition().toCharArray()).c_str()); String secret(ldl->fg[filtergroup]->magic.c_str()); String magic(ip + checkme.url + tempfilename + tempfilemime + tempfiledis + secret); String hashed(magic.md5()); DEBUG_proxy(" -sending magic link to client: ", ip, " ", checkme.url, " ", tempfilename, " ", tempfilemime, " ", tempfiledis, " ", secret, " ", hashed ); String sendurl(checkme.url); if (!sendurl.after("://").contains("/")) { sendurl += "/"; } if (sendurl.contains("?")) { sendurl += "&GSBYPASS="; } else { sendurl += "?GSBYPASS="; } sendurl += hashed; sendurl += "&N="; sendurl += tempfilename; sendurl += "&M="; sendurl += tempfilemime; sendurl += "&D="; sendurl += tempfiledis; docbody.dm_plugin->sendLink(peerconn, sendurl, checkme.url); // can't persist after this - DM plugins don't generally send a Content-Length. //TODO: need to change connection: close if there is plugin involved. persistOutgoing = false; //#endif } else { if (!docheader.header_all_sent) { docheader.out(NULL,&peerconn,__E2HEADER_SENDALL,false); } if (!docbody.out(&peerconn)) { DEBUG_network(" docbody.out returned error"); checkme.pausedtoobig = false; } if (checkme.pausedtoobig) checkme.tunnel_rest = true; } } } //if not grey tunnel response if (!checkme.isItNaughty) { if (checkme.tunnel_rest) { bool chunked = docheader.transferEncoding().contains("chunked"); DEBUG_proxy(" -Tunnelling to client"); DEBUG_proxy(" - Content-Length:", docheader.contentLength(), " cm.docsize:", checkme.docsize); if (!fdt.tunnel(proxysock, peerconn, checkme.isconnect, docheader.contentLength() - checkme.docsize, true, chunked)) persistProxy = false; checkme.docsize += fdt.throughput; } else if (checkme.tunnel_2way) { if (!fdt.tunnel(proxysock, peerconn, true)) persistProxy = false; checkme.docsize = fdt.throughput; } } DEBUG_proxy(" -Forwarding body to client :", " Upfailure is ", String(checkme.upfailure), " isItNaughty is ", String(checkme.isItNaughty)); if (checkme.upfailure || checkme.isItNaughty) { if (denyAccess(&peerconn, &proxysock, &header, &docheader, &checkme.url, &checkme, &clientuser, &clientip, filtergroup, checkme.ispostblock, checkme.headersent, checkme.wasinfected, checkme.scanerror)) persistPeer = false; } //Log doLog(clientuser, clientip, checkme); if (!persistProxy) proxysock.close(); // close connection to proxy if (persistPeer && !checkme.isconnect) { continue; } break; } } catch (std::exception &e) { DEBUG_proxy(" -connection handler caught an exception: ", e.what()); if (o.conn.logconerror) E2LOGGER_error("-connection handler caught an exception %s", e.what()); // close connection to proxy proxysock.close(); return -1; } if (!ismitm) try { DEBUG_proxy(" -Attempting graceful connection close"); //syslog(LOG_INFO, " -Attempting graceful connection close" ); int fd = peerconn.getFD(); if (fd > -1) { if (shutdown(fd, SHUT_WR) == 0) { char buff[2]; peerconn.readFromSocket(buff, 2, 0, 5000); }; }; // close connection to the client peerconn.close(); proxysock.close(); } catch (std::exception &e) { DEBUG_debug(" -connection handler caught an exception on connection closedown: ", e.what() ); // close connection to the client peerconn.close(); proxysock.close(); } return 0; } void ConnectionHandler::doLog(std::string &who, std::string &from, NaughtyFilter &cm) { DEBUG_trace("who: ", who, " from: ", from ); if (cm.isourwebserver) cm.nolog = true; struct timeval theend; gettimeofday(&theend, NULL); String rtype = cm.request_header->requestType(); String where = cm.get_logUrl(); unsigned int port = cm.request_header->port; bool isexception = cm.isexception; std::string *cat = &cm.whatIsNaughtyCategories; std::string what; // don't log if logging disabled entirely, or if it's an ad block and ad logging is disabled, // or if it's an exception and exception logging is disabled if ( (o.log.log_level == 0) || ((cat != NULL) && !o.log.log_ad_blocks && (strstr(cat->c_str(), "ADs") != NULL)) || ((o.log.log_exception_hits == 0) && isexception)) { if (o.log.log_level != 0) { if (isexception) { DEBUG_debug(" -Not logging exceptions"); } else { DEBUG_debug(" -Not logging 'ADs' blocks"); } } cm.nolog = true; } if(!cm.nolog) ldl->fg[filtergroup]->StoryB.runFunctEntry(ENT_STORYB_LOG_CHECK, cm); //if(cm.nolog) return; if (e2logger.isEnabled(LoggerSource::requestlog)) { what = thread_id; } what += cm.whatIsNaughtyLog; String how = rtype; off_t size = cm.docsize; bool isnaughty = cm.isItNaughty; int naughtytype = cm.blocktype; bool istext = cm.is_text; struct timeval *thestart = &cm.thestart; bool cachehit = false; int code = (cm.response_header->returnCode()); if (isnaughty) code = 403; std::string mimetype = cm.response_header->getContentType(); bool wasinfected = cm.wasinfected; bool wasscanned = cm.wasscanned; int naughtiness = cm.naughtiness; int filtergroup = cm.filtergroup; HTTPHeader *reqheader = cm.request_header; int message_no = cm.message_no; bool contentmodified = cm.contentmodified; bool urlmodified = cm.urlmodified; bool headermodified = cm.headermodified; bool headeradded = cm.headeradded; std::string data, cr("\n"); if (!((isexception && (o.log.log_exception_hits == 2)) || isnaughty || o.log.log_level == 3 || (o.log.log_level == 2 && istext))) cm.nolog = true; if(!cm.nolog || (cm.alert && e2logger.isEnabled(LoggerSource::alertlog)) || e2logger.isEnabled(LoggerSource::responselog)){ // put client hostname in log if enabled. // for banned & exception IP/hostname matches, we want to output exactly what was matched against, // be it hostname or IP - therefore only do lookups here when we don't already have a cached hostname, // and we don't have a straight IP match agaisnt the banned or exception IP lists. if (o.log.log_client_hostnames && (cm.clienthost == "") && !matchedip && !cm.anon_user) { DEBUG_debug("logclienthostnames enabled but reverseclientiplookups disabled; lookup forced."); getClientFromIP(from.c_str(),cm.clienthost); } // Build up string describing POST data parts, if any std::ostringstream postdata; for (std::list::iterator i = postparts.begin(); i != postparts.end(); ++i) { // Replace characters which would break log format with underscores std::string::size_type loc = 0; while ((loc = i->filename.find_first_of(",;\t ", loc)) != std::string::npos) i->filename[loc] = '_'; // Build up contents of log column postdata << i->mimetype << "," << i->filename << "," << i->size << "," << i->blocked << "," << i->storedname << "," << i->bodyoffset << ";"; } postdata << std::flush; // Formatting code moved into log_listener in FatController.cpp // Item length limit put back to avoid log listener // overload with very long urls Philip Pearce Jan 2014 if ((cat != NULL) && (cat->length() > o.log.max_logitem_length)) cat->resize(o.log.max_logitem_length); if (what.length() > o.log.max_logitem_length) what.resize(o.log.max_logitem_length); if (where.length() > o.log.max_logitem_length) where.limitLength(o.log.max_logitem_length); if (o.log.dns_user_logging() && !is_real_user) { String user; if (getdnstxt(from, user)) { who = who + ":" + user; SBauth.user_name = user; SBauth.user_source = "dnslog"; }; is_real_user = true; // avoid looping on persistent connections }; std::string l_who = who; std::string l_from = from; std::string l_clienthost; l_clienthost = cm.clienthost; if (cm.anon_user) { l_who = ""; l_from = "0.0.0.0"; l_clienthost = ""; } // populate flags field String flags = cm.getFlags(); DEBUG_debug(" -Building raw log data string... "); data = String(isexception) + cr; data += (cat ? (*cat) + cr : cr); data += String(isnaughty) + cr; data += String(naughtytype) + cr; data += String(naughtiness) + cr; data += where + cr; data += what + cr; data += how + cr; data += l_who + cr; data += l_from + cr; data += String(port) + cr; data += String(wasscanned) + cr; data += String(wasinfected) + cr; data += String(contentmodified) + cr; data += String(urlmodified) + cr; data += String(headermodified) + cr; data += String(size) + cr; data += String(filtergroup) + cr; data += String(code) + cr; data += String(cachehit) + cr; data += String(mimetype) + cr; data += String((*thestart).tv_sec) + cr; data += String((*thestart).tv_usec) + cr; data += String((theend).tv_sec) + cr; data += String((theend).tv_usec) + cr; data += l_clienthost + cr; if (o.log.log_user_agent) data += (reqheader ? reqheader->userAgent() + cr : cr); else data += cr; data += urlparams + cr; data += postdata.str().c_str() + cr; data += String(message_no) + cr; data += String(headeradded) + cr; data += flags + cr; data += cm.search_terms; data += cr; data += String(!cm.nolog) + cr; data += String(cm.alert) + cr; data += String(cm.issemiexception) + cr; DEBUG_debug(" -...built"); // push on log queue o.log.log_Q->push(data); // connect to dedicated logging proc } } void ConnectionHandler::doRQLog(std::string &who, std::string &from, NaughtyFilter &cm, std::string &funct) { DEBUG_trace("who: ", who, " from: ", from, "funct: ", funct); String rtype = cm.request_header->requestType(); String where = cm.logurl; unsigned int port = cm.request_header->port; std::string what = thread_id; what += funct; if (cm.isTLS) what += "_TLS"; if (cm.hasSNI) what += "_SNI"; if (cm.ismitm) what += "_MITM"; String how = rtype; off_t size = cm.docsize; std::string *cat = nullptr; //&cm.whatIsNaughtyCategories; bool isnaughty = cm.isItNaughty; int naughtytype = cm.blocktype; bool isexception = cm.isexception; struct timeval *thestart = &cm.thestart; bool cachehit = false; int code = 0; std::string mimetype = ""; //cm.mimetype; bool wasinfected = false; //cm.wasinfected; bool wasscanned = false; //cm.wasscanned; int naughtiness = 0; //cm.naughtiness; int filtergroup = cm.filtergroup; HTTPHeader *reqheader = cm.request_header; int message_no = cm.message_no; bool contentmodified = false; //cm.contentmodified; bool urlmodified = false; //cm.urlmodified; bool headermodified = false; //cm.headermodified; bool headeradded = false; //cm.headeradded; std::string data, cr("\n"); // if ((isexception && (o.log_exception_hits == 2)) // || isnaughty || o.ll == 3 || (o.ll == 2 && istext)) if(true) { // Item length limit put back to avoid log listener // overload with very long urls Philip Pearce Jan 2014 if (what.length() > o.log.max_logitem_length) what.resize(o.log.max_logitem_length); if (where.length() > o.log.max_logitem_length) where.limitLength(o.log.max_logitem_length); if (o.log.dns_user_logging() && !is_real_user) { String user; if (getdnstxt(from, user)) { who = who + ":" + user; }; is_real_user = true; // avoid looping on persistent connections }; std::string l_who = who; std::string l_from = from; std::string l_clienthost; l_clienthost = cm.clienthost; String flags = cm.getFlags(); DEBUG_debug(" -Building raw log data string... "); data = String(isexception) + cr; data += (cat ? (*cat) + cr : cr); data += String(isnaughty) + cr; data += String(naughtytype) + cr; data += String(naughtiness) + cr; data += where + cr; data += what + cr; data += how + cr; data += l_who + cr; data += l_from + cr; data += String(port) + cr; data += String(wasscanned) + cr; data += String(wasinfected) + cr; data += String(contentmodified) + cr; data += String(urlmodified) + cr; data += String(headermodified) + cr; data += String(size) + cr; data += String(filtergroup) + cr; data += String(code) + cr; data += String(cachehit) + cr; data += String(mimetype) + cr; data += String((*thestart).tv_sec) + cr; data += String((*thestart).tv_usec) + cr; data += String((*thestart).tv_sec) + cr; data += String((*thestart).tv_usec) + cr; data += l_clienthost + cr; if (o.log.log_user_agent) data += (reqheader ? reqheader->userAgent() + cr : cr); else data += cr; data += urlparams + cr; data += cr; data += String(message_no) + cr; data += String(headeradded) + cr; data += flags + cr; data += cr; //cm.search_terms not known yet data += cr; data += cr; // nolog data += cr; // alert data += cr; // semiexception DEBUG_debug(" -...built"); //delete newcat; // push on log queue o.log.RQlog_Q->push(data); // connect to dedicated logging proc } } // based on patch by Aecio F. Neto (afn@harvest.com.br) - Harvest Consultoria (http://www.harvest.com.br) // show the relevant banned page/image/CGI based on report level setting, request type etc. bool ConnectionHandler::genDenyAccess(Socket &peerconn, String &eheader, String &ebody, HTTPHeader *header, HTTPHeader *docheader, String *url, NaughtyFilter *checkme, std::string *clientuser, std::string *clientip, int filtergroup, bool ispostblock, int headersent, bool wasinfected, bool scanerror, bool forceshow) { int reporting_level = ldl->fg[filtergroup]->reporting_level; DEBUG_debug(" -reporting level is ", reporting_level); if (checkme->whatIsNaughty == "" && checkme->message_no > 0) { checkme->whatIsNaughty = o.language_list.getTranslation(checkme->message_no); } if (checkme->whatIsNaughtyLog == "") { if (checkme->log_message_no > 0) { checkme->whatIsNaughtyLog = o.language_list.getTranslation(checkme->log_message_no); } else { checkme->whatIsNaughtyLog = checkme->whatIsNaughty; } } try { // writestring throws exception on error/timeout // flags to enable filter/infection / hash generation bool filterhash = false; bool virushash = false; // flag to enable internal generation of hashes (i.e. obey the "-1" setting; to allow the modes but disable hash generation) // (if disabled, just output '1' or '2' to show that the CGI should generate a filter/virus bypass hash; // otherwise, real hashes get put into substitution variables/appended to the ban CGI redirect URL) bool dohash = false; String flags = checkme->getFlags(); if (reporting_level > 0) { // generate a filter bypass hash if (!wasinfected && (checkme->isbypassallowed) && !ispostblock) { DEBUG_debug(" -Enabling filter bypass hash generation"); filterhash = true; if (ldl->fg[filtergroup]->bypass_mode > 0 ) dohash = true; } // generate an infection bypass hash else if (wasinfected && checkme->isinfectionbypassallowed) { // only generate if scanerror (if option to only bypass scan errors is enabled) if ((*ldl->fg[filtergroup]).infection_bypass_errors_only ? scanerror : true) { DEBUG_debug(" -Enabling infection bypass hash generation"); virushash = true; if (ldl->fg[filtergroup]->infection_bypass_mode > 0) dohash = true; } } } DEBUG_debug(" - filter bypass hash generation", " virushah ", virushash, " dohash", dohash, " filterhash ", filterhash); // the user is using the full whack of custom banned images and/or HTML templates if (reporting_level == 3 || (headersent > 0 && reporting_level > 0) || forceshow || (*header).requestType().startsWith("CONNECT")) { // if reporting_level = 1 or 2 and headersent then we can't // send a redirect so we have to display the template instead if ((*header).requestType().startsWith("CONNECT") && !(peerconn).isSsl()) { // Block ssl website // Buggy with FF < 65 https://bugzilla.mozilla.org/show_bug.cgi?id=1522093 // Connections still opened after a refresh // 403 requests made ICAP error with high load eheader = "HTTP/1.1 302 Redirect"; eheader += "\r\nLocation: http://internal.test.e2guardian.org"; eheader += "\r\nServer: e2guardian"; eheader += "\r\nConnection: close"; eheader += "\r\n\r\n"; } else { // we're dealing with a non-SSL'ed request, and have the option of using the custom banned image/page directly bool replaceimage = false; bool replaceflash = false; if (o.block.use_custom_banned_image) { // It would be much nicer to do a mime comparison // and see if the type is image/* but the header // never (almost) gets back from squid because // it gets denied before then. // This method is prone to over image replacement // but will work most of the time. String lurl((*url)); lurl.toLower(); if (lurl.endsWith(".gif") || lurl.endsWith(".jpg") || lurl.endsWith(".jpeg") || lurl.endsWith(".jpe") || lurl.endsWith(".png") || lurl.endsWith(".bmp") || (*docheader).isContentType("image/", ldl->fg[filtergroup])) { replaceimage = true; } } if (o.block.use_custom_banned_flash) { String lurl((*url)); lurl.toLower(); if (lurl.endsWith(".swf") || (*docheader).isContentType("application/x-shockwave-flash", ldl->fg[filtergroup])) { replaceflash = true; } } // if we're denying an image request, show the image; otherwise, show the HTML page. // (or advanced ad block page, or HTML page with bypass URLs) if (replaceimage) { if (headersent == 0) { eheader = "HTTP/1.1 200 OK\r\n"; } o.block.banned_image.display_hb(eheader, ebody); } else if (replaceflash) { if (headersent == 0) { eheader = "HTTP/1.1 200 OK\r\n"; } o.block.banned_flash.display_hb(eheader, ebody); } else { // advanced ad blocking - if category contains ADs, wrap ad up in an "ad blocked" message, // which provides a link to the original URL if you really want it. primarily // for IFRAMEs, which will end up containing this link instead of the ad (standard non-IFRAMEd // ad images still get image-replaced.) if (strstr(checkme->whatIsNaughtyCategories.c_str(), "ADs") != NULL) { eheader = "HTTP/1.1 200 \r\n"; eheader += o.language_list.getTranslation(1101); // advert blocked eheader += "\r\nContent-Type: text/html\r\n"; ebody = "E2guardian - "; ebody += o.language_list.getTranslation(1101); // advert blocked ebody += "
"; ebody += o.language_list.getTranslation(1101); // advert blocked ebody += "
\r\n"; eheader += "Content-Length: "; eheader += std::to_string(ebody.size()); eheader += "\r\n\r\n"; } // Mod by Ernest W Lessenger Mon 2nd February 2004 // Other bypass code mostly written by Ernest also // create temporary bypass URL to show on denied page else { String hashed; // generate valid hash locally if enabled if (dohash) { hashed = header->hashedURL(url, clientip, virushash, clientuser, *ldl->fg[filtergroup]); } // otherwise, just generate flags showing what to generate else if (filterhash) { hashed = "HASH=1"; } else if (virushash) { hashed = "HASH=2"; } if(ldl->fg[filtergroup]->cgi_bypass_v2) { if (filterhash) { hashed += "&HASH=1"; } else if (virushash) { hashed += "&HASH=2"; } } if (headersent == 0) { eheader = "HTTP/1.1 200 \r\n"; } if (headersent < 2) { eheader += "Content-type: text/html\r\n\r\n"; } // if the header has been sent then likely displaying the // template will break the download, however as this is // only going to be happening if the unsafe trickle // buffer method is used and we want the download to be // broken we don't mind too much //String fullurl = header->getLogUrl(true); // // DISPLAYING TEMPLATE String fullurl = checkme->get_logUrl(); String localip = peerconn.getLocalIP(); ldl->fg[filtergroup]->getHTMLTemplate(checkme->upfailure)->display_hb(ebody, &fullurl, (*checkme).whatIsNaughty, (*checkme).whatIsNaughtyLog, // grab either the full category list or the thresholded list (checkme->usedisplaycats ? checkme->whatIsNaughtyDisplayCategories : checkme->whatIsNaughtyCategories), clientuser, clientip, &(checkme->clienthost), filtergroup, ldl->fg[filtergroup]->name, hashed, localip, flags); } } } } // the user is using the CGI rather than the HTML template - so issue a redirect with parameters filled in on GET string else if (reporting_level > 0) { // grab either the full category list or the thresholded list std::string cats; cats = checkme->usedisplaycats ? checkme->whatIsNaughtyDisplayCategories : checkme->whatIsNaughtyCategories; String hashed; // generate valid hash locally if enabled if (dohash) { hashed = header->hashedURL(url, clientip, virushash, clientuser, *ldl->fg[filtergroup]); //hashed = hashedURL(url, filtergroup, clientip, virushash, clientuser); } // otherwise, just generate flags showing what to generate else if (filterhash) { hashed = "1"; } else if (virushash) { hashed = "2"; } if ((*checkme).whatIsNaughty.length() > 2048) { (*checkme).whatIsNaughty = String((*checkme).whatIsNaughty.c_str()).subString(0, 2048).toCharArray(); } if ((*checkme).whatIsNaughtyLog.length() > 2048) { (*checkme).whatIsNaughtyLog = String((*checkme).whatIsNaughtyLog.c_str()).subString(0, 2048).toCharArray(); } if ((*header).requestType().startsWith("CONNECT") && !(peerconn).isSsl()) { // Block ssl website // Buggy with FF < 65 https://bugzilla.mozilla.org/show_bug.cgi?id=1522093 // Connections still opened after a refresh // 403 requests made ICAP error with high load eheader = "HTTP/1.1 302 Redirect"; eheader += "\r\nLocation: http://internal.test.e2guardian.org"; eheader += "\r\nServer: e2guardian"; eheader += "\r\nConnection: close"; eheader += "\r\n\r\n"; // we're dealing with a non-SSL'ed request, and have the option of using the custom banned image/page directly } else { eheader = "HTTP/1.1 302 Redirect\r\n"; eheader += "Location: "; eheader += ldl->fg[filtergroup]->access_denied_address; } if (ldl->fg[filtergroup]->non_standard_delimiter) { eheader += "?DENIEDURL=="; eheader += miniURLEncode((*url).toCharArray()).c_str(); eheader += "::IP=="; eheader += (*clientip).c_str(); eheader += "::USER=="; eheader += (*clientuser).c_str(); eheader += "::FILTERGROUP=="; eheader += ldl->fg[filtergroup]->name; if (checkme->clienthost != "") { eheader += "::HOST=="; eheader += checkme->clienthost.c_str(); } eheader += "::CATEGORIES=="; eheader += miniURLEncode(cats.c_str()).c_str(); if (virushash || filterhash) { // output either a genuine hash, or just flags if (dohash) { eheader += "::"; eheader += hashed.before("=").toCharArray(); eheader += "=="; eheader += hashed.after("=").toCharArray(); } else { eheader += "::HASH=="; eheader += hashed.toCharArray(); } if(ldl->fg[filtergroup]->cgi_bypass_v2) { String data = *clientip; data += *clientuser; data += ldl->fg[filtergroup]->cgi_magic; String checkh(url->md5(data.c_str())); eheader += "::CHECK=="; eheader += checkh.toCharArray(); } } eheader += "::EXTFLAGS=="; eheader += flags.toCharArray(); eheader += "::REASON=="; } else { eheader += "?DENIEDURL="; eheader += miniURLEncode((*url).toCharArray()).c_str(); eheader += "&IP="; eheader += (*clientip).c_str(); eheader += "&USER="; eheader += (*clientuser).c_str(); eheader += "&FILTERGROUP="; eheader += ldl->fg[filtergroup]->name; if (checkme->clienthost != "") { eheader += "&HOST="; eheader += checkme->clienthost.c_str(); } eheader += "&CATEGORIES="; eheader += miniURLEncode(cats.c_str()).c_str(); if (virushash || filterhash) { // output either a genuine hash, or just flags if (dohash) { eheader += "&"; eheader += hashed.toCharArray(); } else { eheader += "&HASH="; eheader += hashed.toCharArray(); } if(ldl->fg[filtergroup]->cgi_bypass_v2) { String data = *clientip; data += *clientuser; data += ldl->fg[filtergroup]->cgi_magic; String checkh(url->md5(data.c_str())); eheader += "&CHECK="; eheader += checkh.toCharArray(); } } eheader += "&EXTFLAGS="; eheader += flags.toCharArray(); eheader += "&REASON="; } if (reporting_level == 1) { eheader += miniURLEncode((*checkme).whatIsNaughty.c_str()).c_str(); } else { eheader += miniURLEncode((*checkme).whatIsNaughtyLog.c_str()).c_str(); } eheader += "\r\n\r\n"; } // the user is using the barebones banned page else if (reporting_level == 0) { eheader = "HTTP/1.1 200 OK\r\n"; eheader += "Content-type: text/html\r\n"; ebody = "e2guardian - "; ebody += o.language_list.getTranslation(1); // access denied ebody += "

e2guardian - "; ebody += o.language_list.getTranslation(1); // access denied ebody += "

\r\n"; eheader += "Content-Length: "; eheader += std::to_string(ebody.size()); eheader += "\r\n\r\n"; } // stealth mode else if (reporting_level == -1) { (*checkme).isItNaughty = false; // dont block return false; } } catch (std::exception &e) { } return true; } // end of deny request loop // show the relevant banned page/image/CGI based on report level setting, request type etc. bool ConnectionHandler::denyAccess(Socket *peerconn, Socket *proxysock, HTTPHeader *header, HTTPHeader *docheader, String *url, NaughtyFilter *checkme, std::string *clientuser, std::string *clientip, int filtergroup, bool ispostblock, int headersent, bool wasinfected, bool scanerror, bool forceshow) { String eheader, ebody; if (genDenyAccess(*peerconn, eheader, ebody, header, docheader, &(checkme->logurl), checkme, clientuser, clientip, filtergroup, ispostblock, headersent, wasinfected, scanerror, forceshow)) { peerconn->writeString(eheader.toCharArray()); if (ebody.length() > 0) peerconn->writeString(ebody.toCharArray()); }; // we blocked the request, so flush the client connection & close the proxy connection. if ((*checkme).isItNaughty) { (*peerconn).readyForOutput(o.net.proxy_timeout); //as best a flush as I can (*proxysock).close(); // close connection to proxy // we said no to the request, so return true, indicating exit the connhandler return true; } ldl.reset(); return false; } // end of deny request loop // do content scanning (AV filtering) and naughty filtering void ConnectionHandler::contentFilter(HTTPHeader *docheader, HTTPHeader *header, DataBuffer *docbody, Socket *proxysock, Socket *peerconn, int *headersent, bool *pausedtoobig, off_t *docsize, NaughtyFilter *checkme, bool wasclean, int filtergroup, std::deque &responsescanners, std::string *clientuser, std::string *clientip, bool *wasinfected, bool *wasscanned, bool isbypass, String &url, String &domain, bool *scanerror, bool &contentmodified, String *csmessage) { //proxysock->bcheckForInput(120000); bool compressed = docheader->isCompressed(); if (compressed) { DEBUG_debug(" -Decompressing as we go....."); docbody->setDecompress(docheader->contentEncoding()); } DEBUG_debug(docheader->contentEncoding()); DEBUG_debug(" -about to get body from proxy"); (*pausedtoobig) = docbody->in(proxysock, peerconn, header, docheader, !responsescanners.empty(), headersent, ldl->fg[filtergroup]->StoryB, checkme ); // get body from proxy // checkme: surely if pausedtoobig is true, we just want to break here? // the content is larger than max_content_filecache_scan_size if it was downloaded for scanning, // and larger than max_content_filter_size if not. // in fact, why don't we check the content length (when it's not -1) before even triggering the download managers? if ((*pausedtoobig)) { DEBUG_debug(" -got PARTIAL body "); } else { DEBUG_debug(" -got body"); } off_t dblen; bool isfile = false; if (docbody->tempfilesize > 0) { dblen = docbody->tempfilesize; isfile = true; } else { dblen = docbody->data_length; } // don't scan zero-length buffers (waste of AV resources, especially with external scanners (ICAP)). // these were encountered browsing opengroup.org, caused by a stats script. (PRA 21/09/2005) // if we wanted to honour a hypothetical min_content_scan_size, we'd do it here. if (((*docsize) = dblen) == 0) { DEBUG_debug(" -Not scanning zero-length body"); // it's not inconceivable that we received zlib or gzip encoded content // that is, after decompression, zero length. we need to cater for this. // seen on SW's internal MediaWiki. docbody->swapbacktocompressed(); return; } if (!wasclean) { // was not clean or no urlcache // fixed to obey maxcontentramcachescansize if (!responsescanners.empty() && (isfile ? dblen <= o.content.max_content_filecache_scan_size : dblen <= o.content.max_content_ramcache_scan_size)) { int csrc = 0; int k = 0; for (std::deque::iterator i = responsescanners.begin(); i != responsescanners.end(); i++) { (*wasscanned) = true; if (isfile) { DEBUG_debug(" -Running scanFile"); csrc = (*i)->scanFile(header, docheader, clientuser->c_str(), ldl->fg[filtergroup], clientip->c_str(), docbody->tempfilepath.toCharArray(), checkme); if ((csrc != E2CS_CLEAN) && (csrc != E2CS_WARNING)) { unlink(docbody->tempfilepath.toCharArray()); // delete infected (or unscanned due to error) file straight away } } else { DEBUG_debug(" -Running scanMemory"); csrc = (*i)->scanMemory(header, docheader, clientuser->c_str(), ldl->fg[filtergroup], clientip->c_str(), docbody->data, docbody->data_length, checkme); } DEBUG_debug(" -AV scan ", k, " returned: ", csrc); if (csrc == E2CS_WARNING) { // Scanner returned a warning. File wasn't infected, but wasn't scanned properly, either. (*wasscanned) = false; (*scanerror) = false; DEBUG_debug((*i)->getLastMessage()); (*csmessage) = (*i)->getLastMessage(); } else if (csrc == E2CS_BLOCKED) { (*wasscanned) = true; (*scanerror) = false; break; } else if (csrc == E2CS_INFECTED) { (*wasinfected) = true; (*scanerror) = false; break; } //if its not clean / we error then treat it as infected else if (csrc != E2CS_CLEAN) { if (csrc < 0) { E2LOGGER_error("Unknown return code from content scanner: ", csrc); if (ldl->fg[filtergroup]->disable_content_scan_error) { E2LOGGER_error( "disablecontentscanerror is on : bypass actived USER: ", clientip, " URL: ", url); (*wasscanned) = false; (*wasinfected) = false; break; } } else { E2LOGGER_error("scanFile/Memory returned error: ", csrc); } checkme->message_no = 1203; checkme->whatIsNaughty = o.language_list.getTranslation(1203); checkme->whatIsNaughtyLog = (*i)->getLastMessage().toCharArray(); checkme->whatIsNaughtyCategories = o.language_list.getTranslation(72); checkme->isItNaughty = true; checkme->isException = false; (*scanerror) = true; break; } k++; } DEBUG_debug(" -finished running AV"); // rc = system("date"); } else if (!responsescanners.empty()) { DEBUG_debug(" -content length large so skipping content scanning (virus) filtering"); } // rc = system("date"); if (!checkme->isItNaughty && !checkme->isException && !isbypass && (dblen <= o.content.max_content_filter_size) && !docheader->authRequired() && (docheader->OKtoFilterMime(ldl->fg[filtergroup]))) { DEBUG_debug(" -Start content filtering: "); // checkme->checkme(docbody->data, docbody->buffer_length, &url, &domain, // ldl->fg[filtergroup], ldl->fg[filtergroup]->banned_phrase_list, // ldl->fg[filtergroup]->naughtyness_limit); checkme->checkme(docbody->data, docbody->data_length, &url, &domain, ldl->fg[filtergroup], ldl->fg[filtergroup]->banned_phrase_list, ldl->fg[filtergroup]->naughtyness_limit); DEBUG_debug(" -Done content filtering"); } else { DEBUG_debug(" -Skipping content filtering: "); if (dblen > o.content.max_content_filter_size) { DEBUG_debug(" -Content too large"); } else if (checkme->isException) { DEBUG_debug(" -Is flagged as an exception"); } else if (checkme->isItNaughty) { DEBUG_debug(" -Is already flagged as naughty (content scanning)"); } else if (isbypass) { DEBUG_debug(" -Is flagged as a bypass"); } else if (docheader->authRequired()) { DEBUG_debug(" -Is a set of auth required headers"); } else if (!docheader->isContentType("text",ldl->fg[filtergroup])) { DEBUG_debug(" -Not text"); } } } // don't do phrase filtering or content replacement on exception/bypass accesses if (checkme->isException || isbypass) { // don't forget to swap back to compressed! docbody->swapbacktocompressed(); return; } if ((dblen <= o.content.max_content_filter_size) && !checkme->isItNaughty && docheader->isContentType("text", ldl->fg[filtergroup])) { contentmodified = docbody->contentRegExp(ldl->fg[filtergroup]); // content modifying uses global variable } else { DEBUG_debug(" -Skipping content modification: "); if (dblen > o.content.max_content_filter_size) { DEBUG_debug(" -Content too large"); } else if (!docheader->isContentType("text",ldl->fg[filtergroup])) { DEBUG_debug(" -Not text"); } else if (checkme->isItNaughty) { DEBUG_debug(" -Already flagged as naughty"); } } //rc = system("date"); if (contentmodified) { // this would not include infected/cured files // if the content was modified then it must have fit in ram so no // need to worry about swapped to disk stuff DEBUG_debug(" -content modification made"); if (compressed) { docheader->removeEncoding(docbody->data_length); // need to modify header to mark as not compressed // it also modifies Content-Length as well } else { docheader->setContentLength(docbody->data_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 } DEBUG_debug(" Returning from content checking"); } int ConnectionHandler::sendProxyConnect(String &hostname, Socket *sock, NaughtyFilter *checkme) { String connect_request = "CONNECT " + hostname + ":"; connect_request += "443 HTTP/1.1\r\n\r\n"; DEBUG_debug(" -creating tunnel through proxy to ", hostname); //somewhere to hold the header from the proxy HTTPHeader header(__HEADER_RESPONSE); //header.setTimeout(o.pcon_timeout); header.setTimeout(o.net.proxy_timeout); if (!(sock->writeString(connect_request.c_str()) && header.in(sock, true))) { DEBUG_debug(" -Error creating tunnel through proxy", strerror(errno) ); //(*checkme).whatIsNaughty = "Unable to create tunnel through local proxy"; checkme->message_no = 157; (*checkme).whatIsNaughty = o.language_list.getTranslation(157); (*checkme).whatIsNaughtyLog = (*checkme).whatIsNaughty; (*checkme).isItNaughty = true; (*checkme).whatIsNaughtyCategories = o.language_list.getTranslation(70); return -1; } //do http connect if (header.returnCode() != 200) { //connection failed //(*checkme).whatIsNaughty = "Opening tunnel failed"; checkme->message_no = 158; (*checkme).whatIsNaughty = o.language_list.getTranslation(158); (*checkme).whatIsNaughtyLog = (*checkme).whatIsNaughty + " with error code " + String(header.returnCode()); (*checkme).isItNaughty = true; (*checkme).whatIsNaughtyCategories = o.language_list.getTranslation(70); DEBUG_debug(" -Tunnel status was ", header.returnCode(), " expecting 200 ok"); return -1; } return 0; } void ConnectionHandler::checkCertificate(String &hostname, Socket *sslsock, NaughtyFilter *checkme) { DEBUG_debug(" -checking SSL certificate is valid"); long rc = sslsock->checkCertValid(hostname); //check that everything in this certificate is correct appart from the hostname if (rc < 0) { //no certificate if ( ldl->fg[filtergroup]->allow_empty_host_certs) return; checkme->isItNaughty = true; //(*checkme).whatIsNaughty = "No SSL certificate supplied by server"; checkme->message_no = 155; (*checkme).whatIsNaughty = o.language_list.getTranslation(155); (*checkme).whatIsNaughtyLog = (*checkme).whatIsNaughty; (*checkme).whatIsNaughtyCategories = o.language_list.getTranslation(70); return; } else if (rc != X509_V_OK) { //something was wrong in the certificate checkme->isItNaughty = true; // (*checkme).whatIsNaughty = "Certificate supplied by server was not valid"; checkme->message_no = 150; (*checkme).whatIsNaughty = o.language_list.getTranslation(150); (*checkme).whatIsNaughtyLog = (*checkme).whatIsNaughty + ": " + X509_verify_cert_error_string(rc); (*checkme).whatIsNaughtyCategories = o.language_list.getTranslation(70); return; } DEBUG_debug(" -checking SSL certificate hostname"); //check the common name and altnames of a certificate against hostname if (sslsock->checkCertHostname(hostname) < 0) { //hostname was not matched by the certificate checkme->isItNaughty = true; //(*checkme).whatIsNaughty = "Server's SSL certificate does not match domain name"; checkme->message_no = 156; (*checkme).whatIsNaughty = o.language_list.getTranslation(156); (*checkme).whatIsNaughtyLog = (*checkme).whatIsNaughty; (*checkme).whatIsNaughtyCategories = o.language_list.getTranslation(70); return; } } bool ConnectionHandler::getdnstxt(std::string &clientip, String &user) { String ippath; ippath = clientip; // if (o.use_xforwardedfor) { // // grab the X-Forwarded-For IP if available // p2 = header.getXForwardedForIP(); // if (p2.length() > 0) { // ippath = p1 + "-" + p2; // } else { // ippath = p1; // } // } else { // ippath = p1; // } DEBUG_debug("IPPath is ", ippath); // change '.' to '-' ippath.swapChar('.', '-'); DEBUG_debug("IPPath is ", ippath); #ifdef PRT_DNSAUTH // get info from DNS union { HEADER hdr; u_char buf[NS_PACKETSZ]; } response; int responseLen; ns_msg handle; /* handle for response message */ responseLen = res_querydomain(ippath.c_str(), o.log.dns_user_logging_domain.c_str(), ns_c_in, ns_t_txt, (u_char *)&response, sizeof(response)); if (responseLen < 0) { DEBUG_debug("DNS query returned error ", dns_error(h_errno)); return false; } if (ns_initparse(response.buf, responseLen, &handle) < 0) { DEBUG_debug("ns_initparse returned error ", strerror(errno)); return false; } //int rrnum; /* resource record number */ ns_rr rr; /* expanded resource record */ //u_char *cp; //char ans[MAXDNAME]; int i = ns_msg_count(handle, ns_s_an); if (i > 0) { if (ns_parserr(&handle, ns_s_an, 0, &rr)) { DEBUG_debug("ns_paserr returned error ", strerror(errno)); return false; } else { if (ns_rr_type(rr) == ns_t_txt) { DEBUG_debug("ns_rr_rdlen returned ", ns_rr_rdlen(rr)); u_char *k = (u_char *)ns_rr_rdata(rr); char p[400]; unsigned int j = 0; for (unsigned int j1 = 1; j1 < ns_rr_rdlen(rr); j1++) { p[j++] = k[j1]; } // p[j] = (char)NULL; p[j] = '\0'; DEBUG_debug("ns_rr_data returned ", p ); String dnstxt(p); user = dnstxt.before(","); return true; } } } #endif return false; } String ConnectionHandler::dns_error(int herror) { String s; switch (herror) { case HOST_NOT_FOUND: s = "HOST_NOT_FOUND"; break; case TRY_AGAIN: s = "TRY_AGAIN - DNS server failure"; break; case NO_DATA: s = "NO_DATA - unexpected DNS error"; break; default: String S2(herror); s = "DNS - Unexpected error number " + S2; break; } return s; } bool ConnectionHandler::gen_error_mess(Socket &peerconn, NaughtyFilter &cm, String &eheader, String &ebody, int mess_no1, int mess_no2, std::string mess) { cm.message_no = mess_no1; eheader = "HTTP/1.1 " + mess + "\nContent-Type: text/html\r\nConnection: Close\r\n"; if(mess_no1 > 0) { ebody = "e2guardian - "; ebody += mess; ebody += "

e2guardian - "; ebody += mess; ebody += "

"; if (mess_no1 > 0) ebody += o.language_list.getTranslation(mess_no1); if (mess_no2 > 0) ebody += o.language_list.getTranslation(mess_no2); ebody += "\r\n"; } return true; } bool ConnectionHandler::writeback_error(NaughtyFilter &cm, Socket &cl_sock, int mess_no1, int mess_no2, std::string mess) { String eheader, ebody; gen_error_mess(cl_sock, cm, eheader, ebody, mess_no1, mess_no2, mess); cl_sock.writeString(eheader.c_str()); cl_sock.writeString("\r\n"); cl_sock.writeString(ebody.c_str()); return true; } bool ConnectionHandler::goMITM(NaughtyFilter &checkme, Socket &proxysock, Socket &peerconn, bool &persistProxy, bool &authed, bool &persistent_authed, String &ip, stat_rec *&dystat, std::string &clientip, bool transparent) { DEBUG_debug(" Start goMITM nf ", checkme.isItNaughty, " upfail ", checkme.upfailure); DEBUG_debug(" -Intercepting HTTPS connection"); HTTPHeader *header = checkme.request_header; HTTPHeader *docheader = checkme.response_header; bool justLog = false; // CA intialisation now Moved into OptionContainer so now done once on start-up // instead of on every request X509 *cert = NULL; struct ca_serial caser; caser.asn = NULL; caser.charhex = NULL; caser.filepath = NULL; caser.filename = NULL; EVP_PKEY *pkey = NULL; bool certfromcache = false; //generate the cert DEBUG_debug(" -Getting ssl certificate for client connection"); pkey = o.cert.ca->getServerPkey(); //generate the certificate but dont write it to disk (avoid someone //requesting lots of places that dont exist causing the disk to fill //up / run out of inodes certfromcache = o.cert.ca->getServerCertificate(checkme.urldomain.CN().c_str(), &cert, &caser, checkme.isiphost); if (caser.asn == NULL) { DEBUG_debug("caser.asn is NULL"); } // std::cerr << "serials are: " << (char) *caser.asn << " " < caser.charhex << std::endl; //check that the generated cert is not null and fillin checkme if it is if (cert == NULL) { checkme.isItNaughty = true; //checkme.whatIsNaughty = "Failed to get ssl certificate"; checkme.message_no = 151; checkme.whatIsNaughty = o.language_list.getTranslation(151); checkme.whatIsNaughtyLog = checkme.whatIsNaughty; checkme.whatIsNaughtyCategories = o.language_list.getTranslation(70); justLog = true; } else if (pkey == NULL) { checkme.isItNaughty = true; //checkme.whatIsNaughty = "Failed to load ssl private key"; checkme.message_no = 153; checkme.whatIsNaughty = o.language_list.getTranslation(153); checkme.whatIsNaughtyLog = checkme.whatIsNaughty; checkme.whatIsNaughtyCategories = o.language_list.getTranslation(70); justLog = true; X509_free(cert); cert = NULL; } //startsslserver on the connection to the client //if (!checkme.isItNaughty) if (true) { DEBUG_debug(" -Going SSL on the peer connection"); if (!transparent) { //send a 200 to the client no matter what because they managed to get a connection to us //and we can use it for a blockpage if nothing else std::string msg = "HTTP/1.1 200 Connection established\r\n\r\n"; if (!peerconn.writeString(msg.c_str())) { peerDiag("Unable to send 200 connection established to client ", peerconn); if(cert != NULL) { X509_free(cert); cert = NULL; } return false; } } if (peerconn.startSslServer(cert, pkey, o.cert.set_cipher_list) < 0) { //make sure the ssl stuff is shutdown properly so we display the old ssl blockpage peerconn.stopSsl(); checkme.isItNaughty = true; //checkme.whatIsNaughty = "Failed to negotiate ssl connection to client"; checkme.message_no = 154; checkme.whatIsNaughty = o.language_list.getTranslation(154); checkme.whatIsNaughtyLog = checkme.whatIsNaughty; checkme.whatIsNaughtyCategories = o.language_list.getTranslation(70); justLog = true; if(cert != NULL) { X509_free(cert); cert = NULL; } } } if (proxysock.isOpen()) { // tsslclient connected to the proxy and check the certificate of the server DEBUG_debug(" nf ", checkme.isItNaughty, " upfail ", checkme.upfailure); if (!checkme.isItNaughty) { DEBUG_debug(" -Going SSL on upstream connection "); std::string certpath = std::string(o.cert.ssl_certificate_path); if (proxysock.startSslClient(certpath, checkme.urldomain)) { checkme.isItNaughty = true; //checkme.whatIsNaughty = "Failed to negotiate ssl connection to server"; checkme.message_no = 160; checkme.whatIsNaughty = o.language_list.getTranslation(160); checkme.whatIsNaughtyLog = checkme.whatIsNaughty; checkme.whatIsNaughtyCategories = o.language_list.getTranslation(70); } } DEBUG_debug(" nf ", checkme.isItNaughty, " upfail ", checkme.upfailure); if (!checkme.isItNaughty) { DEBUG_debug(" -Checking certificate"); //will fill in checkme of its own accord if (!checkme.nocheckcert) { checkCertificate(checkme.urldomain, &proxysock, &checkme); checkme.badcert = checkme.isItNaughty; } } } DEBUG_debug(" nf ", checkme.isItNaughty, " upfail ", checkme.upfailure); if ((!checkme.isItNaughty) && (!checkme.upfailure)) { bool writecert = true; if (!certfromcache) { writecert = o.cert.ca->writeCertificate(checkme.urldomain.c_str(), cert, &caser); } //if we cant write the certificate its not the end of the world but it is slow if (!writecert) { E2LOGGER_error("Couldn't save certificate to on disk cache"); } DEBUG_debug(" -Handling connections inside ssl tunnel"); if (authed) { persistent_authed = true; } //handleConnection inside the ssl tunnel handleConnection(peerconn, ip, true, proxysock, dystat); DEBUG_debug(" -Handling connections inside ssl tunnel: done"); } o.cert.ca->free_ca_serial(&caser); //stopssl on the proxy connection //if it was marked as naughty then show a deny page and close the connection if (checkme.isItNaughty || checkme.upfailure) { DEBUG_debug(" -SSL Interception failed ", checkme.whatIsNaughty, " nf ", checkme.isItNaughty, " upfail ", checkme.upfailure); doLog(clientuser, clientip, checkme); if(!justLog) denyAccess(&peerconn, &proxysock, header, docheader, &checkme.logurl, &checkme, &clientuser, &clientip, filtergroup, checkme.ispostblock, checkme.headersent, checkme.wasinfected, checkme.scanerror, checkme.badcert); } DEBUG_debug(" -Shutting down ssl to proxy"); proxysock.stopSsl(); DEBUG_debug(" -Shutting down ssl to client"); peerconn.stopSsl(); //tidy up key and cert if(cert != NULL) { X509_free(cert); cert = NULL; } if(pkey != NULL) { EVP_PKEY_free(pkey); pkey = NULL; } persistProxy = false; proxysock.close(); return true; } bool ConnectionHandler::doAuth(int &auth_result, bool &authed, int &filtergroup, AuthPlugin *auth_plugin, Socket &peerconn, HTTPHeader &header, NaughtyFilter &cm, bool only_client_ip, bool isconnect_like) { Socket nullsock; return doAuth(auth_result, authed, filtergroup, auth_plugin, peerconn, nullsock, header, cm, only_client_ip, isconnect_like); } bool ConnectionHandler::doAuth(int &rc, bool &authed, int &filtergroup, AuthPlugin *auth_plugin, Socket &peerconn, Socket &proxysock, HTTPHeader &header, NaughtyFilter &cm, bool only_client_ip, bool isconnect_like) { DEBUG_debug(" -Not got persistent credentials for this connection - querying auth plugins"); bool dobreak = false; rc = 0; if (o.plugins.authplugins.size() != 0) { // We have some auth plugins load // int authloop = 0; rc = 0; String tmp; int p = peerconn.getPort(); for (std::deque::iterator i = o.plugins.authplugins_begin; i != o.plugins.authplugins_end; i++) { DEBUG_debug(" -Querying next auth plugin..."); // try to get the username & parse the return value auth_plugin = (AuthPlugin *) (*i); if ((only_client_ip && !auth_plugin->client_ip_based) || !auth_plugin->port_matched(p)) continue; // auth plugin selection for multi ports // // // Logic changed to allow auth scan with multiple ports as option to replace auth-port // fixed mapping // rc = auth_plugin->identify(peerconn, proxysock, header, clientuser, is_real_user, SBauth, cm); if (rc == E2AUTH_NOMATCH) { DEBUG_auth("Auth plugin did not find a match; querying remaining plugins"); continue; } else if (rc == E2AUTH_REDIRECT) { DEBUG_auth("Auth plugin told us to redirect client to \"", clientuser, "\"; not querying remaining plugins"); if (isconnect_like) // it is connect or trans https so cannot send redirect { dobreak = true; break; } else { // ident plugin told us to redirect to a login page String writestring("HTTP/1.1 302 Redirect\r\nLocation: "); writestring += clientuser; writestring += "\r\n\r\n"; peerconn.writeString(writestring.toCharArray()); // no action on failure dobreak = true; break; } } else if (rc == E2AUTH_OK_NOPERSIST) { DEBUG_auth("Auth plugin returned OK but no persist not setting persist auth"); overide_persist = true; } else if (rc == E2AUTH_OK_GOT_GROUP) { DEBUG_auth("Auth plugin returned OK_GOT_GROUP "); filtergroup = SBauth.filter_group; authed = true; break; // got user and group so break } else if (rc == E2AUTH_OK_GOT_GROUP_NAME) { DEBUG_auth("Auth plugin returned OK_GOT_GROUP_NAME "); filtergroup = ldl->getFgFromName(SBauth.fg_name); if (filtergroup < 0) { // lookup failed filtergroup = o.filter.default_fg; DEBUG_auth("Group ",SBauth.fg_name, " not known - setting to default"); } SBauth.filter_group = filtergroup; authed = true; break; // got user and group so break } else if (rc == E2AUTH_407_SENT) { DEBUG_auth("Auth plugin has sent 407 so break"); dobreak = true; break; } else if (rc < 0) { E2LOGGER_error("Auth plugin returned error code: ", rc); dobreak = true; break; } DEBUG_auth(" -Auth plugin found username ", clientuser, " (", oldclientuser, "), now determining group"); if (clientuser == oldclientuser) { DEBUG_auth(" -Same user as last time, re-using old group no."); authed = true; filtergroup = oldfg; break; } // try to get the filter group & parse the return value rc = auth_plugin->determineGroup(clientuser, filtergroup, ldl->StoryA, cm); if (rc == E2AUTH_OK) { DEBUG_auth("Auth plugin found username & group; not querying remaining plugins"); authed = true; break; } else if (rc == E2AUTH_NOMATCH) { DEBUG_auth("Auth plugin did not find a match; querying remaining plugins"); clientuser = ""; continue; } else if (rc == E2AUTH_NOGROUP) { if (o.plugins.auth.auth_requires_user_and_group || !is_real_user) { clientuser = ""; SBauth.user_source = ""; continue; } DEBUG_auth("Auth plugin found username \"", clientuser, "\" but no associated group; not querying remaining plugins"); authed = true; break; } else if (rc < 0) { E2LOGGER_error("Auth plugin returned error code: ", rc); dobreak = true; break; } } // end of querying all plugins (for) // break the peer loop if (dobreak) return false; //break; if ((!authed) || (filtergroup < 0) || (filtergroup >= o.filter.numfg)) { #ifdef DEBUG_LOW if (!authed) { DEBUG_auth(" -No identity found; using defaults"); } else { DEBUG_auth(" -Plugin returned out-of-range filter group number; using defaults"); } #endif // If none of the auth plugins currently loaded rely on querying the proxy, // such as 'ident' or 'ip', then pretend we're authed. What this flag // actually controls is whether or not the query should be forwarded to the // proxy (without pre-emptive blocking); we don't want this for 'ident' or // 'ip', because Squid isn't necessarily going to return 'auth required'. // All proxy-auths have been removed in v5.5 so set this to 'true' authed = true; clientuser = "-"; } else { DEBUG_auth(" -Identity found; caching username & group"); if (auth_plugin->is_connection_based && !overide_persist) { DEBUG_auth("Auth plugin is for a connection-based auth method - keeping credentials for entire connection"); persistent_authed = true; } oldclientuser = clientuser; oldfg = filtergroup; } } else { // We don't have any auth plugins loaded DEBUG_auth(" -No auth plugins loaded; using defaults & feigning persistency"); authed = true; clientuser = "-"; //filtergroup = 0; //default group - one day configurable? - default now set before call to doAuth persistent_authed = true; } return true; } bool ConnectionHandler::checkByPass(NaughtyFilter &checkme, std::shared_ptr &ldl, HTTPHeader &header, Socket &proxysock, Socket &peerconn, std::string &clientip) { //first check if bypass allowed and set isbypassallowed checkme.isbypassallowed = (ldl->fg[filtergroup]->bypass_mode != 0); checkme.isinfectionbypassallowed = (ldl->fg[filtergroup]->infection_bypass_mode != 0); checkme.isscanbypassallowed = (ldl->fg[filtergroup]->scan_bypass); if (!(checkme.isbypassallowed || checkme.isinfectionbypassallowed)) return false; // int bypasstimestamp = 0; if (isScanBypassURL(checkme.url, ldl->fg[filtergroup]->magic.c_str(), clientip.c_str())) { DEBUG_debug(" -Scan bypass URL match"); checkme.isscanbypass = true; checkme.isbypass = true; checkme.message_no = 608; checkme.log_message_no = 608; checkme.whatIsNaughty= o.language_list.getTranslation(608); } else if ((ldl->fg[filtergroup]->bypass_mode != 0) || (ldl->fg[filtergroup]->infection_bypass_mode != 0)) { DEBUG_debug(" -About to check for bypass..."); if (ldl->fg[filtergroup]->bypass_mode != 0) checkme.bypasstimestamp = isBypassURL(checkme.logurl, ldl->fg[filtergroup]->magic.c_str(), clientip.c_str(), "GBYPASS=", clientuser); if ((checkme.bypasstimestamp == 0) && (ldl->fg[filtergroup]->infection_bypass_mode != 0)) checkme.bypasstimestamp = isBypassURL(checkme.logurl, ldl->fg[filtergroup]->imagic.c_str(), clientip.c_str(), "GIBYPASS=", clientuser); if (checkme.bypasstimestamp > 0) { if (checkme.isvirusbypass) { DEBUG_debug(" -Infection bypass URL match"); header.chopBypass(checkme.logurl, "GIBYPASS="); } else { DEBUG_debug(" -Filter bypass URL match"); header.chopBypass(checkme.logurl, "GBYPASS="); } if (checkme.bypasstimestamp > 1) { // not expired checkme.isbypass = true; checkme.isexception = true; // checkme: need a TR string for virus bypass checkme.whatIsNaughty = o.language_list.getTranslation(606); checkme.whatIsNaughtyLog = checkme.whatIsNaughty; checkme.message_no = 606; checkme.log_message_no = 606; } } else if (ldl->fg[filtergroup]->bypass_mode != 0) { String ud(checkme.urldomain); if (ud.startsWith("www.")) { ud = ud.after("www."); } if (header.isBypassCookie(ud, ldl->fg[filtergroup]->cookie_magic.c_str(), clientip.c_str(), clientuser.c_str())) { DEBUG_debug(" -Bypass cookie match"); checkme.iscookiebypass = true; checkme.isbypass = true; checkme.isexception = true; checkme.whatIsNaughty = o.language_list.getTranslation(607); checkme.whatIsNaughtyLog = checkme.whatIsNaughty; } } DEBUG_debug(" -Finished bypass checks."); } if (checkme.isbypass) { DEBUG_debug(" -bypass activated:"); } // // End of bypass // // Start of scan by pass // if (checkme.isscanbypass) { //we need to decode the URL and send the temp file with the //correct header to the client then delete the temp file checkme.tempfilename = (checkme.url.after("GSBYPASS=").after("&N=")); checkme.tempfilemime = (checkme.tempfilename.after("&M=")); checkme.tempfiledis = (header.decode(checkme.tempfilemime.after("&D="), true)); DEBUG_debug(" -Original filename: " ); String rtype(header.requestType()); checkme.tempfilemime = checkme.tempfilemime.before("&D="); checkme.tempfilename = o.content.download_dir + "/tf" + checkme.tempfilename.before("&M="); return true; } return false; } bool ConnectionHandler::sendScanFile(Socket &peerconn, NaughtyFilter &checkme, bool is_icap, ICAPHeader *icaphead) { try { checkme.docsize = sendFile(&peerconn, checkme, checkme.url, is_icap, icaphead); checkme.request_header->chopBypass(checkme.url,"GSBYPASS="); checkme.logurl = checkme.request_header->getLogUrl(); doLog(clientuser, checkme.clientip, checkme); if (o.content.delete_downloaded_temp_files) { unlink(checkme.tempfilename.toCharArray()); } } catch ( std::exception &e ) { } // persistProxy = false; // proxysock.close(); // close connection to proxy return true; } void ConnectionHandler::check_search_terms(NaughtyFilter &cm) { if (ldl->fg[filtergroup]->searchterm_limit > 0) { String terms; terms = cm.search_terms; // search terms are URL parameter type "0" urlparams.append("0=").append(terms).append(";"); // Add spaces at beginning and end of block before filtering, so // that the quick & dirty trick of putting spaces around words // (Scunthorpe problem) can still be used, bearing in mind the block // of text here is usually very small. terms.insert(terms.begin(), ' '); terms.append(" "); cm.checkme(terms.c_str(), terms.length(), NULL, NULL, ldl->fg[filtergroup], (ldl->fg[filtergroup]->searchterm_flag ? ldl->fg[filtergroup]->searchterm_list : ldl->fg[filtergroup]->banned_phrase_list), ldl->fg[filtergroup]->searchterm_limit, true); if (cm.isItNaughty) { cm.blocktype = 2; } } return; } void ConnectionHandler::check_content(NaughtyFilter &cm, DataBuffer &docbody, Socket &proxysock, Socket &peerconn, std::deque &responsescanners) { if ((cm.response_header->OKtoFilterMime(ldl->fg[filtergroup]) && !cm.isexception) || !responsescanners.empty()) { cm.waschecked = true; if (!responsescanners.empty()) { DEBUG_debug(" -Filtering with expectation of a possible csmessage");; String csmessage; contentFilter(cm.response_header, cm.request_header, &docbody, &proxysock, &peerconn, &cm.headersent, &cm.pausedtoobig, &cm.docsize, &cm, cm.wasclean, filtergroup, responsescanners, &clientuser, &cm.clientip, &cm.wasinfected, &cm.wasscanned, cm.isbypass, cm.urld, cm.urldomain, &cm.scanerror, cm.contentmodified, &csmessage); if (csmessage.length() > 0) { DEBUG_debug(" -csmessage found: ", csmessage);; cm.whatIsNaughty = csmessage.toCharArray(); } } else { DEBUG_debug(" -Calling contentFilter ");; contentFilter(cm.response_header, cm.request_header, &docbody, &proxysock, &peerconn, &cm.headersent, &cm.pausedtoobig, &cm.docsize, &cm, cm.wasclean, filtergroup, responsescanners, &clientuser, &cm.clientip, &cm.wasinfected, &cm.wasscanned, cm.isbypass, cm.urld, cm.urldomain, &cm.scanerror, cm.contentmodified, NULL); } } else { cm.tunnel_rest = true; } DEBUG_debug("End content check isitNaughty is ", cm.isItNaughty); } int ConnectionHandler::handleProxyTLSConnection(Socket &peerconn, String &ip, Socket &upsconn, stat_rec* &dystat) { struct timeval thestart; gettimeofday(&thestart, NULL); peerconn.setTimeout(o.net.pcon_timeout); X509 *cert = NULL; struct ca_serial caser; caser.asn = NULL; caser.charhex = NULL; caser.filepath = NULL; caser.filename = NULL; EVP_PKEY *pkey = NULL; bool certfromcache = false; //generate the cert DEBUG_debug(" -Getting ssl certificate for client TLS proxy connection"); pkey = o.cert.ca->getServerPkey(); //generate the certificate but dont write it to disk (avoid someone //requesting lots of places that dont exist causing the disk to fill //up / run out of inodes certfromcache = o.cert.ca->getServerCertificate(o.net.TLSproxyCN.c_str(), &cert, &caser, o.net.TLSproxyCN_is_ip); if (caser.asn == NULL) { DEBUG_debug("caser.asn is NULL"); } // std::cerr << "serials are: " << (char) *caser.asn << " " < caser.charhex << std::endl; //check that the generated cert is not null if (cert == NULL) { DEBUG_debug(" cert is NULL for TLS proxy"); return 1; } if (peerconn.startSslServer(cert, pkey, o.cert.set_cipher_list) < 0) { peerconn.stopSsl(); if(cert != NULL) { X509_free(cert); cert = NULL; } return 1; } if(!certfromcache) o.cert.ca->writeCertificate(o.net.TLSproxyCN.c_str(), cert, &caser); // Now create a pipe - push one end onto normal proxy queue and then tunnel between other end and the ssled peerconn int socks[2]; //if (socketpair(AF_UNIX,SOCK_STREAM|SOCK_NONBLOCK, 0, socks) != 0) if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) != 0) { E2LOGGER_error("Unable to create socket pair"); return 1; } Socket *s_inside = new Socket(socks[0]); Socket s_outside(socks[1]); s_inside->setClientAddr(peerconn.getPeerIP(),peerconn.getPeerSourcePort()); s_inside->setPort(peerconn.getPort()); s_inside->down_thread_id = thread_id; //Q for service LQ_rec lq_rec; lq_rec.sock = s_inside; lq_rec.ct_type = CT_PROXY; DEBUG_debug("inside pair socket about to push to Q"); o.http_worker_Q.push(lq_rec); DEBUG_debug("inside pair socket pushed to Q"); //DEBUG_network("about to connect to 8084"); // upsconn.connect("127.0.0.1", 8084); // DEBUG_network("connected to 8084 - starting tunnell"); // and then two way tunnel to outside socket; FDTunnel tunn; tunn.tunnel(peerconn, s_outside, true); DEBUG_network("tunnell finished"); peerconn.stopSsl(); peerconn.close(); // if (s_inside != nullptr) delete s_inside; return 0; } int ConnectionHandler::handleTHTTPSConnection(Socket &peerconn, String &ip, Socket &proxysock, stat_rec* &dystat) { DEBUG_trace(""); struct timeval thestart; gettimeofday(&thestart, NULL); peerconn.setTimeout(o.net.pcon_timeout); HTTPHeader docheader(__HEADER_RESPONSE); // to hold the returned page header from proxy HTTPHeader header(__HEADER_REQUEST); // to hold the incoming client request headeri(ldl) NaughtyFilter checkme(header, docheader, SBauth); checkme.listen_port = peerconn.getPort(); checkme.reset(); std::string clientip(ip.toCharArray()); // hold the clients ip docheader.setClientIP(ip); if (clienthost) delete clienthost; clienthost = NULL; // and the hostname, if available matchedip = false; #define CLIENT_HELLO_MAX_SIZE 16384 DEBUG_thttps(" -got peer connection - clientip is ", clientip); try { int rc; //int oldfg = 0; bool authed = false; bool isbanneduser = false; bool firsttime = true; String sni; bool is_ech = false; AuthPlugin *auth_plugin = NULL; // RFC states that connections are persistent //bool persistOutgoing = true; //bool persistPeer = true; bool persistProxy = false; //bool direct = false; char buff[7]; rc = peerconn.readFromSocket(buff, 6, (MSG_PEEK), 20000, true); DEBUG_thttps("bytes peeked ", rc); unsigned int toread = 0; if (rc == 6) { if (buff[0] == 22 && buff[1] == 3 && buff[2] > 0 && buff[2] < 4 && buff[5] == 1) { // has TLS client hello signature toread = two_bytes_to_short((unsigned char*)&(buff[3])); // unsigned int t3 = buff[4]; // toread = (toread * 256) + t3; if ((toread <= CLIENT_HELLO_MAX_SIZE)&&(toread > 52)) { checkme.isTLS = true; toread += 5; } #ifdef DEBUG_HIGH else { //toread = CLIENT_HELLO_MAX_SIZE + 5; DEBUG_thttps("Too small or Oversized clienthello detected"); } #endif //toread = (buff[3] << (8 * 1) | buff[4]) + 5; } } DEBUG_thttps("hello length is ", toread, " magic is ", buff[0], buff[1], buff[2], " isTLS is ", checkme.isTLS); if (checkme.isTLS) { char buff2[CLIENT_HELLO_MAX_SIZE + 6 ]; //char *buff2 = new char[(toread + 1)]; rc = peerconn.readFromSocket(buff2, toread, (MSG_PEEK), 10000); if (rc < 52) { if (o.conn.logconerror) { if (peerconn.getFD() > -1) { int err = peerconn.getErrno(); //int pport = peerconn.getPeerSourcePort(); std::string peerIP = peerconn.getPeerIP(); if (peerconn.isTimedout()) { DEBUG_thttps("Connection timed out"); } E2LOGGER_error("No TLS header recd from client - errno: ", err); } else { E2LOGGER_info("Client connection closed early - no TLS header received"); } } firsttime = false; //persistPeer = false; } else { DEBUG_thttps("bytes peeked ", rc); get_TLS_SNI(buff2, rc, sni, is_ech); if (!sni.empty()) { checkme.url = sni; checkme.hasSNI = true; } checkme.hasECH = is_ech; } ++dystat->reqs; } //delete[] buff2; get_original_ip_port(peerconn, checkme); if (!checkme.hasSNI) { if (checkme.got_orig_ip) checkme.url = checkme.orig_ip; else // no SNI and no orig_ip - so can't do anything sensible return -1; } DEBUG_thttps("hasSNI = ", checkme.hasSNI, " SNI is ", checkme.url, " Orig IP ", checkme.orig_ip, " hasECH = ", checkme.hasECH, " Orig port ", checkme.orig_port); // checkme.url = sni; checkme.hasSNI = true; // End of set-up section while (firsttime) // do just the once { ldl = o.currentLists(); //DataBuffer docbody; //docbody.setTimeout(o.exchange_timeout); FDTunnel fdt; firsttime = false; // // do all of this normalisation etc just the once at the start. checkme.url = "https://" + checkme.url; checkme.setURL(checkme.url); checkme.nomitm = false; gettimeofday(&checkme.thestart, NULL); // Look up reverse DNS name of client if needed if (o.conn.reverse_client_ip_lookups) { getClientFromIP(clientip.c_str(), checkme.clienthost); } filtergroup = o.filter.default_trans_fg; //if(o.log_requests) if (e2logger.isEnabled(LoggerSource::requestlog)) { std::string fnt = "THTTPS"; doRQLog(clientuser, clientip, checkme, fnt); } checkme.clientip = clientip; //CALL SB pre-authcheck ldl->StoryA.runFunctEntry(ENT_STORYA_PRE_AUTH_THTTPS, checkme); DEBUG_thttps("After StoryA thttps-pre-authcheck", checkme.isexception, " mess_no ", checkme.message_no); checkme.isItNaughty = checkme.isBlocked; bool isbannedip = checkme.isBlocked; // // // Start of Authentication Checks // // // don't have credentials for this connection yet? get some! overide_persist = false; if (!(checkme.isItNaughty || checkme.isexception)) { if (!doAuth(checkme.auth_result, authed, filtergroup, auth_plugin, peerconn, proxysock, header, checkme, true, true)) { if ((checkme.auth_result == E2AUTH_REDIRECT) && ldl->fg[filtergroup]->ssl_mitm) { if (!checkme.nomitm)checkme.gomitm = true; checkme.isdone = true; } else { break; } } } checkme.filtergroup = filtergroup; if (!checkme.nomitm) checkme.nomitm = !ldl->fg[filtergroup]->ssl_mitm; DEBUG_thttps(" -username: ", clientuser, " -filtergroup: ", filtergroup); // // // End of Authentication Checking // // // // // Now check if user or machine is banned and room-based checking // // // is this user banned? isbanneduser = false; if (checkme.hasSNI & !checkme.nomitm) checkme.ismitmcandidate = ldl->fg[filtergroup]->ssl_mitm; if (checkme.ismitmcandidate) { checkme.automitm = ldl->fg[filtergroup]->automitm; } // TODO restore this for THTTPS ?? //if (isbannedip) { // matchedip = clienthost == NULL; //} else { // / if (ldl->inRoom(clientip, room, clienthost, &isbannedip, &part_banned, &checkme.isexception, // / checkme.urld)) { // / DEBUG_thttps(" isbannedip = ", isbannedip, "ispart_banned = ", part_banned, " isexception = ", checkme.isexception); //. if (isbannedip) { // matchedip = clienthost == NULL; // checkme.isBlocked = checkme.isItNaughty = true; // / } // if (checkme.isexception) { // do reason codes etc //checkme.exceptionreason = o.language_list.getTranslation(630); //checkme.exceptionreason.append(room); //checkme.exceptionreason.append(o.language_list.getTranslation(631)); //checkme.message_no = 632; //} //} //} // // Start of exception checking // // being a banned user/IP overrides the fact that a site may be in the exception lists // needn't check these lists in bypass modes if (!(checkme.isdone || isbanneduser || isbannedip || checkme.isexception)) { DEBUG_trace("Check StoryB thttps-checkrequest"); ldl->fg[filtergroup]->StoryB.runFunctEntry(ENT_STORYB_THTTPS_REQUEST, checkme); DEBUG_trace("After StoryB thttps-checkrequest", " isException: ", String(checkme.isexception), " mess_no ", String(checkme.message_no)); if (ldl->fg[filtergroup]->reporting_level != -1) { checkme.isItNaughty = checkme.isBlocked; } else { checkme.isItNaughty = false; checkme.isBlocked = false; } } //now send upstream and get response if (!checkme.isItNaughty && !persistProxy) { int out_port; if (checkme.got_orig_ip && o.conn.use_original_ip_port) out_port = checkme.orig_port; else out_port = 443; if (connectUpstream(proxysock, checkme, out_port) > -1) { if (!checkme.isdirect) { if (sendProxyConnect(checkme.connect_site, &proxysock, &checkme) != 0) { checkme.upfailure = true; proxysock.close(); } } } else { checkme.upfailure = true; } } DEBUG_thttps(" after connectUpstream nf ", checkme.isItNaughty, " upfail ", checkme.upfailure); if ((checkme.isItNaughty || checkme.upfailure) && checkme.automitm && checkme.hasSNI) checkme.gomitm = true; // allows us to send splash page if (checkme.isexception && !checkme.upfailure) { checkme.tunnel_rest = true; } else { //if ismitm - GO MITM if (checkme.gomitm && !checkme.nomitm) { DEBUG_thttps("Going MITM ...."); if (!ldl->fg[filtergroup]->mitm_check_cert) checkme.nocheckcert = true; goMITM(checkme, proxysock, peerconn, persistProxy, authed, persistent_authed, ip, dystat, clientip, true); //persistPeer = false; persistProxy = false; //if (!checkme.isItNaughty) break; } else { if (!checkme.upfailure) checkme.tunnel_rest = true; } } //if not grey tunnel response if (!checkme.isItNaughty && checkme.tunnel_rest) { DEBUG_thttps(" -Tunnelling to client"); if (!fdt.tunnel(proxysock, peerconn, true, -1, true)) persistProxy = false; checkme.docsize += fdt.throughput; } // it is not possible to send splash page on Thttps without MITM so do not try! //Log doLog(clientuser, clientip, checkme); proxysock.close(); // close connection to proxy } } catch (std::exception &e) { DEBUG_thttps(" - THTTPS connection handler caught an exception: ", e.what()); if (o.conn.logconerror) E2LOGGER_error(" - THTTPS connection handler caught an exception %s", e.what()); // close connection to proxy proxysock.close(); return -1; } return 0; } bool ConnectionHandler::get_TLS_SNI(char *inbytes, int len, String &r_sni, bool &is_ech) { auto bytes = reinterpret_cast(inbytes); unsigned char *curr; unsigned char *ebytes; char sni[256]; std::memset(sni,0,256); bool got_sni = false; if (len < 52) return false; ebytes = bytes + len; unsigned short int sid_len = bytes[43]; curr = bytes + 1 + 43 + sid_len; // skip past session id if ((curr + 2) > ebytes) return false; unsigned short cslen = two_bytes_to_short(curr); curr += 2 + cslen; // skip past Cipher Suites if ((curr + 1) > ebytes) return false; unsigned short cmplen = *curr; curr += 1 + cmplen; // skip past Compression methods if (curr > ebytes) return false; unsigned char *maxchar = curr + 2 + two_bytes_to_short(curr); // get pointer to end of extensions + 1 curr += 2; unsigned short ext_type = 1; unsigned short ext_len; unsigned short ext_found = 0; unsigned char *ext_end; while(curr < maxchar && ext_found < 2) //while(curr < maxchar && ext_type != 0) { if (curr > ebytes) break; //if (maxchar > ebytes) return nullptr; ext_type = two_bytes_to_short(curr); curr += 2; if ((curr + 2) > ebytes) break; ext_len = two_bytes_to_short(curr); curr += 2; // pointing at start of extension data ext_end = curr + ext_len; switch (ext_type) { case 0: // Is Server name extension { unsigned short list_len = two_bytes_to_short(curr); curr += 2; if (list_len < 7) break; unsigned char *list_end = curr + list_len; if (*curr != 0) break; // check list entry type is DNS hostname i.e == 0 curr += 1; // pointing at length of DNS hostname if (curr > ebytes) break; unsigned short namelen = two_bytes_to_short(curr); curr += 2; // pointing at DNS hostname unsigned char *name_end = curr + namelen; if (name_end > list_end) break; if (name_end > ebytes) break; if ((namelen > 3) && (namelen < 254)) { // SNI must be at least 4 bytes and no more than 253 std::memcpy(sni, curr, (size_t) namelen); got_sni = true; ext_found++; } curr = ext_end; // read only first SNI } break; //*(name_end) = (char)0; // add null char to terminate string case 65037: // has encrypted client hello is_ech = true; ext_found++; default: curr = ext_end; break; } } if ( got_sni) { r_sni = sni; bool b = r_sni.is_valid_domain(); if(b && ext_found == 1) return true; // i.e no ECH if (!b) { #ifdef DEBUG_HIGH if (!is_ech) { DEBUG_thttps("Invalid SNI received: ", r_sni); } #endif r_sni = ""; // blank SNI as it is invalid } } #ifdef DEBUG_HIGH if (is_ech) { DEBUG_thttps("SNI is encyrpted: dummy SNI in outer CH is not reliable", r_sni); } #endif return false; //SNI was not present or present but with ECH so unreliable } bool ConnectionHandler::get_original_ip_port(Socket &peerconn, NaughtyFilter &checkme) { // get original IP destination & port #ifdef SOL_IP // linux #define SO_ORIGINAL_DST 80 sockaddr_in origaddr; socklen_t origaddrlen(sizeof(sockaddr_in)); if ( getsockopt(peerconn.getFD(), SOL_IP, SO_ORIGINAL_DST, &origaddr, &origaddrlen ) < 0 ) { E2LOGGER_error("Failed to get client's original destination IP: ", strerror(errno)); return false; } else { char res[INET_ADDRSTRLEN]; checkme.orig_ip = inet_ntop(AF_INET,&origaddr.sin_addr,res,sizeof(res)); // if orig_ip == one of our box ip's it is not true transparent so return false so that dns lookup is enabled if (o.net.check_ip.size() > 0) { for (auto it = o.net.check_ip.begin(); it != o.net.check_ip.end(); it++) { if (*it == checkme.orig_ip) { checkme.orig_ip = ""; return false; } } } checkme.orig_port = ntohs(origaddr.sin_port); checkme.got_orig_ip = true; return true; } #else // TODO: BSD code needs adding - depends on firewall being used // assign checkme.orig_ip and checkme.orig_port and return true // or return false on error // return false until BSD code added return false; #endif } int ConnectionHandler::handleICAPConnection(Socket &peerconn, String &ip, Socket &proxysock, stat_rec *&dystat) { #ifdef DEBUG_HIGH int pcount = 0; #else #ifdef DEBUG_LOW int pcount = 0; #endif #endif bool ismitm = false; struct timeval thestart; gettimeofday(&thestart, NULL); peerconn.setTimeout(o.net.pcon_timeout); std::string clientip(ip.toCharArray()); // hold the ICAP clients ip if (clienthost) delete clienthost; clienthost = NULL; // and the hostname, if available matchedip = false; //try { //int rc; //bool authed = false; bool firsttime = true; //bool isbanneduser = true; AuthPlugin *auth_plugin = NULL; // RFC states that connections are persistent bool persistPeer = true; // // End of set-up section // Start of main loop // // maintain a persistent connection while ((firsttime || persistPeer) && !ttg) { ICAPHeader icaphead; ldl = o.currentLists(); icaphead.ISTag = ldl->ISTag(); NaughtyFilter checkme(icaphead.HTTPrequest, icaphead.HTTPresponse, SBauth); checkme.listen_port = peerconn.getPort(); DataBuffer docbody; docbody.setTimeout(o.net.exchange_timeout); docbody.setICAP(true); FDTunnel fdt; String wline = ""; if (firsttime) { // reset flags & objects next time round the loop firsttime = false; gettimeofday(&thestart, NULL); checkme.thestart = thestart; } { // another round... DEBUG_icap(" ICAP -persisting (count ", ++pcount, ")", " Client IP: ", clientip); icaphead.reset(); if (!icaphead.in(&peerconn, true)) { if (peerconn.isTimedout()) { DEBUG_icap( " -ICAP Persistent connection timed out"); //send error response wline = "ICAP/1.0 408 Request timeout\r\n"; wline += "Service: "; wline += PACKAGE_STRING; wline += "\r\n"; wline += "Encapsulated: null-body=0\r\n"; wline += "\r\n"; peerconn.writeString(wline.toCharArray()); break; } else { DEBUG_icap( " -ICAP Persistent connection closed"); // TODO: send error reply if needed break; } } ++dystat->reqs; ip = icaphead.clientip; checkme.clientip = ip; // we will actually need to do *lots* of resetting of flags etc. here for pconns to work gettimeofday(&thestart, NULL); checkme.thestart = thestart; //authed = false; //isbanneduser = false; //requestscanners.clear(); //responsescanners.clear(); matchedip = false; urlparams.clear(); postparts.clear(); checkme.mimetype = "-"; //exceptionreason = ""; //exceptioncat = ""; //room = ""; // CHECK THIS - surely room is persistant????? // reset docheader & docbody // headers *should* take care of themselves on the next in() // actually not entirely true for docheader - we may read // certain properties of it (in denyAccess) before we've // actually performed the next in(), so make sure we do a full // reset now. docbody.reset(); docbody.setICAP(true); peerconn.resetChunk(); } DEBUG_icap("service options enabled : ", wline, " icaphead.service_reqmod: ", icaphead.service_reqmod, " icaphead.service_resmod: ", icaphead.service_resmod, " icaphead.service_options: ", " icaphead.icap_reqmod_service: ", icaphead.icap_reqmod_service, " icaphead.icap_resmod_service: ", icaphead.icap_resmod_service, " icaphead.icap_reqmod_service: ", icaphead.icap_reqmod_service); // Check service option REQMOD, RESMOD, OPTIONS and call apropreate function(s) // if (icaphead.service_reqmod && icaphead.icap_reqmod_service) { DEBUG_icap( "Icap reqmod check "); if (handleICAPreqmod(peerconn,ip, checkme, icaphead, auth_plugin) == 0){ continue; }else{ break; } } else if (icaphead.service_resmod && icaphead.icap_resmod_service) { DEBUG_icap("Icap resmod check "); if (handleICAPresmod(peerconn,ip, checkme, icaphead, docbody) == 0) continue; else break; } else if (icaphead.service_options && icaphead.icap_reqmod_service) { // respond with option response wline = "ICAP/1.0 200 OK\r\n"; wline += "Methods: REQMOD\r\n"; wline += "Service: "; wline += PACKAGE_STRING; wline += "\r\n"; wline += "ISTag: \""; wline += ldl->ISTag(); wline += "\"\r\n"; wline += "Encapsulated: null-body=0\r\n"; wline += "Allow: 204\r\n"; // wline += "Preview: 0\r\n"; wline += "\r\n"; peerconn.writeString(wline.toCharArray()); DEBUG_icap("respmod service options response : ", wline); } else if (icaphead.service_options && icaphead.icap_resmod_service) { // respond with option response wline = "ICAP/1.0 200 OK\r\n"; wline += "Methods: RESPMOD\r\n"; wline += "Service: "; wline += PACKAGE_STRING; wline += "\r\n"; wline += "ISTag:"; wline += ldl->ISTag(); wline += "\r\n"; wline += "Encapsulated: null-body=0\r\n"; wline += "Allow: 204\r\n"; // wline += "Preview: 0\r\n"; wline += "\r\n"; peerconn.writeString(wline.toCharArray()); DEBUG_icap("respmod service options response : ", wline); } else if ((icaphead.service_reqmod && !icaphead.icap_reqmod_service) || (icaphead.service_resmod && !icaphead.icap_resmod_service)) { wline = "ICAP/1.0 405 Method not allowed for service\r\n"; wline += "Service: "; wline += PACKAGE_STRING; wline += "\r\n"; wline += "Encapsulated: null-body=0\r\n"; wline += "\r\n"; peerconn.writeString(wline.toCharArray()); DEBUG_icap("ICAP/1.0 405 Method not allowed for service ", wline); } else { //send error response wline = "ICAP/1.0 400 Bad request\r\n"; wline += "Service: "; wline += PACKAGE_STRING; wline += "\r\n"; peerconn.writeString(wline.toCharArray()); DEBUG_icap("ICAP/1.0 400 Bad request : ", wline); } } // } //catch (std::exception & e) if (!ismitm) try { DEBUG_icap("ICAP -Attempting graceful connection close"); int fd = peerconn.getFD(); if (fd > -1) { if (shutdown(fd, SHUT_WR) == 0) { char buff[2]; peerconn.readFromSocket(buff, 2, 0, 5000); }; }; // close connection to the client peerconn.close(); } catch (std::exception &e) { DEBUG_icap(" -ICAP connection handler caught an exception on connection closedown: ", e.what()); // close connection to the client peerconn.close(); } return 0; } int ConnectionHandler::handleICAPreqmod(Socket &peerconn, String &ip, NaughtyFilter &checkme, ICAPHeader &icaphead, AuthPlugin *auth_plugin) { bool authed = false; String clientip = ip; // do all of this normalisation etc just the once at the start. checkme.setURL(); String res_hdr, res_body, req_hdr, req_body; // checks for bad URLs to prevent security holes/domain obfuscation. if (icaphead.HTTPrequest.malformedURL(checkme.url)) { // The requested URL is malformed. gen_error_mess(peerconn, checkme, res_hdr, res_body, 200, 0, "400 Bad Request"); checkme.isdone = true; icaphead.errorResponse(peerconn, res_hdr, res_body); if (icaphead.req_body_flag) { peerconn.drainChunk(peerconn.getTimeout()); // drains body } return 0; } // // // Start of Authentication Checks // // // don't have credentials for this connection yet? get some! overide_persist = false; filtergroup = o.filter.default_icap_fg; DEBUG_icap("filtergroup set to ICAP default ", filtergroup); clientuser = icaphead.username; //if(o.log_requests) { if (e2logger.isEnabled(LoggerSource::requestlog)) { std::string fnt = "REQMOD"; doRQLog(clientuser, clientip, checkme, fnt); } int rc = E2AUTH_NOUSER; if (!(clientuser.empty() || clientuser == "-")) { SBauth.user_name = clientuser; SBauth.user_source = "icaph"; rc = determineGroup(clientuser, filtergroup, ldl->StoryA, checkme, ENT_STORYA_AUTH_ICAP); DEBUG_icap("filter group set from filtergroupslist: ", clientuser, " ICAP -filtergroup: ", filtergroup); } else { oldclientuser = "user not known"; } if (rc != E2AUTH_OK) { DEBUG_icap("filter group NOT set from filtergroupslist: trying auth plugins"); persistent_authed = false; if (!doAuth(checkme.auth_result, authed, filtergroup, auth_plugin, peerconn, icaphead.HTTPrequest, checkme, true, true)) { DEBUG_icap("error return from doAuth"); //break; // TODO Error return???? } else { DEBUG_icap("OK return from doAuth"); } if (!(icaphead.username.empty() || icaphead.username == "-")) { checkme.user = icaphead.username; // restore username if we had one from icap header clientuser = icaphead.username; // restore username if we had one from icap header } } authed = true; checkme.filtergroup = filtergroup; //int unrealgroup = filtergroup+1; DEBUG_icap("-username: ", clientuser, " ICAP -filtergroup: ", filtergroup); // // // End of Authentication Checking // // // // // Now check if user or machine is banned and room-based checking // // // is this user banned? bool isbanneduser = false; checkme.clientip = clientip; // Look up reverse DNS name of client if needed if (o.conn.reverse_client_ip_lookups) { getClientFromIP(clientip.c_str(),checkme.clienthost); // std::unique_ptr > hostnames; // hostnames.reset(ipToHostname(clientip.c_str())); // checkme.clienthost = std::string(hostnames->front().toCharArray()); } //CALL SB pre-authcheck ldl->StoryA.runFunctEntry(ENT_STORYA_PRE_AUTH_ICAP, checkme); DEBUG_debug("After StoryA icap-pre-authcheck", checkme.isexception, " mess_no ", checkme.message_no); checkme.isItNaughty = checkme.isBlocked; bool isbannedip = checkme.isBlocked; //bool part_banned; if (isbannedip) { // matchedip = clienthost == NULL; } else { #ifdef NOTDEF // TODO does this need restoring??? if (ldl->inRoom(clientip, room, clienthost, &isbannedip, &part_banned, &checkme.isexception, checkme.urld)) { DEBUG_debug("ICAP isbannedip = ", isbannedip, "ispart_banned = ", part_banned, " isexception = ", checkme.isexception); if (isbannedip) { // matchedip = clienthost == NULL; checkme.isBlocked = checkme.isItNaughty = true; } if (checkme.isexception) { // do reason codes etc checkme.exceptionreason = o.language_list.getTranslation(630); checkme.exceptionreason.append(room); checkme.exceptionreason.append(o.language_list.getTranslation(631)); checkme.message_no = 632; } } #endif } // // Start of by pass if (!checkme.is_ssl) { if (checkByPass(checkme, ldl, icaphead.HTTPrequest, peerconn, peerconn, clientip) && sendScanFile(peerconn, checkme, true, &icaphead)) { return 0; // returns only when Scanfile sent. Sets checkme.isbypass if it is a bypass. } } // // End of scan by pass // bool done = false; // // Start of exception checking // // being a banned user/IP overrides the fact that a site may be in the exception lists // needn't check these lists in bypass modes if (!(isbanneduser || isbannedip || checkme.isexception)) { // Main checking is now done in Storyboard function(s) ldl->fg[filtergroup]->StoryB.runFunctEntry(ENT_STORYB_ICAP_REQMOD, checkme); DEBUG_debug("After StoryB checkreqmod", checkme.isexception, " mess_no ", checkme.message_no, " allow_204 : ", icaphead.allow_204); if (ldl->fg[filtergroup]->reporting_level != -1){ checkme.isItNaughty = checkme.isBlocked; } else { checkme.isItNaughty = false; checkme.isBlocked = false; } } if (checkme.isbypass && !(checkme.iscookiebypass || checkme.isvirusbypass)) { DEBUG_debug("Setting GBYPASS cookie; bypasstimestamp = ", checkme.bypasstimestamp); String ud(checkme.urldomain); if (ud.startsWith("www.")) { ud = ud.after("www."); } String outhead = "HTTP/1.1 302 Redirect\r\n"; outhead += "Set-Cookie: GBYPASS="; outhead += icaphead.HTTPrequest.hashedCookie(&ud, ldl->fg[filtergroup]->cookie_magic.c_str(), &clientip, checkme.bypasstimestamp, clientuser).toCharArray(); // outhead += hashedCookie(&ud, ldl->fg[filtergroup]->cookie_magic.c_str(), &clientip, // checkme.bypasstimestamp).toCharArray(); outhead += "; path=/; domain=."; outhead += ud; outhead += "\r\n"; outhead += "Location: "; outhead += icaphead.HTTPrequest.getUrl(true); outhead += "\r\n"; outhead += "\r\n"; icaphead.out_res_header = outhead; icaphead.out_res_hdr_flag = true; icaphead.respond(peerconn); return 0; } // TODO add logic for 204 response etc. if (checkme.isexception || checkme.logcategory) { std::string code; if (checkme.isvirusbypass) code = "V"; else if (checkme.isbypass) code = "Y"; else if (checkme.logcategory) code = "L"; else code = "E"; icaphead.set_icap_com(clientuser,code, filtergroup, checkme.message_no, checkme.log_message_no, checkme.whatIsNaughtyLog); if (icaphead.allow_204) { icaphead.respond(peerconn, "204 No Content", false, false); if (icaphead.req_body_flag) { peerconn.drainChunk(peerconn.getTimeout()); // drains any body } done = true; } else { // pipe through headers and body icaphead.respond(peerconn, "200 OK", true); if (icaphead.req_body_flag) { peerconn.loopChunk(peerconn.getTimeout()); // echos any body } done = true; } } //check for redirect // URL regexp search and edirect if (checkme.urlredirect) { checkme.url = icaphead.HTTPrequest.redirecturl(); String writestring("HTTP/1.1 302 Redirect\r\nLocation: "); writestring += checkme.url; writestring += "\r\n\r\n"; res_hdr = writestring; icaphead.errorResponse(peerconn, res_hdr, res_body); if (icaphead.req_body_flag) { peerconn.drainChunk(peerconn.getTimeout()); // drains any body } done = true; } //if is a search - content check search terms if (!done && !checkme.isdone && checkme.isGrey && checkme.isSearch) check_search_terms(checkme); // will set isItNaughty if needed // check for CONNECT redirect if ((icaphead.HTTPrequest.requestType() == "CONNECT") && checkme.urlmodified) { // DEBUG_debug("is CONNECT logurl:", checkme.logurl, " conn site:", checkme.connect_site, " fullurl:", checkme.baseurl, " urldomain:", checkme.urldomain); if (checkme.connect_site != checkme.urldomain) { icaphead.HTTPrequest.setConnect(checkme.connect_site); // DEBUG_debug("after setURL logurl:", checkme.logurl, " conn site:", checkme.connect_site, " fullurl:", checkme.baseurl, " urldomain:", checkme.urldomain); } } // TODO V5 call POST scanning code New NaughtyFilter function???? if (!done && checkme.isItNaughty) { if (genDenyAccess(peerconn, res_hdr, res_body, &icaphead.HTTPrequest, &icaphead.HTTPresponse, &checkme.logurl, &checkme, &clientuser, &clientip, filtergroup, checkme.ispostblock, checkme.headersent, checkme.wasinfected, checkme.scanerror)) { icaphead.errorResponse(peerconn, res_hdr, res_body); if (icaphead.req_body_flag) { peerconn.drainChunk(peerconn.getTimeout()); // drains any body } done = true; DEBUG_debug("ICAP Naughty"); // break loop "// maintain a persistent connection" // return 1; }; } if (!done) { icaphead.set_icap_com(clientuser, "G", filtergroup, checkme.message_no, checkme.log_message_no, checkme.whatIsNaughtyLog); icaphead.respond(peerconn, "200 OK", true); if (icaphead.req_body_flag) { peerconn.loopChunk(peerconn.getTimeout()); // echoes any body } } //Log doLog(clientuser, clientip, checkme); return 0; } int ConnectionHandler::handleICAPresmod(Socket &peerconn, String &ip, NaughtyFilter &checkme, ICAPHeader &icaphead, DataBuffer &docbody) { //bool authed = false; bool persistPeer = true; bool done = false; String clientip = ip; String res_hdr, res_body; std::deque responsescanners; // do all of this normalisation etc just the once at the start. checkme.setURL(); overide_persist = false; if (icaphead.icap_com.filtergroup < 0) //i.e. no X-ICAP-E2G { String wline = "ICAP/1.0 418 Bad composition - X-ICAP-E2G header not present\r\n"; wline += "Service: "; wline += PACKAGE_STRING; wline += "\r\n"; wline += "Encapsulated: null-body=0\r\n"; wline += "\r\n"; peerconn.writeString(wline.toCharArray()); DEBUG_icap(" ICAP Error: ", wline); return 1; } filtergroup = icaphead.icap_com.filtergroup; checkme.filtergroup = icaphead.icap_com.filtergroup; clientuser = icaphead.icap_com.user; docbody.set_current_config(ldl->fg[filtergroup]); if (icaphead.icap_com.EBG == "E") { // exception checkme.isexception = true; checkme.message_no = icaphead.icap_com.mess_no; checkme.log_message_no = icaphead.icap_com.log_mess_no; checkme.whatIsNaughtyLog = icaphead.icap_com.mess_string; } else if (icaphead.icap_com.EBG == "G") // grey - content check checkme.isGrey = true; else if (icaphead.icap_com.EBG == "Y") { // ordinary bypass checkme.isbypass = true; checkme.isexception = true; checkme.message_no = icaphead.icap_com.mess_no; checkme.log_message_no = icaphead.icap_com.log_mess_no; checkme.whatIsNaughtyLog = icaphead.icap_com.mess_string; } else if (icaphead.icap_com.EBG == "V") { // virus bypass checkme.isvirusbypass = true; checkme.isbypass = true; checkme.isexception = true; checkme.message_no = icaphead.icap_com.mess_no; checkme.log_message_no = icaphead.icap_com.log_mess_no; checkme.whatIsNaughtyLog = icaphead.icap_com.mess_string; } else if (icaphead.icap_com.EBG == "L") { // only log - do not block checkme.isexception = true; checkme.logcategory = true; checkme.message_no = icaphead.icap_com.mess_no; checkme.log_message_no = icaphead.icap_com.log_mess_no; checkme.whatIsNaughtyLog = icaphead.icap_com.mess_string; } //int unrealfiltergroup = filtergroup + 1; DEBUG_icap("ICAP Respmod enabled - username: ", clientuser, " -filtergroup: ", filtergroup, " icaphead.icap_com.EBG: ", icaphead.icap_com.EBG, " icaphead.res_body_flag: ", icaphead.res_body_flag); checkme.clientip = ip; checkme.filtergroup = filtergroup; //if(o.log_requests) { if (e2logger.isEnabled(LoggerSource::requestlog)) { std::string fnt = "RESPMOD"; doRQLog(clientuser, clientip, checkme, fnt); } // Look up reverse DNS name of client if needed if (o.conn.reverse_client_ip_lookups) { getClientFromIP(clientip.c_str(), checkme.clienthost); } //bool part_banned; // virus checkichurchillng candidate? // checkme.noviruscheck defaults to true DEBUG_icap("Virus scan checkme.isexception: ", checkme.isexception, " checkme.noviruscheck: ", checkme.noviruscheck, " content_scan_exceptions: ", ldl->fg[filtergroup]->content_scan_exceptions, " checkme.isBlocked: ", checkme.isBlocked, " disable_content_scan: ", ldl->fg[filtergroup]->disable_content_scan, " csplugins: ", o.plugins.csplugins.size() ); if (icaphead.res_body_flag // can only scan if body present && !(checkme.isBlocked) // or not already blocked && (o.plugins.csplugins.size() > 0) // and we have scan plugins && !ldl->fg[filtergroup]->disable_content_scan // and is not disabled && !(checkme.isexception && !ldl->fg[filtergroup]->content_scan_exceptions) && !checkme.isvirusbypass // and is not virus bypass // and not exception unless scan exceptions enable ) { checkme.noviruscheck = false; // note this may be reset by Storyboard to enable exceptions } // // being a banned user/IP overrides the fact that a site may be in the exception lists // needn't check these lists in bypass modes if (!(checkme.isexception) || !checkme.noviruscheck) { // Main checking is done in Storyboard function(s) ldl->fg[filtergroup]->StoryB.runFunctEntry(ENT_STORYB_ICAP_RESMOD,checkme); DEBUG_icap("After StoryB icapcheckresmod", checkme.isexception, " mess_no ", checkme.message_no, " checkme.noviruscheck: ", checkme.noviruscheck, " content_scan_exceptions: ", ldl->fg[filtergroup]->content_scan_exceptions); if (ldl->fg[filtergroup]->reporting_level != -1){ checkme.isItNaughty = checkme.isBlocked; } else { checkme.isItNaughty = false; checkme.isBlocked = false; } } if (checkme.isexception && !checkme.noviruscheck && !ldl->fg[filtergroup]->content_scan_exceptions) checkme.noviruscheck = true; if (ldl->fg[filtergroup]->content_scan_exceptions && checkme.isexception) checkme.noviruscheck = false; if ((checkme.isexception && checkme.noviruscheck)|| !icaphead.res_body_flag) { if (icaphead.allow_204) { icaphead.respond(peerconn, "204 No Content", false, false); if (icaphead.res_body_flag) { peerconn.drainChunk(peerconn.getTimeout()); // drains any body } } else { // pipe through headers and body icaphead.respond(peerconn, "200 OK", true); if (icaphead.res_body_flag) { peerconn.loopChunk(peerconn.getTimeout()); // echos any body } } done = true; } // should now only be left with grey which has content body //- if grey content check // can't do content filtering on HEAD or redirections (no content) // actually, redirections CAN have content if (!done && !checkme.isItNaughty) { if(!checkme.noviruscheck) { for (std::deque::iterator i = o.plugins.csplugins_begin; i != o.plugins.csplugins_end; ++i) { int csrc = ((CSPlugin *)(*i))->willScanRequest(checkme.url, clientuser.c_str(), ldl->fg[filtergroup], clientip.c_str(), false, false, checkme.isexception, checkme.isbypass); if (csrc > 0) responsescanners.push_back((CSPlugin *)(*i)); else if (csrc < 0) E2LOGGER_error("willScanRequest returned error: ", csrc); } } check_content(checkme, docbody,peerconn, peerconn,responsescanners); } //send response header to client if (!done && !checkme.isItNaughty) { icaphead.respond(peerconn, "200 OK", true); if(checkme.waschecked) { if (!docbody.out(&peerconn)) checkme.pausedtoobig = false; if (checkme.pausedtoobig) checkme.tunnel_rest = true; } if (checkme.tunnel_rest){ peerconn.loopChunk(peerconn.getTimeout()); // echos any body done = true; } } if(checkme.isItNaughty) { if(genDenyAccess(peerconn,res_hdr, res_body, &icaphead.HTTPrequest, &icaphead.HTTPresponse, &checkme.logurl, &checkme, &clientuser, &ip, filtergroup, checkme.ispostblock,checkme.headersent, checkme.wasinfected, checkme.scanerror)) { icaphead.errorResponse(peerconn, res_hdr, res_body); if (icaphead.res_body_flag) { peerconn.drainChunk(peerconn.getTimeout()); // drains any body } done = true; } persistPeer = false; } //Log if (checkme.isItNaughty) { // don't log requests to the web server & and normal response checkme.whatIsNaughtyLog = "ICAP Response filtering: "; checkme.whatIsNaughtyLog += checkme.whatIsNaughty; } else { checkme.nolog = true; } doLog(clientuser, clientip, checkme); if (persistPeer) return 0; else return 1; } // determine what filter group the given username is in // return -1 when user not found int ConnectionHandler::determineGroup(std::string &user, int &fg, StoryBoard &story, NaughtyFilter &cm, int story_entry) { if (user.length() < 1 || user == "-") { return E2AUTH_NOMATCH; } cm.user = user; if (!story.runFunctEntry(story_entry, cm)) { DEBUG_debug("User not in filter groups list for: icap "); return E2AUTH_NOGROUP; } DEBUG_debug("Group found for: ", user.c_str(), " in icap "); fg = cm.filtergroup; return E2AUTH_OK; } unsigned short ConnectionHandler::two_bytes_to_short(unsigned char *bytes) { unsigned short local_res = (*bytes); unsigned short two = *(bytes + 1); local_res = (local_res * 256) + two; return local_res; } e2guardian-5.5.8r/src/ConnectionHandler.hpp000066400000000000000000000201121477372360500206430ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_CONNECTIONHANDLER #define __HPP_CONNECTIONHANDLER // INCLUDES #include #include #include "OptionContainer.hpp" #include "FOptionContainer.hpp" #include "LOptionContainer.hpp" #include "NaughtyFilter.hpp" #include "Socket.hpp" #include "HTTPHeader.hpp" #include "ICAPHeader.hpp" #include "FatController.hpp" #include "Auth.hpp" // DECLARATIONS // add a known clean URL to the cache void addToClean(String &url, const int fg); // record for storing information about POST data parts // used for building up the POST data log column struct postinfo { // MIME type & original filename (if available) std::string mimetype; std::string filename; // name of file containing headers & body info // for this POST part (if it has been stored) std::string storedname; // size of part size_t size; // offset of body data from start of file // (if post part was stored on disk) size_t bodyoffset; bool blocked; postinfo() : size(0), bodyoffset(0), blocked(false){}; }; // the ConnectionHandler class - handles filtering, scanning, and blocking of // data passed between a client and the external proxy. class ConnectionHandler { public: ConnectionHandler(); // : clienthost(NULL) { // ch_isiphost.comp(",*[a-z|A-Z].*"); // ldl = o.currentList(); // load_id = ldl->reload_id; //} ~ConnectionHandler() { delete clienthost; }; int load_id; // pass data between proxy and client, filtering as we go. int handlePeer(Socket &peerconn, String &ip, stat_rec* &dystat, unsigned int LC_type); auth_rec SBauth; // record persists for whole connection private: int filtergroup; int oldfg = 0; bool matchedip; bool persistent_authed; bool overide_persist; bool is_real_user = false; std::string clientuser; std::string oldclientuser; std::string *clienthost; std::string urlparams; std::list postparts; String lastcategory; std::shared_ptr ldl; void peerDiag(const char *message, Socket &peersock ); void upstreamDiag(const char *message, Socket &proxysock ); int handleConnection(Socket &peerconn, String &ip, bool ismitm, Socket &proxyconn, stat_rec* &dystat); int handleProxyTLSConnection(Socket &peerconn, String &ip, Socket &upssock, stat_rec* &dystat); int handleTHTTPSConnection(Socket &peerconn, String &ip, Socket &proxysock, stat_rec* &dystat); int handleICAPConnection(Socket &peerconn, String &ip, Socket &proxysock, stat_rec* &dystat); int handleICAPreqmod(Socket &peerconn, String &ip, NaughtyFilter &checkme, ICAPHeader &icaphead, AuthPlugin *auth_plugin) ; int handleICAPresmod(Socket &peerconn, String &ip, NaughtyFilter &checkme, ICAPHeader &icaphead, DataBuffer &docbody) ; bool get_original_ip_port(Socket &peerconn, NaughtyFilter &checkme); bool getdnstxt(std::string &clientip, String &user); String dns_error(int herror); // write a log entry containing the given data (if required) // void doLog(std::string &who, std::string &from, String &where, unsigned int &port, // std::string &what, String &how, off_t &size, std::string *cat, bool isnaughty, int naughtytype, // bool isexception, bool istext, struct timeval *thestart, bool cachehit, int code, // std::string &mimetype, bool wasinfected, bool wasscanned, int naughtiness, int filtergroup, // HTTPHeader *reqheader, int message_no = 999, bool contentmodified = false, // bool urlmodified = false, bool headermodified = false, // bool headeradded = false); void doLog(std::string &who, std::string &from, NaughtyFilter &cm); void doRQLog(std::string &who, std::string &from, NaughtyFilter &cm, std::string &funct); bool goMITM(NaughtyFilter &checkme, Socket &proxysock, Socket &peerconn,bool &persistProxy, bool &authed, bool &persistent_authed, String &ip, stat_rec* &dystat, std::string &clientip, bool transparent = false); // perform URL encoding on a string std::string miniURLEncode(const char *s); // RegExp ch_isiphost; // RegResult Rch_isiphost; bool genDenyAccess(Socket &peerconn, String &eheader, String &ebody, HTTPHeader *header, HTTPHeader *docheader, String *url, NaughtyFilter *checkme, std::string *clientuser, std::string *clientip, int filtergroup, bool ispostblock, int headersent, bool wasinfected, bool scanerror, bool forceshow = false); // show the relevant banned page depending upon the report level settings, request type, etc. bool denyAccess(Socket *peerconn, Socket *proxysock, HTTPHeader *header, HTTPHeader *docheader, String *url, NaughtyFilter *checkme, std::string *clientuser, std::string *clientip, int filtergroup, bool ispostblock, int headersent, bool wasinfected, bool scanerror, bool forceshow = false); // create temporary ban bypass URLs/cookies String hashedURL(String *url, int filtergroup, std::string *clientip, bool infectionbypass, std::string *user); // is this a temporary filter bypass URL? int isBypassURL(String url, const char *magic, const char *clientip, std::string btype, std::string &user); // is this a scan bypass URL? (download previously scanned file) bool isScanBypassURL(String url, const char *magic, const char *clientip); String hashedCookie(String *url, const char *magic, std::string *clientip, int bypasstimestamp); // do content scanning (AV filtering) and naughty filtering void contentFilter(HTTPHeader *docheader, HTTPHeader *header, DataBuffer *docbody, Socket *proxysock, Socket *peerconn, int *headersent, bool *pausedtoobig, off_t *docsize, NaughtyFilter *checkme, bool wasclean, int filtergroup, std::deque &responsescanner, std::string *clientuser, std::string *clientip, bool *wasinfected, bool *wasscanned, bool isbypass, String &url, String &domain, bool *scanerror, bool &contentmodified, String *csmessage); // send a file to the client - used during bypass of blocked downloads off_t sendFile(Socket *peerconn, NaughtyFilter &cm, String &url, bool is_icap = false, ICAPHeader *icap_head = NULL); bool writeback_error( NaughtyFilter &cm, Socket & cl_sock, int mess_no1, int mess_no2, std::string mess); bool gen_error_mess( Socket &peerconn, NaughtyFilter &cm, String &eheader, String &ebody, int mess_no1, int mess_no2, std::string mess); bool doAuth(int &auth_result, bool &authed, int &filtergroup,AuthPlugin* auth_plugin, Socket & peerconn, Socket &proxysock, HTTPHeader & header, NaughtyFilter &cm, bool only_client_ip = false, bool isconnect_like = false); bool doAuth(int &auth_result, bool &authed, int &filtergroup,AuthPlugin* auth_plugin, Socket & peerconn, HTTPHeader & header, NaughtyFilter &cm, bool only_client_ip = false, bool isconnect_like = false); bool checkByPass( NaughtyFilter &checkme, std::shared_ptr & ldl, HTTPHeader &header, Socket & proxysock, Socket &peerconn, std::string &clientip ); bool sendScanFile( Socket &peerconn, NaughtyFilter &checkme, bool is_icap = false, ICAPHeader *icaphead = NULL); void check_search_terms(NaughtyFilter &cm); void check_content(NaughtyFilter &cm, DataBuffer &docbody, Socket &proxysock, Socket &peerconn, std::deque &responsescanners); //ssl certificat checking void checkCertificate(String &hostname, Socket *sslSock, NaughtyFilter *checkme); int sendProxyConnect(String &hostname, Socket *sock, NaughtyFilter *checkme); int determineGroup(std::string &user, int &fg, StoryBoard & uglc, NaughtyFilter &checkme, int story_entry); int connectUpstream(Socket &sock, NaughtyFilter &cm,int port); unsigned short two_bytes_to_short(unsigned char *bytes); bool get_TLS_SNI(char *bytes, int len, String &sni,bool &is_ech); }; #endif e2guardian-5.5.8r/src/ContentScanner.cpp000066400000000000000000000275461477372360500202070ustar00rootroot00000000000000// Implements CSPlugin class and cs_plugin_loader base class // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #include #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "String.hpp" #include "HTTPHeader.hpp" #include "NaughtyFilter.hpp" #include "ContentScanner.hpp" #include "ConfigVar.hpp" #include "OptionContainer.hpp" #include "Logger.hpp" #include #include #include #include #include #include #include #include // GLOBALS extern OptionContainer o; // find the class factory functions for the CS plugins we've been configured to build #ifdef ENABLE_CLAMD extern cscreate_t clamdcreate; #endif #ifdef ENABLE_AVASTD extern cscreate_t avastdcreate; #endif #ifdef ENABLE_ICAP extern cscreate_t icapcreate; #endif #ifdef ENABLE_KAVD extern cscreate_t kavdcreate; #endif #ifdef ENABLE_COMMANDLINE extern cscreate_t commandlinecreate; #endif // IMPLEMENTATION // CSPlugin class CSPlugin::CSPlugin(ConfigVar &definition) : scanpost(false) { cv = definition; } // start the plugin - i.e. read in the configuration int CSPlugin::init(void *args) { if (cv["scanpost"] == "on") scanpost = true; else scanpost = false; if (!readStandardLists()) { //always return E2CS_ERROR; //include } // these return E2CS_OK; } // make a temporary file for storing data which is to be scanned // returns FD in int and saves filename to String pointer // filename is not used as input int CSPlugin::makeTempFile(String *filename) { int tempfilefd; String tempfilepath(o.content.download_dir.c_str()); tempfilepath += "/csXXXXXX"; char *tempfilepatharray = new char[tempfilepath.length() + 1]; strcpy(tempfilepatharray, tempfilepath.toCharArray()); // mode_t mask = umask(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); // this mask is reversed umask(0007); // only allow access to e2g user and group if ((tempfilefd = mkstemp(tempfilepatharray)) < 1) { E2LOGGER_error("Could not create temp file for contentscanner."); tempfilefd = -1; } else { (*filename) = tempfilepatharray; } delete[] tempfilepatharray; return tempfilefd; } // write a temporary file containing the memory buffer which is to be scanned // if your CS plugin does not have the ability to scan memory directly (e.g. clamdscan), this gets used by the default scanMemory to turn it into a file int CSPlugin::writeMemoryTempFile(const char *object, unsigned int objectsize, String *filename) { int tempfd = makeTempFile(filename); // String gets modified if (tempfd < 0) { E2LOGGER_error("Error creating temp file in writeMemoryTempFile."); return E2CS_ERROR; } errno = 0; DEBUG_avscan("About to writeMemoryTempFile ", (*filename), " size: ", objectsize); while (true) { if (write(tempfd, object, objectsize) < 0) { if (errno == EINTR) { continue; // was interupted by a signal so restart } } break; // end the while } close(tempfd); // finished writing so close file return E2CS_OK; // all ok } // default implementation of scanMemory, which defers to scanFile. int CSPlugin::scanMemory(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc, const char *ip, const char *object, unsigned int objectsize, NaughtyFilter *checkme, const String *disposition, const String *mimetype) { // there is no capability to scan memory with some AV as we pass it // a file name to scan. So we save the memory to disk and pass that. // Then delete the temp file. String tempfilepath; if (writeMemoryTempFile(object, objectsize, &tempfilepath) != E2CS_OK) { E2LOGGER_error("Error creating/writing temp file for scanMemory."); return E2CS_SCANERROR; } int rc = scanFile(requestheader, docheader, user, foc, ip, tempfilepath.toCharArray(), checkme, disposition, mimetype); #ifndef DEBUG_LOW unlink(tempfilepath.toCharArray()); // delete temp file #endif return rc; } // read in all the lists of various things we do not wish to scan bool CSPlugin::readStandardLists() // this is now done in Storyboard { return true; #ifdef NOTDEF exceptionvirusmimetypelist.reset(); // incase this is a reload exceptionvirusextensionlist.reset(); exceptionvirussitelist.reset(); exceptionvirusurllist.reset(); if (!exceptionvirusmimetypelist.readItemList(cv["exceptionvirusmimetypelist"].toCharArray(), false, 0)) { E2LOGGER_error("Error opening exceptionvirusmimetypelist"); return false; } exceptionvirusmimetypelist.doSort(false); if (!exceptionvirusextensionlist.readItemList(cv["exceptionvirusextensionlist"].toCharArray(), false, 0)) { E2LOGGER_error("Error opening exceptionvirusextensionlist"); return false; } exceptionvirusextensionlist.doSort(false); if (!exceptionvirussitelist.readItemList(cv["exceptionvirussitelist"].toCharArray(), false, 0)) { E2LOGGER_error("Error opening exceptionvirussitelist"); return false; } exceptionvirussitelist.doSort(false); if (!exceptionvirusurllist.readItemList(cv["exceptionvirusurllist"].toCharArray(), true, 0)) { E2LOGGER_error("Error opening exceptionvirusurllist"); return false; } exceptionvirusurllist.doSort(true); return true; #endif } // Test whether or not a particular request's incoming/outgoing data should be scanned. // This is an early-stage (request headers only) test; no other info is known about // the actual data itself when this is called. int CSPlugin::willScanRequest(const String &url, const char *user, FOptionContainer* &foc, const char *ip, bool post, bool reconstituted, bool exception, bool bypass) { // Most content scanners only deal with original, unmodified content if (reconstituted) { DEBUG_avscan("willScanRequest: ignoring reconstituted data"); return E2CS_NOSCAN; } // Deal with POST file uploads conditionally, but subject only to the "scanpost" // option, not to the domain & URL lists - uploading files does not have the same // implications as downlaoding them. if (post) { if (scanpost) { DEBUG_avscan("willScanRequest: I'm interested in uploads"); return E2CS_NEEDSCAN; } else { DEBUG_avscan("willScanRequest: Not interested in uploads"); return E2CS_NOSCAN; } } // all list checkng is now done in Storyboard request // and filetype checking in Storyboard response // return E2CS_NEEDSCAN; #ifdef NOTDEF String urld(HTTPHeader::decode(url)); String lc; urld.removeWhiteSpace(); urld.toLower(); urld.removePTP(); String domain, tempurl, foundurl, path; unsigned int fl; if (urld.contains("/")) { domain = urld.before("/"); path = "/" + urld.after("/"); path.hexDecode(); path.realPath(); } else { domain = urld; } // Don't scan the web server which hosts the access denied page if (((foc->reporting_level == 1) || (foc->reporting_level == 2)) && domain.startsWith(foc->access_denied_domain)) { DEBUG_avscan("willScanRequest: ignoring our own webserver"); return E2CS_NOSCAN; } // exceptionvirussitelist tempurl = domain; while (tempurl.contains(".")) { if (exceptionvirussitelist.findInList(tempurl.toCharArray(), lc) != NULL) { DEBUG_avscan("willScanRequest: ignoring exception virus site"); return E2CS_NOSCAN; // exact match } tempurl = tempurl.after("."); // check for being in higher level domains } if (tempurl.length() > 1) { // allows matching of .tld tempurl = "." + tempurl; if (exceptionvirussitelist.findInList(tempurl.toCharArray(), lc) != NULL) { DEBUG_avscan("willScanRequest: ignoring exception virus site"); return E2CS_NOSCAN; // exact match } } // exceptionvirusurllist tempurl = domain + path; if (tempurl.endsWith("/")) { tempurl.chop(); // chop off trailing / if any } while (tempurl.before("/").contains(".")) { char *i = exceptionvirusurllist.findStartsWith(tempurl.toCharArray(),lc ); if (i != NULL) { foundurl = i; fl = foundurl.length(); if (tempurl.length() > fl) { unsigned char c = tempurl[fl]; if (c == '/' || c == '?' || c == '&' || c == '=') { DEBUG_avscan("willScanRequest: ignoring exception virus URL"); return E2CS_NOSCAN; // matches /blah/ or /blah/foo but not /blahfoo } } else { DEBUG_avscan("willScanRequest: ignoring exception virus URL"); return E2CS_NOSCAN; // exact match } } tempurl = tempurl.after("."); // check for being in higher level domains } DEBUG_avscan("willScanRequest: I'm interested"); return E2CS_NEEDSCAN; #endif } // Test whether or not a particular request's incoming/outgoing data should be scanned. // This is a later-stage test; info is known about the actual data itself when this is called. int CSPlugin::willScanData(const String &url, const char *user, FOptionContainer* &foc, const char *ip, bool post, bool reconstituted, bool exception, bool bypass, const String &disposition, const String &mimetype, off_t size) { // this function is no longer required as mime/extension exceptions are now handled by Storyboard response return E2CS_NEEDSCAN; // match } //set the blocking information void CSPlugin::blockFile(std::string *_category, std::string *_message, NaughtyFilter *checkme) { std::string category; std::string message; if (_category == NULL) { category = "Content scanning"; } else { category = *_category; } if (_message == NULL) { message = lastvirusname.toCharArray(); } else { message = *_message; } checkme->whatIsNaughty = o.language_list.getTranslation(1100); if (message.length() > 0) { checkme->whatIsNaughty += " "; checkme->whatIsNaughty += message.c_str(); } checkme->whatIsNaughtyLog = checkme->whatIsNaughty; checkme->whatIsNaughtyCategories = category.c_str(); checkme->isItNaughty = true; checkme->isException = false; } // take in a configuration file, find the CSPlugin class associated with the plugname variable, and return an instance CSPlugin *cs_plugin_load(const char *pluginConfigPath) { ConfigVar cv; if (cv.readVar(pluginConfigPath, "=") > 0) { E2LOGGER_error("Unable to load plugin config ", pluginConfigPath); return NULL; } String plugname(cv["plugname"]); if (plugname.length() < 1) { E2LOGGER_error("Unable read plugin config plugname variable %s",pluginConfigPath); return NULL; } #ifdef ENABLE_CLAMD if (plugname == "clamdscan") { DEBUG_avscan("Enabling ClamDscan CS plugin"); return clamdcreate(cv); } #endif #ifdef ENABLE_AVASTD if (plugname == "avastdscan") { DEBUG_avscan("Enabling AvastDscan CS plugin"); return avastdcreate(cv); } #endif #ifdef ENABLE_KAVD if (plugname == "kavdscan") { DEBUG_avscan("Enabling KAVDscan CS plugin"); return kavdcreate(cv); } #endif #ifdef ENABLE_ICAP if (plugname == "icapscan") { DEBUG_avscan("Enabling ICAPscan CS plugin"); return icapcreate(cv); } #endif #ifdef ENABLE_COMMANDLINE if (plugname == "commandlinescan") { DEBUG_avscan("Enabling command-line CS plugin"); return commandlinecreate(cv); } #endif E2LOGGER_error("Unable to load plugin ", pluginConfigPath); return NULL; } e2guardian-5.5.8r/src/ContentScanner.hpp000066400000000000000000000113271477372360500202020ustar00rootroot00000000000000// Defines the class interface to be implemented by ContentScanner plugins // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_CONTENTSCANNER #define __HPP_CONTENTSCANNER // INCLUDES #include "NaughtyFilter.hpp" #include "String.hpp" #include "ConfigVar.hpp" #include "DataBuffer.hpp" #include "Socket.hpp" #include "HTTPHeader.hpp" #include "ListContainer.hpp" #include "Plugin.hpp" #include // DEFINES #define E2CS_OK 0 #define E2CS_ERROR -1 #define E2CS_WARNING 3 #define E2CS_NOSCAN 0 #define E2CS_NEEDSCAN 1 #define E2CS_TESTERROR -1 #define E2CS_CLEAN 0 #define E2CS_SCANERROR -1 #define E2CS_INFECTED 1 //#define E2CS_CURED 2 // not used #define E2CS_BLOCKED 4 #define E2CS_MAX 5 // use values above this for custom return codes // DECLARATIONS class CSPlugin; // class factory functions for CS plugins typedef CSPlugin *cscreate_t(ConfigVar &); // CS plugin interface proper - to be implemented by plugins themselves class CSPlugin : public Plugin { public: //constructor with CS plugin configuration passed in CSPlugin(ConfigVar &definition); virtual ~CSPlugin(){}; // Test for whether or nor a particular ContentScanner is likely to be interested // in scanning data associated with the given HTTP request. If "post" is true, // the data which will be scanned is outgoing (i.e. POST requests - file uploads & // form submissions). If "reconstituted" is true, the data which will be passed // in is not exactly as it appeared in the request (i.e. URL-encoded form data, // but with form control names and URL encoding stripped away to leave a single // block of text). virtual int willScanRequest(const String &url, const char *user, FOptionContainer* &foc, const char *ip, bool post, bool reconstituted, bool exception, bool bypass); // Test whether, in addition to the above, a particular ContentScanner is actually // interested in the data we have for it. This is split into a separate function // because, for incoming data (as opposed to POST data), we do not have the // information necessary for this test until the response has been received. // Content disposition & MIME type will be empty if not available, and size will be -1 if // not known. // Will not be called for request/response data where willScanRequest previously // returned false. virtual int willScanData(const String &url, const char *user, FOptionContainer* &foc, const char *ip, bool post, bool reconstituted, bool exception, bool bypass, const String &disposition, const String &mimetype, off_t size); // scanning functions themselves // docheader will be NULL if the data is from a POST request, rather than a response // disposition & MIME type may be NULL or empty strings, in which case docheader should be checked for them (if it is not NULL itself) virtual int scanMemory(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user,FOptionContainer* &foc , const char *ip, const char *object, unsigned int objectsize, NaughtyFilter *checkme, const String *disposition = NULL, const String *mimetype = NULL); virtual int scanFile(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc , const char *ip, const char *filename, NaughtyFilter *checkme, const String *disposition = NULL, const String *mimetype = NULL) = 0; const String &getLastMessage() { return lastmessage; }; const String &getLastVirusName() { return lastvirusname; }; // start, restart and stop the plugin virtual int init(void *args); virtual int quit() { return E2CS_OK; }; private: // lists of all the various things we may not want to scan // ListContainer exceptionvirusmimetypelist; // ListContainer exceptionvirusextensionlist; // ListContainer exceptionvirussitelist; // ListContainer exceptionvirusurllist; protected: ConfigVar cv; String lastmessage; String lastvirusname; // whether or not to AV scan POST uploads bool scanpost; void blockFile(std::string *_category, std::string *_message, NaughtyFilter *checkme); // read in scan exception lists bool readStandardLists(); // make & write to temp files, primarily for plugins with no direct memory scanning capability (e.g. clamdscan) int makeTempFile(String *filename); int writeMemoryTempFile(const char *object, unsigned int objectsize, String *filename); }; // Return an instance of the plugin defined in the given configuration file CSPlugin *cs_plugin_load(const char *pluginConfigPath); #endif e2guardian-5.5.8r/src/DataBuffer.cpp000066400000000000000000000560711477372360500172610ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "DataBuffer.hpp" #include "HTTPHeader.hpp" #include "OptionContainer.hpp" #include "Logger.hpp" #include #include #include #include #include #include #include #include #include #include // DEFINES #define __E2HEADER_SENDALL 0 #define __E2HEADER_SENDFIRSTLINE 1 #define __E2HEADER_SENDREST 2 // GLOBALS extern OptionContainer o; // IMPLEMENTATION DataBuffer::DataBuffer() : data(new char[1]), data_length(0) { data[0] = '\0'; } DataBuffer::DataBuffer(const void *indata, off_t length) //not used! PIP : data(new char[length]), data_length(length) { memcpy(data, indata, length); } void DataBuffer::reset() { delete[] data; delete[] compresseddata; data = new char[1]; data[0] = '\0'; compresseddata = nullptr; data_length = 0; buffer_length = 0; compressed_buffer_data_length = 0; if (tempfilefd > -1) { close(tempfilefd); if (!preservetemp) { unlink(tempfilepath.toCharArray()); } tempfilefd = -1; tempfilesize = 0; } bytesalreadysent = 0; dontsendbody = false; preservetemp = false; decompress = ""; chunked = false; icap = false; } // delete the memory block when the class is destroyed DataBuffer::~DataBuffer() { delete[] data; if (compresseddata != nullptr) { delete[] compresseddata; compresseddata = nullptr; } if (tempfilefd > -1) { close(tempfilefd); if (!preservetemp) { unlink(tempfilepath.toCharArray()); } tempfilefd = -1; tempfilesize = 0; } } void DataBuffer::set_current_config (FOptionContainer *newfgc) { fgc = newfgc; } //} // swap back to a compressed version of the data, if one exists // also delete uncompressed version // if body was decompressed but not modified, this can save bandwidth void DataBuffer::swapbacktocompressed() { if (compresseddata != nullptr && compressed_buffer_data_length> 0) { delete[] data; data_length = compressed_buffer_data_length; data = compresseddata; compresseddata = nullptr; compressed_buffer_data_length = 0; DEBUG_network("Compressed data size ", data_length); } } int DataBuffer::readInFromSocket(Socket *sock, int size, bool wantall, int &result) { int rc; DEBUG_trace("size:", size, " wantall:", wantall); if (size < 1) { E2LOGGER_error("read request is negative"); return -1; } if (wantall) { if (!swappedtodisk) { // if not swapped to disk and file is too large for RAM, then swap to disk if (data_length > o.content.max_content_ramcache_scan_size) { DEBUG_debug("swapping to disk"); tempfilefd = getTempFileFD(); if (tempfilefd < 0) { E2LOGGER_error("error buffering to disk so skipping disk buffering"); result = DB_TOBIG; return -1; } write(tempfilefd, data, data_length); swappedtodisk = true; tempfilesize = data_length; } } else if (tempfilesize > o.content.max_content_filecache_scan_size) { // if swapped to disk and file too large for that too, then give up DEBUG_debug("defaultdm: file too big to be scanned, halting download"); result = DB_TOBIG | DB_TOBIG_SCAN; return -1; } } else { if (data_length > o.content.max_content_filter_size) { // if we aren't downloading for virus scanning, and file too large for filtering, give up DEBUG_debug("defaultdm: file too big to be filtered, halting download"); result = DB_TOBIG | DB_TOBIG_FILTER; return -1; } } DEBUG_debug("swappedtodisk:", swappedtodisk); if (!swappedtodisk) { if (size > (buffer_length - data_length)) { if(!increase_buffer(size - (buffer_length - data_length))) { size = (buffer_length - data_length); if (size == 0) { result = DB_TOBIG; return -1; // it is too big } } } if (chunked) { DEBUG_debug("readChunk:", data_length, ",", size); rc = sock->readChunk((data + data_length), size, timeout); } else { DEBUG_debug("bufferReadFromSocket:", data_length, ",", size); rc = bufferReadFromSocket(sock, (data + data_length), size, timeout); } if (rc <= 0) { if (chunked) got_all = true; return -1; // an error occurred so end the while() // or none received so pipe iis closed or chunking has ended } else { bytes_toget -= rc; data_length += rc; data[data_length] = '\0'; } } else { if (chunked) { rc = sock->readChunk(data, buffer_length, timeout); } else { rc = bufferReadFromSocket(sock, data, // if not getting everything until connection close, grab only what is left (!geteverything && (bytes_toget < buffer_length) ? bytes_toget : buffer_length), timeout); } if (rc <= 0) { if (chunked) got_all = true; result = 0; return 0; } else { bytes_toget -= rc; write(tempfilefd, data, rc); tempfilesize += rc; DEBUG_debug("written to disk:", rc, " total:", tempfilesize); } } result = 0; DEBUG_debug("rc=", rc, " bytes_toget=", bytes_toget, " data_length=", data_length); return rc; } bool DataBuffer::increase_buffer(int extra) { int more = 65536; if (extra > more) more = extra; if ((buffer_length + more) > o.content.max_content_filter_size) { more = o.content.max_content_filter_size - buffer_length; } if (more > 0) { char *temp = new char[buffer_length + more + 1]; // replacement store temp[buffer_length + more] = '\0'; memcpy(temp, data, data_length); // copy the current data delete[] data; // delete the current data block data = temp; temp = nullptr; buffer_length += more; // update data size counter DEBUG_debug("data buffer extended by ", more, " to ", buffer_length); return true; } return false; } // a much more efficient reader that does not assume the contents of // the buffer gets filled thus reducing memcpy()ing and new()ing int DataBuffer::bufferReadFromSocket(Socket *sock, char *buffer, int size, int sockettimeout) { int pos = 0; int rc; while (pos < size) { rc = sock->readFromSocket(&buffer[pos], size - pos, 0, sockettimeout, true); if (rc < 1) { // none recieved or an error if (pos > 0) { return pos; // some was recieved previous into buffer } return rc; // just return with the return code } pos += rc; } return size; // full buffer } // a much more efficient reader that does not assume the contents of // the buffer gets filled thus reducing memcpy()ing and new()ing. // in addition to the actual socket timeout, used for each individual read, this version // incorporates a "global" timeout within which all reads must complete. int DataBuffer::bufferReadFromSocket(Socket *sock, char *buffer, int size, int sockettimeout, int stimeout) { DEBUG_trace(""); int pos = 0; int rc; struct timeval starttime; struct timeval nowadays; gettimeofday(&starttime, NULL); while (pos < size) { if (chunked) { rc = sock->readChunk(&buffer[pos], size - pos,sockettimeout ); } else { rc = sock->readFromSocket(&buffer[pos], size - pos, 0, sockettimeout, true); } if (rc < 1) { // none recieved or an error if (pos > 0) { return pos; // some was recieved previous into buffer } return rc; // just return with the return code } pos += rc; gettimeofday(&nowadays, NULL); if (nowadays.tv_sec - starttime.tv_sec > stimeout) { DEBUG_debug("buffered socket read more than timeout"); return pos; // just return how much got so far then } } return size; // full buffer } // make a temp file and return its FD. only currently used in DM plugins. int DataBuffer::getTempFileFD() { if (tempfilefd > -1) { return tempfilefd; } tempfilepath = o.content.download_dir.c_str(); tempfilepath += "/tfXXXXXX"; char *tempfilepatharray = new char[tempfilepath.length() + 1]; strcpy(tempfilepatharray, tempfilepath.toCharArray()); umask(0007); // only allow access to e2g user and group if ((tempfilefd = mkstemp(tempfilepatharray)) < 0) { E2LOGGER_error("Could not create temp file to store download for scanning: ", strerror(errno)); tempfilefd = -1; tempfilepath = ""; } else { tempfilepath = tempfilepatharray; } delete[] tempfilepatharray; return tempfilefd; } // check the client's user agent, see if we have a DM plugin compatible with it, and use it to download the body of the given request bool DataBuffer::in(Socket *sock, Socket *peersock, HTTPHeader *requestheader, HTTPHeader *docheader, bool runav, int *headersent,StoryBoard &story, NaughtyFilter *cm) { //Socket *sock = where to read from //Socket *peersock = browser to send stuff to for keeping it alive //HTTPHeader *requestheader = header client used to request //HTTPHeader *docheader = header used for sending first line of reply //bool runav = to determine if limit is av or not //int *headersent = to use to send the first line of header if needed // or to mark that the header has already been sent // so we know if we only partially downloaded from // upstream so later, if allowed, we can send the rest bool toobig = false; // match request to download manager so browsers potentially can have a prettier version // and software updates, stream clients, etc. can have a compatible version. //int rc = 0; int j = 0; // int rc = -1; DEBUG_trace(""); for (std::deque::iterator i = o.plugins.dmplugins_begin; i != o.plugins.dmplugins_end; i++) { ++j; if ((i + 1) == o.plugins.dmplugins_end) { DEBUG_debug("Got to final download manager so defaulting to always match."); dm_plugin = (DMPlugin *)(*i); dm_plugin->in(this, sock, peersock, requestheader, docheader, runav, headersent, &toobig); break; } else { dm_plugin = (DMPlugin *)(*i); if (story.runFunctEntry(dm_plugin->story_entry, *cm)) { DEBUG_debug("Matching download manager number: ", j); dm_plugin->in(this, sock, peersock, requestheader, docheader, runav, headersent, &toobig); break; } } j++; } return toobig; } // send the request body to the client after having been handled by a DM plugin bool DataBuffer::out(Socket *sock) { if (dontsendbody) { DEBUG_debug("dontsendbody true; not sending"); return true; } // if (!(*sock).readyForOutput(timeout)) return false; // exceptions on timeout or error DEBUG_trace(""); if (tempfilefd > -1) { // must have been too big for ram so stream from disk in blocks DEBUG_debug("Sending ", tempfilesize - bytesalreadysent, " bytes from temp file (", bytesalreadysent, " already sent)");; off_t sent = bytesalreadysent; int rc; if (lseek(tempfilefd, bytesalreadysent, SEEK_SET) < 0) return false; int block_len; if(chunked) block_len = 4048; else block_len = data_length; while (sent < tempfilesize) { rc = read(tempfilefd, data, block_len); DEBUG_debug("reading temp file rc:", rc); if (rc < 0) { DEBUG_debug("error reading temp file so throwing exception"); return false; } if (rc == 0) { DEBUG_debug("got zero bytes reading temp file"); break; // should never happen } // as it's cached to disk the buffer must be reasonably big if(chunked) { if (!sock->writeChunk(data, rc, timeout)) return false; } else { if (!sock->writeToSocket(data, rc, 0, timeout)) { return false; } } sent += rc; DEBUG_debug("total sent from temp:", sent); } if (chunked && got_all) { String n; if (!sock->writeChunkTrailer(n)) return false; } close(tempfilefd); tempfilefd = -1; tempfilesize = 0; unlink(tempfilepath.toCharArray()); } else { off_t sent = bytesalreadysent; DEBUG_debug("Sending ", data_length, " bytes from RAM (", buffer_length, " in buffer; ", bytesalreadysent, " already sent)" ); // it's in RAM, so just send it, no streaming from disk int block_len; if(chunked) block_len = 4048; else block_len = data_length; if (data_length != 0) { while (sent < data_length) { if( block_len > (data_length - sent)) block_len = (data_length - sent); if (chunked) { if (!sock->writeChunk(data + sent, block_len, timeout)) { DEBUG_network("writeChunk failed after ", sent, " bytes"); return false; } } else { if (!sock->writeToSocket(data + sent, data_length - sent, 0, timeout)) return false; } sent += block_len; } if (chunked && got_all) { String n; if (!sock->writeChunkTrailer(n)) return false; } } else { if(chunked) { if (!sock->writeChunk(data + bytesalreadysent, 0, timeout)) return false; } else { if (!sock->writeToSocket("\r\n\r\n", 4, 0, timeout)) return false; } } DEBUG_debug("Sent ", data_length - bytesalreadysent," bytes from RAM (", buffer_length); } return true; } // zlib decompression void DataBuffer::zlibinflate(bool header) { if (data_length < 12) { return; // it can't possibly be zlib'd } DEBUG_debug("compressed size:", data_length); #if ZLIB_VERNUM < 0x1210 #warning ************************************ #warning For gzip support you need zlib 1.2.1 #warning or later to be installed. #warning You can ignore this warning but #warning internet bandwidth may be wasted. #warning ************************************ if (header) { return; } #endif int newsize = data_length * 5; // good estimate of deflated HTML char *block = new char[newsize]; block[0] = '\0'; char *temp = NULL; off_t bytesgot = 0; int err; z_stream d_stream; d_stream.zalloc = (alloc_func)0; d_stream.zfree = (free_func)0; d_stream.opaque = (voidpf)0; d_stream.next_in = (Bytef *)data; d_stream.avail_in = data_length; d_stream.next_out = (Bytef *)block; d_stream.avail_out = newsize; // inflate either raw zlib, or possibly gzip with a header if (header) { err = inflateInit2(&d_stream, 15 + 32); } else { err = inflateInit2(&d_stream, -15); } if (err != Z_OK) { // was a problem so just return delete[] block; // don't forget to free claimed memory DEBUG_debug("bad init inflate: ", err); return; } while (true) { DEBUG_debug("inflate loop"); err = inflate(&d_stream, Z_SYNC_FLUSH); bytesgot = d_stream.total_out; if (err == Z_STREAM_END) { err = inflateEnd(&d_stream); if (err != Z_OK) { delete[] block; DEBUG_debug("bad inflateEnd: ", d_stream.msg); return; } break; } if (err != Z_OK) { // was a problem so just return delete[] block; // don't forget to free claimed memory DEBUG_debug("bad inflate: ", d_stream.msg); err = inflateEnd(&d_stream); if (err != Z_OK) { DEBUG_debug("bad inflateEnd: ", d_stream.msg); } return; } if (bytesgot > o.content.max_content_filter_size) { delete[] block; // don't forget to free claimed memory DEBUG_debug("inflated file larger than maxcontentfiltersize, not inflating further"); err = inflateEnd(&d_stream); if (err != Z_OK) { DEBUG_debug("bad inflateEnd: ", d_stream.msg); } return; } // inflation is going ok, but we don't have enough room in the output buffer newsize = bytesgot * 2; temp = new char[newsize]; memcpy(temp, block, bytesgot); delete[] block; block = temp; temp = NULL; d_stream.next_out = (Bytef *)(block + bytesgot); d_stream.avail_out = newsize - bytesgot; } compresseddata = data; compressed_buffer_data_length = data_length; // change from buffer_length buffer_length = bytesgot; DEBUG_debug("decompressed size: ", data_length); data = new char[bytesgot + 1]; data[bytesgot] = '\0'; memcpy(data, block, bytesgot); data_length = bytesgot; delete[] block; } // Does a regexp search and replace. struct newreplacement { int match; String replacement; }; bool DataBuffer::contentRegExp(FOptionContainer* &foc) { DEBUG_debug("Starting content reg exp replace"); bool contentmodified = false; unsigned int i; unsigned int j, k, m; unsigned int s = (*foc).content_regexp_list_comp.size(); unsigned int matches; unsigned int submatch, submatches; RegExp *re; RegResult Rre; String *replacement; unsigned int replen; int sizediff; char *newblock; char *dstpos; unsigned int srcoff; unsigned int nextoffset; unsigned int matchlen; std::queue matchqueue; for (i = 0; i < s; i++) { re = &((*foc).content_regexp_list_comp[i]); if (re->match(data, Rre)) { replacement = &((*foc).content_regexp_list_rep[i]); //replen = replacement->length(); matches = Rre.numberOfMatches(); sizediff = 0; m = 0; for (j = 0; j < matches; j++) { srcoff = Rre.offset(j); matchlen = Rre.length(j); // Count matches for ()'s for (submatches = 0; j + submatches + 1 < matches; submatches++) if (Rre.offset(j + submatches + 1) + Rre.length(j + submatches + 1) > srcoff + matchlen) break; // \1 and $1 replacement // store match no. and default (empty) replacement string newreplacement *newrep = new newreplacement; newrep->match = j; newrep->replacement = ""; // iterate over regex's replacement string for (k = 0; k < replacement->length(); k++) { // look for \1..\9 and $1..$9 if (((*replacement)[k] == '\\' || (*replacement)[k] == '$') && (*replacement)[k + 1] >= '1' && (*replacement)[k + 1] <= '9') { // determine match number submatch = (*replacement)[++k] - '0'; // add submatch contents to replacement string if (submatch <= submatches) { newrep->replacement += Rre.result(j + submatch).c_str(); } } else { // unescape \\ and \$, and add other non-backreference characters if ((*replacement)[k] == '\\' && ((*replacement)[k + 1] == '\\' || (*replacement)[k + 1] == '$')) k++; newrep->replacement += replacement->subString(k, 1); } } matchqueue.push(newrep); // update size difference between original and modified content sizediff -= Rre.length(j); sizediff += newrep->replacement.length(); // skip submatches to next top level match j += submatches; m++; } // now we know eventual size of content-replaced block, allocate memory for it newblock = new char[data_length + sizediff + 1]; newblock[data_length + sizediff] = '\0'; srcoff = 0; dstpos = newblock; matches = m; DEBUG_debug("content matches:", matches); // replace top-level matches using filled-out replacement strings newreplacement *newrep; for (j = 0; j < matches; j++) { newrep = matchqueue.front(); nextoffset = Rre.offset(newrep->match); if (nextoffset > srcoff) { memcpy(dstpos, data + srcoff, nextoffset - srcoff); dstpos += nextoffset - srcoff; srcoff = nextoffset; } replen = newrep->replacement.length(); memcpy(dstpos, newrep->replacement.toCharArray(), replen); dstpos += replen; srcoff += Rre.length(newrep->match); delete newrep; matchqueue.pop(); } if (srcoff < data_length) { memcpy(dstpos, data + srcoff, data_length - srcoff); } delete[] data; data = newblock; data_length = data_length + sizediff; contentmodified = true; } } return contentmodified; } void DataBuffer::setChunked(bool ch = true) { chunked = ch; return; } void DataBuffer::setICAP(bool ch = true) { chunked = ch; icap = ch; return; } e2guardian-5.5.8r/src/DataBuffer.hpp000066400000000000000000000063211477372360500172570ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_DATABUFFER #define __HPP_DATABUFFER #include #include #include #include "Socket.hpp" #include "String.hpp" #include "FOptionContainer.hpp" //#include "LOptionContainer.hpp" #include "StoryBoard.hpp" #define DB_TOBIG 1 #define DB_TOBIG_SCAN 2 #define DB_TOBIG_FILTER 4 class DMPlugin; class DataBuffer { public: char *data; off_t data_length = 0; off_t buffer_length = 0; char *compresseddata= nullptr; off_t compressed_buffer_data_length = 0; off_t tempfilesize = 0; String tempfilepath; bool dontsendbody = false; // used for fancy download manager for example bool chunked = false; bool icap = false; bool got_all = false; // used with chunked it all read-in int tempfilefd = -1; // std::shared_ptr ldl; FOptionContainer *fgc; // the download manager we used during the last "in" DMPlugin *dm_plugin; DataBuffer(); DataBuffer(const void *indata, off_t length); ~DataBuffer(); bool increase_buffer(int extra); void set_current_config (FOptionContainer *newfgc); int length() { return data_length; }; void copyToMemory(char *location) { memcpy(location, data, data_length); }; // read body in from proxy // gives true if it pauses due to too much data bool in(Socket *sock, Socket *peersock, class HTTPHeader *requestheader, class HTTPHeader *docheader, bool runav, int *headersent, StoryBoard &story, NaughtyFilter *cm); // send body to client bool out(Socket *sock); void setTimeout(int t) { timeout = t; stimeout = t / 1000; }; void setDecompress(String d) { decompress = d; }; // swap back to compressed version of body data (if data was decompressed but not modified; saves bandwidth) void swapbacktocompressed(); // content regexp search and replace bool contentRegExp(FOptionContainer* &foc); // create a temp file and return its FD - NOT a simple accessor function int getTempFileFD(); void setChunked(bool ch); void setICAP(bool ch); void reset(); private: // DM plugins do horrible things to our innards - this is acceptable pending a proper cleanup friend class DMPlugin; friend class dminstance; friend class fancydm; friend class trickledm; int timeout = 20000; // in msecs int stimeout = 20; // in secs off_t bytesalreadysent = 0; off_t bytes_toget = 0; bool geteverything = false; bool swappedtodisk = false; bool doneinitialdelay = false; bool preservetemp = false; String decompress; void zlibinflate(bool header); int readInFromSocket(Socket *sock, int size, bool wantall, int &result); // buffered socket reads - one with an extra "global" timeout within which all individual reads must complete int bufferReadFromSocket(Socket *sock, char *buffer, int size, int sockettimeout); int bufferReadFromSocket(Socket *sock, char *buffer, int size, int sockettimeout, int timeout); }; #endif e2guardian-5.5.8r/src/DownloadManager.cpp000066400000000000000000000132561477372360500203160ustar00rootroot00000000000000// Implements dm_plugin_load and base DMPlugin methods // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "DownloadManager.hpp" #include "ConfigVar.hpp" #include "OptionContainer.hpp" #include "ConnectionHandler.hpp" #include "RegExp.hpp" #include "Logger.hpp" #include #include #include #include #include // GLOBALS extern OptionContainer o; extern dmcreate_t defaultdmcreate; // find the class factory functions for any DM plugins we've been configured to build extern dmcreate_t fancydmcreate; extern dmcreate_t trickledmcreate; // IMPLEMENTATION // // DMPlugin // // constructor DMPlugin::DMPlugin(ConfigVar &definition) : alwaysmatchua(false), cv(definition) { } // default initialisation procedure int DMPlugin::init(void *args) { return 0; } // default method for sending the client a download link bool DMPlugin::sendLink(Socket &peersock, String &linkurl, String &prettyurl) { // 1220 "

Scan complete.

Click here to download: " String message(o.language_list.getTranslation(1220)); message += "" + prettyurl + "

\n"; return peersock.writeString(message.toCharArray()); } #ifdef NOTDEF // default method for deciding whether we will handle a request bool DMPlugin::willHandle(HTTPHeader *requestheader, HTTPHeader *docheader) { // match user agent first (quick) RegResult Rre; if (!(alwaysmatchua || ua_match.match(requestheader->userAgent().toCharArray(),Rre))) return false; // then check standard lists (mimetypes & extensions) // mimetypes String mimetype(""); bool matchedmime = false; if (mimelistenabled) { mimetype = docheader->getContentType(); DEBUG_dwload("mimetype: ", mimetype); String lc; if (mimetypelist.findInList(mimetype.toCharArray(), lc) == NULL) { if (!extensionlistenabled) return false; } else matchedmime = true; } else { DEBUG_dwload("NO mimelistenabled!"); } if (extensionlistenabled && !matchedmime) { // determine the extension String path(requestheader->decode(requestheader->getUrl())); path.removeWhiteSpace(); path.toLower(); path.removePTP(); path = path.after("/"); path.hexDecode(); path.realPath(); String disposition(docheader->disposition()); String extension; if (disposition.length() > 2) { extension = disposition; while (extension.contains(".")) { extension = extension.after("."); } extension = "." + extension; } else { if (!path.contains("?")) { extension = path; } else { if (mimetype.length() == 0) mimetype = docheader->getContentType(); if (mimetype.contains("application/")) { extension = path; if (extension.contains("?")) { extension = extension.before("?"); } } } } DEBUG_dwload("extension: ", extension); // check the extension list String lc; if (!extension.contains(".") || (extensionlist.findEndsWith(extension.toCharArray(), lc) == NULL)) return matchedmime; } else { DEBUG_dwload("NO extensionlistenabled!"); } return true; } #endif #ifdef NOTDEF // read in all the lists of various things we wish to handle bool DMPlugin::readStandardLists() { mimetypelist.reset(); // incase this is a reload extensionlist.reset(); String filename(cv["managedmimetypelist"]); if (filename.length() > 0) { if (!mimetypelist.readItemList(filename.toCharArray(),"", false, 0)) { E2LOGGER_error("Error opening managedmimetypelist"); return false; } mimetypelist.doSort(false); mimelistenabled = true; } else { mimelistenabled = false; } filename = cv["managedextensionlist"]; if (filename.length() > 0) { if (!extensionlist.readItemList(filename.toCharArray(), "", false, 0)) { E2LOGGER_error("Error opening managedextensionlist"); return false; } extensionlist.doSort(false); extensionlistenabled = true; } else { extensionlistenabled = false; } return true; } #endif // take in a DM plugin configuration file, find the DMPlugin descendent matching the value of plugname, and store its class factory funcs for later use DMPlugin *dm_plugin_load(const char *pluginConfigPath) { ConfigVar cv; if (cv.readVar(pluginConfigPath, "=") > 0) { E2LOGGER_error("Unable to load plugin config: ", pluginConfigPath); return NULL; } String plugname(cv["plugname"]); if (plugname.length() < 1) { E2LOGGER_error("Unable read plugin config plugname variable: ", pluginConfigPath); return NULL; } if (plugname == "default") { DEBUG_dwload("Enabling default DM plugin"); return defaultdmcreate(cv); } #ifdef FANCY_READY // Plug-in not working yet if (plugname == "fancy") { DEBUG_dwload("Enabling fancy DM plugin"); return fancydmcreate(cv); } #endif if (plugname == "trickle") { DEBUG_dwload("Enabling trickle DM plugin"); return trickledmcreate(cv); } E2LOGGER_error("Unable to load plugin: ", plugname); return NULL; } e2guardian-5.5.8r/src/DownloadManager.hpp000066400000000000000000000045301477372360500203160ustar00rootroot00000000000000//Defines the DMPlugin base class, and dm_plugin_loader function // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_DOWNLOADMANAGER #define __HPP_DOWNLOADMANAGER // INCLUDES #include "String.hpp" #include "ConfigVar.hpp" #include "Socket.hpp" #include "HTTPHeader.hpp" #include "ListContainer.hpp" #include "FOptionContainer.hpp" #include "DataBuffer.hpp" #include "Plugin.hpp" #include // DECLARATIONS class DMPlugin; // class factory functions for DM plugins typedef DMPlugin *dmcreate_t(ConfigVar &); // the DMPlugin interface - inherit & implement this to make download managers class DMPlugin : public Plugin { public: DMPlugin(ConfigVar &definition); virtual ~DMPlugin(){}; // plugin initialise/quit routines. // if lastplugin is true, this is being loaded as the fallback option, // and needn't load in purely request matching related options. virtual int init(void *args); virtual int quit() { return 0; }; // will this download manager handle this request? // virtual bool willHandle(HTTPHeader *requestheader, HTTPHeader *docheader); // download the body for the given request virtual int in(DataBuffer *d, Socket *sock, Socket *peersock, HTTPHeader *requestheader, HTTPHeader *docheader, bool wantall, int *headersent, bool *toobig) = 0; // send a download link to the client (the actual link, and the clean "display" version of the link) virtual bool sendLink(Socket &peersock, String &linkurl, String &prettyurl); int story_entry; private: // regular expression for matching supported user agents RegExp ua_match; // if there isn't one, set this flag bool alwaysmatchua; protected: // our configuration values // derived classes could definitely have a use for these ConfigVar cv; // standard lists //ListContainer mimetypelist; //ListContainer extensionlist; // .. and their enable flags //bool mimelistenabled; //bool extensionlistenabled; // read managedmimetypelist and managedextensionlist //bool readStandardLists(); }; // create an instance of the plugin given in the configuration file DMPlugin *dm_plugin_load(const char *pluginConfigPath); #endif e2guardian-5.5.8r/src/DynamicIPList.cpp000066400000000000000000000066141477372360500177250ustar00rootroot00000000000000// DynamicIPList - maintains a sorted list of IP addresses, for checking & // limiting the number of concurrent proxy users. // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "DynamicIPList.hpp" #include "Logger.hpp" #include #include #include #include #include #include // GLOBALS extern thread_local std::string thread_id; // IMPLEMENTATION // constructor - store our options & allocate our lists DynamicIPList::DynamicIPList(int maxitems, int maxitemage) : data(new unsigned long int[maxitems]), datatime(new unsigned long int[maxitems]), size(maxitems), items(0), maxage(maxitemage) { } // delete the memory block when the class is destryed DynamicIPList::~DynamicIPList() { delete[] data; delete[] datatime; } // store the timestamp for the given entry, used to determine age during purge void DynamicIPList::stamp(unsigned int pos) { datatime[pos] = time(NULL); } // binary search for the given item int DynamicIPList::search(int a, int s, unsigned long int ip) { if (a > s) return (-1 - a); int m = (a + s) / 2; unsigned long int i = data[m]; if (ip == i) return m; if (ip < i) return search(m + 1, s, ip); if (a == s) return (-1 - a); return search(a, m - 1, ip); } // remove entries older then maxage void DynamicIPList::purgeOldEntries() { if (items < 1) return; unsigned long int timenow = time(NULL); for (int i = 0; i < items; i++) { if ((timenow - datatime[i]) > (unsigned)maxage) { data[i] = 0; } } empties(); } // search for given item in list // -ve if not found 0-(pos + 1) is where it would go // 0 to size if found int DynamicIPList::posInList(unsigned long int ip) { #ifdef DEBUG_LOW DEBUG_debug("****** ip cache table ******"); DEBUG_debug("items: ", items); int d; for (d = 0; d < items; d++) { DEBUG_debug(data[d]); } DEBUG_debug("****** ip cache table ******"); #endif if (items == 0) { return -1; } return search(0, items - 1, ip); } // return whether or not given IP is in/could be added to list // (i.e. returns false if list already full & this IP's not in it) bool DynamicIPList::inList(unsigned long int ip) { // is item already in list? int pos = posInList(ip); if (pos > -1) { stamp(pos); return true; } // is list full? if (items >= size) { return false; } // list isn't full, and IP not already there, so add it pos = 0 - pos - 1; int i; for (i = items; i > pos; i--) { data[i] = data[i - 1]; datatime[i] = datatime[i - 1]; } data[pos] = ip; stamp(pos); items++; return true; } // shuffle the list to remove gaps left by a purge void DynamicIPList::empties() { int decrement = 0; unsigned long int t; int i; if (data[0] == 0) { decrement = 1; } for (i = 1; i < items; i++) { t = data[i]; if (t == 0) { decrement++; } else { if (decrement > 0) { data[i - decrement] = t; datatime[i - decrement] = datatime[i]; } } } items -= decrement; } e2guardian-5.5.8r/src/DynamicIPList.hpp000066400000000000000000000026411477372360500177260ustar00rootroot00000000000000// DynamicIPList - maintains a sorted list of IP addresses, for checking & // limiting the number of concurrent proxy users. // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_DYNAMICIPLIST #define __HPP_DYNAMICIPLIST // DECLARATIONS class DynamicIPList { public: DynamicIPList(int maxitems, int maxitemage); ~DynamicIPList(); #ifdef DEBUG_LOW int getListSize() { return size; }; #endif int getNumberOfItems() { return items; }; // return whether or not given IP is in/could be added to list // (i.e. returns false if list already full & this IP's not in it) bool inList(unsigned long int ip); // remove entries older than maxage void purgeOldEntries(); private: // IPs and their ages unsigned long int *data; unsigned long int *datatime; // list size; no. of items currently in list; max. allowed item age int size; int items; int maxage; void stamp(unsigned int pos); // binary search for given ip int search(int a, int s, unsigned long int ip); // compacts list removing blanks void empties(); // returns position of given IP in list, or (0-pos)-1 where pos is where // IP should be inserted to retain sorting. int posInList(unsigned long int ip); }; #endif e2guardian-5.5.8r/src/DynamicURLList.cpp000066400000000000000000000256471477372360500200660ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "OptionContainer.hpp" #include "DynamicURLList.hpp" #include "Logger.hpp" #include #include #include #include #include // GLOBALS extern OptionContainer o; // IMPLEMENTATION // note - the list of URLs itself is simply a ring buffer, so the oldest entry/next empty entry is always the // last position we wrote to plus one, wrapping round when we get to the top. // however, we also maintain a seperate index list, which points to the entries in alphabetical order. // constructor - initialise values to empty defaults DynamicURLList::DynamicURLList() : index(NULL), urlreftime(NULL), urls(NULL), groups(NULL), size(0), agepos(0), timeout(0), items(0) { } // delete the memory block when the class is destryed DynamicURLList::~DynamicURLList() { delete[] index; delete[] urlreftime; delete[] urls; delete[] groups; } // "flush" the list (not quite) void DynamicURLList::flush() { // make all entries old so they won't be used for (int i = 0; i < items; i++) { urlreftime[index[i]] = 0; } } // find the position of the given URL in the list // return value ranges: // -ve if not found. 0-(pos + 1) is where it should be inserted to retain sorting. // 0 to size if found int DynamicURLList::posInList(const char *url) { if (items == 0) { DEBUG_debug("url list cache is empty"); // if the list is empty, indicate that the entry should go in pos 0 return -1; } E2LOGGER_error("url list cache: performing search..."); return search(0, items - 1, url); } // binary search the list for a given URL // note the list itself is not ordered, but the index array is int DynamicURLList::search(int a, int s, const char *url) { if (a > s) return (-1 - a); int m = (a + s) / 2; // look up the url pointed to by this entry in the index char *i = index[m] * 1000 + urls; int alen = strlen(i); int blen = strlen(url); int maxlen = alen < blen ? alen : blen; char *apos = (char *)i; char *bpos = (char *)url; int j = 0; int c = 0; unsigned char achar; unsigned char bchar; while (j < maxlen) { achar = apos[0]; bchar = bpos[0]; if (achar > bchar) { c = 1; break; } if (achar < bchar) { c = -1; break; } j++; apos++; bpos++; } if (c == 0) { if (alen > blen) { c = 1; } else if (alen < blen) { c = -1; } } // otherwise, assume both equal // we found the entry if (c == 0) return m; // we didn't, but it's in the lower half if (c == -1) return search(m + 1, s, url); // we got the search window down to 1 entry if (a == s) // therefore, we know where the entry we were looking for // should be added if we are to maintain a sorted list. // return its position, -ve (and strictly below 0), so as to // provide useful information and not mess up return value checks. return (-1 - a); // we didn't find it, but it's in the upper half return search(a, m - 1, url); } // sets how many URLs the list should store, and the maximum age of an entry before it goes inactive bool DynamicURLList::setListSize(unsigned int s, unsigned int t) { if (s < 2) { return false; } if (t < 2) { return false; } size = s; timeout = t; agepos = 0; items = 0; delete[] index; delete[] urlreftime; delete[] urls; delete[] groups; index = new unsigned int[size]; urlreftime = new unsigned long int[size]; urls = new char[size * 1000]; // allows url up to 999 in length groups = new std::string[size]; return true; } // see if the given URL is in the list bool DynamicURLList::inURLList(const char *url, const int fg) { DEBUG_debug("url cache search request: ", fg, " ", url); if (items == 0) { return false; } #ifdef DEBUG_LOW DEBUG_debug("****** url cache table ******"); DEBUG_debug("items: ", items); for (int i = 0; i < items; i++) { std::stringstream ss; for (unsigned int j = 0; j < groups[index[i]].length(); j++) { ss << (unsigned int)(groups[index[i]][j]) << " "; } ss << (char *)(index[i] * 1000 + urls); DEBUG_debug(ss.str()); } DEBUG_debug("****** url cache table ******"); #endif // truncate URL if necessary, as we have a length limit on our buffers int pos; if (strlen(url) > 999) { String r(String(url, 999)); pos = posInList(r.toCharArray()); } else { pos = posInList(url); } DEBUG_debug("pos: ", pos); // if we have found an entry, also check to see that it hasn't gone inactive. // todo: could we speed things up a little by simply refreshing the timer on the // old entry here, instead of having addEntry look for possible duplicates? // actually, i dunno. are URLs from time-limited lists cached? this might cause them to // continue being seen as good/bad (depending on the nature of the list) outside the // allotted window if they get put in the cache during it. if (pos > -1) { unsigned long int timenow = time(NULL); if ((timenow - urlreftime[index[pos]]) > timeout) { DEBUG_debug("found but url ttl exceeded: ", (timenow - urlreftime[index[pos]])); return false; } // o.filter_groups + 1 is a special case, meaning clean for all groups std::string lookfor; lookfor += (char)fg; lookfor += (char)o.filter.filter_groups + 1; if (groups[index[pos]].find_first_of(lookfor) == std::string::npos) { #ifdef DEBUG_LOW std::stringstream ss; for (unsigned int j = 0; j < groups[index[pos]].length(); j++) { ss << (unsigned int)(groups[index[pos]][j]) << " "; } DEBUG_debug("found but url not flagged clean for this group: ", fg, " (is clean for: ", ss.str(), ")" ); #endif return false; } return true; } return false; } // add an entry to the URL list - if it's already there, but timed out due to age, simply refresh the timer // also, maintain the lists' sorting, to allow binary search to be performed void DynamicURLList::addEntry(const char *url, const int fg) { DEBUG_debug("url cache add request: ", fg, " ", url, " itemsbeforeadd: ", items); int len = strlen(url); bool resized = false; char *u; // truncate the URL if it's too long if (len > 999) { u = new char[1000]; u[999] = '\0'; // bugfix - previously, truncation did the above termination, but no string copy! // this might never have been noticed due to high buffer size & correct termination preventing the bug causing a crash. memcpy(u, url, 999); resized = true; len = 999; } else { u = (char *)url; } int pos = posInList(u); if (pos >= 0) { // found if (resized) { delete[] u; } DEBUG_debug("Entry found at pos: ", pos); urlreftime[index[pos]] = time(NULL); // reset refresh counter if (groups[index[pos]].find((char)fg, 0) == std::string::npos) groups[index[pos]] += (char)fg; // flag it as clean for this filter group return; // not going to add entry thats there already } pos = 0 - pos - 1; // now contains the insertion point DEBUG_debug("insertion pos: ", pos, " size: ", size); // the list isn't full, so simply push new entry onto the back if (items < size) { DEBUG_debug("items pos; i--) { index[i] = index[i - 1]; } index[pos] = items; groups[items] = (char)fg; items++; if (resized) { delete[] u; } return; } // list is full, so we can't simply push a new entry on to it. // now replace the oldest entry but first need to find it in // the index to remove from there. old entries don't get deleted, just overwritten! // we know the pos in the list of the oldest URL, but not where it is in our sorted index list char *oldestref = urls + agepos * 1000; // now contains pos in sorted index of what we're going to overwrite int delpos = posInList(oldestref); // do the actual overwriting, including termination and setting birthdate to now memcpy(oldestref, u, len); oldestref[len] = '\0'; urlreftime[agepos] = time(NULL); groups[agepos] = (char)fg; // now shuffle the index list to remain sorted // remember: pos contains the alphabetical position of what we just added, // delpos contains the alphabetical position of the previous oldest entry, // and agepos contains the actual position in the string list of the entry we just wrote if (delpos == pos) { // the alphabetical pos of what we just deleted and what we just wrote are one and the same // so simply update the one index value to contain a ref to the new entry index[pos] = agepos; } else if (delpos < pos) { // the alphabetical pos of what we deleted was less than what we just wrote // so shift the list entries between the two up by one (losing delpos), and insert our entry int endpos = pos - 1; for (int i = delpos; i < endpos; i++) { index[i] = index[i + 1]; } index[pos - 1] = agepos; } else if (delpos > pos) { // the alphabetical pos of what we just deleted was greater than what we just added, // so starting from the old pos, work backwards, shifting the entries down by one // then insert our new entry for (int i = delpos; i > pos; i--) { index[i] = index[i - 1]; } index[pos] = agepos; } // the index is now sorted, but the actual list of strings itself is sorted oldest first. // increase the age pointer to the next empty entry - once we get to the end, wrap round // to the top, and overwrite the entries oldest first. // todo: things might speed up if we simply maintain the agepos as an index into the sorted index array, // rather than an index on the real URL list. then we wouldn't need to do a posInList to find the agepos's // sorted index every time we do an add. agepos++; if (agepos == size) { agepos = 0; } if (resized) { delete[] u; } } e2guardian-5.5.8r/src/DynamicURLList.hpp000066400000000000000000000031431477372360500200560ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_DYNAMICURLLIST #define __HPP_DYNAMICURLLIST // dynamic URL lists - used to cache known clean URLs so filtering can be bypassed class DynamicURLList { public: DynamicURLList(); ~DynamicURLList(); // set list size and timeout on entries (old entries aren't deleted, simply overwritten) bool setListSize(unsigned int s, unsigned int t); // flush the list (set all entries to old) void flush(); // is an entry in the list? bool inURLList(const char *url, const int fg); // add a URL - if it's already there but marked as too old, simply rejuvenate it void addEntry(const char *url, const int fg); private: // index list - points to URL list entries in alphabetical order unsigned int *index; // age list, sorted in the same order as index unsigned long int *urlreftime; // actual URL list char *urls; // group list - which group(s) is this URL clean for? std::string *groups; // size of list int size; // pointer to oldest entry (real entry, not sorted index) int agepos; unsigned int timeout; int items; // binary search for the pos of the given url in the sorted index // returns 0-(pos+1) on failure, where pos is where the entry would be inserted to retain sorting int search(int a, int s, const char *url); // find the index for the given url - makes use of the above search func int posInList(const char *url); }; #endif e2guardian-5.5.8r/src/FDTunnel.cpp000066400000000000000000001031061477372360500167250ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // This class is a generic multiplexing tunnel // that uses blocking poll() to be as efficient as possible. It tunnels // between the two supplied FDs. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include #include #include #include #include #include #include #include #include "FDTunnel.hpp" #include "Logger.hpp" // IMPLEMENTATION FDTunnel::FDTunnel() : throughput(0) { } void FDTunnel::reset() { throughput = 0; } // tunnel data from fdfrom to fdto (unfiltered) // return false if throughput larger than target throughput bool FDTunnel::tunnel(Socket &sockfrom, Socket &sockto, bool twoway, off_t targetthroughput, bool ignore, bool chunked) { if (chunked) { DEBUG_network("tunnelling chunked data."); int maxlen = 32000; timeout = sockfrom.getTimeout(); int rd = 0; int total_rd = 0; while ((rd = sockfrom.readChunk(sfbuff, maxlen, timeout)) > 0) { sockto.writeChunk(sfbuff, rd, timeout); total_rd += rd; } sockto.writeChunkTrailer(sockfrom.chunked_trailer); DEBUG_network("tunnelled chunked data.", total_rd, " bytes"); throughput = total_rd; return true; } throughput = 0; bool st_isSsl = sockto.isSsl(); bool sf_isSsl = sockfrom.isSsl(); if (targetthroughput == 0) { DEBUG_network("No data expected, tunnelling aborted."); return true; } if (targetthroughput < 0) { DEBUG_network("Tunnelling without known content-length"); } else { DEBUG_network("Tunnelling with content length ", targetthroughput); } int fdfrom, fdto; fdfrom = sockfrom.getFD(); fdto = sockto.getFD(); twayfds[0].fd = fdfrom; twayfds[0].events = 0; twayfds[1].events = 0; twayfds[0].revents = 0; // simplifies 1st loop logic twayfds[1].revents = 0; //if (ignore && !twoway) { // this porb will not work with non-block https // twayfds[1].fd = -1; // twayfds[1].revents = 0; //} //else twayfds[1].fd = fdto; char stbuff[32768]; // buffer for the return timeout = 120000; // should be made setable in conf files if (twoway) { ignore = false; } int sfbuff_cnt = 0; int stbuff_cnt = 0; // Functionb now split into different functions for different cases as ssl sockets need treating differently to // normal sockets with different order of operations for each case // Yes, it is untidy, but makes bug-fixing much easier. PP. if (!st_isSsl && !sf_isSsl) { // both sockets not ssl return nots2nots_tunnel(sockfrom, sockto, fdto, fdfrom, twoway, targetthroughput, ignore); } if (sf_isSsl && !st_isSsl) { // from socket is ssl - to is not ssl return iss2nots_tunnel(sockfrom, sockto, fdto, fdfrom, twoway, targetthroughput, ignore); } if (sf_isSsl && st_isSsl) { // from socket is ssl - to is ssl return iss2iss_tunnel(sockfrom, sockto, fdto, fdfrom, twoway, targetthroughput, ignore); } bool sf_read_wait = true; bool st_read_wait = false; //if (twoway) st_read_wait = true; bool sf_write_wait = false; bool st_write_wait = false; short int sf_read_wait_flags = 0; short int st_read_wait_flags = 0; short int sf_write_wait_flags = 0; short int st_write_wait_flags = 0; // Note: poll has to be after read (or write) for ssl sockets, but before read (or write) for non-ssl! //if (!sf_isSsl) { sf_read_wait = true; // wait for poll before reading sf_read_wait_flags = POLLIN; sf_write_wait_flags = POLLOUT; //} if (twoway) { st_read_wait = true; // wait for poll before reading st_read_wait_flags = POLLIN; st_write_wait_flags = POLLOUT; } bool done = false; // so we get past the first while // disable poll before read/write = only affects non SSL i/o sockfrom.doCheck = false; sockto.doCheck = false; // v5.5 changed socket logic to psuedio non-blocking so that poll is used in MITM // after read/write - PP if ((sockfrom.bufflen - sockfrom.buffstart) > 0) { int to_write = (sockfrom.bufflen - sockfrom.buffstart); if ((targetthroughput > 0) && (targetthroughput < to_write)) to_write = targetthroughput; sfbuff_cnt = sockfrom.readFromSocket(sfbuff, to_write, 0, 0); if (sfbuff_cnt < 1) { //should never happen! return true; } DEBUG_network("Data in fdfrom's buffer now in sfbuffer ", to_write, " bytes"); //throughput += sfbuff_cnt; st_write_wait = true; st_write_wait_flags = POLLOUT; } while (!done && (targetthroughput > -1 ? throughput < targetthroughput : true)) { done = true; // if we don't make a successful read and write this // flag will stay true and so the while() will exit //DEBUG_network("Start of tunnel loop: throughput:", throughput, " target:", targetthroughput); // 1st Try 'from' socket for input if not waiting for write on socket // if ((sfbuff_cnt == 0) && !sf_write_wait) { DEBUG_network("stage 1 - read from"); //std::cout < -1) // we have a target throughput - only read in the exact amount of data we've been told to sfbuff_cnt = sockfrom.readFromSocket(sfbuff, (((int) sizeof(sfbuff) < ((targetthroughput - throughput))) ? sizeof(sfbuff) : (targetthroughput - throughput)), 0, 0, true); else sfbuff_cnt = sockfrom.readFromSocket(sfbuff, sizeof(sfbuff), 0, 0, true); DEBUG_network("tunnel got return rom sockfrom:read ", sfbuff_cnt, " bytes"); if (sfbuff_cnt < 0) { sfbuff_cnt = 0; if (sockfrom.timedout && sf_isSsl) { //do data yet sf_read_wait = true; sf_read_wait_flags = sockfrom.get_wait_flag(false); done = false; DEBUG_network(" sfread_flags ", sf_read_wait_flags); } else if (sockfrom.sockError()) { break; // an error occurred so end the while() } } else if (sfbuff_cnt == 0) { done = true; // none received so pipe is closed so flag it break; } else { // some data read DEBUG_network("tunnel got data from sockfrom: ", sfbuff_cnt, " bytes"); // throughput += sfbuff_cnt; // increment our counter used to log DEBUG_network("throughput is ", throughput, " of ", targetthroughput); // if(sf_isSsl) { sf_read_wait = true; // } else { // if(!st_isSsl) { st_write_wait_flags = POLLOUT; st_write_wait = true; // } // } done = false; } } } // 2nd try 'to' socket for input // IF twoway get input from 'to' socket if no write waiting on socket // else if ignore not set if any pending input in buffer stop tunneling if (twoway) { if ((stbuff_cnt == 0) && !st_write_wait) { DEBUG_network("stage 2 - read to"); if (st_isSsl && sockto.checkForInput(0)) st_read_wait = false; if ((!st_read_wait) || ((twayfds[1].revents & st_read_wait_flags) == st_read_wait_flags) //|| (st_isSsl && sockto.checkForInput(0)) ) { DEBUG_network("stage 2 about to read"); stbuff_cnt = sockto.readFromSocket(stbuff, sizeof(stbuff), 0, 0, true); DEBUG_network("tunnel got return rom sockto:read ", stbuff_cnt, " bytes"); if (stbuff_cnt < 0) { stbuff_cnt = 0; if (sockto.timedout && st_isSsl) { //do data yet DEBUG_network(" got timeout"); st_read_wait = true; st_read_wait_flags = sockto.get_wait_flag(false); done = false; DEBUG_network(" stread_flags ", st_read_wait_flags); } else if (sockto.sockError()) { break; // an error occurred so end the while() } } else if (stbuff_cnt == 0) { done = true; // none received so pipe is closed so flag it break; } else { // some data read DEBUG_network("tunnel got data from sockto: ", stbuff_cnt, " bytes"); throughput += stbuff_cnt; // if(st_isSsl) { st_read_wait = false; // } else { // if(!sf_isSsl) { sf_write_wait = true; sf_write_wait_flags = POLLOUT; // } // } done = false; } } } } else { // !twoway = one way if (!ignore) { if (st_isSsl) { if (sockto.s_checkPending() > 0) break; } else { // not ssl if (twayfds[1].revents & POLLIN) // can't use this for ssl as POLLIN may be for a write break; } } } // 3rd try and write to 'to' socket if any data in buffer DEBUG_network("stage 3 - write to"); if ((sfbuff_cnt > 0) && ((!st_write_wait) || ((twayfds[1].revents & st_write_wait_flags) == st_write_wait_flags))) { DEBUG_network("stage 3 about to write "); if (!sockto.writeToSocket(sfbuff, sfbuff_cnt, 0, 0)) { if (sockto.timedout && st_isSsl) { //do data yet st_write_wait = true; st_write_wait_flags = sockto.get_wait_flag(true); done = false; } else if (sockto.sockError()) { break; // an error occurred so end the while() } } else { // data written DEBUG_network("tunnel wrote data out: ", sfbuff_cnt, " bytes"); st_write_wait = false; throughput += sfbuff_cnt; // increment our counter used to log sfbuff_cnt = 0; if (!sf_isSsl) { sf_read_wait = true; sf_read_wait_flags = POLLIN; } done = false; } } // 4th try and write to 'from' socket if any data in buffer DEBUG_network("stage 4 - write from"); if ((stbuff_cnt > 0) && ((!sf_write_wait) || ((twayfds[0].revents & sf_write_wait_flags) == sf_write_wait_flags))) { DEBUG_network("stage 4 about to write "); if (!sockfrom.writeToSocket(stbuff, stbuff_cnt, 0, 0)) { if (sockfrom.timedout && sf_isSsl) { //do data yet sf_write_wait = true; sf_write_wait_flags = sockfrom.get_wait_flag(true); done = false; } else if (sockfrom.sockError()) { break; // an error occurred so end the while() } } else { // data written DEBUG_network("tunnel wrote data out: ", stbuff_cnt, " bytes"); sf_write_wait = false; stbuff_cnt = 0; if (!st_isSsl) { st_read_wait = true; st_read_wait_flags = POLLIN; } done = false; } } // 4th Break if either socket is hung up - has to be done after read as data can be pending when hung up if ((twayfds[0].revents & POLLHUP) || (twayfds[1].revents & POLLHUP)) { break; } DEBUG_network("sf_ww is ", sf_write_wait, " st_ww is ", st_write_wait, " sf_rw is ", sf_read_wait, " st_rw is ", st_read_wait); DEBUG_network("sf_ww_f is ", sf_write_wait_flags, " st_ww_f is ", st_write_wait_flags, " sf_rw_f is ", sf_read_wait_flags, " st_rw_f is ", st_read_wait_flags); if (sf_write_wait || st_write_wait || sf_read_wait || st_read_wait) { if ((targetthroughput == -1) || (throughput < targetthroughput)) { // 5th set up and do poll twayfds[0].events = 0; if (sf_write_wait) twayfds[0].events = sf_write_wait_flags; else if (sf_read_wait) twayfds[0].events = sf_read_wait_flags; twayfds[1].events = 0; if (st_write_wait) twayfds[1].events = st_write_wait_flags; else if (st_read_wait) twayfds[1].events = st_read_wait_flags; if (twayfds[0].events == 0) twayfds[0].events = POLLIN; // set for read to avoid deadlock if (!ignore && (twayfds[1].events == 0)) twayfds[1].events = POLLIN; // set for read to avoid deadlock int rc = poll(twayfds, 2, timeout); if (rc < 1) { DEBUG_network("tunnel tw poll returned error or timeout::", rc); break; // an error occurred or it timed out so end while() } DEBUG_network("tunnel tw poll returned ok:", rc); DEBUG_network("tunnel tw poll returned revents:", twayfds[0].revents, " ", twayfds[1].revents); if ((twayfds[0].revents & POLLIN)) { done = false; continue; } if ((twayfds[1].revents & POLLIN)) { done = false; continue; } if ((twayfds[0].revents & POLLERR) || (twayfds[1].revents & POLLERR)) { break; } done = false; } } } // re-enable poll before read/write = only affects non SSL i/o sockfrom.doCheck = true; sockto.doCheck = true; if ((throughput >= targetthroughput) && (targetthroughput > -1)) { DEBUG_network("All expected data tunnelled. (expected ", targetthroughput, "; tunnelled ", throughput, ")"); } else { DEBUG_network("Tunnel closed."); } return (targetthroughput > -1) ? (throughput >= targetthroughput) : true; } bool FDTunnel::nots2nots_tunnel(Socket &sockfrom, Socket &sockto, int &fdto, int &fdfrom, bool twoway, off_t targetthroughput, bool ignore) { // it is a twoway with both sockets not ssl bool done = false; // so we get past the first while ssize_t rc = 0; tooutfds[0].fd = fdto; tooutfds[0].events = POLLOUT; tooutfds[0].revents = 0; fromoutfds[0].fd = fdfrom; fromoutfds[0].events = POLLOUT; fromoutfds[0].revents = 0; twayfds[0].events = POLLIN; twayfds[0].revents = 0; twayfds[1].revents = 0; if (twoway) twayfds[1].events = POLLIN; if ((sockfrom.bufflen - sockfrom.buffstart) > 0) { int to_write = (sockfrom.bufflen - sockfrom.buffstart); if ((targetthroughput > 0) && (targetthroughput < to_write)) to_write = targetthroughput; DEBUG_network("Data in fdfrom's buffer; sending ", to_write, " bytes"); if (!sockto.writeToSocket(sockfrom.buffer + sockfrom.buffstart, to_write, 0, 120000)) return false; DEBUG_network("Data in fdfrom's buffer; sent ", to_write, " bytes"); throughput += to_write; sockfrom.buffstart += to_write; if (sockfrom.buffstart == sockfrom.bufflen) { sockfrom.bufflen = 0; sockfrom.buffstart = 0; } } while (!done && (targetthroughput > -1 ? throughput < targetthroughput : true)) { done = true; // if we don't make a sucessful read and write this // flag will stay true and so the while() will exit DEBUG_network("Start of tunnel loop: throughput:", throughput, " target:", targetthroughput); //FD_CLR(fdto, &inset); { int rc = poll(twayfds, 2, timeout); if (rc < 1) { DEBUG_network("tunnel tw poll returned error or timeout::", rc); break; // an error occurred or it timed out so end while() } DEBUG_network("tunnel tw poll returned ok:", rc, " twayfds.revents:", twayfds[0].revents, " ", twayfds[1].revents); DEBUG_network("POLLIN is ", POLLIN); } if (twayfds[0].revents & (POLLIN | POLLHUP)) { DEBUG_network("Reading from"); if (targetthroughput > -1) { // we have a target throughput - only read in the exact amount of data we've been told to rc = recv(fdfrom, sfbuff, (((int) sizeof(sfbuff) < ((targetthroughput - throughput) /*+2*/)) ? sizeof(sfbuff) : ( targetthroughput - throughput)), 0); } else rc = recv(fdfrom, sfbuff, sizeof(sfbuff), 0); DEBUG_network("read from returned ", rc); // read as much as is available if (rc < 0) { break; // an error occurred so end the while() } else if (!rc) { done = true; // none received so pipe is closed so flag it } else { // some data read DEBUG_network("tunnel got data from sockfrom: ", rc, " bytes"); throughput += rc; // increment our counter used to log if (poll(tooutfds, 1, timeout) < 1) { break; // an error occurred or timed out so end while() } if (tooutfds[0].revents & POLLOUT) { rc = send(fdto, sfbuff, rc, 0); if (rc < 1) { // write data break; // was an error writing } DEBUG_network("tunnel wrote data out: ", rc, " bytes"); done = false; // flag to say data still to be handled } else { break; // should never get here } } } if (twoway && twayfds[1].revents & (POLLIN | POLLHUP)) { DEBUG_network("Reading to"); // read as much as is available rc = recv(fdto, sfbuff, sizeof(sfbuff), 0); DEBUG_network("read to returned", rc); if (rc < 0) { DEBUG_network("read to error is", errno); break; // an error occurred so end the while() } else if (!rc) { done = true; // none received so pipe is closed so flag it break; } else { // some data read if (poll(fromoutfds, 1, timeout) < 1) { break; // an error occurred or timed out so end while() } if (fromoutfds[0].revents & POLLOUT) { rc = send(fdfrom, sfbuff, rc, 0); if (rc < 1) { // write data break; // was an error writing } done = false; // flag to say data still to be handled } else { break; // should never get here } } } } if ((throughput >= targetthroughput) && (targetthroughput > -1)) { DEBUG_network("All expected data tunnelled. (expected ", targetthroughput, "; tunnelled ", throughput, ")"); } else { DEBUG_network("Tunnel closed."); } return (targetthroughput > -1) ? (throughput >= targetthroughput) : true; } bool FDTunnel::iss2nots_tunnel(Socket &sockfrom, Socket &sockto, int &fdto, int &fdfrom, bool twoway, off_t targetthroughput, bool ignore) { // it is a twoway with from socket ssl and to socket not ssl bool done = false; // so we get past the first while int l_timeout = timeout; ssize_t rc = 0; tooutfds[0].fd = fdto; tooutfds[0].events = POLLOUT; tooutfds[0].revents = 0; fromoutfds[0].fd = fdfrom; fromoutfds[0].events = POLLOUT; fromoutfds[0].revents = 0; twayfds[0].events = POLLIN; twayfds[0].revents = 0; twayfds[1].revents = 0; if (twoway) twayfds[1].events = POLLIN; if ((sockfrom.bufflen - sockfrom.buffstart) > 0) { int to_write = (sockfrom.bufflen - sockfrom.buffstart); if ((targetthroughput > 0) && (targetthroughput < to_write)) to_write = targetthroughput; DEBUG_network("Data in fdfrom's buffer; sending ", to_write, " bytes"); if (!sockto.writeToSocket(sockfrom.buffer + sockfrom.buffstart, to_write, 0, 120000)) return false; DEBUG_network("Data in fdfrom's buffer; sent ", to_write, " bytes"); throughput += to_write; sockfrom.buffstart += to_write; if (sockfrom.buffstart == sockfrom.bufflen) { sockfrom.bufflen = 0; sockfrom.buffstart = 0; } } bool do_poll = true; while (!done && (targetthroughput > -1 ? throughput < targetthroughput : true)) { done = true; // if we don't make a sucessful read and write this // flag will stay true and so the while() will exit DEBUG_network("Start of fromSsl to toNotSsl tunnel loop: throughput:", throughput, " target:", targetthroughput); //FD_CLR(fdto, &inset); // 1st check pending before doing poll { // 1st check pending before doing poll if ((sockfrom.s_checkPending() || (twayfds[0].revents & POLLIN)) && sockfrom.s_checkShutdown() == 0) { DEBUG_network("Reading from"); if (targetthroughput > -1) { // we have a target throughput - only read in the exact amount of data we've been told to rc = SSL_read(sockfrom.ssl, sfbuff, (((int) sizeof(sfbuff) < ((targetthroughput - throughput) /*+2*/)) ? sizeof(sfbuff) : ( targetthroughput - throughput))); } else { rc = SSL_read(sockfrom.ssl, sfbuff, sizeof(sfbuff)); } if (rc < 0) { break; // an error occurred so end the while() } else if (!rc) { done = true; // none received so pipe is closed so flag it break; } else { // some data read DEBUG_network("tunnel got data from sockfrom: ", rc, " bytes"); throughput += rc; // increment our counter used to log if (poll(tooutfds, 1, timeout) < 1) { break; // an error occurred or timed out so end while() } if (tooutfds[0].revents & POLLOUT) { // rc = send(fdto, sfbuff, rc, 0); rc = sockto.writeToSocket(sfbuff, rc, 0, 0); if (rc < 1) { // write data break; // was an error writing } DEBUG_network("tunnel wrote data out: ", rc, " bytes"); // if (sockfrom.s_checkPending())) //do_poll = false; //else //do_poll = true; done = false; // flag to say data still to be handled } else { break; // should never get here } } } } bool pending = sockfrom.s_checkPending(); if (pending) do_poll = false; DEBUG_network("has_pending is ", pending, " do_poll is ", do_poll); if (do_poll) { rc = poll(twayfds, 2, l_timeout); //int rc = poll(twayfds, 2, 10000); // reduced timeout for testing if (rc < 1) { DEBUG_network("tunnel tw poll returned error or timeout::", rc); break; // an error occurred or it timed out so end while() } done = false; DEBUG_network("tunnel tw poll returned ok:", rc, " twayfds.revents:", twayfds[0].revents, " ", twayfds[1].revents); DEBUG_network("POLLIN is ", POLLIN); } else { do_poll = true; } if (twoway && twayfds[1].revents & (POLLIN | POLLHUP)) { DEBUG_network("Reading to"); // read as much as is available rc = recv(fdto, sfbuff, sizeof(sfbuff), 0); DEBUG_network("read to returned", rc); if (rc < 0) { DEBUG_network("read to error is", errno); break; // an error occurred so end the while() } else if (!rc) { done = true; // none received so pipe is closed so flag it break; } else { // some data read if (poll(fromoutfds, 1, timeout) < 1) { break; // an error occurred or timed out so end while() } if ((fromoutfds[0].revents & POLLOUT) && sockfrom.s_checkShutdown() == 0) { rc = sockfrom.writeToSocket(sfbuff, rc, 0, timeout); if (rc < 1) { // write data break; // was an error writing } //if (sockfrom.s_checkPending()) //do_poll = false; done = false; // flag to say data still to be handled } else { break; // should never get here } } } } if ((throughput >= targetthroughput) && (targetthroughput > -1)) { DEBUG_network("All expected data tunnelled. (expected ", targetthroughput, "; tunnelled ", throughput, ")"); } else { DEBUG_network("Tunnel closed."); } return (targetthroughput > -1) ? (throughput >= targetthroughput) : true; } bool FDTunnel::iss2iss_tunnel(Socket &sockfrom, Socket &sockto, int &fdto, int &fdfrom, bool twoway, off_t targetthroughput, bool ignore) { // it is a tunnel with from socket ssl and to socket ssl bool done = false; // so we get past the first while ssize_t rc = 0; tooutfds[0].fd = fdto; tooutfds[0].events = POLLOUT; tooutfds[0].revents = 0; fromoutfds[0].fd = fdfrom; fromoutfds[0].events = POLLOUT; fromoutfds[0].revents = 0; twayfds[0].events = POLLIN; twayfds[0].revents = 0; twayfds[1].revents = 0; if (twoway) twayfds[1].events = POLLIN; if ((sockfrom.bufflen - sockfrom.buffstart) > 0) { int to_write = (sockfrom.bufflen - sockfrom.buffstart); if ((targetthroughput > 0) && (targetthroughput < to_write)) to_write = targetthroughput; DEBUG_network("Data in fdfrom's buffer; sending ", to_write, " bytes"); if (!sockto.writeToSocket(sockfrom.buffer + sockfrom.buffstart, to_write, 0, 120000)) return false; DEBUG_network("Data in fdfrom's buffer; sent ", to_write, " bytes"); throughput += to_write; sockfrom.buffstart += to_write; if (sockfrom.buffstart == sockfrom.bufflen) { sockfrom.bufflen = 0; sockfrom.buffstart = 0; } } while (!done && (targetthroughput > -1 ? throughput < targetthroughput : true)) { done = true; // if we don't make a sucessful read and write this // flag will stay true and so the while() will exit DEBUG_network("Start of fromSsl to toSsl tunnel loop: throughput:", throughput, " target:", targetthroughput); //FD_CLR(fdto, &inset); // 1st check pending before doing poll bool do_poll = true; { // 1st check pending before doing poll if ((sockfrom.s_checkPending()) || twayfds[0].revents & POLLIN) { DEBUG_network("Reading from"); if (targetthroughput > -1) { // we have a target throughput - only read in the exact amount of data we've been told to //rc = SSL_read(sockfrom.ssl, sfbuff, rc = sockfrom.readFromSocket(sfbuff, (((int) sizeof(sfbuff) < ((targetthroughput - throughput) /*+2*/)) ? sizeof(sfbuff) : ( targetthroughput - throughput)), 0, 0, true); } else { rc = sockfrom.readFromSocket(sfbuff, 4096, 0, 0, true); //rc = sockfrom.readFromSocket(sfbuff, sizeof(sfbuff),0,0,true); //rc = SSL_read(sockfrom.ssl, sfbuff, sizeof(sfbuff)); } if (rc < 0) { break; // an error occurred so end the while() } else if (!rc) { done = true; // none received so pipe is closed so flag it break; } else { // some data read if (sockfrom.s_checkPending()) do_poll = false; DEBUG_network("tunnel got data from sockfrom: ", rc, " bytes"); throughput += rc; // increment our counter used to log if (poll(tooutfds, 1, timeout) < 1) { break; // an error occurred or timed out so end while() } if (tooutfds[0].revents & POLLOUT) { rc = sockto.writeToSocket(sfbuff, rc, 0, timeout); if (rc < 1) { // write data break; // was an error writing } DEBUG_network("tunnel wrote data out: ", rc, " bytes"); done = false; // flag to say data still to be handled if (twoway && (sockto.s_checkPending())) do_poll = false; } else { break; // should never get here } } } } if (twoway && ((sockto.s_checkPending()) || twayfds[1].revents & (POLLIN | POLLHUP))) { DEBUG_network("Reading to"); // read as much as is available rc = sockto.readFromSocket(sfbuff, sizeof(sfbuff), 0, 0, true); DEBUG_network("read to returned", rc); if (rc < 0) { DEBUG_network("read to error is", errno); break; // an error occurred so end the while() } else if (!rc) { done = true; // none received so pipe is closed so flag it break; } else { // some data read if (poll(fromoutfds, 1, timeout) < 1) { break; // an error occurred or timed out so end while() } if (sockto.s_checkPending()) do_poll = false; if (fromoutfds[0].revents & POLLOUT) { rc = sockfrom.writeToSocket(sfbuff, rc, 0, timeout); if (rc < 1) { // write data break; // was an error writing } done = false; // flag to say data still to be handled if (sockfrom.s_checkPending()) do_poll = false; } else { break; // should never get here } } } if ((throughput >= targetthroughput) && (targetthroughput > -1)) { do_poll = false; } if (do_poll) { rc = poll(twayfds, 2, timeout); if (rc < 1) { DEBUG_network("tunnel tw poll returned error or timeout::", rc); break; // an error occurred or it timed out so end while() } done = false; DEBUG_network("tunnel tw poll returned ok:", rc, " twayfds.revents:", twayfds[0].revents, " ", twayfds[1].revents); DEBUG_network("POLLIN is ", POLLIN); } else { do_poll = true; } } if ((throughput >= targetthroughput) && (targetthroughput > -1)) { DEBUG_network("All expected data tunnelled. (expected ", targetthroughput, "; tunnelled ", throughput, ")"); } else { DEBUG_network("Tunnel closed."); } return (targetthroughput > -1) ? (throughput >= targetthroughput) : true; } e2guardian-5.5.8r/src/FDTunnel.hpp000066400000000000000000000031331477372360500167310ustar00rootroot00000000000000// This class is a generic multiplexing tunnel // that uses blocking select() to be as efficient as possible. It tunnels // between the two supplied FDs. // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_FDTUNNEL #define __HPP_FDTUNNEL // INCLUDES #include "Socket.hpp" #include #include // DECLARATIONS // transparently forward data from one FD to another, i.e. tunnel between them class FDTunnel { public: off_t throughput; // used to log total data from from to to FDTunnel(); // tunnel from fdfrom to fdto // return false if throughput larger than target throughput (for post upload size checking) bool tunnel(Socket &sockfrom, Socket &sockto, bool twoway = false, off_t targetthroughput = -1, bool ignore = false, bool chunked = false); void reset(); private: struct pollfd fromoutfds[1]; struct pollfd tooutfds[1]; struct pollfd twayfds[2]; int timeout = 120000; char sfbuff[32768]; // buffer for the input bool nots2nots_tunnel(Socket &sockfrom, Socket &sockto, int &fdto, int &fdfrom, bool twoway, off_t targetthroughput, bool ignore); bool iss2nots_tunnel(Socket &sockfrom, Socket &sockto, int &fdto, int &fdfrom, bool twoway, off_t targetthroughput, bool ignore); bool iss2iss_tunnel(Socket &sockfrom, Socket &sockto, int &fdto, int &fdfrom, bool twoway, off_t targetthroughput, bool ignore); }; #endif e2guardian-5.5.8r/src/FOptionContainer.cpp000066400000000000000000001031171477372360500204710ustar00rootroot00000000000000// FOptionContainer class - contains the options for a filter group, // including the banned/grey/exception site lists and the content/site/url regexp lists // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "FOptionContainer.hpp" #include "OptionContainer.hpp" #include "ListMeta.hpp" #include "Logger.hpp" #include #include #include #include // for gethostby #include // for address structures #include // for inet_aton() #include // GLOBALS extern OptionContainer o; // IMPLEMENTATION // reverse DNS lookup on IP. be aware that this can return multiple results, unlike a standard lookup. std::deque *ipToHostname(const char *ip) { std::deque *result = new std::deque; //struct in_addr address, **addrptr; struct in_addr address; if (inet_aton(ip, &address)) { // convert to in_addr struct hostent *answer; answer = gethostbyaddr((char *)&address, sizeof(address), AF_INET); if (answer) { // success in reverse dns result->push_back(String(answer->h_name)); } } return result; } void getClientFromIP(const char *ip, std::string &clienthost) { std::unique_ptr > hostnames; hostnames.reset(ipToHostname(ip)); if(hostnames->empty()) { clienthost = ip; } else { clienthost = std::string(hostnames->front().toCharArray()); } } FOptionContainer::~FOptionContainer() { reset(); } void FOptionContainer::reset() { conffile.clear(); if (neterr_page != nullptr) { delete neterr_page; neterr_page = nullptr; } if (banned_page != nullptr) { delete banned_page; banned_page = nullptr; } resetJustListData(); } void FOptionContainer::resetJustListData() { if (banned_phrase_flag) o.lm.deRefList(banned_phrase_list); banned_phrase_flag = false; content_regexp_flag = false; ssl_mitm = false; banned_phrase_list_index.clear(); // conffile.clear(); content_regexp_list_comp.clear(); content_regexp_list_rep.clear(); } // grab this FG's HTML template HTMLTemplate *FOptionContainer::getHTMLTemplate(bool upfail) { if(upfail && neterr_page) return neterr_page; if (banned_page) return banned_page; return &(o.html_template); } // read in the given file, write the list's ID into the given identifier, // sort using startsWith or endsWith depending on sortsw, // listname is used in error messages. bool FOptionContainer::readFile(const char *filename, const char *list_pwd, unsigned int *whichlist, bool sortsw, bool cache, const char *listname) { DEBUG_trace(filename); if (strlen(filename) < 3) { E2LOGGER_error("Required Listname ", listname, " is not defined"); return false; } int res = o.lm.newItemList(filename, list_pwd, sortsw, 1, true); if (res < 0) { E2LOGGER_error("Error opening ", listname); return false; } (*whichlist) = (unsigned)res; if (!(*o.lm.l[(*whichlist)]).used) { if (sortsw) (*o.lm.l[(*whichlist)]).doSort(true); else (*o.lm.l[(*whichlist)]).doSort(false); (*o.lm.l[(*whichlist)]).used = true; } return true; } bool FOptionContainer::readConfFile(const char *filename, String &list_pwd) { DEBUG_trace(filename); std::string linebuffer; String now_pwd(list_pwd); String temp; // for tempory conversion and storage std::ifstream conffiles(filename, std::ios::in); // e2guardianfN.conf if (!conffiles.good()) { E2LOGGER_error("Error reading: ", filename); return false; } String base_dir(filename); base_dir.baseDir(); //String base_dir = filename; //size_t fnsize; //if ((fnsize = base_dir.find_last_of("/")) > 0) // base_dir = base_dir.subString(1,fnsize); while (!conffiles.eof()) { getline(conffiles, linebuffer); if (!conffiles.fail() && linebuffer.length() != 0) { if (linebuffer[0] != '#') { // i.e. not commented out temp = (char *) linebuffer.c_str(); if (temp.contains("#")) { temp = temp.before("#"); } temp.removeWhiteSpace(); // get rid of spaces at end of line // check for LISTDIR and add replace with now_pwd while (temp.contains("__LISTDIR__")) { String temp2 = temp.before("__LISTDIR__"); temp2 += now_pwd; temp2 += temp.after("__LISTDIR__"); temp = temp2; } // deal with included files if (temp.startsWith(".")) { String temp2 = temp.after(".Include<").before(">"); if (temp2.length() > 0) { temp2.fullPath(base_dir); if (!readConfFile(temp2.toCharArray(), now_pwd)) { conffiles.close(); return false; } continue; } temp2 = temp.after(".Define LISTDIR <").before(">"); if (temp2.length() > 0) { now_pwd = temp2; //if(!now_pwd.endsWith("/")) //now_pwd += "/"; // std::cerr << "now_pwd set to " << now_pwd; } continue; } // append ,listdir=now_pwd if line contains a file path - so that now_pwd can be passed // to list file handler so that it can honour __LISTDIR__ in Included listfiles if (temp.contains("path=") && !temp.contains("listdir=")) { temp += ",listdir="; temp += now_pwd; } linebuffer = temp.toCharArray(); conffile.push_back(linebuffer); // stick option in deque } } } conffiles.close(); return true; } bool FOptionContainer::read(const char *filename) { try { // all sorts of exceptions could occur reading conf files std::string linebuffer; String temp; // for tempory conversion and storage String list_pwd = __CONFDIR; list_pwd += "/lists/group"; list_pwd += String(filtergroup); //list_pwd += "/"; if(!readConfFile(filename, list_pwd)){ E2LOGGER_error("Error reading: ", filename); return false; } DEBUG_config("Read conf into memory: ", filename); if (findoptionS("disablecontentscan") == "on") { disable_content_scan = true; } else { disable_content_scan = false; } if (findoptionS("disablecontentscanerror") == "on") { disable_content_scan_error = true; } else { disable_content_scan_error = false; } if (findoptionS("contentscanexceptions") == "on") { content_scan_exceptions = true; } else { content_scan_exceptions = false; } DEBUG_debug("disable_content_scan: ", String(disable_content_scan), " disablecontentscanerror: ", String(disable_content_scan_error), " contentscanexceptions: ", String(content_scan_exceptions) ); String mimes = findoptionS("textmimetypes"); if (mimes != "") { size_t comma = mimes.find(','); while (comma != std::string::npos) { text_mime.push_back(mimes.substr(0, comma)); mimes = mimes.substr(comma + 1); comma = mimes.find(','); } text_mime.push_back(mimes.substr(0, comma)); mimes = mimes.substr(comma + 1); #ifdef DEBUG_HIGH int size = (int) text_mime.size(); int i; for (i = 0; i < size; i++) { DEBUG_debug("mimes filtering : ", text_mime[i]); } #endif } String mimestop = findoptionS("stoptextmimetypes"); if (mimestop != "") { size_t comma = mimestop.find(','); while (comma != std::string::npos) { text_mime_stop.push_back(mimestop.substr(0, comma)); mimes = mimestop.substr(comma + 1); comma = mimestop.find(','); } text_mime_stop.push_back(mimestop.substr(0, comma)); mimestop = mimestop.substr(comma + 1); #ifdef DEBUG_HIGH int size = (int) text_mime_stop.size(); int i; for (i = 0; i < size; i++) { DEBUG_debug("mimes filtering : ", text_mime_stop[i]); } #endif } if (findoptionS("sslcheckcert") == "on") { if(o.cert.enable_ssl) { ssl_check_cert = true; } else { E2LOGGER_error("Warning: To use sslcheckcert, enablessl in e2guardian.conf must be on"); ssl_check_cert = false; } } else { ssl_check_cert = false; } if (findoptionS("sslmitm") == "on") { if(o.cert.enable_ssl) { ssl_mitm = true; if (findoptionS("automitm") == "off") { automitm= false; } else { automitm= true; } if (findoptionS("mitmcheckcert") == "off") mitm_check_cert = false; allow_empty_host_certs = false; if (findoptionS("allowemptyhostcert") == "on") allow_empty_host_certs = true; } else { E2LOGGER_error("Warning: sslmitm requires ssl to be enabled in e2guardian.conf "); ssl_mitm = false; } } else { ssl_mitm = false; } #ifdef ENABLE_EMAIL // Email notification patch by J. Gauthier if (findoptionS("usesmtp") == "on") { use_smtp = true; } else { use_smtp = false; } if (findoptionS("thresholdbyuser") == "on") { byuser = true; } else { byuser = false; } if (findoptionS("notifyav") == "on") { if (!use_smtp) { E2LOGGER_error("notifyav cannot be on while usesmtp is off."); return false; } notifyav = true; } else { notifyav = false; } if (findoptionS("notifycontent") == "on") { if (!use_smtp) { E2LOGGER_error("notifycontent cannot be on while usesmtp is off."); return false; } notifycontent = true; } else { notifycontent = false; } violations = findoptionI("violations"); current_violations = 0; violationbody = ""; threshold = findoptionI("threshold"); avadmin = findoptionS("avadmin"); if (avadmin.length() == 0) { if (notifyav == 1) { E2LOGGER_error("avadmin cannot be blank while notifyav is on."); return false; } } contentadmin = findoptionS("contentadmin"); if (contentadmin.length() == 0) { if (use_smtp) { E2LOGGER_error("contentadmin cannot be blank while usesmtp is on."); return false; } } mailfrom = findoptionS("mailfrom"); if (mailfrom.length() == 0) { if (use_smtp) { E2LOGGER_error("mailfrom cannot be blank while usesmtp is on."); return false; } } avsubject = findoptionS("avsubject"); if (avsubject.length() == 0 && notifyav == 1 && use_smtp == 1) { E2LOGGER_error("avsubject cannot be blank while notifyav is on."); return false; } contentsubject = findoptionS("contentsubject"); if (contentsubject.length() == 0 && use_smtp) { E2LOGGER_error("contentsubject cannot be blank while usesmtp is on."); return false; } #endif // override default reporting level if (findoptionS("reportinglevel").empty()) { //uses value from e2guardian.conf if empty reporting_level = o.block.reporting_level; } else { reporting_level = findoptionI("reportinglevel"); if (!realitycheck(reporting_level, -1, 3, "reportinglevel")) { return false; } } if (reporting_level == 0) { E2LOGGER_error("Reporting_level is : ", String(reporting_level), " file ", filename); } long temp_max_upload_size; if (findoptionS("maxuploadsize").empty()) { temp_max_upload_size = -1; } else { temp_max_upload_size = findoptionI("maxuploadsize"); } if ((realitycheck(temp_max_upload_size, -1, 10000000, "max_uploadsize")) && (temp_max_upload_size != 0)) { max_upload_size = temp_max_upload_size; if (temp_max_upload_size > 0) max_upload_size *= 1024; } else { E2LOGGER_error( "Invalid maxuploadsize: ", String(temp_max_upload_size) ); return false; } DEBUG_debug("maxuploadsize: ", String(temp_max_upload_size) ); // override default access denied address if (reporting_level == 1 || reporting_level == 2) { String temp_ada, temp_add; temp_ada = findoptionS("accessdeniedaddress"); if (temp_ada != "") { access_denied_address = temp_ada.toCharArray(); access_denied_domain = access_denied_address.c_str(); access_denied_domain = access_denied_domain.after("://"); access_denied_domain.removeWhiteSpace(); if (access_denied_domain.contains("/")) { access_denied_domain = access_denied_domain.before("/"); // access_denied_domain now contains the FQ host name of the // server that serves the accessdenied.html file } if (access_denied_domain.contains(":")) { access_denied_domain = access_denied_domain.before(":"); // chop off the port number if any } } else { access_denied_domain = "localhost"; // No initialized value if (access_denied_domain.length() < 4) { E2LOGGER_error("Warning accessdeniedaddress setting appears to be wrong."); } } } if (reporting_level == 3) { if (access_denied_domain.length() > 1) { E2LOGGER_error("Warning accessdeniedaddress setting appears to be wrong in reportinglevel 3"); return false; } // override default banned page String html_template(findoptionS("htmltemplate")); if (html_template != "") { html_template = o.config.languagepath + html_template; banned_page = new HTMLTemplate; if (!(banned_page->readTemplateFile(html_template.toCharArray()))) { E2LOGGER_error("Error reading HTML Template file: ", html_template); return false; } } else { html_template = o.config.languagepath + "template.html"; banned_page = new HTMLTemplate; if (!(banned_page->readTemplateFile(html_template.toCharArray()))) { E2LOGGER_error("Error reading default HTML Template file: ", html_template); return false; } } String neterr_template(findoptionS("neterrtemplate")); if (neterr_template != "") { neterr_template = o.config.languagepath + neterr_template; neterr_page = new HTMLTemplate; if (!(neterr_page->readTemplateFile(neterr_template.toCharArray()))) { E2LOGGER_error("Error reading NetErr HTML Template file: ", neterr_template); return false; // HTML template file } } else { // if blank will default to HTML template file neterr_template = o.config.languagepath + "neterr_template.html"; neterr_page = new HTMLTemplate; if (!(neterr_page->readTemplateFile(neterr_template.toCharArray()))) { E2LOGGER_error("Error reading default HTML and NetErr Template file: ", html_template); return false; } } } if (findoptionS("nonstandarddelimiter") == "off") { non_standard_delimiter = false; } else { non_standard_delimiter = true; } // grab group name (if not using external group names file) if (!o.filter.use_group_names_list) { if (findoptionS("groupname").length() > 0) { name = findoptionS("groupname"); } else { name = "group"; name += String(filtergroup); } DEBUG_debug("Group name: ", name); } embedded_url_weight = findoptionI("embeddedurlweight"); DEBUG_debug("Embedded URL Weight: ", std::to_string(embedded_url_weight)); category_threshold = findoptionI("categorydisplaythreshold"); DEBUG_debug("Category display threshold: ", std::to_string(category_threshold)); // Support weighted phrase mode per group if (findoptionS("weightedphrasemode").length() > 0) { weighted_phrase_mode = findoptionI("weightedphrasemode"); if (!realitycheck(weighted_phrase_mode, 0, 3, "weightedphrasemode")) return false; } std::string exception_phrase_list_location(findoptionS("exceptionphraselist")); std::string weighted_phrase_list_location(findoptionS("weightedphraselist")); std::string banned_phrase_list_location(findoptionS("bannedphraselist")); std::string storyboard_location(findoptionS("storyboard")); if( storyboard_location.empty()) { storyboard_location = __CONFDIR; storyboard_location += "/group"; storyboard_location += String(filtergroup); storyboard_location += ".story"; } DEBUG_trace("Read settings into memory"); DEBUG_trace("Reading phrase, URL and site lists into memory"); if (weighted_phrase_mode > 0) { naughtyness_limit = findoptionI("naughtynesslimit"); if (naughtyness_limit == 0) { naughtyness_limit = 60; } if (!realitycheck(naughtyness_limit, 1, 0, "naughtynesslimit")) return false; if (!o.lm.readbplfile(banned_phrase_list_location.c_str(), exception_phrase_list_location.c_str(), weighted_phrase_list_location.c_str(), banned_phrase_list, force_quick_search, naughtyness_limit)) { return false; } // read banned, exception, weighted phrase list banned_phrase_flag = true; } { std::deque dq = findoptionM("ipsitelist"); DEBUG_debug("ipsitelist deque is size ", String(dq.size()) ); if(!LMeta.load_type(LIST_TYPE_IPSITE, dq)) read_errors = true; } { std::deque dq = findoptionM("iplist"); DEBUG_debug("iplist deque is size ", String(dq.size()) ); if(!LMeta.load_type(LIST_TYPE_IP, dq))read_errors = true; } { std::deque dq = findoptionM("timelist"); DEBUG_debug("timelist deque is size ", String(dq.size()) ); if(!LMeta.load_type(LIST_TYPE_TIME, dq))read_errors = true; } { std::deque dq = findoptionM("sitelist"); DEBUG_debug("sitelist deque is size ", String(dq.size()) ); if(!LMeta.load_type(LIST_TYPE_SITE, dq))read_errors = true; } { std::deque dq = findoptionM("urllist"); DEBUG_debug("urllist deque is size ", String(dq.size()) ); if(!LMeta.load_type(LIST_TYPE_URL, dq)) read_errors = true; } { std::deque dq = findoptionM("searchlist"); DEBUG_debug("searchlist deque is size ", String(dq.size()) ); if(!LMeta.load_type(LIST_TYPE_SEARCH, dq)) read_errors = true; } { std::deque dq = findoptionM("fileextlist"); DEBUG_debug("fileextlist deque is size ", String(dq.size()) ); if(!LMeta.load_type(LIST_TYPE_FILE_EXT, dq)) read_errors = true; } { std::deque dq = findoptionM("mimelist"); DEBUG_debug("mimelist deque is size ", String(dq.size()) ); if(!LMeta.load_type(LIST_TYPE_MIME, dq)) read_errors = true; } { std::deque dq = findoptionM("regexpboollist"); DEBUG_debug("regexpboollist deque is size ", String(dq.size()) ); if(!LMeta.load_type(LIST_TYPE_REGEXP_BOOL, dq)) read_errors = true; } { std::deque dq = findoptionM("regexpreplacelist"); DEBUG_debug("regexpreplacelist deque is size ", String(dq.size()) ); if(!LMeta.load_type(LIST_TYPE_REGEXP_REP, dq)) read_errors = true; } { std::deque dq = findoptionM("categorylist"); DEBUG_debug("categorylist deque is size ", String(dq.size()) ); if(!LMeta.load_type(LIST_TYPE_CATEGORY, dq)) read_errors = true; } #ifdef PRT_DNSAUTH auth_exception_url_flag = true; #endif if (weighted_phrase_mode > 0) { searchterm_limit = findoptionI("searchtermlimit"); if (!realitycheck(searchterm_limit, 0, 0, "searchtermlimit")) { return false; } // Optionally override the normal phrase lists for search term blocking. // We need all three lists to build a phrase tree, so fail if we encounter // anything other than all three enabled/disabled simultaneously. if (searchterm_limit > 0) { std::string exception_searchterm_list_location(findoptionS("exceptionsearchtermlist")); std::string weighted_searchterm_list_location(findoptionS("weightedsearchtermlist")); std::string banned_searchterm_list_location(findoptionS("bannedsearchtermlist")); if (!(exception_searchterm_list_location.length() == 0 && weighted_searchterm_list_location.length() == 0 && banned_searchterm_list_location.length() == 0)) { // At least one is enabled - try to load all three. if (!o.lm.readbplfile(banned_searchterm_list_location.c_str(), exception_searchterm_list_location.c_str(), weighted_searchterm_list_location.c_str(), searchterm_list, force_quick_search, naughtyness_limit)) { return false; } searchterm_flag = true; } } } std::string content_regexp_list_location(findoptionS("contentregexplist")); if (content_regexp_list_location.length() > 1) { unsigned int content_regexp_list; if (!LMeta.readRegExReplacementFile(content_regexp_list_location.c_str(), list_pwd.toCharArray(), "contentregexplist", content_regexp_list, content_regexp_list_rep, content_regexp_list_comp)) { return false; } else { content_regexp_flag = true; } } else { content_regexp_flag = false; } DEBUG_trace("Lists in memory"); if(read_errors) { E2LOGGER_warning("***List Error*** Reading of some lists defined or included in lists defined in ",filename, " failed" ); if(o.lists.abort_on_missing_list) return false; } DEBUG_trace("Read Storyboard"); if (!StoryB.readFile(storyboard_location.c_str(), LMeta, true)) return false; if(!StoryB.setEntry(ENT_STORYB_PROXY_REQUEST,"checkrequest")) { E2LOGGER_error("Required storyboard entry function 'checkrequest' is missing"); return false; } if(!StoryB.setEntry(ENT_STORYB_PROXY_RESPONSE,"checkresponse")) { E2LOGGER_error("Required storyboard entry function 'checkresponse' is missing"); return false; } if(!StoryB.setEntry(ENT_STORYB_LOG_CHECK,"checklogging")) { E2LOGGER_error( "Required storyboard entry function 'checklogging' is missing" ); return false; } if((o.net.transparenthttps_port > 0) && !StoryB.setEntry(ENT_STORYB_THTTPS_REQUEST,"thttps-checkrequest")) { E2LOGGER_error("Required storyboard entry function 'thttps-checkrequest' is missing"); return false; } if((o.net.icap_port > 0) && !StoryB.setEntry(ENT_STORYB_ICAP_REQMOD,"icap-checkrequest")) { E2LOGGER_error("Required storyboard entry function 'icap-checkrequest' is missing"); return false; } if((o.net.icap_port > 0) && !StoryB.setEntry(ENT_STORYB_ICAP_RESMOD,"icap-checkresponse")) { E2LOGGER_error("Required storyboard entry function 'icap-checkresponse' is missing"); return false; } if (!precompileregexps()) { return false; } // precompiled reg exps for speed if((o.net.icap_port > 0) && !StoryB.setEntry(ENT_STORYB_ICAP_RESMOD,"icap-checkresponse")) { E2LOGGER_error("Required storyboard entry function 'icap-checkresponse' is missing"); return false; } if (o.story.dm_entry_dq.size() > 0) { for (std::deque::const_iterator i = o.story.dm_entry_dq.begin(); i != o.story.dm_entry_dq.end(); ++i) { if (!StoryB.setEntry(i->entry_id, i->entry_function)) { E2LOGGER_error("Required DM storyboard entry function", i->entry_function, " is missing from pre_auth.stoary" ); } } } if (!precompileregexps()) { return false; } // precompiled reg exps for speed bypass_mode = findoptionI("bypass"); cgi_bypass = (findoptionS("cgibypass")== "on" ); cgi_infection_bypass = (findoptionS("cgiinfectionbypass") == "on"); bypass_version = findoptionI("bypassversion"); if (!realitycheck(bypass_version, 0, 2, "bypassversion")) { return false; } if (bypass_version == 0) bypass_version = 1; //default if (bypass_version == 2) bypass_v2 = true; //default bypass_version = findoptionI("bypassversion"); if (!realitycheck(bypass_version, 0, 2, "bypassversion")) { return false; } if (bypass_version == 0) bypass_version = 2; //default if (bypass_version == 2) bypass_v2 = true; //default if(bypass_mode == -1) cgi_bypass = true; // for backward compatibility // we use the "magic" key here both for filter bypass *and* for filter bypass after virus scan (fancy DM). if ((bypass_mode != 0) || (disable_content_scan != 1)) { magic = findoptionS("bypasskey"); if (magic.length() < 9) { std::string s(16u, ' '); for (int i = 0; i < 16; i++) { s[i] = (rand() % 26) + 'A'; } magic = s; } DEBUG_debug("Setting magic key to '", magic, "'"); // Create the Bypass Cookie magic key cookie_magic = std::string(16u, ' '); for (int i = 0; i < 16; i++) { cookie_magic[i] = (rand() % 26) + 'A'; } } infection_bypass_mode = findoptionI("infectionbypass"); if (!realitycheck(infection_bypass_mode, -1, 0, "infectionbypass")) { return false; } if(infection_bypass_mode == -1) cgi_infection_bypass = true; // for backward compatibility if (infection_bypass_mode != 0) { imagic = findoptionS("infectionbypasskey"); if (imagic.length() < 9) { std::string s(16u, ' '); for (int i = 0; i < 16; i++) { s[i] = (rand() % 26) + 'A'; } imagic = s; } DEBUG_debug("Setting imagic key to '", imagic, "'"); if (findoptionS("infectionbypasserrorsonly") == "off") { infection_bypass_errors_only = false; } else { DEBUG_debug("Only allowing infection bypass on scan error"); infection_bypass_errors_only = true; } } if (findoptionS("scanbypass") == "on") { scan_bypass = true; } else { scan_bypass = false; } if(((cgi_bypass)||(cgi_infection_bypass)) && (bypass_version == 2)) { cgi_magic = findoptionS("cgikey"); if ( cgi_magic.length() < 9 ) { E2LOGGER_error("A valid cgikey must be provided with bypass cgi version 2"); return false; }; cgi_bypass_v2 = true; if(cgi_infection_bypass && infection_bypass_mode < 60) { E2LOGGER_error("infectionbypassmode must be greater than 60 with bypass cgi version 2"); return false; } else if(bypass_mode < 60) { E2LOGGER_error("bypassmode must be greater than 60 with bypass cgi version 2"); return false; } } } catch (std::exception &e) { E2LOGGER_error(e.what()); // when called the daemon has not // detached so we can do this return false; } return true; } int FOptionContainer::findoptionI(const char *option) { int res = String(findoptionS(option).c_str()).toInteger(); return res; } std::string FOptionContainer::findoptionS(const char *option) { // findoptionS returns a found option stored in the deque String temp; String temp2; String o(option); // for (int i = 0; i < (signed)conffile.size(); i++) for (int i = (signed)conffile.size() - 1; i > -1; i--) // reverse search so that later entries will overwrite any earlier ones. { temp = conffile[i].c_str(); temp2 = temp.before("="); while (temp2.endsWith(" ")) { // get rid of tailing spaces before = temp2.chop(); } if (o == temp2) { temp = temp.after("="); while (temp.startsWith(" ")) { // get rid of heading spaces temp.lop(); } if (temp.startsWith("'")) { // inverted commas temp.lop(); } while (temp.endsWith(" ")) { // get rid of tailing spaces temp.chop(); } if (temp.endsWith("'")) { // inverted commas temp.chop(); } return temp.toCharArray(); } } return ""; } std::deque FOptionContainer::findoptionM(const char *option) { // findoptionS returns all the matching options String temp; String temp2; String o(option); std::deque results; for (std::deque::iterator i = conffile.begin(); i != conffile.end(); i++) { if ((*i).empty()) continue; temp = (*i).c_str(); temp2 = temp.before("="); while (temp2.endsWith(" ")) { // get rid of tailing spaces before = temp2.chop(); } if (o == temp2) { temp = temp.after("="); while (temp.startsWith(" ")) { // get rid of heading spaces temp.lop(); } // if (temp.startsWith("'")) { // inverted commas // temp.lop(); // } temp.removeMultiChar('\''); while (temp.endsWith(" ")) { // get rid of tailing spaces temp.chop(); } // if (temp.endsWith("'")) { // inverted commas // temp.chop(); // } results.push_back(temp); } } return results; } bool FOptionContainer::realitycheck(int l, int minl, int maxl, const char *emessage) { // realitycheck checks a String for certain expected criteria // so we can spot problems in the conf files easier if ((l < minl) || ((maxl > 0) && (l > maxl))) { E2LOGGER_error("Config problem; check allowed values for ", emessage); return false; } return true; } bool FOptionContainer::precompileregexps() { if (!isiphost.comp(".*[a-z|A-Z].*")) { E2LOGGER_error("Error compiling RegExp isiphost."); return false; } return true; } bool FOptionContainer::isOurWebserver(String url) { // reporting levels 0 and 3 don't use the CGI if (reporting_level == 1 || reporting_level == 2) { url.removeWhiteSpace(); // just in case of weird browser crap url.toLower(); url.removePTP(); // chop off the ht(f)tp(s):// if (url.contains("/")) { url = url.before("/"); // chop off any path after the domain } if (url.startsWith(access_denied_domain)) { // don't filter our web server return true; } } return false; } e2guardian-5.5.8r/src/FOptionContainer.hpp000066400000000000000000000127221477372360500204770ustar00rootroot00000000000000// FOptionContainer class - contains the options for a filter group, // including the banned/grey/exception site lists and the content/site/url regexp lists // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_FOPTIONCONTAINER #define __HPP_FOPTIONCONTAINER // INCLUDES #include "String.hpp" #include "HTMLTemplate.hpp" #include "ListContainer.hpp" #include "ListMeta.hpp" #include "LanguageContainer.hpp" #include "ImageContainer.hpp" #include "RegExp.hpp" //#include "HTTPHeader.hpp" //#include "NaughtyFilter.hpp" #include "StoryBoard.hpp" #include #include // DECLARATIONS std::deque *ipToHostname(const char *ip); void getClientFromIP(const char *ip, std::string &clienthost); class FOptionContainer { public: int reporting_level; int category_threshold; bool infection_bypass_errors_only; bool disable_content_scan; bool disable_content_scan_error; bool content_scan_exceptions; bool automitm; int weighted_phrase_mode; unsigned int banned_phrase_list; int group_mode; int embedded_url_weight; int naughtyness_limit; int searchterm_limit; off_t max_upload_size; int filtergroup; bool non_standard_delimiter; //SSL certificate checking bool ssl_check_cert = false; //SSL Man in the middle bool ssl_mitm = false; bool mitm_check_cert = true; #ifdef ENABLE_EMAIL // Email notification patch by J. Gauthier bool notifyav; bool notifycontent; bool use_smtp; int violations; int current_violations; int threshold; long threshold_stamp; bool byuser; #endif bool reverse_lookups; bool force_quick_search; int bypass_mode = 0; bool bypass_v2 = false; bool cgi_bypass_v2 = false; int bypass_version = 2; bool cgi_bypass = false; int infection_bypass_mode; bool cgi_infection_bypass = false; bool scan_bypass = false; std::string name; std::string magic; std::string imagic; std::string cookie_magic; std::string cgi_magic; #ifdef ENABLE_EMAIL // Email notification patch by J. Gauthier std::string mailfrom; std::string avadmin; std::string contentadmin; std::string avsubject; std::string contentsubject; std::string violationbody; #endif #ifdef PRT_DNSAUTH unsigned int auth_exception_site_list; unsigned int auth_exception_url_list; #endif bool use_only_local_allow_lists; bool url_redirect_regexp_flag; bool allow_empty_host_certs = false; // regex search & replace lists std::deque content_regexp_list_comp; std::deque content_regexp_list_rep; RegExp isiphost; // access denied address & domain - if they override the defaults std::string access_denied_address; String access_denied_domain; bool ssl_denied_rewrite = false; // search term blocking unsigned int searchterm_list; bool searchterm_flag = false; bool read_errors = false; FOptionContainer() : searchterm_flag(false), banned_page(NULL) {}; ~FOptionContainer(); bool read(const char *filename); bool readConfFile(const char *filename, String &list_pwd); void reset(); void resetJustListData(); bool isOurWebserver(String url); #ifdef PRT_DNSAUTH bool inAuthExceptionSiteList(String url, bool doblanket = false, bool ip = false, bool ssl = false); bool inAuthExceptionURLList(String url, bool doblanket = false, bool ip = false, bool ssl = false); #endif StoryBoard StoryB; // get HTML template for this group HTMLTemplate *getHTMLTemplate(bool upfail); std::deque text_mime; std::deque text_mime_stop; // used to exclude text/ mime type from standard e2g phrase scan private: // HTML template - if it overrides the default HTMLTemplate *banned_page = nullptr; HTMLTemplate *neterr_page = nullptr; ListMeta LMeta; bool banned_phrase_flag = false; bool content_regexp_flag = false; #ifdef PRT_DNSAUTH bool auth_exception_site_flag; bool auth_exception_url_flag; #endif bool banned_search_flag; std::deque banned_phrase_list_index; std::deque conffile; bool precompileregexps(); bool readFile(const char *filename, const char *list_pwd, unsigned int *whichlist, bool sortsw, bool cache, const char *listname); bool compileRegExMatchFile(unsigned int list, std::deque &list_comp, std::deque &list_source, std::deque &list_ref); bool readRegExReplacementFile(const char *filename, const char *listname, unsigned int &listid, std::deque &list_rep, std::deque &list_comp); int findoptionI(const char *option); std::string findoptionS(const char *option); std::deque findoptionM(const char *option); bool realitycheck(int l, int minl, int maxl, const char *emessage); int inRegExpURLList(String &url, std::deque &list_comp, std::deque &list_ref, unsigned int list, String &lastcategory); char *inURLList(String &url, unsigned int list, bool doblanket , bool ip , bool ssl , String &lastcategory); char *inSiteList(String &url, unsigned int list, bool doblanket , bool ip , bool ssl , String &lastcategory); char *testBlanketBlock(unsigned int list, bool ip, bool ssl, String &lastcategory); }; #define __HPP_FOPTIONCONTAINER #endif e2guardian-5.5.8r/src/FatController.cpp000066400000000000000000001711171477372360500200330ustar00rootroot00000000000000// http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include // LINUX ONLY FEATURE //#ifdef HAVE_SYS_EPOLL_H //#include //#endif #include #include #include #include #include #include #include #include #include #include #include #include "FatController.hpp" #include "ConnectionHandler.hpp" #include "DynamicURLList.hpp" #include "DynamicIPList.hpp" #include "String.hpp" #include "SocketArray.hpp" #include "UDSocket.hpp" #include "SysV.hpp" #include "Queue.hpp" #include "OptionContainer.hpp" #include "Logger.hpp" #ifdef VALGRD extern "C" { static void* execute_native_thread_routine(void* __p) { std::thread::_Impl_base* __t = static_cast(__p); std::thread::__shared_base_type __local; __local.swap(__t->_M_this_ptr); __try { __t->_M_run(); } __catch(const __cxxabiv1::__forced_unwind&) { __throw_exception_again; } __catch(...) { std::terminate(); } return nullptr; } } // extern "C" void std::thread::_M_start_thread(__shared_base_type __b) { if (!__gthread_active_p()) #if __cpp_exceptions throw system_error(make_error_code(errc::operation_not_permitted), "Enable multithreading to use std::thread"); #else __throw_system_error(int(errc::operation_not_permitted)); #endif _M_start_thread(std::move(__b), nullptr); } void std::thread::_M_start_thread(__shared_base_type __b, void (*)()) { auto ptr = __b.get(); ptr->_M_this_ptr = std::move(__b); int __e = __gthread_create(&_M_id._M_thread, &execute_native_thread_routine, ptr); if (__e) { ptr->_M_this_ptr.reset(); __throw_system_error(__e); } } #endif // GLOBALS std::atomic ttg; std::atomic e2logger_ttg; std::atomic gentlereload; std::atomic reloadconfig; std::atomic reload_cnt; std::atomic rotate_access; std::atomic rotate_request; std::atomic rotate_dstat; extern std::atomic g_is_starting; extern OptionContainer o; extern bool is_daemonised; pid_t master_pid = 0; void stat_rec::clear() { conx = 0; reqs = 0; }; void stat_rec::start(bool firsttime = true) { if (firsttime) { clear(); start_int = time(NULL); end_int = start_int + o.dstat.dstat_interval; maxusedfd = 0; } if (o.dstat.dstat_log_flag) { // now opened by Logger //mode_t old_umask; //old_umask = umask(S_IWGRP | S_IWOTH); //fs = fopen(o.dstat_location.c_str(), "a"); std::string outmess; if (o.dstat.stats_human_readable) { outmess = "time httpw busy httpwQ logQ conx conx/s reqs reqs/s maxfd LCcnt"; } else { outmess = "time httpw busy httpwQ logQ conx conx/s reqs reqs/s maxfd LCcnt"; } E2LOGGER_dstatslog(outmess); e2logger.flush(LoggerSource::dstatslog); }; }; void stat_rec::reset() { time_t now = time(NULL); int bc = busychildren; long period = now - start_int; long cnx = (long) conx; long rqx = (long) reqs; int mfd = maxusedfd; int LC = o.LC_cnt; // clear and reset stats now so that stats are less likely to be missed clear(); if ((end_int + o.dstat.dstat_interval) > now) start_int = end_int; else start_int = now; end_int = start_int + o.dstat.dstat_interval; if (rotate_dstat) { e2logger.rotate(LoggerSource::dstatslog); start(false); // output header rotate_dstat = false; } char outbuff[201]; long cps = cnx / period; long rqs = rqx / period; if (o.dstat.stats_human_readable) { struct tm *timeinfo; time(&now); timeinfo = localtime(&now); char buffer[50]; strftime(buffer, 50, "%Y-%m-%d %H:%M", timeinfo); snprintf(outbuff, 200, "%s %d %d %d %d %ld %ld %ld %ld %d %d", buffer, o.proc.http_workers, bc, o.http_worker_Q.size(), o.log.log_Q->size(), cnx, cps, rqx, rqs, mfd, LC); } else { snprintf(outbuff, 200, "%ld %d %d %d %d %ld %ld %ld %ld %d %d", now, o.proc.http_workers, bc, o.http_worker_Q.size(), o.log.log_Q->size(), cnx, cps, rqx, rqs, mfd, LC); } std::string outs(outbuff); E2LOGGER_dstatslog(outs); e2logger.flush(LoggerSource::dstatslog); //fflush(fs); }; void stat_rec::close() { if (fs != NULL) fclose(fs); }; //int cache_erroring; // num cache errors reported by children //int restart_cnt = 0; //int restart_numchildren; // numchildren at time of gentle restart //int hup_index; //int gentle_to_hup = 0; //bool gentle_in_progress = false; int top_child_fds; // cross platform maxchildren position in children array int failurecount; int serversocketcount; SocketArray serversockets; // the sockets we will listen on for connections Socket *peersock(NULL); // the socket which will contain the connection //String peersockip; // which will contain the connection ip void monitor_flag_set(bool action) { String fulink = o.monitor.monitor_flag_prefix; String ftouch = fulink; if (action) { fulink += "paused"; ftouch += "running"; } else { fulink += "running"; ftouch += "paused"; } //mode_t old_umask; //old_umask = umask(S_IWOTH); umask(S_IWOTH); FILE *fs = fopen(ftouch.c_str(), "w"); if (!fs) { E2LOGGER_error("Unable to open monitor_flag ", ftouch, " for writing"); o.monitor.monitor_flag_flag = false; } fclose(fs); if (unlink(fulink.c_str()) == -1) { E2LOGGER_error("Unable to unlink monitor_flag ", fulink, " error: ", strerror(errno)); } return; } stat_rec dstat; stat_rec *dystat = &dstat; // DECLARATIONS // Signal handlers extern "C" { //void sig_chld(int signo); //void sig_term(int signo); // This is so we can kill our children //void sig_termsafe(int signo); // This is so we can kill our children safer //void sig_hup(int signo); // This is so we know if we should re-read our config. //void sig_usr1(int signo); // This is so we know if we should re-read our config but not kill current connections //void sig_childterm(int signo); //#ifdef ENABLE_SEGV_BACKTRACE //void sig_segv(int signo, siginfo_t *info, void *secret); // Generate a backtrace on segfault //#endif } // logging & URL cache processes void log_listener(Queue *log_Q, bool is_RQlog); // fork off into background bool daemonise(int pidfilefd); // create specified amount of child threads //void handle_connections(int tindex); // needs changing to be threadish // setuid() to proxy user (not just seteuid()) - used by child processes & logger/URL cache for security & resource usage reasons bool drop_priv_completely(); // IMPLEMENTATION // completely drop our privs - i.e. setuid, not just seteuid bool drop_priv_completely() { // This is done to solve the problem where the total processes for the // uid rather than euid is taken for RLIMIT_NPROC and so can't fork() // as many as expected. // It is also more secure. // // Suggested fix by Lawrence Manning Tue 25th February 2003 // int rc = seteuid(o.proc.root_user); // need to be root again to drop properly if (rc == -1) { E2LOGGER_error("Unable to seteuid(suid)"); return false; // setuid failed for some reason so exit with error } rc = setuid(o.proc.proxy_user); if (rc == -1) { E2LOGGER_error("Unable to setuid()"); return false; // setuid failed for some reason so exit with error } return true; } // Fork ourselves off into the background bool daemonise(int pidfileid) { int nullfd = -1; if ((nullfd = open("/dev/null", O_WRONLY, 0)) == -1) { E2LOGGER_error("Couldn't open /dev/null"); return false; } pid_t pid; if ((pid = fork()) < 0) { // Error!! close(nullfd); return false; } else if (pid != 0) {// parent goes...A master_pid = pid; if (nullfd != -1) { close(nullfd); } int rc = sysv_writepidfile(pidfileid, master_pid); if (rc != 0) { E2LOGGER_error("Error writing to the e2guardian.pid file: ", strerror(errno)); } // bye-bye exit(0); } // child continues dup2(nullfd, 0); // stdin dup2(nullfd, 1); // stdout dup2(nullfd, 2); // stderr close(nullfd); setsid(); // become session leader //int dummy = chdir("/"); // change working directory if (chdir("/") != 0) {// change working directory E2LOGGER_error(" Can't change / directory !"); return false; } umask(0); // clear our file mode creation mask umask(S_IWGRP | S_IWOTH); // set to mor sensible setting?? is_daemonised = true; return true; } // * // * // * worker thread code // * // * // handle any connections received by this thread void handle_connections(int tindex) { (thread_id = "hw") += std::to_string(tindex) += ": "; try { while (!ttg) { // extra loop in order to delete and create ConnentionHandler on new lists or error ConnectionHandler h; // the class that handles the connections String ip; while (!ttg) { DEBUG_debug(" waiting connection on http_worker_Q "); LQ_rec rec = o.http_worker_Q.pop(); Socket *peersock = rec.sock; DEBUG_debug(" popped connection from http_worker_Q"); if (ttg) break; String peersockip = peersock->getPeerIP(); if (peersock->getFD() < 0 || peersockip.length() < 7) { // if (o.conn.logconerror) E2LOGGER_info("Error accepting. (Ignorable)"); continue; } ++dystat->busychildren; ++dystat->conx; #ifdef DEBUG_LOW int rc = h.handlePeer(*peersock, peersockip, dystat, rec.ct_type); // deal with the connection DEBUG_debug("handle_peer returned: ", rc); #else h.handlePeer(*peersock, peersockip, dystat, rec.ct_type); // deal with the connection #endif --dystat->busychildren; if(peersock != nullptr) delete peersock; break; }; }; } catch (...) { E2LOGGER_error("worker thread caught unexpected exception - exiting"); } } // * // * // * end of worker thread code // * // * #ifdef REMOVE_IN_55 void tell_monitor(bool active) //may not be needed { String buff(o.monitor_helper); String buff1; if (active) buff1 = " start"; else buff1 = " stop"; E2LOGGER_error("Monitorhelper called: ", buff, buff1); pid_t childid; childid = fork(); if (childid == -1) { E2LOGGER_error("Unable to fork to tell monitorhelper error: ", strerror(errno)); return; }; if (childid == 0) { // Am the child int rc = seteuid(o.proc.root_user); if (rc != -1) { int systemreturn = execl(buff.c_str(), buff.c_str(), buff1.c_str(), (char *) NULL); // should not return from call if (systemreturn == -1) { E2LOGGER_error("Unable to exec: ", buff, buff1, " : errno ", errno, " ", strerror(errno)); exit(0); } } else { E2LOGGER_error("Unable to set uid root"); exit(0); } }; if (childid > 0) { // Am the parent int rc; int status; rc = waitpid(childid, &status, 0); if (rc == -1) { E2LOGGER_error("Wait for monitorhelper returned : errno ", strerror(errno)); return; }; if (WIFEXITED(status)) { return; } else { E2LOGGER_error("Monitorhelper exited abnormally"); return; }; }; }; #endif #ifdef NOTDEF void wait_for_proxy() { Socket proxysock; int rc; try { // ...connect to proxy rc = proxysock.connect(o.proxy_ip, o.proxy_port); if (!rc) { proxysock.close(); //cache_erroring = 0; return; } if (errno == EINTR) { return; } } catch (std::exception &e) { DEBUG_debug(" -exception while creating proxysock: ", e.what()); } E2LOGGER_error("Proxy is not responding - Waiting for proxy to respond"); if (o.monitor_flag_flag) monitor_flag_set(false); if (o.monitor_helper_flag) tell_monitor(false); int wait_time = 1; //int report_interval = 600; // report every 10 mins to log int cnt_down = o.proxy_failure_log_interval; while (true) { rc = proxysock.connect(o.proxy_ip, o.proxy_port); if (!rc) { proxysock.close(); //cache_erroring = 0; E2LOGGER_error("Proxy now responding - resuming after %d seconds", wait_time); if (o.monitor_flag_flag) monitor_flag_set(true); if (o.monitor_helper_flag) tell_monitor(true); return; } else { if (ttg) return; wait_time++; cnt_down--; if (cnt_down < 1) { E2LOGGER_error("Proxy not responding - still waiting after %d seconds", wait_time); cnt_down = o.proxy_failure_log_interval; } std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } } } #endif // * // * // * logger, IP list and URL cache main loops // * // * void log_listener(Queue *log_Q, bool is_RQlog) { if (is_RQlog) thread_id = "RQlog: "; else thread_id = "log: "; try { DEBUG_trace("log listener started"); #ifdef ENABLE_EMAIL // no longer needed - replace with some sort of external script reading alert log?? // Email notification patch by J. Gauthier std::map violation_map; std::map timestamp_map; std::map vbody_map; int curv_tmp, stamp_tmp, byuser; #endif //String where, what, how; std::string cr("\n"); std::string where, what, how, cat, clienthost, from, who, mimetype, useragent, ssize, sweight, params, message_no; std::string stype, postdata, flags, searchterms; int port = 80, isnaughty = 0, isexception = 0, issemiexception = 0, code = 200, naughtytype = 0; int do_access_log = 0, do_alert_log =0; int cachehit = 0, wasinfected = 0, wasscanned = 0, filtergroup = 0; long tv_sec = 0, tv_usec = 0, endtv_sec = 0, endtv_usec = 0; int contentmodified = 0, urlmodified = 0, headermodified = 0; int headeradded = 0; String server(""); // Get server name - only needed for formats 5 & 7 if ((o.log.log_file_format == 5) || (o.log.log_file_format == 7)) { server = o.net.server_name; } std::string semiexception_word = o.language_list.getTranslation(51); semiexception_word = "*" + semiexception_word + "* "; std::string exception_word = o.language_list.getTranslation(51); exception_word = "*" + exception_word + "* "; std::string denied_word = o.language_list.getTranslation(52); denied_word = "*" + denied_word; std::string infected_word = o.language_list.getTranslation(53); infected_word = "*" + infected_word + "* "; std::string scanned_word = o.language_list.getTranslation(54); scanned_word = "*" + scanned_word + "* "; std::string contentmod_word = o.language_list.getTranslation(55); contentmod_word = "*" + contentmod_word + "* "; std::string urlmod_word = o.language_list.getTranslation(56); urlmod_word = "*" + urlmod_word + "* "; std::string headermod_word = o.language_list.getTranslation(57); headermod_word = "*" + headermod_word + "* "; std::string headeradd_word = o.language_list.getTranslation(58); headeradd_word = "*" + headeradd_word + "* "; std::string neterr_word = o.language_list.getTranslation(59); neterr_word = "*" + neterr_word + "* "; std::string blank_str; if (o.log.use_dash_for_blanks) blank_str = "-"; else blank_str = ""; while (!e2logger_ttg) { // loop, essentially, for ever std::string loglines; loglines.append(log_Q->pop()); // get logdata from queue if (e2logger_ttg) break; if (is_RQlog && rotate_request) { e2logger.rotate(LoggerSource::requestlog); rotate_request = false; } else if (!is_RQlog && rotate_access) { e2logger.rotate(LoggerSource::accesslog); rotate_access = false; } DEBUG_debug("received a log request"); // Formatting code migration from ConnectionHandler // and email notification code based on patch provided // by J. Gauthier // read in the various parts of the log string bool error = true; int itemcount = 0; //char * dup = strdup(loglines.c_str()); //const char *delim = "\n"; std::istringstream iss(loglines); std::string logline; std::shared_ptr ldl; ldl = o.currentLists(); while (std::getline(iss, logline)) { // Loop around reading in data, because we might have huge URLs std::string s; if (o.log.use_dash_for_blanks && logline == "") { s = "-"; } else if (!o.log.use_dash_for_blanks && logline == "-") { s = ""; } else { s = logline; } switch (itemcount) { case 0: isexception = atoi(logline.c_str()); break; case 1: cat = s; break; case 2: isnaughty = atoi(logline.c_str()); break; case 3: naughtytype = atoi(logline.c_str()); break; case 4: sweight = s; break; case 5: where = s; break; case 6: what = s; break; case 7: how = s; break; case 8: who = s; break; case 9: from = s; break; case 10: port = atoi(logline.c_str()); break; case 11: wasscanned = atoi(logline.c_str()); break; case 12: wasinfected = atoi(logline.c_str()); break; case 13: contentmodified = atoi(logline.c_str()); break; case 14: urlmodified = atoi(logline.c_str()); break; case 15: headermodified = atoi(logline.c_str()); break; case 16: ssize = s; break; case 17: filtergroup = atoi(logline.c_str()); if (filtergroup < 0 || filtergroup > o.filter.numfg) filtergroup = 0; break; case 18: code = atoi(logline.c_str()); break; case 19: cachehit = atoi(logline.c_str()); break; case 20: mimetype = s; break; case 21: tv_sec = atol(logline.c_str()); break; case 22: tv_usec = atol(logline.c_str()); break; case 23: endtv_sec = atol(logline.c_str()); break; case 24: endtv_usec = atol(logline.c_str()); break; case 25: clienthost = s; break; case 26: useragent = s; break; case 27: params = s; break; case 28: postdata = s; break; case 29: message_no = s; break; case 30: headeradded = atoi(logline.c_str()); break; case 31: flags = s; break; case 32: searchterms = s; break; case 33: do_access_log = atoi(logline.c_str()); break; case 34: do_alert_log = atoi(logline.c_str()); break; case 35: issemiexception = atoi(logline.c_str()); error = false; break; } itemcount++; } // don't build the log line if we couldn't read all the component parts if (error) { E2LOGGER_error("Error in logline ", itemcount, " ", loglines); continue; } // Start building the log line if (port != 0 && port != 80) { // put port numbers of non-standard HTTP requests into the logged URL String newwhere(where); if (newwhere.after("://").contains("/")) { String proto, host, path; proto = newwhere.before("://"); host = newwhere.after("://"); path = host.after("/"); host = host.before("/"); newwhere = proto; newwhere += "://"; newwhere += host; newwhere += ":"; newwhere += String((int) port); newwhere += "/"; newwhere += path; where = newwhere; } else { where += ":"; where += String((int) port); } } bool neterr = false; // stamp log entries so they stand out/can be searched switch (naughtytype) { case 1: stype = "-POST"; break; case 2: stype = "-PARAMS"; break; case 3: neterr = true; break; default: stype.clear(); } if (isnaughty) { if (neterr) what = neterr_word + what; else what = denied_word + stype + "* " + what; } else if (isexception && (o.log.log_exception_hits == 2)) { if (issemiexception) { what = semiexception_word + what; } else { what = exception_word + what; } } if (wasinfected) what = infected_word + stype + "* " + what; else if (wasscanned) what = scanned_word + what; if (contentmodified) { what = contentmod_word + what; } if (urlmodified) { what = urlmod_word + what; } if (headermodified) { what = headermod_word + what; } if (headeradded) { what = headeradd_word + what; } std::string builtline, year, month, day, hour, min, sec, when, vbody, utime; // create a string representation of UNIX timestamp if desired if (o.log.log_timestamp || (o.log.log_file_format == 3) || (o.log.log_file_format > 4)) { String temp((int) (endtv_usec / 1000)); while (temp.length() < 3) { temp = "0" + temp; } if (temp.length() > 3) { temp = "999"; } utime = temp; utime = "." + utime; utime = String((int) endtv_sec) + utime; } if ((o.log.log_file_format <= 2) || (o.log.log_file_format == 4)) { // "when" not used in format 3, and not if logging timestamps instead in formats 5-8 //time_t now = time(NULL); time_t now = endtv_sec; char date[32]; struct tm *tm = localtime(&now); strftime(date, sizeof date, "%Y.%m.%d %H:%M:%S", tm); when = date; // append timestamp if desired if (o.log.log_timestamp) when += " " + utime; } // blank out IP, hostname and username if desired if (o.log.anonymise_logs) { who = ""; from = "0.0.0.0"; clienthost.clear(); } else if ((clienthost == blank_str) || (clienthost == "DNSERROR")) { clienthost = from; } String groupname; String stringcode(code); String stringgroup(filtergroup + 1); if (is_RQlog) { groupname = ""; } else { if (stringcode == "407") { groupname = "negotiate_identification"; } else { groupname = ldl->fg[filtergroup]->name; } } switch (o.log.log_file_format) { case 4: builtline = when + "\t" + who + "\t" + from + "\t" + where + "\t" + what + "\t" + how + "\t" + ssize + "\t" + sweight + "\t" + cat + "\t" + stringgroup + "\t" + stringcode + "\t" + mimetype + "\t" + clienthost + "\t" + groupname #ifdef SG_LOGFORMAT + "\t" + useragent + "\t\t" + o.log.logid_1 + "\t" + o.log.prod_id + "\t" + params + "\t" + o.log.logid_2 + "\t" + postdata; #else + "\t" + useragent + "\t" + params + "\t" + o.log.logid_1 + "\t" + o.log.logid_2 + "\t" + postdata; #endif break; case 3: { // as certain bits of info are logged in format 3, their creation is best done here, not in all cases. std::string duration, hier, hitmiss; long durationsecs, durationusecs; durationsecs = (endtv_sec - tv_sec); durationusecs = endtv_usec - tv_usec; durationusecs = (durationusecs / 1000) + durationsecs * 1000; String temp((int) durationusecs); while (temp.length() < 6) { temp = " " + temp; } duration = temp; if (code == 403) { hitmiss = "TCP_DENIED/403"; } else { if (cachehit) { hitmiss = "TCP_HIT/"; hitmiss.append(stringcode); } else { hitmiss = "TCP_MISS/"; hitmiss.append(stringcode); } } hier = "DEFAULT_PARENT/"; hier += o.net.proxy_ip; builtline = utime + " " + duration + " " + ((clienthost.length() > 0) ? clienthost : from) + " " + hitmiss + " " + ssize + " " + how + " " + where + " " + who + " " + hier + " " + mimetype; break; } case 2: builtline = "\"" + when + "\",\"" + who + "\",\"" + from + "\",\"" + where + "\",\"" + what + "\",\"" + how + "\",\"" + ssize + "\",\"" + sweight + "\",\"" + cat + "\",\"" + stringgroup + "\",\"" + stringcode + "\",\"" + mimetype + "\",\"" + clienthost + "\",\"" + groupname + "\",\"" + useragent + "\",\"" + params + "\",\"" + o.log.logid_1 + "\",\"" + o.log.logid_2 + "\",\"" + postdata + "\""; break; case 1: builtline = when + " " + who + " " + from + " " + where + " " + what + " " + how + " " + ssize + " " + sweight + " " + cat + " " + stringgroup + " " + stringcode + " " + mimetype + " " + clienthost + " " + groupname + " " + useragent + " " + params + " " + o.log.logid_1 + " " + o.log.logid_2 + " " + postdata; break; case 5: case 6: case 7: case 8: default: std::string duration; long durationsecs, durationusecs; durationsecs = (endtv_sec - tv_sec); durationusecs = endtv_usec - tv_usec; durationusecs = (durationusecs / 1000) + durationsecs * 1000; String temp((int) durationusecs); duration = temp; builtline = utime + "\t" + server + "\t" + who + "\t"; if (o.log.log_client_host_and_ip) { builtline += from + "\t"; builtline += clienthost + "\t"; } else { if (clienthost.length() > 2) builtline += clienthost + "\t"; else builtline += from + "\t"; } builtline += where + "\t" + how + "\t" + stringcode + "\t" + ssize + "\t" + mimetype + "\t" + (o.log.log_user_agent ? useragent : blank_str) + "\t" + blank_str + "\t" // squid result code + duration + "\t" + blank_str + "\t" // squid peer code + message_no + "\t" // dg message no + what + "\t" + sweight + "\t" + cat + "\t" + groupname + "\t" + stringgroup; } if (o.log.log_file_format > 6) { builtline += "\t"; builtline += searchterms; builtline += "\t"; builtline += flags; } // Send to Log DEBUG_trace("Now sending to Log"); if (is_RQlog) { E2LOGGER_requestlog(builtline); } else { if(do_access_log) { E2LOGGER_accesslog(builtline); } if(do_alert_log) { E2LOGGER_alertlog(builtline); } E2LOGGER_responselog(builtline); // Will only work if responselog is enabled. } #ifdef ENABLE_EMAIL // do the notification work here, but fork for speed if (ldl->fg[filtergroup]->use_smtp == true) { // run through the gambit to find out of we're sending notification // because if we're not.. then fork()ing is a waste of time. // virus if ((wasscanned && wasinfected) && (ldl->fg[filtergroup]->notifyav)) { // Use a double fork to ensure child processes are reaped adequately. pid_t smtppid; if ((smtppid = fork()) != 0) { // Parent immediately waits for first child waitpid(smtppid, NULL, 0); } else { // First child forks off the *real* process, but immediately exits itself if (fork() == 0) { // Second child - do stuff setsid(); FILE *mail = popen(o.mailer.c_str(), "w"); if (mail == NULL) { E2LOGGER_error("Unable to contact defined mailer."); } else { fprintf(mail, "To: %s\n", ldl->fg[filtergroup]->avadmin.c_str()); fprintf(mail, "From: %s\n", ldl->fg[filtergroup]->mailfrom.c_str()); fprintf(mail, "Subject: %s\n", ldl->fg[filtergroup]->avsubject.c_str()); fprintf(mail, "A virus was detected by e2guardian.\n\n"); fprintf(mail, "%-10s%s\n", "Data/Time:", when.c_str()); if (who != blank_str) fprintf(mail, "%-10s%s\n", "User:", who.c_str()); fprintf(mail, "%-10s%s (%s)\n", "From:", from.c_str(), ((clienthost.length() > 0) ? clienthost.c_str() : blank_str.c_str())); fprintf(mail, "%-10s%s\n", "Where:", where.c_str()); // specifically, the virus name comes after message 1100 ("Virus or bad content detected.") String swhat(what); fprintf(mail, "%-10s%s\n", "Why:", swhat.after(o.language_list.getTranslation(1100).c_str()).toCharArray()); fprintf(mail, "%-10s%s\n", "Method:", how.c_str()); fprintf(mail, "%-10s%s\n", "Size:", ssize.c_str()); fprintf(mail, "%-10s%s\n", "Weight:", sweight.c_str()); if (cat.c_str() != NULL) fprintf(mail, "%-10s%s\n", "Category:", cat.c_str()); fprintf(mail, "%-10s%s\n", "Mime type:", mimetype.c_str()); fprintf(mail, "%-10s%s\n", "Group:", ldl->fg[filtergroup]->name.c_str()); fprintf(mail, "%-10s%s\n", "HTTP resp:", stringcode.c_str()); pclose(mail); } // Second child exits _exit(0); } // First child exits _exit(0); } } // naughty OR virus else if ((isnaughty || (wasscanned && wasinfected)) && (ldl->fg[filtergroup]->notifycontent)) { byuser = ldl->fg[filtergroup]->byuser; // if no violations so far by this user/group, // reset threshold counters if (byuser) { if (!violation_map[who]) { // set the time of the first violation timestamp_map[who] = time(0); vbody_map[who] = ""; } } else if (!ldl->fg[filtergroup]->current_violations) { // set the time of the first violation ldl->fg[filtergroup]->threshold_stamp = time(0); ldl->fg[filtergroup]->violationbody = ""; } // increase per-user or per-group violation count if (byuser) violation_map[who]++; else ldl->fg[filtergroup]->current_violations++; // construct email report char *vbody_temp = new char[8192]; sprintf(vbody_temp, "%-10s%s\n", "Data/Time:", when.c_str()); vbody += vbody_temp; if ((!byuser) && (who != blank_str)) { sprintf(vbody_temp, "%-10s%s\n", "User:", who.c_str()); vbody += vbody_temp; } sprintf(vbody_temp, "%-10s%s (%s)\n", "From:", from.c_str(), ((clienthost.length() > 0) ? clienthost.c_str() : blank_str.c_str())); vbody += vbody_temp; sprintf(vbody_temp, "%-10s%s\n", "Where:", where.c_str()); vbody += vbody_temp; sprintf(vbody_temp, "%-10s%s\n", "Why:", what.c_str()); vbody += vbody_temp; sprintf(vbody_temp, "%-10s%s\n", "Method:", how.c_str()); vbody += vbody_temp; sprintf(vbody_temp, "%-10s%s\n", "Size:", ssize.c_str()); vbody += vbody_temp; sprintf(vbody_temp, "%-10s%s\n", "Weight:", sweight.c_str()); vbody += vbody_temp; if (cat.c_str() != NULL) { sprintf(vbody_temp, "%-10s%s\n", "Category:", cat.c_str()); vbody += vbody_temp; } sprintf(vbody_temp, "%-10s%s\n", "Mime type:", mimetype.c_str()); vbody += vbody_temp; sprintf(vbody_temp, "%-10s%s\n", "Group:", ldl->fg[filtergroup]->name.c_str()); vbody += vbody_temp; sprintf(vbody_temp, "%-10s%s\n\n", "HTTP resp:", stringcode.c_str()); vbody += vbody_temp; delete[] vbody_temp; // store the report with the group/user if (byuser) { vbody_map[who] += vbody; curv_tmp = violation_map[who]; stamp_tmp = timestamp_map[who]; } else { ldl->fg[filtergroup]->violationbody += vbody; curv_tmp = ldl->fg[filtergroup]->current_violations; stamp_tmp = ldl->fg[filtergroup]->threshold_stamp; } // if threshold exceeded, send mail if (curv_tmp >= ldl->fg[filtergroup]->violations) { if ((ldl->fg[filtergroup]->threshold == 0) || ((time(0) - stamp_tmp) <= ldl->fg[filtergroup]->threshold)) { // Use a double fork to ensure child processes are reaped adequately. pid_t smtppid; if ((smtppid = fork()) != 0) { // Parent immediately waits for first child waitpid(smtppid, NULL, 0); } else { // First child forks off the *real* process, but immediately exits itself if (fork() == 0) { // Second child - do stuff setsid(); FILE *mail = popen(o.mailer.c_str(), "w"); if (mail == NULL) { E2LOGGER_error("Unable to contact defined mailer."); } else { fprintf(mail, "To: %s\n", ldl->fg[filtergroup]->contentadmin.c_str()); fprintf(mail, "From: %s\n", ldl->fg[filtergroup]->mailfrom.c_str()); if (byuser) fprintf(mail, "Subject: %s (%s)\n", ldl->fg[filtergroup]->contentsubject.c_str(), who.c_str()); else fprintf(mail, "Subject: %s\n", ldl->fg[filtergroup]->contentsubject.c_str()); fprintf(mail, "%i violation%s ha%s occurred within %i seconds.\n", curv_tmp, (curv_tmp == 1) ? "" : "s", (curv_tmp == 1) ? "s" : "ve", ldl->fg[filtergroup]->threshold); fprintf(mail, "%s\n\n", "This exceeds the notification threshold."); if (byuser) fprintf(mail, "%s", vbody_map[who].c_str()); else fprintf(mail, "%s", ldl->fg[filtergroup]->violationbody.c_str()); pclose(mail); } // Second child exits _exit(0); } // First child exits _exit(0); } } if (byuser) violation_map[who] = 0; else ldl->fg[filtergroup]->current_violations = 0; } } // end naughty OR virus } // end usesmtp #endif continue; // go back to listening } if (!e2logger_ttg) DEBUG_debug("log_listener exiting with error"); } catch (...) { E2LOGGER_error("log_listener caught unexpected exception - exiting"); } if (!e2logger_ttg) { E2LOGGER_error("log_listener exiting with error"); } else if (o.conn.logconerror) { E2LOGGER_error("log_listener exiting"); } return; // It is only possible to reach here with an error } void accept_connections(int index) // thread to listen on a single listening socket { try { unsigned int ct_type = serversockets.getType(index); int errorcount = 0; thread_id = "listen_"; thread_id += std::to_string(index); thread_id += "_"; switch (ct_type) { case CT_PROXY: thread_id += "proxy: "; break; case CT_PROXY_TLS: thread_id += "tls_proxy: "; break; case CT_ICAP: thread_id += "icap: "; break; case CT_THTTPS: thread_id += "thttps: "; break; } thread_id += std::to_string(ct_type); thread_id += ": "; while ((errorcount < 30) && !ttg) { Socket *peersock = serversockets[index]->accept(); int err = serversockets[index]->getErrno(); if (err == 0 && peersock != NULL && peersock->getFD() > -1) { if (ttg) { delete peersock; break; } DEBUG_debug("got connection from accept"); if (peersock->getFD() > dstat.maxusedfd) dstat.maxusedfd = peersock->getFD(); errorcount = 0; LQ_rec rec; rec.sock = peersock; rec.ct_type = ct_type; o.http_worker_Q.push(rec); DEBUG_debug("pushed connection to http_worker_Q"); } else { if (ttg) { if (peersock != nullptr) delete peersock; break; } E2LOGGER_error("Error on accept: errorcount ", String(errorcount), " errno: ", String(err)); ++errorcount; std::this_thread::sleep_for(std::chrono::milliseconds(50)); } }; if (!ttg) E2LOGGER_error("Error count on accept exceeds 30"); serversockets[index]->close(); } catch (...) { E2LOGGER_error("listener thread caught unexpected exception exiting"); } if (o.conn.logconerror) { E2LOGGER_info("listener thread exiting"); } } // * // * // * end logger, IP list and URL cache code // * // * // Does lots and lots of things - creates url cache & logger threads, creates child threads for connection handling, does tidying up on exit // also handles the various signalling options e2g supports (reload config, flush cache, kill all processes etc.) int fc_controlit() // { int rc; bool is_starting = true; ttg = false; e2logger_ttg = false; reloadconfig = false; gentlereload = false; reload_cnt = 0; rotate_request = false; rotate_access = false; rotate_dstat = false; o.lm.garbageCollect(); thread_id = "master: "; // allocate & create our server sockets if (o.net.filter_ip.size() > 0) { serversocketcount = o.net.filter_ip.size() * o.net.filter_ports.size(); if (!o.net.TLS_filter_ports.empty()) { serversocketcount += (o.net.filter_ip.size() * o.net.TLS_filter_ports.size()); } } else { serversocketcount = o.net.filter_ports.size() + o.net.TLS_filter_ports.size(); } int serversocktopproxy = serversocketcount; if (o.net.transparenthttps_port > 0) ++serversocketcount; if (o.net.icap_port > 0) ++serversocketcount; serversockets.reset(serversocketcount); int *serversockfds = serversockets.getFDAll(); //std::thread *listen_treads[serversocketcount]; for (int i = 0; i < serversocketcount; i++) { // if the socket fd is not +ve then the socket creation failed if (serversockfds[i] < 0) { E2LOGGER_error("Error creating server socket ", String(i)); delete[] serversockfds; return 1; } } DEBUG_trace("seteuiding for low port binding/pidfile creation"); if (!o.proc.become_root_user()) { E2LOGGER_error("Unable to seteuid() to bind filter port."); delete[] serversockfds; return 1; } // we have to open/create as root before drop privs int pidfilefd = sysv_openpidfile(o.proc.pid_filename); if (pidfilefd < 0) { E2LOGGER_error("Error creating/opening pid file."); delete[] serversockfds; return 1; } int ss_index = 0; // we expect to find a valid filter ip 0 specified in conf if multiple IPs are in use. if (o.net.filter_ip[0].length() > 6) { if (serversockets.bindAll(o.net.filter_ip, o.net.filter_ports,ss_index,CT_PROXY)) { E2LOGGER_error("Error binding HTTP proxy server socket (is something else running on the filter port and ip?"); close(pidfilefd); delete[] serversockfds; return 1; } if (!o.net.TLS_filter_ports.empty()) { if (serversockets.bindAll(o.net.filter_ip, o.net.TLS_filter_ports, ss_index, CT_PROXY_TLS)) { E2LOGGER_error( "Error binding TLS proxy server socket (is something else running on the filter port and ip?)"); close(pidfilefd); delete[] serversockfds; return 1; } } } else { // listen/bind to a port (or ports) on any interface if (serversockets.bindSingleM(o.net.filter_ports, ss_index, CT_PROXY)) { E2LOGGER_error("Error binding HTTP proxy server sockets: (", strerror(errno), ")"); close(pidfilefd); delete[] serversockfds; return 1; } if (serversockets.bindSingleM(o.net.TLS_filter_ports, ss_index, CT_PROXY_TLS)) { E2LOGGER_error("Error binding TLS proxy server sockets: (", strerror(errno), ")"); close(pidfilefd); delete[] serversockfds; return 1; } } if (o.net.transparenthttps_port > 0) { if (serversockets.bindSingle(serversocktopproxy++, o.net.transparenthttps_port, CT_THTTPS)) { E2LOGGER_error("Error binding server thttps socket: (", strerror(errno), ")"); close(pidfilefd); delete[] serversockfds; return 1; } }; if (o.net.icap_port > 0) { if (serversockets.bindSingle(serversocktopproxy, o.net.icap_port, CT_ICAP)) { E2LOGGER_error("Error binding server icap socket: (", strerror(errno), ")"); close(pidfilefd); delete[] serversockfds; return 1; } }; // Made unconditional for same reasons as above //if (needdrop) #ifdef HAVE_SETREUID rc = setreuid((uid_t)-1, o.proc.proxy_user); #else rc = seteuid(o.proc.proxy_user); // become low priv again #endif if (rc == -1) { E2LOGGER_error("%sUnable to re-seteuid()"); close(pidfilefd); delete[] serversockfds; return 1; // seteuid failed for some reason so exit with error } if (serversockets.listenAll(256)) { // set it to listen mode with a kernel // queue of 256 backlog connections E2LOGGER_error("Error listening to server socket"); close(pidfilefd); delete[] serversockfds; return 1; } #ifndef DEBUG_LOW // remain in foreground when in low debug mode if (!(is_daemonised||o.proc.no_daemon)) { if (!daemonise(pidfilefd)) { // detached daemon E2LOGGER_error("Error daemonising"); close(pidfilefd); delete[] serversockfds; return 1; } } #endif //init open ssl SSL_load_error_strings(); OpenSSL_add_all_algorithms(); OpenSSL_add_all_digests(); if (o.cert.use_openssl_conf) { if (o.cert.have_openssl_conf) { if (CONF_modules_load_file(o.cert.openssl_conf_path.c_str(), nullptr, 0) != 1) { E2LOGGER_error("Error reading openssl config file ", o.cert.openssl_conf_path.c_str()); return false; } } else { if (CONF_modules_load_file(nullptr, nullptr, 0) != 1) { E2LOGGER_error("Error reading default openssl config files"); return false; } } } SSL_library_init(); // only done now if not daemonised as pidfile must be written from parent when forking - required so that systemd will pick up master pid from pidfile if (!is_daemonised) { rc = sysv_writepidfile(pidfilefd, 0); // also closes the fd if (rc != 0) { E2LOGGER_error("Error writing to the e2guardian.pid file: ", strerror(errno)); delete[] serversockfds; return false; } } // We are now a daemon so all errors need to go in the syslog, rather // than being reported on screen as we've detached from the console and // trying to write to stdout will not be nice. g_is_starting = false; struct sigaction sa; memset(&sa, 0, sizeof(sa)); sigset_t signal_set; sigemptyset(&signal_set); sigaddset(&signal_set, SIGHUP); sigaddset(&signal_set, SIGPIPE); sigaddset(&signal_set, SIGTERM); sigaddset(&signal_set, SIGUSR1); #ifdef __OpenBSD__ // OpenBSD does not support posix sig_timed_wait, so have to use timer and SIGALRM // set up timer for main loop struct itimerval timeout; timeout.it_interval.tv_sec = 0; timeout.it_interval.tv_usec = (suseconds_t) 0; timeout.it_value.tv_usec = (suseconds_t) 0; sigaddset(&signal_set, SIGALRM); #else struct timespec timeout; timeout.tv_sec = 0; timeout.tv_nsec = (long) 0; #endif int stat; stat = pthread_sigmask(SIG_BLOCK, &signal_set, NULL); if (stat != 0) { E2LOGGER_error("Error setting sigmask"); return 1; } DEBUG_trace("sig handlers done"); // Now start creating threads so main thread can just handle signals, list reloads and stats // This removes need for select and/or epoll greatly simplifying the code // Threads are created for logger, a separate thread for each listening port // and an array of worker threads to deal with the work. //if (!o.no_logger) { if (e2logger.isEnabled(LoggerSource::accesslog)) { std::thread log_thread(log_listener, o.log.log_Q, false); log_thread.detach(); DEBUG_trace("log_listener thread created"); } //if(o.log_requests) { if (e2logger.isEnabled(LoggerSource::requestlog)) { std::thread RQlog_thread(log_listener, o.log.RQlog_Q, true); RQlog_thread.detach(); DEBUG_trace("RQlog_listener thread created"); } dystat->busychildren = 0; // to keep count of our children // worker thread generation std::vector http_wt; http_wt.reserve(o.proc.http_workers); int i; for (i = 0; i < o.proc.http_workers; i++) { http_wt.push_back(std::thread(handle_connections, i)); } for (auto &i : http_wt) { i.detach(); } DEBUG_trace("http_worker threads created"); // set listener threads going std::vector listen_threads; listen_threads.reserve(serversocketcount); for (int i = 0; i < serversocketcount; i++) { listen_threads.push_back(std::thread(accept_connections, i)); } for (auto &i : listen_threads) { i.detach(); } DEBUG_trace("listen threads created"); // I am the main thread here onwards. DEBUG_trace("Master thread created threads"); time_t tmaxspare; time(&tmaxspare); failurecount = 0; // as we don't exit on an error with select() // due to the fact that these errors do happen // every so often on a fully working, but busy // system, we just watch for too many errors // consecutivly. is_starting = true; if (reloadconfig) { E2LOGGER_info("Reconfiguring E2guardian: done"); } else { E2LOGGER_info("Started successfully."); dystat->start(true); } reloadconfig = false; if (is_starting) { if (o.monitor.monitor_flag_flag) monitor_flag_set(true); // if (o.monitor_helper_flag) { // tell_monitor(true); // } is_starting = false; } while (failurecount < 30 && !ttg && !reloadconfig) { // loop, essentially, for ever until 30 // consecutive errors in which case something // is badly wrong. // OR, its timetogo - got a sigterm // OR, we need to exit to reread config if (gentlereload) { DEBUG_trace("gentle reload activated"); E2LOGGER_info("Reconfiguring E2guardian: gentle reload starting"); if (o.createLists(++reload_cnt)) { E2LOGGER_info("Reconfiguring E2guardian: gentle reload completed"); } else { reload_cnt--; E2LOGGER_info("%sReconfiguring E2guardian: gentle reload failed"); } gentlereload = false; continue; // OK to continue even if gentle failed - just continue to use previous lists } #ifdef __OpenBSD__ // OpenBSD does not support posix sig_timed_wait, so have to use timer and SIGALRM timeout.it_value.tv_sec = 5; setitimer(ITIMER_REAL, &timeout, NULL); int rsig; rc = sigwait(&signal_set, &rsig); if (rc < 0) { if (errno != EAGAIN) { E2LOGGER_info("Unexpected error from sigtimedwait(): ", String(errno), " ", strerror(errno)); } } else { if (rsig == SIGUSR1) { rotate_access = true; rotate_request = true; rotate_dstat = true; } if (rsig == SIGTERM) ttg = true; if (rsig == SIGHUP) gentlereload = true; if (rsig != SIGALRM) { // unset alarm timeout.it_value.tv_sec = 0; //timer_settime(timerid,0,&timeout, NULL); setitimer(ITIMER_REAL, &timeout, NULL); DEBUG_debug("signal:", String(rc)); if (o.conn.logconerror) { E2LOGGER_info("sigtimedwait() signal recd:", String(rsig) ); } } } #else // other posix compliant platforms timeout.tv_sec = 5; rc = sigtimedwait(&signal_set, NULL, &timeout); if (rc < 0) { if (errno != EAGAIN) { E2LOGGER_info("Unexpected error from sigtimedwait():", String(errno), " ", strerror(errno)); } } else { if (rc == SIGUSR1) { rotate_access = true; rotate_request = true; rotate_dstat = true; } if (rc == SIGTERM) ttg = true; if (rc == SIGHUP) gentlereload = true; DEBUG_debug("signal: ", String(rc)); if (o.conn.logconerror) { E2LOGGER_info("ssigtimedwait() signal recd:", String(rc)); } } #endif // end __OpenBSD__ else int q_size = o.http_worker_Q.size(); DEBUG_debug("busychildren:", String(dystat->busychildren), " worker Q size:", q_size); if (o.dstat.dstat_log_flag) { if (q_size > 10) { E2LOGGER_info("Warning: all ", o.proc.http_workers, " http_worker threads are busy and ", q_size, " connections are waiting in the queue."); } } else { int busy_child = dystat->busychildren; if (busy_child > (o.proc.http_workers - 10)) E2LOGGER_info("Warning system is full : max httpworkers: ", o.proc.http_workers, " Used: ", busy_child); } // if (is_starting) time_t now = time(NULL); if (o.dstat.dstat_log_flag && (now >= dystat->end_int)) dystat->reset(); } // tidy-up sigfillset(&signal_set); pthread_sigmask(SIG_BLOCK, &signal_set, NULL); E2LOGGER_info("Stopping"); if (o.monitor.monitor_flag_flag) monitor_flag_set(false); // if (o.monitor_helper_flag) // tell_monitor(false); // tell monitor that we are not accepting any more connections if (o.conn.logconerror) { E2LOGGER_info("sending null socket to http_workers to stop them"); } Socket *NS = NULL; LQ_rec rec; rec.sock = NS; rec.ct_type = CT_PROXY; for (i = 0; i < o.proc.http_workers; i++) { o.http_worker_Q.push(rec); } // dystat->reset(); // remove this line for production version //std::this_thread::sleep_for(std::chrono::milliseconds(2000)); //E2LOGGER_info("2nd wait complete"); e2logger_ttg = true; std::string nullstr(""); o.log.log_Q->push(nullstr); //if (o.log_requests) { if (e2logger.isEnabled(LoggerSource::requestlog)) { o.log.RQlog_Q->push(nullstr); } if (o.conn.logconerror) { E2LOGGER_info("stopping any remaining connections"); } serversockets.self_connect(); // stop accepting connections if (o.conn.logconerror) { E2LOGGER_info("connections stopped"); } std::this_thread::sleep_for(std::chrono::milliseconds(2000)); if (o.dstat.dstat_log_flag) dystat->close(); delete[] serversockfds; if (o.conn.logconerror) { E2LOGGER_info("Main thread exiting."); } return 0; } e2guardian-5.5.8r/src/FatController.hpp000066400000000000000000000017741477372360500200410ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_FATCONTROLLER #define __HPP_FATCONTROLLER // INCLUDES #include "OptionContainer.hpp" #include "UDSocket.hpp" #include #include // DECLARATIONS // program main loop - pass in FD of pidfile int fc_controlit(); struct stat_rec { long births; // num of child forks in stat interval long deaths; // num of child deaths in stat interval std::atomic conx ; // num of client connections in stat interval std::atomic reqs; // num of client requests in stat interval time_t start_int; // time of start of this stat interval time_t end_int; // target end time of stat interval std::atomic maxusedfd; // max fd reached std::atomic busychildren; FILE *fs; // file stream void reset(); void start(bool firsttime); void clear(); void close(); }; #endif e2guardian-5.5.8r/src/HTMLTemplate.cpp000066400000000000000000000173141477372360500175130ustar00rootroot00000000000000//Implements the HTMLTemplate class, for displaying template-based banned pages to clients // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "HTMLTemplate.hpp" #include "RegExp.hpp" #include "String.hpp" #include "OptionContainer.hpp" #include "Logger.hpp" #include #include #include #include #include // GLOBALS extern OptionContainer o; // IMPLEMENTATION // wipe the loaded template void HTMLTemplate::reset() { html.clear(); } // push a line onto our string list void HTMLTemplate::push(String s) { if (s.length() > 0) { html.push_back(s); } } // read in HTML template and find URL, reason, category etc. placeholders bool HTMLTemplate::readTemplateFile(const char *filename, const char *placeholders) { std::string linebuffer; RegExp re; // compile regexp for matching supported placeholders // allow optional custom placeholder string re.comp(placeholders ? placeholders : "-URL-|-REASONGIVEN-|-REASONLOGGED-|-USER-|-IP-|-HOST-|-FILTERGROUP-|-RAWFILTERGROUP-|-BYPASS-|-CATEGORIES-|-SHORTURL-|-SERVERIP-|-EXTFLAGS-|-SERVERNAME-"); RegResult Rre; unsigned int offset; String result; String line; std::ifstream templatefile(filename, std::ios::in); // e2guardian.conf if (!templatefile.good()) { E2LOGGER_error("error reading HTML template file: ", filename); return false; } while (!templatefile.eof()) { std::getline(templatefile, linebuffer); line = linebuffer.c_str(); // look for placeholders re.match(line.toCharArray(),Rre); while (Rre.numberOfMatches() > 0) { // whenever we find one, push the text before it onto the list, then the placeholder, then the text after it offset = Rre.offset(0); result = Rre.result(0).c_str(); if (offset > 0) { push(line.subString(0, offset)); push(result); line = line.subString(offset + result.length(), line.length() - offset - result.length()); } else { push(result); line = line.subString(result.length(), line.length() - result.length()); } re.match(line.toCharArray(),Rre); } // if any text remains, or we didn't find a placeholder, push the remainder of the line if (line.length() > 0) { push(line); } } if (html.size() < 1) { E2LOGGER_error("Unable to parse template file: ", filename); return false; } // add space as last line - to make safe i+1 and size -1 logic in display_hb line = " "; push(line); templatefile.close(); return true; } // encode quotes and angle brackets using URL encoding to prevent XSS in the block page void makeURLSafe(String &url) { url.replaceall("'", "%27"); url.replaceall("\"", "%22"); url.replaceall("<", "%3C"); url.replaceall(">", "%3E"); } void HTMLTemplate::display_hb(String &ebody, String *url, std::string &reason, std::string &logreason, std::string &categories, std::string *user, std::string *ip, std::string *host, int filtergroup, String grpname, String &hashed , String &localip, String &extflags) { DEBUG_debug("Displaying TEMPLATE. url:", url, " reason:", reason, " logreason:", logreason); String line; bool newline; unsigned int sz = html.size(); if (sz < 1) //template is empty return; sz--; //ignore last space line added by read function String safeurl(*url); // Take a copy of the URL so we can encode it to stop XSS bool safe = false; String servername(""); servername = o.net.server_name; for (unsigned int i = 0; i < sz; i++) { // preserve newlines from original file newline = false; line = html[i]; DEBUG_debug("Displaying TEMPLATE: ", line); // Take care SSLMITM negotiation error if (line.length() < 1){ ebody += "\n"; E2LOGGER_error("Corrupted TEMPLATE returns: ", url->c_str()); break; } // look for placeholders (split onto their own line by readTemplateFile) and replace them if (line == "-URL-") { if (!safe) { makeURLSafe(safeurl); safe = true; } line = safeurl; } else if (line == "-SHORTURL-") { if (!safe) { makeURLSafe(safeurl); safe = true; } line = safeurl; if (line.length() > 41) { line = line.subString(0, 40); line += "..."; } } else if (line == "-SERVERIP-") { line = localip; } else if (line == "-REASONGIVEN-") { String safereason = reason; makeURLSafe(safereason); line = safereason; } else if (line == "-REASONLOGGED-") { line = logreason; } else if (line == "-SERVERNAME-") { line = servername; } else if (line == "-USER-") { String safeuser(*user); makeURLSafe(safeuser); line = safeuser; } else if (line == "-IP-") { line = *ip; } else if (line == "-HOST-") { if (host == NULL) { line = ""; } else { line = *host; } } else if (line == "-FILTERGROUP-") { line = grpname; } else if (line == "-RAWFILTERGROUP-") { line = String(filtergroup + 1); } else if (line == "-CATEGORIES-") { if (categories.length() > 0) { line = categories; } else { line = "N/A"; } } else if (line == "-BYPASS-") { if (hashed.length() > 0) { if (!safe) { makeURLSafe(safeurl); safe = true; } line = safeurl; if (!(url->after("://").contains("/"))) { line += "/"; } if (url->contains("?")) { line += "&" + hashed; } else { line += "?" + hashed; } } else { line = ""; } } else if (line == "-EXTFLAGS-") { line = extflags; } else { // if this line wasn't a placeholder, and neither is the // next line, then output a newline, thus preserving line breaks // from the original template file. if (html[i + 1][0] != '-') { newline = true; } } if (line.length() > 0) { ebody += line; } if (newline) { ebody += "\n"; } } //ebody += html[sz].toCharArray(); - no longer needed as html[sz] is added as padding //ebody += "\n"; } #ifdef NOTDEF // fill in placeholders with the given information and send the resulting page to the client // only useful if you used the default set of placeholders void HTMLTemplate::display(Socket *s, String *url, std::string &reason, std::string &logreason, std::string &categories, std::string *user, std::string *ip, std::string *host, int filtergroup, String grpname, String &hashed) { String ebody; String localip = s->getLocalIP(); display_hb(ebody, url, reason, logreason, categories, user, ip, host, filtergroup, grpname, hashed, localip); s->writeString(ebody.toCharArray()); } #endif e2guardian-5.5.8r/src/HTMLTemplate.hpp000066400000000000000000000033271477372360500175170ustar00rootroot00000000000000//Declares the HTMLTemplate class, for displaying template-based banned pages to clients // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_HTMLTEMPLATE #define __HPP_HTMLTEMPLATE // INCLUDES #include "String.hpp" #include "Socket.hpp" #include #include // DECLARATIONS class HTMLTemplate { public: // string list for holding the template // public so that it can be accessed directly for display without using // the default set of placeholders std::deque html; // wipe the loaded template void reset(); // load in a template from the given file, looking for placeholder strings (reason, URL, category etc.) // optionally, provide your own set of placeholders bool readTemplateFile(const char *filename, const char *placeholders = NULL); #ifdef NOTDEF // fill in the template with the given info and send it to the client over the given socket // only useful if you used the default set of placeholders void display(Socket *s, String *url, std::string &reason, std::string &logreason, std::string &categories, std::string *user, std::string *ip, std::string *host, int filtergroup, String grpname, String &hashed); #endif void display_hb(String &ebody, String *url, std::string &reason, std::string &logreason, std::string &categories, std::string *user, std::string *ip, std::string *host, int filtergroup, String grpname, String &hashed, String &localip, String &extflags); private: // add a string to the list void push(String s); }; #endif e2guardian-5.5.8r/src/HTTPHeader.cpp000066400000000000000000001736351477372360500171540ustar00rootroot00000000000000//ll support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "HTTPHeader.hpp" #include "Socket.hpp" #include "OptionContainer.hpp" #include "FDTunnel.hpp" #include "Logger.hpp" #include #include #include #include #include #include #include #include // GLOBALS extern OptionContainer o; extern thread_local std::string thread_id; // regexp for decoding %xx in URLs extern RegExp urldecode_re; // IMPLEMENTATION // set timeout for socket operations void HTTPHeader::setTimeout(int t) { timeout = t; } // reset header object for future use void HTTPHeader::reset() { if (dirty) { header.clear(); waspersistent = false; ispersistent = false; cachedurl = ""; addheaderchecked = false; isheaderadded = false; searchwds = ""; issearch = false; searchchecked = false; clcached = false; expects_100 = false; mitm = false; isdirect = false; chunked = false; phost = NULL; pport = NULL; pcontentlength = NULL; pcontenttype = NULL; pproxyauthorization = NULL; pauthorization = NULL; pproxyauthenticate = NULL; pcontentdisposition = NULL; puseragent = NULL; pxforwardedfor = NULL; pcontentencoding = NULL; ptransfercoding = NULL; ptransferencoding = NULL; pproxyconnection = NULL; pkeepalive = NULL; useragent = ""; contenttype = ""; contentencoding = ""; transferencoding = ""; contentlength = -1; requesttype = ""; returncode = 0; first_line_sent = false; header_all_sent = false; dirty = false; isProxyRequest = false; icap = false; delete postdata; postdata = NULL; postdata_len = 0; } } // * // * // * header value and type checks // * // * // grab request type (GET, HEAD etc.) String HTTPHeader::requestType() { return requesttype; } // grab return code int HTTPHeader::returnCode() { return returncode; } // grab content length off_t HTTPHeader::contentLength() { return contentlength; } // grab the auth type String HTTPHeader::getAuthType() { if (pproxyauthorization != NULL) { return pproxyauthorization->after(" ").before(" "); } return ""; } // check the request's return code to see if it's an auth required message bool HTTPHeader::authRequired() { if (returncode == 407) return true; else return false; } // grab content disposition String HTTPHeader::disposition() { if (pcontentdisposition != NULL) { String filename(pcontentdisposition->after("filename").after("=")); if (filename.contains(";")) filename = filename.before(";"); filename.removeWhiteSpace(); // incase of trailing space if (filename.contains("\"")) { return filename.after("\"").before("\""); } return filename; // example format: // Content-Disposition: attachment; filename="filename.ext" // Content-Disposition: attachment; filename=filename.ext // Content-Disposition: filename="filename.ext" // 3rd format encountered from download script on realVNC's // website. notice it does not contain any semicolons! PRA 4-11-2005 } return ""; // it finds the header proposed filename } // grab the user agent String HTTPHeader::userAgent() { return useragent; } // grab the content type header String HTTPHeader::getContentType() { if (pcontenttype != NULL) { String mimetype(pcontenttype->after(" ")); if (mimetype.length() < 1) return "-"; mimetype.chop(); if (mimetype.contains(" ")) mimetype = mimetype.before(" "); if (mimetype.contains(";")) mimetype = mimetype.before(";"); mimetype.toLower(); return mimetype; } return "-"; } // grab the boundary for multi-part MIME POST data String HTTPHeader::getMIMEBoundary() { if (pcontenttype != NULL) { String boundary(pcontenttype->after(" ")); boundary.chop(); if (!boundary.contains("boundary=")) return ""; boundary = boundary.after("boundary="); if (boundary.contains(";")) boundary = boundary.after(";"); if (boundary[0] == '"') { boundary.lop(); boundary.chop(); } return boundary; } return ""; } // does the given content type string match our headers? bool HTTPHeader::isContentType(const String &t, FOptionContainer* &foc) { DEBUG_debug("mime type: ", getContentType()); // Do standard check first! if (getContentType().startsWith(t)) return true; // Only check text_mime types if ContentType request is 'text' if (t == "text") { String mime = getContentType(); std::deque text_mime = foc->text_mime; int size = (int) text_mime.size(); int i; for (i = 0; i < size; i++) { if (mime.startsWith(text_mime[i])) { DEBUG_debug( "mimes match : ", text_mime[i]); return true; } else { DEBUG_debug("mimes check : ", text_mime[i]); } } } DEBUG_debug("mimes result : false !"); return false; } bool HTTPHeader::OKtoFilterMime(FOptionContainer* &foc) { String mime = getContentType(); // check stop list std::deque text_mime = foc->text_mime_stop; int size = (int) text_mime.size(); int i; for (i = 0; i < size; i++) { if (mime.startsWith(text_mime[i])) { DEBUG_debug( "mimes match stop: ", text_mime[i]); return false; } else { DEBUG_debug("mimes check stop: ", text_mime[i]); } } //check if 'text' if(isContentType("text",foc) || isContentType("-",foc)) return true; return false; } // grab contents of X-Forwarded-For header // Modification based on a submitted patch by // Jimmy Myrick (jmyrick@tiger1.tiger.org) std::string HTTPHeader::getXForwardedForIP() { if (pxforwardedfor != NULL) { String line(pxforwardedfor->after(": ")); line.chop(); return std::string(line.toCharArray()); } return ""; } // check the return code to see if it's a redirection request bool HTTPHeader::isRedirection() { // The 1st line of the header for a redirection is thus: // HTTP/1.(0|1) 3xx if (header.size() < 1) { return false; } // sometimes get called b 4 read String answer(header.front().after(" ").before(" ")); if (answer[0] == '3' && answer.length() == 3) { return true; } return false; } // grab the contents of Proxy-Authorization header // returns base64-decoding of the chunk of data after the auth type string std::string HTTPHeader::getAuthData() { if (pproxyauthorization != NULL) { String line(pproxyauthorization->after(" ").after(" ")); //line.chop(); DEBUG_auth(("pauth string size:"),line.length()," ", line); String ret = decodeb64(line); DEBUG_auth(("pauth after decodeb64:"), ret); ret.removeChar('\0'); DEBUG_auth(("returning:"), ret); return ret; // it's base64 MIME encoded } return ""; } std::string HTTPHeader::getAuthHeader() { if (pheaderident != NULL) { String line (pheaderident->after(" ")); line.resize(line.length() - 1); return line; } return ""; } // grab raw contents of Proxy-Authorization header without decoding std::string HTTPHeader::getRawAuthData() { if (pproxyauthorization != NULL) { return pproxyauthorization->after(" ").after(" "); } return ""; } // do we have a non-identity content encoding? this means body is compressed bool HTTPHeader::isCompressed() { if (pcontentencoding != NULL) { if (pcontentencoding->indexOf("identity") != -1) { // http says this // should not be here, but not must not return false; } DEBUG_debug( "is compressed!"); return true; // i.e. encoded with something other than clear } return false; } // grab content encoding header String HTTPHeader::contentEncoding() { if (pcontentencoding != NULL) { String ce(pcontentencoding->after(": ")); ce.toLower(); return ce; } return ""; // we need a default don't we? } // grab transfer encoding header String HTTPHeader::transferEncoding() { if (ptransferencoding != NULL) { String ce(ptransferencoding->after(": ")); ce.toLower(); return ce; } return ""; // we need a default don't we? } // * // * // * header modifications // * // * // squid adds this so if more support it it may be useful one day void HTTPHeader::addXForwardedFor(const std::string &clientip) { if (!isdirect) { std::string line("X-Forwarded-For: " + clientip + "\r"); header.push_back(String(line.c_str())); } } // set content length header to report given lenth void HTTPHeader::setContentLength(int newlen) { if (pcontentlength != NULL) { (*pcontentlength) = "Content-Length: " + String(newlen) + "\r"; } contentlength = newlen; clcached = true; } // set the proxy-connection header to allow persistence (or not) void HTTPHeader::makePersistent(bool persist) { if (persist) { // Only make persistent if it originally was, but now isn't. // The intention isn't to change browser behaviour, just to // un-do any connection downgrading which E2 may have performed // earlier. if (waspersistent && !ispersistent) { if (pproxyconnection != NULL) { (*pproxyconnection) = pproxyconnection->before(":") + ": keep-alive\r"; } else { header.push_back(String("Connection: keep-alive\r")); pproxyconnection = &(header.back()); } ispersistent = true; } } else { // Only downgrade to non-persistent if it isn't currently persistent. if (ispersistent) { if (pproxyconnection != NULL) { (*pproxyconnection) = pproxyconnection->before(":") + ": close\r"; } else { header.push_back(String("Connection: close\r")); pproxyconnection = &(header.back()); } ispersistent = false; } } } // make the request look like it's come from/to the origin server void HTTPHeader::makeTransparent(bool incoming) { DEBUG_trace(""); if (incoming) { // remove references to the proxy before sending to browser if (pproxyconnection != NULL) { String temp = pproxyconnection->after(":"); (*pproxyconnection) = "Connection:"; (*pproxyconnection) += temp; } if (pproxyauthenticate != NULL) { String temp = pproxyauthenticate->after(":"); (*pproxyauthenticate) = "WWW-Authenticate:"; (*pproxyauthenticate) += temp; } if (returnCode() == 407) { String temp = header.front().before(" "); String temp2 = header.front().after(" ").after(" "); header.front() = temp + " 401 "; header.front() += temp2; } } else { // remove references to origin server before sending to proxy if (pauthorization != NULL) { String temp = pauthorization->after(":"); (*pauthorization) = "Proxy-Authorization:"; (*pauthorization) += temp; pproxyauthorization = pauthorization; pauthorization = NULL; } if (pproxyconnection != NULL) { String temp = pproxyconnection->after(":"); (*pproxyconnection) = "Connection:"; (*pproxyconnection) += temp; } // call this to fudge the URL into something Squid likes getUrl(); } } // return a modified accept-encoding header, based on the one supplied, // but with "identity" added and only supported encodings allowed. String HTTPHeader::modifyEncodings(String e) { // There are 4 types of encoding: gzip, deflate, compress and identity // deflate is in zlib format // compress is in unix compress format // identity is uncompressed and supported by all browsers (obviously) // we do not support compress e.toLower(); String o("Accept-Encoding: identity"); #if ZLIB_VERNUM < 0x1210 #warning 'Accept-Encoding: gzip' is disabled #else if (e.contains("gzip")) { o += ",gzip"; } #endif if (e.contains("deflate")) { o += ",deflate"; } if (e.contains("pack200-gzip")) { o += ",pack200-gzip"; } return o; } // set content length to report the given length, and strip content encoding void HTTPHeader::removeEncoding(int newlen) { if (pcontentlength != NULL) { (*pcontentlength) = "Content-Length: " + String(newlen) + "\r"; } // this may all be overkill. since we strip everything out of the outgoing // accept-encoding header that we don't support, we won't be getting anything // back again that we don't support, in theory. leave new code commented // unless it proves to be necessary further down the line. PRA 20-10-2005 if (pcontentencoding != NULL) { /* DEBUG_debug("Stripping Content-Encoding header"); DEBUG_debug("Old: ", header[i]); // only strip supported compression types String temp(header[i].after(":")); temp.removeWhiteSpace(); String newheader; // iterate over comma-separated list of encodings while (temp.length() != 0) { if (!(temp.startsWith("gzip") || temp.startsWith("deflate"))) { // add other, unstripped encoding types back into the header if (newheader.length() != 0) newheader += ", "; newheader += (temp.before(",").length() != 0 ? temp.before(",") : temp); } temp = temp.after(","); temp.removeWhiteSpace(); } if (newheader.length() == 0)*/ (*pcontentencoding) = "X-DansGuardian-Removed: Content-Encoding\r"; /* else header[i] = "Content-Encoding: "+newheader; DEBUG_debug("New: ", header[i]); */ } } void HTTPHeader::setConnect(String &con_site) { if (requestType() != "CONNECT") return; header.front() = header.front().before(" ") + " " + con_site + ":" + String(port) + " " + header.front().after(" ").after(" "); //remove all other headers if (header.size() > 1) { header.erase(header.begin()+1, header.end()); } if (phost != NULL) { phost = nullptr; } } // modifies the URL in all relevant header lines after a regexp search and replace // setURL Code originally from from Ton Gorter 2004 void HTTPHeader::setURL(String &url) { String hostname; bool https = (url.before("://") == "https"); if(requestType() == "CONNECT"){ https = true; } int port = (https ? 443 : 80); if (!url.after("://").contains("/")) { url += "/"; } hostname = url.after("://").before("/"); if (hostname.contains("@")) { // Contains a username:password combo hostname = hostname.after("@"); } if (hostname.contains(":")) { port = hostname.after(":").toInteger(); if (port == 0 || port > 65535) { port = (https ? 443 : 80); } hostname = hostname.before(":"); // chop off the port bit } DEBUG_debug("setURL: header.front() changed from: ", header.front()); if (https && header.front().startsWith("CONNECT")) // Should take form of "CONNECT example.com:443 HTTP/1.0" for SSL header.front() = header.front().before(" ") + " " + hostname + ":" + String(port) + " " + header.front().after(" ").after(" "); else header.front() = header.front().before(" ") + " " + url + " " + header.front().after(" ").after(" "); DEBUG_debug(" to: ", header.front()); if (phost != NULL) { DEBUG_debug("setURL: header[] line changed from: ", (*phost)); (*phost) = String("Host: ") + hostname; if (port != (https ? 443 : 80)) { (*phost) += ":"; (*phost) += String(port); } (*phost) += "\r"; DEBUG_debug(" to ", (*phost)); } if (pport != NULL) { DEBUG_debug("setURL: header[] line changed from: ", (*pport)); (*pport) = String("Port: ") + String(port) + "\r"; DEBUG_debug(" to ", (*pport)); } // Don't just cache the URL we're sent - getUrl() performs some other // processing, notably stripping the port part. Caching here will // bypass all that. //cachedurl = url.toCharArray(); cachedurl = ""; // blank cachedurl so that getUrl will re-generate it } // Does a regexp search and replace. // urlRegExp Code originally from from Ton Gorter 2004 bool HTTPHeader::regExp(String &line, std::deque ®exp_list, std::deque &replacement_list) { RegExp *re; RegResult Rre; String replacement; String repstr; String newLine; bool linemodified = false; unsigned int i; unsigned int j, k; unsigned int s = regexp_list.size(); unsigned int matches, submatches; unsigned int match; unsigned int srcoff; unsigned int nextoffset; unsigned int matchlen; unsigned int oldlinelen; if ( (line.empty()) || line.length() < 3) return false; // iterate over our list of precompiled regexes for (i = 0; i < s; i++) { newLine = ""; re = &(regexp_list[i]); if (re->match(line.toCharArray(), Rre)) { repstr = replacement_list[i]; matches = Rre.numberOfMatches(); srcoff = 0; for (j = 0; j < matches; j++) { nextoffset = Rre.offset(j); matchlen = Rre.length(j); // copy next chunk of unmodified data if (nextoffset > srcoff) { newLine += line.subString(srcoff, nextoffset - srcoff); srcoff = nextoffset; } // Count number of submatches (brackets) in replacement string for (submatches = 0; j + submatches + 1 < matches; submatches++) if (Rre.offset(j + submatches + 1) + Rre.length(j + submatches + 1) > srcoff + matchlen) break; // \1 and $1 replacement replacement = ""; for (k = 0; k < repstr.length(); k++) { // find \1..\9 and $1..$9 and fill them in with submatched strings if ((repstr[k] == '\\' || repstr[k] == '$') && repstr[k + 1] >= '1' && repstr[k + 1] <= '9') { match = repstr[++k] - '0'; if (match <= submatches) { replacement += Rre.result(j + match).c_str(); } } else { // unescape \\ and \$, and add non-backreference characters to string if (repstr[k] == '\\' && (repstr[k + 1] == '\\' || repstr[k + 1] == '$')) k++; replacement += repstr.subString(k, 1); } } // copy filled in replacement string newLine += replacement; srcoff += matchlen; j += submatches; } oldlinelen = line.length(); if (srcoff < oldlinelen) { newLine += line.subString(srcoff, oldlinelen - srcoff); } DEBUG_debug("Line modified! (", line, " -> ", newLine, ")" ); // copy newLine into line and continue with other regexes line = newLine; linemodified = true; } } return linemodified; } String HTTPHeader::redirecturl() { return redirect; } bool HTTPHeader::addHeader(String &newheader) { if (newheader.size() > 0) { isheaderadded = true; addheaderchecked = true; std::string line(newheader + "\r"); header.push_back(String(line.c_str())); DEBUG_debug("addheader = ", newheader); return true; } return false; } void HTTPHeader::setPostData(const char *data, size_t len) { if(postdata != nullptr) delete postdata; postdata = new char[len]; memcpy(postdata, data, len); postdata_len = len; } // * // * // * detailed header checks & fixes // * // * // is a URL malformed? bool HTTPHeader::malformedURL(const String &url) { String host(url.after("://")); if (host.contains("/")) host = host.before("/"); if (host.length() < 2) { DEBUG_debug("host len too small!"); return true; } if (host.contains(":")) host = host.before(":"); // endsWith . check removed as this format is used by Apple Configurator Updates // if (host.contains("..") || host.endsWith(".")) { if (host.contains("..")) { DEBUG_debug("double dots in domain name!"); return true; } int i, len; unsigned char c; len = host.length(); bool containsletter = false; for (i = 0; i < len; i++) { c = (unsigned char)host[i]; // If it contains something other than numbers, dots, or [a-fx] (hex encoded IPs), // IP obfuscation can be ruled out. if (!containsletter && (((c < '0') || (c > '9')) && (c != '.') && (c != 'x') && (c != 'X') && ((c < 'a') || (c > 'f')) && ((c < 'A') || (c > 'F')))) containsletter = true; if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9') && c != '.' && c != '-' && c != '_') { // only allowed letters, digits, hiphen, dots DEBUG_debug("bad char in hostname!"); return true; } } // no IP obfuscation going on if (containsletter) return false; else DEBUG_debug("Checking for IP obfuscation in ", host); // Check no IP obfuscation is going on // This includes IPs encoded as a single decimal number, // fully or partly hex encoded, and octal encoded bool first = true; bool obfuscation = false; if (host.endsWith(".")) host.chop(); do { if (!first) host = host.after("."); first = false; String hostpart(host); if (host.contains(".")) hostpart = hostpart.before("."); // If any part of the host starts with a letter, any letter, // then we must have a hostname rather than an IP (obscured // or otherwise). TLDs never start with a number. if ((hostpart[0] >= 'a' && hostpart[0] <= 'z') || (hostpart[0] >= 'A' && hostpart[0] <= 'Z')) return false; // If any part of the host begins with 0, it may be hex or octal if ((hostpart[0] == '0') && (hostpart.length() > 1)) { obfuscation = true; continue; } // Also check range, for decimal obfuscation. int part = hostpart.toInteger(); if ((part < 0) || (part > 255)) obfuscation = true; } while (host.contains(".")); // If we have any obfuscated parts, and haven't proven it's a hostname, it's invalid. return obfuscation; } // Show headers values for debug purpose void HTTPHeader::dbshowheader(String *url, const char *clientip) { return; // temporally disable std::string reqres, inout; if (is_response) { reqres = "RES"; } else { reqres = "REQ"; } //const char *tid = thread_id.c_str(); if (header.size() != 0){ String *line; DEBUG_debug("Client: START ", reqres, "-------------------------------" ); for (std::deque::iterator i = header.begin(); i != header.end(); i++) { line = &(*i); String line2 = *line; if (header.front().startsWith("HT")) { inout = "IN"; } else { inout = "OUT"; } DEBUG_debug(inout, ": Client IP ", clientip, " ", line2 ); } DEBUG_debug("Client: END ", reqres, " -------------------------------"); } else { DEBUG_debug("Call : from HTTPHeader.cpp to dbshowheader but", reqres, " header is empty" ); } } void HTTPHeader::dbshowheader(bool outgoing) { return; // temporally disable std::string reqres, inout; if (is_response) { reqres = "RES"; } else { reqres = "REQ"; } if (outgoing) inout = reqres + "OUT"; else inout = reqres + "IN"; // std::hash htid; // std::thread::id tid = std::this_thread::get_id(); if (header.size() != 0){ String *line; DEBUG_debug("Client: START-------------------------------"); for (std::deque::iterator i = header.begin(); i != header.end(); i++) { line = &(*i); String line2 = *line; DEBUG_debug(inout,": dbshowheader bool: ", line2); } DEBUG_debug("Client: END-------------------------------"); } else { DEBUG_debug("Call : from HTTPHeader.cpp to dbshowheader but header is empty"); } } // fix bugs in certain web servers that don't obey standards. // actually, it's us that don't obey standards - HTTP RFC says header names // are case-insensitive. - Anonymous SF Poster, 2006-02-23 void HTTPHeader::checkheader(bool allowpersistent) { bool outgoing = !is_response; // if (header.front().startsWith("HT")) { // outgoing = false; // } String tp; if (header.size() > 1) { for (std::deque::iterator i = header.begin() + 1; i != header.end(); i++) { // check each line in the headers // index headers - try to perform the checks in the order the average browser sends the headers. // also only do the necessary checks for the header type (sent/received). // Sequencial if else if (outgoing && (phost == NULL) && i->startsWithLower("host:")) { phost = &(*i); // don't allow through multiple host headers } else if (outgoing && (phost != NULL) && i->startsWithLower("host:")) { i->assign("X-E2G-IgnoreMe: removed multiple host headers\r"); } else if (outgoing && (puseragent == NULL) && i->startsWithLower("user-agent:")) { puseragent = &(*i); useragent = *i; if(!useragent.headerVal()) puseragent = NULL; } else if (outgoing && i->startsWithLower("accept-encoding:")) { (*i) = "Accept-Encoding:" + i->after(":"); (*i) = modifyEncodings(*i); (*i) += "\r"; } else if ((!outgoing) && (pcontentencoding == NULL) && i->startsWithLower("content-encoding:")) { pcontentencoding = &(*i); } else if ((!outgoing) && (pkeepalive == NULL) && i->startsWithLower("keep-alive:")) { pkeepalive = &(*i); } else if ((pcontenttype == NULL) && i->startsWithLower("content-type:")) { pcontenttype = &(*i); } else if ((pcontentlength == NULL) && i->startsWithLower("content-length:")) { pcontentlength = &(*i); tp = *i; DEBUG_debug("tp =", tp); if(!tp.headerVal()) pcontentlength = NULL; else { contentlength = tp.toOffset(); } DEBUG_debug("tp =", tp, " Contentlen.int =", contentlength); } // is this ever sent outgoing? else if ((pcontentdisposition == NULL) && i->startsWithLower("content-disposition:")) { pcontentdisposition = &(*i); } else if ((pproxyauthorization == NULL) && i->startsWithLower("proxy-authorization:")) { pproxyauthorization = &(*i); } else if ((pauthorization = NULL) && i->startsWithLower("authorization:")) { pauthorization = &(*i); } else if ((pproxyauthenticate == NULL) && i->startsWithLower("proxy-authenticate:")) { pproxyauthenticate = &(*i); } else if ((pproxyconnection == NULL) && (i->startsWithLower("proxy-connection:") || i->startsWithLower("connection:"))) { pproxyconnection = &(*i); } else if (outgoing && (pxforwardedfor == NULL) && i->startsWithLower("x-forwarded-for:")) { pxforwardedfor = &(*i); } // this one's non-standard, so check for it last else if (outgoing && (pport == NULL) && i->startsWithLower("port:")) { pport = &(*i); } else if ( i->startsWithLower("transfer-encoding:")) { ptransferencoding = &(*i); if (i->contains("chunked")) chunked = true; } else if ( i->startsWithLower("transfer-coding:")) { ptransfercoding = &(*i); } else if ( i->startsWithLower("expect: 100-continue")) { expects_100 = true; } //Can be placed anywhere .. if (outgoing && i->startsWithLower("upgrade-insecure-requests:")) { i->assign("X-E2G-IgnoreMe: removed upgrade-insecure-requests\r"); } // if ((o.log_header_value.size() != 0) && outgoing && (plogheadervalue == NULL) && i->startsWithLower(o.log_header_value)) { // plogheadervalue = &(*i); // } if ((o.header.ident_header_value.size() != 0) && outgoing && (pheaderident == NULL) && i->startsWithLower(o.header.ident_header_value)) { pheaderident = &(*i); } DEBUG_debug("Header value from client: ", *i); } } //checkfirstheaderline(allowpersistent); if (outgoing && !requesttype.startsWith("P")) // is not POST or PUT no body is allowed { DEBUG_debug("zero contentlength on request due to not POST/PUT "); contentlength = 0; } if (is_response && ((returncode < 200) || (returncode == 204) || (returncode == 304))) // no content body allowed { DEBUG_debug("zero contentlength on response due to returncode ", String(returncode) ); contentlength = 0; } //work out if we should explicitly close this connection after this request bool connectionclose = false; if(!icap) { if (pproxyconnection != NULL) { if (pproxyconnection->contains("lose")) { DEBUG_debug("CheckHeader: P-C says close"); connectionclose = true; } else { connectionclose = false; } } else { connectionclose = true; } } // Do not allow persistent connections on CONNECT requests - the browser thinks it has a tunnel // directly to the external server, not a connection to the proxy, so it won't be re-used in the // manner expected by E2 and will result in waiting for time-outs. Bug identified by Jason Deasi. bool isconnect = false; if (outgoing && header.front()[0] == 'C') { DEBUG_debug("CheckHeader: CONNECT request detected"); isconnect = true; } DEBUG_debug("CheckHeader flags before normalisation: ", " AP=", String(allowpersistent), " PPC=", String(pproxyconnection != NULL), " 1.1=", String(onepointone), " connectionclose=", String(connectionclose), " CL=", String(pcontentlength != NULL) ); if (connectionclose || (!onepointone && (outgoing ? isconnect : (pcontentlength == NULL)))) { // couldnt have done persistency even if we wanted to allowpersistent = false; } if(!icap) { if (outgoing) { // Even though persistent CONNECT requests usually break things, waspersistent should // reflect the intention of the original request headers, or NTLM breaks. if (isconnect && !connectionclose) { waspersistent = true; } } else { if (!connectionclose && !(pcontentlength == NULL)) { waspersistent = true; } } } DEBUG_debug("CheckHeader flags after normalisation: ", " AP=", String(allowpersistent), " WP=", String(waspersistent) ); if(!icap) { // force the headers to reflect whether or not persistency is allowed // (modify pproxyconnection or add connection close/keep-alive - Client version, of course) if (allowpersistent ) { if (pproxyconnection == NULL) { DEBUG_debug("CheckHeader: Adding our own Proxy-Connection: Keep-Alive"); header.push_back("Connection: keep-alive\r"); pproxyconnection = &(header.back()); } else { (*pproxyconnection) = "Connection: keep-alive\r"; } } else { if (pproxyconnection == NULL) { DEBUG_debug("CheckHeader: Adding our own Proxy-Connection: Close"); header.push_back("Connection: close\r"); pproxyconnection = &(header.back()); } else { (*pproxyconnection) = "Connection: close\r"; } } } ispersistent = allowpersistent; // Normalise request headers (fix host, port, first line of header, etc. to all be consistent) if (outgoing) { String newurl(getUrl(true)); setURL(newurl); } } bool HTTPHeader::checkfirstheaderline() { if(!header.front().contains("HTTP/")) { DEBUG_debug("CheckfirstHeader: not HTTP!"); return false; } //if its http1.1 onepointone = false; if (header.front().after("HTTP/").startsWith("1.1")) { DEBUG_debug("CheckHeader: HTTP/1.1 detected"); onepointone = true; } if (!is_response) { // i.e. is request - set request Type requesttype = header.front().before(" "); if(header.front().after(" ").startsWith("/")) isProxyRequest = false; else isProxyRequest = true; } else { // is response - set status code String tp = header.front().after(" ").before(" "); tp.removeWhiteSpace(); returncode = tp.toInteger(); if (returncode < 100 || returncode == 102) { DEBUG_debug("responsecode not valid ",returncode); returncode = 0; return false; } } return true; } String HTTPHeader::getLogUrl(bool withport, bool isssl) { String answer = getUrl(withport, isssl); if (mitm || isssl) { answer = "https://" + answer.after("://"); } return answer; } String HTTPHeader::getUrl(bool withport, bool isssl) { // Version of URL *with* port is not cached, // as vast majority of our code doesn't like // port numbers in URLs. if (cachedurl.length() > 0 && !withport) return cachedurl; port = 80; bool https = false; if (isssl) { mitm = isssl; isdirect = true; } if ( header.size() == 0) { String ans; return ans; } String hostname; String userpassword; String answer(header.front().after(" ")); answer.removeMultiChar(' '); if (answer.after(" ").startsWith("HTTP/")) { answer = answer.before(" HTTP/"); } else { answer = answer.before(" http/"); // just in case! } if (requestType() == "CONNECT") { https = true; port = 443; if (!answer.startsWith("https://")) { answer = "https://" + answer; } } if (pport != NULL) { port = pport->after(" ").toInteger(); if (port == 0 || port > 65535) port = (https ? 443 : 80); } if (answer.length()) { if (answer[0] == '/') { // must be the latter above if (phost != NULL) { hostname = phost->after(" "); hostname.removeWhiteSpace(); if (hostname.contains(":")) { port = hostname.after(":").toInteger(); if (port == 0 || port > 65535) { port = (https ? 443 : 80); } hostname = hostname.before(":"); } while (hostname.endsWith(".")) hostname.chop(); if (withport && (port != (https ? 443 : 80))) hostname += ":" + String(port); hostname = "http://" + hostname; answer = hostname + answer; } // Squid doesn't like requests in this format. Work around the fact. header.front() = requestType() + " " + answer + " HTTP/" + header.front().after(" HTTP/"); } else { // must be in the form GET http://foo.bar:80/ HTML/1.0 if (!answer.after("://").contains("/")) { answer += "/"; // needed later on so correct host is extracted } String protocol(answer.before("://")); hostname = answer.after("://"); String url(hostname.after("/")); url.removeWhiteSpace(); // remove rubbish like ^M and blanks if (hostname.endsWith(".")) { hostname.chop(); } if (url.length() > 0) { url = "/" + url; } hostname = hostname.before("/"); // extra / was added 4 here if (hostname.contains("@")) { // Contains a username:password combo userpassword = hostname.before("@"); hostname = hostname.after("@"); } if (hostname.contains(":")) { port = hostname.after(":").toInteger(); if (port == 0 || port > 65535) { port = (https ? 443 : 80); } hostname = hostname.before(":"); // chop off the port bit } while (hostname.endsWith(".")) hostname.chop(); if (withport && (port != (https ? 443 : 80))) hostname += ":" + String(port); if (userpassword.length()) answer = protocol + "://" + userpassword + "@" + hostname + url; else answer = protocol + "://" + hostname + url; } } // Some sites do now have urls ending with '//' and will not answer to just '/' // if (answer.endsWith("//")) { // answer.chop(); // } DEBUG_debug("from header url:", answer); // Don't include port numbers in the URL in the cached version. // Most of the code only copes with URLs *without* port specifiers. if (!withport) cachedurl = answer.toCharArray(); return answer; } String HTTPHeader::url() { return getUrl(); } // * // * // * Bypass URL/Cookie funcs // * // * // create a temporary bypass URL for the banned page String HTTPHeader::hashedURL(String *url, std::string *clientip, bool infectionbypass, std::string *user, FOptionContainer &fdl, bool fakecgi) { // filter/virus bypass hashes last for a certain time only String timecode(time(NULL) + (infectionbypass ? fdl.infection_bypass_mode : fdl.bypass_mode)); // use the standard key in normal bypass mode, and the infection key in infection bypass mode String magic(infectionbypass ? fdl.imagic.c_str() : fdl.magic.c_str()); magic += clientip->c_str(); if(fdl.bypass_v2) magic += user->c_str(); magic += timecode; String res(infectionbypass ? "GIBYPASS=" : "GBYPASS="); if (fakecgi) // it is an oversized file bypass res = "GOSBYPASS="; String hash; if (!url->after("://").contains("/")) { String newurl((*url)); newurl += "/"; hash = newurl.md5(magic.toCharArray()); } else { hash = url->md5(magic.toCharArray()); } if (fakecgi && fdl.cgi_bypass) //used for oversized files in fancy download manager hash = hash.md5(fdl.cgi_magic.c_str()); res += hash; res += timecode; DEBUG_debug(" -generate Bypass hashedurl data ", clientip, " ", *url, " ", user, " ", timecode, " result ", res); return res; } // create temporary bypass cookie String HTTPHeader::hashedCookie(String *url, const char *magic, std::string *clientip, int bypasstimestamp, std::string user) { String timecode(bypasstimestamp); String data(magic); data += clientip->c_str(); // if(ldl->fg[filtergroup]->bypass_v2) data += user; data += timecode; DEBUG_debug(" -generate Bypass hashedCookie data ", clientip, " ", *url, " ", user, " ", timecode); String res(url->md5(data.toCharArray())); res += timecode; DEBUG_debug(" -Bypass hashedCookie=", res ); return res; } // chop the GBYPASS or GIBYPASS variable out of a bypass URL // This function ASSUMES that you really know what you are doing // Do NOT run this function unless you know that the URL contains a valid bypass code // Ernest W Lessenger //void HTTPHeader::chopBypass(String url, bool infectionbypass) //{ // chopBypass(url, infectionbypass ? "GIBYPASS=" : "GBYPASS="); //if (url.contains(infectionbypass ? "GIBYPASS=" : "GBYPASS=")) { //if (url.contains(infectionbypass ? "?GIBYPASS=" : "?GBYPASS=")) { //String bypass(url.after(infectionbypass ? "?GIBYPASS=" : "?GBYPASS=")); ////header.front() = header.front().before(infectionbypass ? "?GIBYPASS=" : "?GBYPASS=") + header.front().after(bypass.toCharArray()); //} else { //String bypass(url.after(infectionbypass ? "&GIBYPASS=" : "&GBYPASS=")); //header.front() = header.front().before(infectionbypass ? "&GIBYPASS=" : "&GBYPASS=") + header.front().after(bypass.toCharArray()); //} //} //cachedurl = ""; //} // same for scan bypass //void HTTPHeader::chopScanBypass(String url) //{ // chopBypass(url,"GSBYPASS="); // if (url.contains("GSBYPASS=")) { // if (url.contains("?GSBYPASS=")) { // String bypass(url.after("?GSBYPASS=")); // header.front() = header.front().before("?GSBYPASS=") + header.front().after(bypass.toCharArray()); // } else { // String bypass(url.after("&GSBYPASS=")); // header.front() = header.front().before("&GSBYPASS=") + header.front().after(bypass.toCharArray()); // } // } // cachedurl = ""; //} // New generic chopBypass - allows new types to be added void HTTPHeader::chopBypass(String url,std::string btype) // Note: type must include = , e.g. GBYPASS= { if (url.contains(reinterpret_cast(btype.c_str()))) { std::string qtype("?"); qtype += btype; // = ?GIBYPASS= or ?GBYPASS= etc if (url.contains(reinterpret_cast(qtype.c_str()))) { String bypass(url.after(qtype.c_str())); String after(header.front().after(bypass.toCharArray())); header.front() = header.front().before(qtype.c_str()); header.front() += after; } else { std::string qtype("&"); qtype += btype; // = &GIBYPASS= or &GBYPASS= etc if (url.contains(reinterpret_cast(qtype.c_str()))) { String bypass(url.after(qtype.c_str())); String after(header.front().after(bypass.toCharArray())); header.front() = header.front().before(qtype.c_str()); header.front() += after; } } cachedurl = ""; } } // I'm not proud of this... --Ernest String HTTPHeader::getCookie(const char *cookie) { String line; // TODO - do away with loop here somehow, or otherwise speed it up? for (std::deque::iterator i = header.begin(); i != header.end(); i++) { if (i->startsWithLower("cookie:")) { line = i->after(": "); if (line.contains(cookie)) { // We know we have the cookie line = line.after(cookie); line.lop(); // Get rid of the '=' if (line.contains(";")) { line = line.before(";"); } } // break; // Technically there should be only one Cookie: header, but... } } line.removeWhiteSpace(); DEBUG_debug("Found cookie:", line ); return line; } // add cookie with given name & value to outgoing headers void HTTPHeader::setCookie(const char *cookie, const char *domain, const char *value) { String line("Set-Cookie: "); line += cookie; line += "="; line += value; line += "; path=/; domain=."; line += domain; line += "\r"; header.push_back(line); DEBUG_debug("Setting cookie:", line); // no expiry specified so ends with the browser session } // is this a temporary filter bypass cookie? bool HTTPHeader::isBypassCookie(String url, const char *magic, const char *clientip, const char *user) { String cookie(getCookie("GBYPASS")); if (!cookie.length()) { DEBUG_debug("No bypass cookie"); return false; } String cookiehash(cookie.subString(0, 32)); String cookietime(cookie.after(cookiehash.toCharArray())); String mymagic(magic); mymagic += clientip; mymagic += user; mymagic += cookietime; bool matched = false; while (url.contains(".")) { String hashed(url.md5(mymagic.toCharArray())); DEBUG_debug("Bypass cookie:", cookiehash, " hashed: ", hashed, " contains ", clientip, " ", user, " ", url, " ", cookietime); if (hashed == cookiehash) { matched = true; break; } url = url.after("."); } if (not matched) { DEBUG_debug("Cookie GBYPASS not match"); return false; } time_t timen = time(NULL); time_t timeu = cookietime.toLong(); if (timeu < timen) { DEBUG_debug("Cookie GBYPASS expired: ", timeu, " ", timen ); return false; } return true; } String HTTPHeader::getReferer() { String line; for (std::deque::iterator i = header.begin(); i != header.end(); i++) { if (i->startsWithLower("referer:")) { line = i->after(": "); break; } } line.removeWhiteSpace(); DEBUG_debug("Found Referer URL:", line); return line; } // * // * // * URL and Base64 decoding funcs // * // * // URL decoding (%xx) // uses regex pre-compiled on startup String HTTPHeader::decode(const String &s, bool decodeAll) { if (s.length() < 3) { return s; } DEBUG_trace("decoding url"); RegResult Rre; if (!urldecode_re.match(s.c_str(),Rre)) { return s; } // exit if not found int match; int offset; int pos = 0; int size = s.length(); String result; String n; for (match = 0; match < Rre.numberOfMatches(); match++) { offset = Rre.offset(match); if (offset > pos) { result += s.subString(pos, offset - pos); } n = Rre.result(match).c_str(); n.lop(); // remove % result += hexToChar(n, decodeAll); // DEBUG_debug("encoded: ", Rre.result(match), " decoded: ", hexToChar(n), " string so far: ", result); pos = offset + 3; } if (size > pos) { result += s.subString(pos, size - pos); } else { n = "%" + n; } return result; } // turn %xx back into original character String HTTPHeader::hexToChar(const String &n, bool all) { if (n.length() < 2) { return String(n); } static char buf[2]; unsigned int a, b; unsigned char c; a = n[0]; b = n[1]; if (a >= 'a' && a <= 'f') { a -= 87; } else if (a >= 'A' && a <= 'F') { a -= 55; } else if (a >= '0' && a <= '9') { a -= 48; } else { return String("%") + n; } if (b >= 'a' && b <= 'f') { b -= 87; } else if (b >= 'A' && b <= 'F') { b -= 55; } else if (b >= '0' && b <= '9') { b -= 48; } else { return String("%") + n; } c = a * 16 + b; if (all || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '-')) { buf[0] = c; buf[1] = '\0'; return String(buf); } else { return String("%") + n; } } // decode a line of base64 std::string HTTPHeader::decodeb64(const String &line) { // decode a block of b64 MIME - will not work with binary data only with text - trailing cr or newline are ignored long four = 0; int d; std::string result; int len = line.length() - 3; for (int i = 0; i < len; i += 4) { four = 0; d = decode1b64(line[i + 0]); four = four | d; d = decode1b64(line[i + 1]); four = (four << 6) | d; d = decode1b64(line[i + 2]); four = (four << 6) | d; d = decode1b64(line[i + 3]); four = (four << 6) | d; d = (four & 0xFF0000) >> 16; result += (char)d; // first char cannot be padding so this is safe d = (four & 0xFF00) >> 8; if (d > 0) { // if d is 0 = padding so do not put ^@ in string! result += (char)d; } d = four & 0xFF; if (d > 0) result += (char)d; // if d is 0 = padding so do not put ^@ in string! } return result; } // decode an individual base64 character int HTTPHeader::decode1b64(char c) { unsigned char i = '\0'; switch (c) { case '+': i = 62; break; case '/': i = 63; break; case '=': i = 0; break; default: // must be A-Z, a-z or 0-9 i = '9' - c; if (i > 0x3F) { // under 9 i = 'Z' - c; if (i > 0x3F) { // over Z i = 'z' - c; if (i > 0x3F) { // over z so invalid i = 0x80; // so set the high bit } else { // a-z i = c - 71; } } else { // A-Z i = c - 65; } } else { // 0-9 i = c + 4; } break; } return (int)i; } // * // * // * encode funcs // * // get encoded URL? String HTTPHeader::URLEncode() { std::string encoded; String newurl(getUrl()); const char *s = newurl.c_str(); char *buf = new char[3]; unsigned char c; for (int i = 0; i < (signed)strlen(s); i++) { c = s[i]; // allowed characters in a url that have non special meaning if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) { encoded += c; continue; } // all other characters get encoded sprintf(buf, "%02x", c); encoded += "%"; encoded += buf; } delete[] buf; String returnS(encoded); return returnS; } String HTTPHeader::stringHeader() { String l; DEBUG_debug("stringHeader started hsize=", header.size() ); if (header.size() > 0) { for (std::deque::iterator i = header.begin(); i != header.end(); i++) { if (! (*i).startsWith("X-E2G-IgnoreMe")){ DEBUG_debug("Found Header: ", *i ); l += (*i) + "\n"; } else { DEBUG_debug("Found Header X-E2G-IgnoreMe: ", *i ); } } l += "\r\n"; } return l; } // * // * // * network send/receive funcs // * // * // send headers out over the given socket // "reconnect" flag gives permission to reconnect to the socket on write error // - this allows us to re-open the proxy connection on pconns if squid's end has // timed out but the client's end hasn't. not much use with NTLM, since squid // will throw a 407 and restart negotiation, but works well with basic & others. //void HTTPHeader::out(Socket *peersock, Socket *sock, int sendflag, bool reconnect) throw(std::exception) bool HTTPHeader::out(Socket *peersock, Socket *sock, int sendflag, bool reconnect ) { String l; // for amalgamating to avoid conflict with the Nagel algorithm if (header_all_sent) return true; if ((sendflag == __E2HEADER_SENDALL || sendflag == __E2HEADER_SENDFIRSTLINE) && !first_line_sent) { if (header.size() > 0) { l = header.front() + "\n"; first_line_sent = true; if(is_response) { DEBUG_debug("response headerout:", l); } else { DEBUG_debug("request headerout:", l); } //if a socket is ssl we want to send relative paths not absolute urls //also HTTP responses dont want to be processed (if we are writing to an ssl client socket then we are doing a request) if (sock->isSsl() && !sock->isSslServer()) { setDirect(); } if (isdirect && !is_response) { l = header.front().before(" ") + " /" + header.front().after("://").after("/").before(" ") + " HTTP/1.1\r\n"; DEBUG_debug("request headerout (modified for direct):", l); } // first reconnect loop - send first line while (true) { if (!sock->writeToSocket(l.toCharArray(), l.length(), 0, timeout)) { // reconnect & try again if we've been told to if (reconnect && !isdirect) { // don't try more than once E2LOGGER_error( "Proxy connection broken (1); trying to re-establish..."); reconnect = false; sock->reset(); int rc = sock->connect(o.net.proxy_ip, o.net.proxy_port); if (rc) return false; // throw std::exception(); continue; } // throw std::exception(); return false; } // if we got here, we succeeded, so break the reconnect loop DEBUG_debug("headertoclient: ", l.substr(0, l.length()-1), " timeout:", String(timeout)); break; } } if (sendflag == __E2HEADER_SENDFIRSTLINE) { return true; } } l = ""; if (header.size() > 1) { for (std::deque::iterator i = header.begin() + 1; i != header.end(); i++) { if (! (*i).startsWith("X-E2G-IgnoreMe")){ DEBUG_debug("Found Header: ", *i); l += (*i) + "\n"; } else { DEBUG_debug("Found Header X-E2G-IgnoreMe: ", *i ); } } } if (!is_response && o.header.forwarded_for && !isdirect) { std::string line("X-Forwarded-For: "); line.append(s_clientip).append("\r\n"); DEBUG_debug("Adding Header: ", line); l += line; } l += "\r\n"; // second reconnect loop while (true) { // send header to the output stream // need exception for bad write if (!sock->writeToSocket(l.toCharArray(), l.length(), 0, timeout)) { // reconnect & try again if we've been told to if (reconnect && !isdirect) { // don't try more than once E2LOGGER_error("Proxy connection broken (2); trying to re-establish..."); reconnect = false; sock->reset(); int rc = sock->connect(o.net.proxy_ip, o.net.proxy_port); if (rc) return false; // throw std::exception(); // include the first line on the retry l = header.front() + "\n" + l; continue; } //throw std::exception(); return false; } // if we got here, we succeeded, so break the reconnect loop break; } DEBUG_debug("Header written - pstdata_len:", postdata_len ); if (postdata_len > 0) { DEBUG_debug("Sending manually set POST data"); if (!sock->writeToSocket(postdata, postdata_len, 0, timeout)) { DEBUG_debug("Could not send POST data!"); //throw std::exception(); return false; } } else if (!is_response && (peersock != nullptr) && ((contentlength > 0) || chunked) && !expects_100) { DEBUG_debug("Opening tunnel for POST data"); FDTunnel fdt; if (!fdt.tunnel(*peersock, *sock, false, contentLength(), true, chunked) ) return false; } DEBUG_debug("Returning from header:out "); #ifdef DEBUG_LOW dbshowheader(true); #endif header_all_sent = true; return true; } // discard remainder of POST data void HTTPHeader::discard(Socket *sock, off_t cl) { static char header[4096]; if (cl == -2) cl = contentLength(); int rc; while (cl > 0) { rc = sock->readFromSocket(header, ((cl > 4096) ? 4096 : cl), 0, timeout); if (rc > 0) cl -= rc; else break; } } void HTTPHeader::setClientIP(String &ip) { s_clientip = ip.toCharArray(); } String HTTPHeader::getClientIP() { return s_clientip; } void HTTPHeader::setDirect() { isdirect = true; } bool HTTPHeader::in_handle_100(Socket *sock, bool allowpersistent, bool expect_100) { int max_100s = 10; while( max_100s > 0) { if( in(sock,allowpersistent)) { if (returncode == 100) { if (expect_100) { return true; } // discard 100 continue header and get next header max_100s--; continue; } else { // all other return codes return true; } } else { return false; } } DEBUG_debug("Too many repeat 100 continues"); return false; } bool HTTPHeader::in(Socket *sock, bool allowpersistent) { if (dirty) reset(); dirty = true; #ifdef DEBUG_LOW if(is_response) { DEBUG_debug("Start of response header:in"); } else { DEBUG_debug("Start of request header:in"); } #endif // the RFCs don't specify a max header line length so this should be // dynamic really. Pointed out (well reminded actually) by Daniel Robbins char buff[32768]; // setup a buffer to hold the incomming HTTP line String line; // temp store to hold the line after processing line = "----"; // so we get past the first while bool firsttime = true; bool discard = false; while (line.length() > 3 || discard) { // loop until the stream is // failed or we get to the end of the header (a line by itself) // get a line of header from the stream // on the first time round the loop, honour the reloadconfig flag if desired // - this lets us break when waiting for the next request on a pconn, but not // during receipt of a request in progress. bool truncated = false; int rc; if (firsttime) { DEBUG_debug("header:in firsttime before getLine - timeout:", timeout ); rc = sock->getLine(buff, 32768, timeout, NULL, &truncated); DEBUG_debug("firstime: header:in after getLine "); if (rc < 0 || truncated) { ispersistent = false; DEBUG_debug("firstime: header:in after getLine: rc: ", rc, " truncated: ", truncated ); #ifdef DEBUG_LOW dbshowheader(false); #endif return false; } line = buff; header.push_back(line); // stick the line in the deque that holds the header if(!checkfirstheaderline()) { DEBUG_debug("firstime: checkfirstheaerline returned false ", header.front(), "pad " ); header.clear(); return false; } // Take this out as it appears that 100 continue can have other header lines and is terminated with a blank line like other status returns // if(is_response && returncode == 100) { //DEBUG_debug("firstime: return code is 100" ); // return true; //} } else { rc = sock->getLine(buff, 32768, timeout, NULL, &truncated); if (rc < 0 || truncated) { ispersistent = false; DEBUG_debug("not firstime header:in after getLine: rc: ", rc, " truncated: ", truncated ); #ifdef DEBUG_LOW dbshowheader(false); #endif return false; // do not allow non-terminated headers } } if (header.size() > o.header.max_header_lines) { E2LOGGER_error("header:size too big: %lu, see maxheaderlines", header.size()); dbshowheader(false); ispersistent = false; return false; } // throw std::exception(); // getline will throw an exception if there is an error which will // only be caught by HandleConnection() ????????????????????? if (rc > 0 ) line = buff; else line = "";// convert the line to a String if(firsttime && is_response) { // check first line header if (!(line.length() > 11 && line.startsWith("HTTP/") && (line.after(" ").before(" ").toInteger() > 99))) { if(o.conn.logconerror) E2LOGGER_error("Returning from header:in Server did not respond with HTTP "); #ifdef DEBUG_LOW dbshowheader(false); #endif return false; } } // ignore crap left in buffer from old pconns (in particular, the IE "extra CRLF after POST" bug) discard = false; //if ((firsttime && line.length() <= 3)) { // discard = true; // DEBUG_debug("Discarding unwanted bytes at head of request (pconn closed or IE multipart POST bug)"); //} else { // header.push_back(line); // stick the line in the deque that holds the header // } if(firsttime) { firsttime = false; } else { header.push_back(line); // stick the line in the deque that holds the header } // End of while } // At this point there should be at least two header lines including the final blank line - should fix # if (header.size() < 2) { DEBUG_debug("header:size < 2 - incomplete "); return false; } header.pop_back(); // remove the final blank line of a header DEBUG_debug("header:size = ", header.size()); if (header.size() > 0) { DEBUG_debug("first line = ", header[0]); } checkheader(allowpersistent); // sort out a few bits in the header DEBUG_debug("isProxyRequest is ", isProxyRequest); return true; } e2guardian-5.5.8r/src/HTTPHeader.hpp000066400000000000000000000220441477372360500171440ustar00rootroot00000000000000 // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_HTTPHeader #define __HPP_HTTPHeader // DEFINES #define __E2HEADER_SENDALL 0 #define __E2HEADER_SENDFIRSTLINE 1 #define __E2HEADER_SENDREST 2 #define __HEADER_REQUEST 1 #define __HEADER_RESPONSE 2 // INCLUDES #include #include "String.hpp" //#include "DataBuffer.hpp" #include "Socket.hpp" #include "RegExp.hpp" #include "ListMeta.hpp" #include "FOptionContainer.hpp" // DECLARATIONS class HTTPHeader { public: std::deque header; //DataBuffer postdata; unsigned int port; bool is_response = false; bool icap; bool first_line_sent = false; bool header_all_sent = false; String redirect; String useragent; String contenttype; String contentencoding; String transferencoding; // reset header object for future use void reset(); // network communication funcs void setTimeout(int t); bool in(Socket *sock, bool allowpersistent = false ); bool in_handle_100(Socket *sock, bool allowpersistent = false, bool expect_100 = false ); void setClientIP(String &ip); String getClientIP(); String stringHeader(); // output header as a String (used by ICAP) // send headers out over the given socket // "reconnect" flag gives permission to reconnect to the socket on write error // - this allows us to re-open the proxy connection on pconns if squid's end has // timed out but the client's end hasn't. not much use with NTLM, since squid // will throw a 407 and restart negotiation, but works well with basic & others. //void out(Socket *peersock, Socket *sock, int sendflag, bool reconnect = false) throw(std::exception); bool out(Socket *peersock, Socket *sock, int sendflag, bool reconnect = false); // discard remainder of POST data // amount to discard can be passed in, or will default to contentLength() void discard(Socket *sock, off_t cl = -2); // header value and type checks // request type: GET, HEAD, POST etc. String requestType(); String requesttype; int returnCode(); int returncode = 0; bool onepointone = false; // get content length - returns -1 if undetermined off_t contentLength(); String getContentType(); bool OKtoFilterMime(FOptionContainer* &foc); String getMIMEBoundary(); // check received content type against given content type //bool isContentType(const String &t,int filtergroup); bool isContentType(const String &t,FOptionContainer* &foc); // check HTTP message code to see if it's an auth required message bool authRequired(); // Content-Disposition String disposition(); String userAgent(); // grab contents of X-Forwarded-For std::string getXForwardedForIP(); // check HTTP message code to see if it's a redirect bool isRedirection(); // see if content-type is something other than "identity" bool isCompressed(); bool addHeader(String & xheader); //bool isHeaderAdded(int filtergroup); bool isHeaderAdded(FOptionContainer* &foc); bool addheaderchecked; bool isheaderadded; String *plogheadervalue; String *pheaderident; String *ptransfercoding; String *ptransferencoding; std::string getAuthHeader(); // see if search usl and set searchwords bool isSearch(FOptionContainer* &foc); String searchwords(); String searchterms(); bool searchchecked; bool chunked; bool expects_100 = false; String contentEncoding(); String transferEncoding(); // grab the contents of Proxy-Authorization header // returns base64-decoding of the chunk of data after the auth type string std::string getAuthData(); // grab raw contents of Proxy-Authorization header, without b64 decode std::string getRawAuthData(); // Debug show header void dbshowheader(String *url, const char *clientip); void dbshowheader(bool outgoing); // check whether a connection is persistent bool isPersistent() { return ispersistent; }; bool wasPersistent() { return waspersistent; }; // set POST data for outgoing requests. // assumes that existing POST data has already been discarded // or retrieved elsewhere, and sends this data instead when ::out // is called. void setPostData(const char *data, size_t len); void setDirect(); // detailed value/type checks bool malformedURL(const String &url); String getAuthType(); String getUrl(bool withport = false, bool isssl = false); String getLogUrl(bool withport = false, bool isssl = false); String url(); String redirecturl(); // header modifications void addXForwardedFor(const std::string &clientip); // strip content-encoding, and simultaneously set content-length to newlen void removeEncoding(int newlen); void setContentLength(int newlen); //bool DenySSL(FOptionContainer* &foc); // make a connection persistent - or not void makePersistent(bool persist = true); // make the request look as if its coming from the origin server void makeTransparent(bool incoming); // modifies the URL in all relevant header lines after a regexp search and replace // setURL Code originally from from Ton Gorter 2004 void setURL(String &url); // modifies connect site only - leaves other headers alone void setConnect(String &con_site); // do URL decoding (%xx) on string // decode everything, or just numbers, letters and - static String decode(const String &s, bool decodeAll = false); // Bypass URL & Cookie funcs bool isBypassCookie(String url, const char *magic, const char *clientip, const char *user); //void chopBypass(String url, bool infectionbypass); void chopBypass(String url,std::string bp_type); //void chopScanBypass(String url); // add cookie to outgoing headers with given name & value void setCookie(const char *cookie, const char *domain, const char *value); bool isProxyRequest; // encode url String URLEncode(); // grab referer url from headers String getReferer(); HTTPHeader() // : port(0), timeout(120000), contentlength(0), postdata(NULL), dirty(true), is_response(false) : port(0), timeout(120000), contentlength(0), postdata(NULL), dirty(true) { reset(); }; HTTPHeader(int type) // : port(0), timeout(120000), contentlength(0), postdata(NULL), dirty(true), is_response(false) : port(0), timeout(120000), contentlength(0), postdata(NULL), dirty(true) { reset(); setType(type); }; ~HTTPHeader() { delete postdata; }; void setType(int type) { if (type == __HEADER_RESPONSE) is_response = true; else is_response = false; }; // generate bypass hashed url String hashedURL(String *url, std::string *clientip, bool infectionbypass, std::string *user, FOptionContainer &fdl, bool fakecgi = false); // generate bypass hashed cookie String hashedCookie(String *url, const char *magic, std::string *clientip, int bypasstimestamp, std::string user); // base64 decode a complete string std::string decodeb64(const String &line); private: // timeout for socket operations int timeout; // header index pointers String *phost; String *pport; String *pcontentlength; String *pcontenttype; String *pproxyauthorization; String *pauthorization; String *pproxyauthenticate; String *pcontentdisposition; String *puseragent; String *pxforwardedfor; String *pcontentencoding; String *pproxyconnection; String *pkeepalive; // cached result of getUrl() std::string cachedurl; // used to record if it is a header within a MITM bool mitm = false; // is direct rather than via proxy bool isdirect = false; String searchwds; //std::string searchwds; //std::string searchtms; String searchtms; bool issearch; // cached result of contentLength() off_t contentlength; bool clcached; // replacement POST data for sending during ::out char *postdata; size_t postdata_len = 0; bool ispersistent, waspersistent; bool dirty; std::string s_clientip; // check & fix headers from servers that don't obey standards void checkheader(bool allowpersistent); bool checkfirstheaderline(); // convert %xx back to original character static String hexToChar(const String &n, bool all = false); // base64 decode an individual char int decode1b64(char c); // modify supplied accept-encoding header, adding "identity" and stripping unsupported compression types String modifyEncodings(String e); // Generic search & replace code, called by urlRegExp and headerRegExp // urlRegExp Code originally from from Ton Gorter 2004 bool regExp(String &line, std::deque ®exp_list, std::deque &replacement_list); // grab cookies from headers String getCookie(const char *cookie); }; #endif e2guardian-5.5.8r/src/ICAPHeader.cpp000066400000000000000000000504241477372360500170770ustar00rootroot00000000000000//ll support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "ICAPHeader.hpp" #include "Socket.hpp" #include "OptionContainer.hpp" #include "FDTunnel.hpp" #include "Logger.hpp" #include #include #include #include #include #include #include #include // GLOBALS extern OptionContainer o; // IMPLEMENTATION // set timeout for socket operations void ICAPHeader::setTimeout(int t) { timeout = t; } bool ICAPHeader::setEncapRecs() { String t = *pencapsulated; DEBUG_icap("pencapsulated is ", t); t = t.after(": "); while ( t.length() ) { String t1 = t.before(","); if (t1 == "") t1 = t; struct encap_rec erec; erec.name = t1.before("="); String t2 = t1.after("="); if (erec.name == "" || t2 == "") return false; erec.value = t2.toInteger(); encap_recs.push_back(erec); t = t.after(", "); } return true; } // reset header object for future use void ICAPHeader::reset() { HTTPresponse.setType(__HEADER_RESPONSE); HTTPresponse.icap = true; HTTPrequest.icap = true; if (dirty) { header.clear(); encap_recs.clear(); waspersistent = false; ispersistent = false; pproxyconnection = NULL; pencapsulated= NULL; pauthorization = NULL; pallow= NULL; allow_204 = false; pfrom= NULL; phost = NULL; preferer = NULL; puseragent = NULL; pxforwardedfor = NULL; pkeepalive = NULL; pupgrade = NULL; pencapsulated = NULL; pproxyauthorization = NULL; pproxyauthenticate = NULL; pcontentdisposition = NULL; pclientip= NULL; pclientuser= NULL; req_hdr_flag = false; res_hdr_flag = false; out_req_hdr_flag = false; out_res_hdr_flag = false; out_req_body_flag = false; out_res_body_flag = false; req_body_flag = false; res_body_flag = false; opt_body_flag = false; null_body_flag = false; service_reqmod = false; service_resmod = false; service_options = false; icap_reqmod_service = false; icap_resmod_service = false; icap_com.filtergroup = -1; username = ""; dirty = false; } } // * // * // * header value and type checks // * // * // grab request type (REQMOD, RESPMOD, OPTIONS) String ICAPHeader::requestType() { return header.front().before(" "); } // grab return code int ICAPHeader::returnCode() // does not apply to ICAP ? May do if we use for ICAP client { if (header.size() > 0) { return header.front().after(" ").before(" ").toInteger(); }else { return 0; } } // * // * // * detailed header checks & fixes // * // * void ICAPHeader::checkheader(bool allowpersistent) { if (header.size() > 1) { for (std::deque::iterator i = header.begin() + 1; i != header.end(); i++) { // check each line in the headers // index headers - try to perform the checks in the order the average browser sends the headers. // also only do the necessary checks for the header type (sent/received). // Sequencial if else if ((phost == NULL) && i->startsWithLower("host:")) { phost = &(*i); } else if ((pauthorization == NULL) && i->startsWithLower("authorization:")) { pauthorization = &(*i); } else if ((pallow == NULL) && i->startsWithLower("allow:")) { pallow = &(*i); allow_204 = pallow->contains("204"); allow_206 = pallow->contains("206"); } else if ((pfrom == NULL) && i->startsWithLower("from:")) { pfrom = &(*i); } else if ((phost == NULL) && i->startsWithLower("host:")) { phost = &(*i); } else if ((ppreview == NULL) && i->startsWithLower("preview:")) { ppreview = &(*i); allow_204 = true; } else if ((pkeepalive == NULL) && i->startsWithLower("keep-alive:")) { pkeepalive = &(*i); } else if (i->startsWithLower("encapsulated:")) { pencapsulated = &(*i); setEncapRecs(); //i->assign("X-E2G-IgnoreMe: encapuslated always regenerated\r"); } else if (i->startsWithLower("x-client-ip:")) { pclientip = &(*i); String t = pclientip->after(": "); t.chop(); setClientIP(t); } else if (i->startsWithLower("x-client-username:")) { pclientuser = &(*i); username = pclientuser->after(": "); username.chop(); } else if (i->startsWithLower("x-icap-e2g:")) { String t = *i; t.chop(); // remove '\r' t = t.after(":"); icap_com.user = t.before(","); t = t.after(","); icap_com.EBG = t.before(","); t = t.after(","); icap_com.filtergroup = t.before(",").toInteger(); t = t.after(","); icap_com.mess_no = t.before(",").toInteger(); t = t.after(","); icap_com.log_mess_no = t.before(",").toInteger(); icap_com.mess_string = t.after(","); } String t2 = *i; DEBUG_icap("Header value from ICAP client: ", t2, "allow_204 is ", allow_204, " allow_206 is ", allow_206 ); } } } #ifdef NOTDEF String ICAPHeader::getUrl() { // Version of URL *with* port is not cached, // as vast majority of our code doesn't like // port numbers in URLs. port = 80; bool https = false; String hostname; String userpassword; String answer(header.front().after(" ")); answer.removeMultiChar(' '); if (answer.after(" ").startsWith("ICAP/")) { answer = answer.before(" ICAP/"); } else { answer = answer.before(" icap/"); // just in case! } if (answer.length()) { if (answer[0] == '/') { // must be the latter above if (phost != NULL) { hostname = phost->after(" "); hostname.removeWhiteSpace(); if (hostname.contains(":")) { port = hostname.after(":").toInteger(); hostname = hostname.before(":"); } while (hostname.endsWith(".")) hostname.chop(); hostname = "icap://" + hostname; answer = hostname + answer; } header.front() = requestType() + " " + answer + " ICAP/" + header.front().after(" ICAP/"); } else { // must be in the form GET http://foo.bar:80/ HTML/1.0 if (!answer.after("://").contains("/")) { answer += "/"; // needed later on so correct host is extracted } String protocol(answer.before("://")); hostname = answer.after("://"); String url(hostname.after("/")); url.removeWhiteSpace(); // remove rubbish like ^M and blanks if (hostname.endsWith(".")) { hostname.chop(); } if (url.length() > 0) { url = "/" + url; } hostname = hostname.before("/"); // extra / was added 4 here if (hostname.contains("@")) { // Contains a username:password combo userpassword = hostname.before("@"); hostname = hostname.after("@"); } if (hostname.contains(":")) { port = hostname.after(":").toInteger(); if (port == 0 || port > 65535) { port = (https ? 443 : 80); } hostname = hostname.before(":"); // chop off the port bit } while (hostname.endsWith(".")) hostname.chop(); if (userpassword.length()) answer = protocol + "://" + userpassword + "@" + hostname + url; else answer = protocol + "://" + hostname + url; } } DEBUG_icap("from header url:", answer); return answer; } String ICAPHeader::url() { return getUrl(); } #endif // * // * // * URL and Base64 decoding funcs // * // * // turn %xx back into original character String ICAPHeader::hexToChar(const String &n, bool all) { if (n.length() < 2) { return String(n); } static char buf[2]; unsigned int a, b; unsigned char c; a = n[0]; b = n[1]; if (a >= 'a' && a <= 'f') { a -= 87; } else if (a >= 'A' && a <= 'F') { a -= 55; } else if (a >= '0' && a <= '9') { a -= 48; } else { return String("%") + n; } if (b >= 'a' && b <= 'f') { b -= 87; } else if (b >= 'A' && b <= 'F') { b -= 55; } else if (b >= '0' && b <= '9') { b -= 48; } else { return String("%") + n; } c = a * 16 + b; if (all || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '-')) { buf[0] = c; buf[1] = '\0'; return String(buf); } else { return String("%") + n; } } // * // * // * network send/receive funcs // * // * // send headers out over the given socket bool ICAPHeader::respond(Socket &sock, String res_code, bool echo, bool encap) { DEBUG_icap("ICAP response starting - RCode ", res_code, " echo is ", echo); String l; // for amalgamating to avoid conflict with the Nagel algorithm if(echo) { if (service_reqmod && !(out_res_hdr_flag || out_req_hdr_flag)) { out_req_header = HTTPrequest.stringHeader(); DEBUG_icap("out_req_header copied from HTTPrequest :", out_req_header); out_req_hdr_flag = true; out_req_body_flag = req_body_flag; if (req_body > 0) size_req_body = req_body; // TODO Check this! } if (service_resmod && !(out_res_hdr_flag)) { out_res_header = HTTPresponse.stringHeader(); DEBUG_icap("out_res_header is ", out_res_header); out_res_hdr_flag = true; out_res_body_flag = res_body_flag; if (res_body > 0) { size_res_body = res_body;// TODO Check this! } } } l = "ICAP/1.0 " + res_code + "\r\n"; l += "ISTag:"; l += ISTag; l += "\r\n"; // add ICAP communication header if (service_reqmod && icap_com.filtergroup > -1) { l += "X-ICAP-E2G:"; l += icap_com.user; l += ","; l += icap_com.EBG; l += ","; l += std::to_string(icap_com.filtergroup); l += ","; l += std::to_string(icap_com.mess_no); l += ","; l += std::to_string(icap_com.log_mess_no); l += ","; l += icap_com.mess_string; l += "\r\n"; } if (encap) { // add Encapsulated header logic int offset = 0; String soffset(offset); String sep = " "; l += "Encapsulated:"; if (out_req_hdr_flag && (out_req_header.size() > 0)) { l += sep; sep = ", "; l += "req-hdr="; l += soffset; offset += out_req_header.size(); soffset = offset; } if (out_res_hdr_flag && out_res_header.size() > 0) { l += sep; sep = ", "; l += "res-hdr="; l += soffset; offset += out_res_header.size(); soffset = offset; } if (out_req_body_flag) { l += sep; l += "req-body="; l += soffset; } else if (out_res_body_flag) { l += sep; l += "res-body="; l += soffset; } else { l += sep; l += "null-body="; l += soffset; } l += "\r\n"; } l += "\r\n"; if(encap) { // send headers to the output stream // need exception for bad write if (out_req_hdr_flag) { String temp = out_req_header.toCharArray(); l += temp; } if (out_res_hdr_flag) { String temp = out_res_header.toCharArray(); l += temp; } } DEBUG_icap("Icap response header is: ", l); if (!sock.writeToSocket(l.toCharArray(), l.length(), 0, timeout)) { return false; } DEBUG_icap("Returning from icapheader:respond"); return true; } bool ICAPHeader::errorResponse(Socket &peerconn, String &res_header, String &res_body) { // set IAP outs and then output ICAP header and res_header/body out_res_header = res_header; out_res_hdr_flag = true; out_req_body_flag = false; if (res_body.length() > 0) { out_res_body = res_body; out_res_body_flag = true; } DEBUG_icap("out_res_header: ",out_res_header); DEBUG_icap("out_res_body: ", out_res_body); if (!respond(peerconn)) return false; if(out_res_body_flag) { if (!peerconn.writeChunk((char *) res_body.toCharArray(), res_body.length(), timeout)) return false; String n; if (!peerconn.writeChunk((char*) n.toCharArray(),0, timeout)) return false; } return true; } void ICAPHeader::setClientIP(String &ip) { clientip = ip; HTTPrequest.setClientIP(ip); } bool ICAPHeader::in(Socket *sock, bool allowpersistent) { if (dirty) reset(); dirty = true; #ifdef DEBUG_HIGH if(is_response) { DEBUG_icap("Start of response ICAPheader:in"); } else { DEBUG_icap("Start of request ICAPheader:in"); } #endif // the RFCs don't specify a max header line length so this should be // dynamic really. Pointed out (well reminded actually) by Daniel Robbins char buff[32768]; // setup a buffer to hold the incomming ICAP line String line; // temp store to hold the line after processing line = "----"; // so we get past the first while bool firsttime = true; bool discard = false; while (line.length() > 3 || discard) { // loop until the stream is // failed or we get to the end of the header (a line by itself) // get a line of header from the stream bool truncated = false; int rc; if (firsttime) { DEBUG_icap("ICAPheader:in before getLine - timeout: ", timeout); rc = sock->getLine(buff, 32768, timeout, NULL, &truncated); DEBUG_icap("firstime: ICAPheader:in after getLine "); if (rc == 0) return false; if (rc < 0 || truncated) { ispersistent = false; DEBUG_icap("firstime: ICAPheader:in after getLine: rc: ", rc, " truncated: ", truncated ); return false; } } else { rc = sock->getLine(buff, 32768, timeout, NULL, &truncated); // timeout reduced to 100ms for lines after first if (rc == 0) return false; if (rc < 0 || truncated) { ispersistent = false; DEBUG_icap("not firstime: ICAPheader:in after getLine: rc: ", rc, " truncated: ", truncated ); return false; // do not allow non-terminated headers } } if (header.size() > o.header.max_header_lines) { DEBUG_icap("ICAP header:size too big = ", header.size() ); E2LOGGER_info(" header:size too big: ", header.size(), ", see maxheaderlines"); ispersistent = false; return false; } // throw std::exception(); // getline will throw an exception if there is an error which will // only be caught by HandleConnection() ????????????????????? if (rc > 0) line = buff; else line = "";// convert the line to a String if (firsttime) { // check first line header if (is_response) { if (!(line.length() > 11 && line.startsWith("ICAP/") && (line.after(" ").before(" ").toInteger() > 99))) { if (o.conn.logconerror) { E2LOGGER_error("Server did not respond with ICAP"); } DEBUG_icap("Returning from header:in Server did not respond with ICAP length: ", line.length(), " content: ", line ); return false; } } else { method = line.before(" "); DEBUG_icap("Returning from header:in client requests with ICAP length: ", line.length(), " content: ", line); String t = line.after(" ").before(" "); DEBUG_icap("Request is ", t, " size: ", line.length(), " content: ", line); if (t.startsWith("icap://")) { // valid protocol } else { icap_error = "400 Bad Request"; DEBUG_icap("Request error is: ", icap_error, " Line: ", t); return false; } t = t.after("//").after("/"); if (t == o.icap.icap_reqmod_url) { icap_reqmod_service = true; } else if (t == o.icap.icap_resmod_url) { icap_resmod_service = true; } else { icap_error = "404 ICAP Service not found"; } if (method == "OPTIONS") { service_options = true; } else if (icap_reqmod_service && method == "REQMOD") { service_reqmod = true; } else if (icap_resmod_service && method == "RESPMOD") { service_resmod = true; } else { icap_error = "405 Method not allowed for service"; } DEBUG_icap("Request method is: ", method, " error?: ", icap_error, " url value: ", t); } } // ignore crap left in buffer from old pconns (in particular, the IE "extra CRLF after POST" bug) discard = false; if (not(firsttime && line.length() <= 3)) { header.push_back(line); // stick the line in the deque that holds the header } else { discard = true; DEBUG_icap("Discarding unwanted bytes at head of request (pconn closed or IE multipart POST bug)"); } firsttime = false; // End of while } if (header.size() == 0) { DEBUG_icap("ICAP header:size = 0 "); return false; } header.pop_back(); // remove the final blank line of a header checkheader(allowpersistent); // sort out a few bits in the header DEBUG_icap("checkheader done- ", encap_recs.size(), " encap_recs"); //now need to get http req and res headers - if present HTTPrequest.reset(); HTTPresponse.reset(); if(encap_recs.size() > 0) { for (std::deque::iterator i = encap_recs.begin(); i < encap_recs.end(); i++) { if (i->name == "req-hdr") { req_hdr_flag = HTTPrequest.in(sock); if (!req_hdr_flag) return false; req_hdr = i->value; } else if (i->name == "res-hdr") { res_hdr_flag = HTTPresponse.in(sock); if (!res_hdr_flag) return false; res_hdr = i->value; } else if (i->name == "req-body") { req_body_flag = true; req_body = i->value; } else if (i->name == "res-body") { res_body_flag = true; res_body = i->value; } else if (i->name == "null-body") { null_body_flag = true; null_body = i->value; } else if (i->name == "opt-body") { // may not need this as only sent in respone opt_body_flag = true; opt_body = i->value; } // add further checking in here for REQMOD, RESPMOD and OPTIONS } } return true; } void ICAPHeader::set_icap_com(std::string &user, String EBG, int &filtergroup, int &mess_no, int &log_mess_no, std::string &mess) { icap_com.user = user; icap_com.EBG = EBG; icap_com.filtergroup = filtergroup; icap_com.mess_no = mess_no; icap_com.log_mess_no = log_mess_no; icap_com.mess_string = mess; } e2guardian-5.5.8r/src/ICAPHeader.hpp000066400000000000000000000104121477372360500170750ustar00rootroot00000000000000 // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_ICAPHeader #define __HPP_ICAPHeader // INCLUDES #include #include "String.hpp" #include "Socket.hpp" #include "RegExp.hpp" #include "ListMeta.hpp" #include "FOptionContainer.hpp" #include "HTTPHeader.hpp" // DECLARATIONS class ICAPHeader { public: std::deque header; unsigned int port = 0; bool is_response = false; HTTPHeader HTTPrequest; HTTPHeader HTTPresponse; String icap_error; bool service_options = false; bool service_reqmod = false; bool service_resmod = false; bool icap_reqmod_service = false; bool icap_resmod_service = false; bool req_hdr_flag = false; bool res_hdr_flag = false; bool req_body_flag = false; bool res_body_flag = false; bool opt_body_flag = false; bool null_body_flag = false; bool out_req_hdr_flag = false; bool out_res_hdr_flag = false; String out_req_header; String out_res_header; bool out_req_body_flag = false; bool out_res_body_flag = false; String out_req_body; String out_res_body; int size_req_body; int size_res_body; String ISTag; String username; String clientip; int req_hdr = 0; int res_hdr = 0; int req_body = 0; int res_body = 0; int opt_body = 0; int null_body = 0; bool allow_204 = false; bool allow_206 = false; struct encap_rec { String name; int value = 0; }; std::deque encap_recs; struct icap_com_rec { String user; String EBG; int filtergroup = 0; int mess_no = 0; int log_mess_no = 0; String mess_string; }; icap_com_rec icap_com; void set_icap_com (std::string &user, String EBG, int &filtergroup, int &mess_no, int &log_mess_no, std::string &mess_string); // reset header object for future use void reset(); // network communication funcs void setTimeout(int t); bool in(Socket *sock, bool allowpersistent = false ); void setClientIP(String &ip); bool setEncapRecs(); // respond with ICAP and HTTP headers and if given body bool respond(Socket &peersock, String rescode = "200 OK", bool echo = false, bool encap = true); bool errorResponse(Socket &peersock, String &reshdr, String & resbody); // discard remainder of POST data // amount to discard can be passed in, or will default to contentLength() void discard(Socket *sock, off_t cl = -2); // header value and type checks // request type: GET, HEAD, POST etc. String requestType(); int returnCode(); // get content length - returns -1 if undetermined String getContentType(); String userAgent(); String url(); String getUrl(); String redirecturl(); // header modifications void removeEncoding(int newlen); void setURL(String &url); // do URL decoding (%xx) on string // decode everything, or just numbers, letters and - static String decode(const String &s, bool decodeAll = false); ICAPHeader() { reset(); }; ICAPHeader(int type) { reset(); setType(type); }; ~ICAPHeader() { }; void setType(int type) { if (type == __HEADER_RESPONSE) is_response = true; else is_response = false; }; private: // timeout for socket operations int timeout = 120000; // header index pointers //general String *pproxyconnection; String *pencapsulated; //requests String *pauthorization; String *pallow; String *pfrom; String *phost; String *preferer; String *puseragent; String *ppreview; String *pxforwardedfor; String *pproxyauthorization; String *pproxyauthenticate; String *pcontentdisposition; String *pkeepalive; String *pupgrade; String *pclientip; String *pclientuser; String method; bool ispersistent, waspersistent; bool dirty = true; // check & fix headers from servers that don't obey standards void checkheader(bool allowpersistent); // convert %xx back to original character static String hexToChar(const String &n, bool all = false); }; #endif e2guardian-5.5.8r/src/IPList.cpp000066400000000000000000000220221477372360500164070ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include #include "OptionContainer.hpp" #include "FOptionContainer.hpp" #include "Logger.hpp" #include #include #include #include #include #include #include #include #include // GLOBALS extern OptionContainer o; // INPLEMENTATION // clear out the list void IPList::reset() { iplist.clear(); iprangelist.clear(); // ipsubnetlist.clear(); hostlist.clear(); } // search for IP in list of individual IPs, ranges, subnets and - if reverse lookups are enabled - hostnames. bool IPList::inList(const std::string &ipstr, std::string *&host) const { struct in_addr addr; inet_aton(ipstr.c_str(), &addr); uint32_t ip = ntohl(addr.s_addr); // start with individual IPs if (std::binary_search(iplist.begin(), iplist.end(), ip)) { // only return a hostname if that's what we matched against delete host; host = NULL; return true; } // ranges //for (std::vector::const_iterator i = iprangelist.begin(); i != iprangelist.end(); ++i) { //if ((ip >= i->startaddr) && (ip <= i->endaddr)) { //delete host; //host = NULL; //return true; //} //} if (!iprangelist.empty()) { ipl_rangestruct t; t.startaddr = ip; auto one_above = std::upper_bound(iprangelist.begin(), iprangelist.end(),t); if (one_above != iprangelist.begin()) { auto i = one_above; i--; // move pointer to record which is the highest value that is less or equal to ip. if ((ip >= i->startaddr) && (ip <= i->endaddr)) { delete host; host = NULL; return true; } } } // subnets //for (std::list::const_iterator i = ipsubnetlist.begin(); i != ipsubnetlist.end(); ++i) { //if (i->maskedaddr == (ip & i->mask)) { //delete host; //host = NULL; //return true; //} //} // hostnames // TODO - take in a suggested hostname, look up only if not supplied, and return suggestion if found if (o.conn.reverse_client_ip_lookups) { std::unique_ptr > hostnames; if (host == NULL) hostnames.reset(ipToHostname(ipstr.c_str())); else { hostnames.reset(new std::deque); hostnames->push_back(*host); } for (std::deque::iterator i = hostnames->begin(); i != hostnames->end(); ++i) { if (std::binary_search(hostlist.begin(), hostlist.end(), *i)) { delete host; host = new std::string(i->toCharArray()); return true; } } // Even if we don't match anything, return a hostname // if desired for logging and we don't already have one. if (o.log.log_client_hostnames && (host == NULL) && (hostnames->size() > 0)) host = new std::string(hostnames->front().toCharArray()); } return false; } bool IPList::ifsreadIPMelangeList(std::ifstream *input, bool checkendstring, const char *endstring) { // compile regexps for determining whether a list entry is an IP, a subnet (IP + mask), or a range RegExp matchIP, matchSubnet, matchRange, matchCIDR; #ifdef HAVE_PCRE matchIP.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$"); matchSubnet.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$"); matchSubnet.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$"); matchCIDR.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,2}$"); matchRange.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}-\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$"); #else matchIP.comp("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"); matchSubnet.comp("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"); matchCIDR.comp("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2}$"); matchRange.comp("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}-[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"); #endif RegResult Rre; // read in the file String line; char buffer[2048]; while (input) { if (!input->getline(buffer, sizeof(buffer))) { break; } line = buffer; if (checkendstring && line.startsWith(endstring)) { break; } // ignore comments if (buffer[0] == '#') continue; // ignore blank lines if (strlen(buffer) < 7) continue; DEBUG_trace(""); // store the IP address (numerically, not as a string) and filter group in either the IP list, subnet list or range list if (matchIP.match(line.toCharArray(),Rre)) { struct in_addr address; if (inet_aton(line.toCharArray(), &address)) { uint32_t addr = ntohl(address.s_addr); iplist.push_back(addr); } } else if (matchSubnet.match(line.toCharArray(),Rre)) { struct in_addr address; struct in_addr addressmask; String subnet(line.before("/")); String mask(line.after("/")); if (inet_aton(subnet.toCharArray(), &address) && inet_aton(mask.toCharArray(), &addressmask)) { ipl_rangestruct s; uint32_t addr = ntohl(address.s_addr); uint32_t imask = ntohl(addressmask.s_addr); s.startaddr = addr; s.endaddr = addr | ~imask; iprangelist.push_back(s); } } else if (matchCIDR.match(line.toCharArray(),Rre)) { struct in_addr address; struct in_addr addressmask; String subnet(line.before("/")); String cidr(line.after("/")); int m = cidr.toInteger(); int host_part = 32 - m; if (host_part > -1) { String mask = (0xFFFFFFFF << host_part); if (inet_aton(subnet.toCharArray(), &address) && inet_aton(mask.toCharArray(), &addressmask)) { ipl_rangestruct s; uint32_t addr = ntohl(address.s_addr); uint32_t imask = ntohl(addressmask.s_addr); s.startaddr = addr; s.endaddr = addr | ~imask; iprangelist.push_back(s); } } } else if (matchRange.match(line.toCharArray(),Rre)) { struct in_addr addressstart; struct in_addr addressend; String start(line.before("-")); String end(line.after("-")); if (inet_aton(start.toCharArray(), &addressstart) && inet_aton(end.toCharArray(), &addressend)) { ipl_rangestruct r; r.startaddr = ntohl(addressstart.s_addr); r.endaddr = ntohl(addressend.s_addr); iprangelist.push_back(r); } } // hmmm. the line didn't match any of our regular expressions. // assume it's a hostname. else { line.toLower(); hostlist.push_back(line); } } DEBUG_trace("starting sort"); std::sort(iplist.begin(), iplist.end()); std::sort(iprangelist.begin(), iprangelist.end()); std::sort(hostlist.begin(), hostlist.end()); DEBUG_trace("sort complete"); #ifdef DEBUG_LOW DEBUG_debug("ip list dump:"); std::vector::iterator i = iplist.begin(); while (i < iplist.end()) { DEBUG_debug("IP: ", String(*i)); ++i; } //DEBUG_debug("subnet list dump:"); //std::list::iterator j = ipsubnetlist.begin(); //while (j != ipsubnetlist.end()) { // DEBUG_debug("Masked IP: ", String(j->maskedaddr), " Mask: ", String(j->mask)); // ++j; // } DEBUG_debug("range list dump:"); std::vector::iterator k = iprangelist.begin(); while (k < iprangelist.end()) { DEBUG_debug("Start IP: ", String(k->startaddr), " End IP: ", String(k->endaddr)); ++k; } DEBUG_debug("host list dump:"); std::vector::iterator l = hostlist.begin(); while (l < hostlist.end()) { DEBUG_debug("Hostname: ", *l ); ++l; } #endif return true; } // read in a list linking IPs, subnets & IP ranges to filter groups bool IPList::readIPMelangeList(const char *filename) { // load in the list file std::ifstream input(filename); if (!input) { E2LOGGER_error("Error reading file (does it exist?): ", filename); return false; } DEBUG_debug("reading: ", filename); if (ifsreadIPMelangeList(&input, false, NULL)) { input.close(); return true; } input.close(); return false; } e2guardian-5.5.8r/src/IPList.hpp000066400000000000000000000021261477372360500164170ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_IPLIST #define __HPP_IPLIST // INCLUDES #include #include // DECLARATIONS // convenience structs for subnets and IP ranges struct ipl_subnetstruct { uint32_t maskedaddr; uint32_t mask; }; struct ipl_rangestruct { public: uint32_t startaddr; uint32_t endaddr; int operator<(const ipl_rangestruct &a) const { if (startaddr < a.startaddr) return 1; return 0; } }; // IP subnet/range/mask & hostname list class IPList { public: void reset(); bool inList(const std::string &ipstr, std::string *&host) const; bool ifsreadIPMelangeList(std::ifstream *input, bool checkendstring, const char *endstring); bool readIPMelangeList(const char *filename); private: std::vector iplist; std::vector hostlist; std::vector iprangelist; //std::list ipsubnetlist; }; #endif e2guardian-5.5.8r/src/ImageContainer.cpp000066400000000000000000000047161477372360500201420ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "ImageContainer.hpp" #include "Logger.hpp" #include #include #include #include #include #include // GLOBALS // IMPLEMENTATION ImageContainer::ImageContainer() { image = NULL; imagelength = 0; } ImageContainer::~ImageContainer() { delete[] image; } // wipe the loaded image void ImageContainer::reset() { delete[] image; image = NULL; mimetype = ""; imagelength = 0; } // send image to client //void ImageContainer::display(Socket *s) bool ImageContainer::display(Socket *s) { DEBUG_debug("Displaying custom image file mimetype: ", mimetype); s->writeString("Content-type: "); s->writeString(mimetype.toCharArray()); s->writeString("\n\n"); if (!s->writeToSocket(image, imagelength, 0, s->getTimeout())) // throw std::runtime_error(std::string("Can't write to socket: ") + strerror(errno)); return false; return true; } bool ImageContainer::display_hb(String &eheader, String &ebody) { eheader += "Content-type: " ; eheader += mimetype.toCharArray(); eheader += "\n\n"; ebody = image; return true; } // read image from file bool ImageContainer::read(const char *filename) { String temp(filename); temp.toLower(); if (temp.endsWith(".jpg") || temp.endsWith(".jpeg") || temp.endsWith(".jpe")) { mimetype = "image/jpg"; } else if (temp.endsWith("png")) mimetype = "image/png"; else if (temp.endsWith("swf")) mimetype = "application/x-shockwave-flash"; else { mimetype = "image/gif"; } std::ifstream imagefile; imagefile.open(filename, std::ifstream::binary); imagefile.seekg(0, std::ios::end); imagelength = imagefile.tellg(); imagefile.seekg(0, std::ios::beg); if (imagelength) { if (image != NULL) delete[] image; image = new char[imagelength + 1]; imagefile.read(image, imagelength); if (!imagefile.good()) { E2LOGGER_error("Error reading custom image file: ", filename); return false; } } else { E2LOGGER_error("Error reading custom image file: ", filename); return false; } imagefile.close(); return true; } e2guardian-5.5.8r/src/ImageContainer.hpp000066400000000000000000000013321477372360500201360ustar00rootroot00000000000000// ImageContainer - container class for custom banned image // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_IMAGECONTAINER #define __HPP_IMAGECONTAINER // INCLUDES #include "Socket.hpp" #include "String.hpp" class ImageContainer { public: ImageContainer(); ~ImageContainer(); // wipe loaded image void reset(); // read image from file bool read(const char *filename); // send image to client bool display(Socket *s); bool display_hb(String &eheader, String &ebody); private: long int imagelength; String mimetype; char *image; }; #endif e2guardian-5.5.8r/src/LOptionContainer.cpp000066400000000000000000000535471477372360500205120ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "LOptionContainer.hpp" #include "OptionContainer.hpp" #include "RegExp.hpp" #include "ConfigVar.hpp" #include "Logger.hpp" //#include //#include #include #include #include #include // GLOBALS extern OptionContainer o; // IMPLEMENTATION LOptionContainer::LOptionContainer() { } LOptionContainer::LOptionContainer(int load_id) { DEBUG_trace(load_id); char buff[40]; sprintf(buff, "%ld", time(NULL)); start_time = buff; loaded_ok = true; { DEBUG_config("iplist size is ", o.lists.iplist_dq.size()); if(!LMeta.load_type(LIST_TYPE_IP, o.lists.iplist_dq)) loaded_ok = false; } { DEBUG_config("sitelist size is ", o.lists.sitelist_dq.size()); if(!LMeta.load_type(LIST_TYPE_SITE, o.lists.sitelist_dq)) loaded_ok = false; } { DEBUG_config("ipsitelist size is ", o.lists.ipsitelist_dq.size()); if(!LMeta.load_type(LIST_TYPE_IPSITE, o.lists.ipsitelist_dq)) loaded_ok = false; } { DEBUG_config("urllist size is ", o.lists.urllist_dq.size()); if(!LMeta.load_type(LIST_TYPE_URL, o.lists.urllist_dq)) loaded_ok = false; } { DEBUG_config("regexpboollist size is ", o.lists.regexpboollist_dq.size());; if(!LMeta.load_type(LIST_TYPE_REGEXP_BOOL, o.lists.regexpboollist_dq)) loaded_ok = false; } { DEBUG_config("maplist size is ", o.lists.maplist_dq.size()); if(!LMeta.load_type(LIST_TYPE_MAP, o.lists.maplist_dq)) loaded_ok = false; } { DEBUG_config("ipmaplist size is ", o.lists.ipmaplist_dq.size()); if(!LMeta.load_type(LIST_TYPE_IPMAP, o.lists.ipmaplist_dq)) loaded_ok = false; } DEBUG_config("read Storyboard: ", o.story.storyboard_location); if (!StoryA.readFile(o.story.storyboard_location.c_str(), LMeta, true)) { E2LOGGER_error("Storyboard not loaded OK"); sb_loaded_ok = false; is_fatal = true; } if (sb_loaded_ok && !StoryA.setEntry(ENT_STORYA_PRE_AUTH,"pre-authcheck")) { E2LOGGER_error("Required storyboard entry function 'pre-authcheck' is missing"); sb_loaded_ok = false; is_fatal = true; } if (sb_loaded_ok && (o.net.transparenthttps_port > 0) && !StoryA.setEntry(ENT_STORYA_PRE_AUTH_THTTPS,"thttps-pre-authcheck")) { E2LOGGER_error("Required storyboard entry function 'thttps-pre-authcheck' is missing"); sb_loaded_ok = false; is_fatal = true; } if (sb_loaded_ok && (o.net.icap_port > 0) && !StoryA.setEntry(ENT_STORYA_PRE_AUTH_ICAP,"icap-pre-authcheck")) { E2LOGGER_error("Required storyboard entry function 'icap-pre-authcheck' is missing"); sb_loaded_ok = false; is_fatal = true; } // if (loaded_ok && o.use_filter_groups_list) { // if (!doReadItemList(o.filter_groups_list_location.c_str(), &filter_groups_list, "filtergroupslist", true)) { // E2LOGGER_error("Failed to read filtergroupslist"); // loaded_ok = false; // } // } DEBUG_trace(""); if (sb_loaded_ok && o.story.auth_entry_dq.size() > 0) { for (std::deque::const_iterator i = o.story.auth_entry_dq.begin(); i != o.story.auth_entry_dq.end(); ++i) { if (!StoryA.setEntry(i->entry_id, i->entry_function)) { E2LOGGER_error("Required auth storyboard entry function", i->entry_function, " is missing from pre_auth.story"); loaded_ok = false; is_fatal = true; } } } if(sb_loaded_ok && (!readFilterGroupConf() || (o.lists.abort_on_missing_list && o.config_error))) { loaded_ok = false; E2LOGGER_error("Error in reading filter group files"); } reload_id = load_id; ++o.LC_cnt; if (load_id == 0) o.filter.numfg = numfg; // do this on first load only } bool LOptionContainer::inSiteList(String &url, ListContainer *lc, bool ip, bool ssl, String &match) { String lastcategory; url.removeWhiteSpace(); // just in case of weird browser crap url.toLower(); url.removePTP(); // chop off the ht(f)tp(s):// if (url.contains("/")) { url = url.before("/"); // chop off any path after the domain } //const char *i; bool i; String result; //bool isipurl = isIPHostname(url); while (url.contains(".")) { i = lc->findInList(url.toCharArray(), lastcategory, match, result); if (i ) { return i; // exact match } url = url.after("."); // check for being in higher level domains } if (url.length() > 1) { // allows matching of .tld url = "." + url; i = lc->findInList(url.toCharArray(), lastcategory, match, result); if (i ) { return i; // exact match } } return false; // and our survey said "UUHH UURRGHH" } // look in given URL list for given URL bool LOptionContainer::inURLList(String &url, ListContainer *lc, bool ip, bool ssl, String &match) { unsigned int fl; //char *i; bool i = false; String lastcategory; String foundurl; DEBUG_trace("inURLList"); 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 } DEBUG_debug("inURLList (processed): ", url); while (url.before("/").contains(".")) { i = lc->findStartsWith(url.toCharArray(), lastcategory, match); if (i) { foundurl = match; fl = foundurl.length(); DEBUG_debug("foundurl: ", foundurl, ":", foundurl.length()); DEBUG_debug("url: ", url, ":", fl); if (url.length() > fl) { if (url[fl] == '/' || url[fl] == '?' || url[fl] == '&' || url[fl] == '=') { 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; } // TODO: Filter rules should migrate to FOptionContainer.cpp ? -- No, these are not filtergroup rules but nmaybe to their own cpp?? bool LOptionContainer::inRoom(const std::string &ip, std::string &room, std::string *host, bool *block, bool *part_block, bool *isexception, String url) { String temp; String match; for (std::list::const_iterator i = rooms.begin(); i != rooms.end(); ++i) { if (i->iplist->inList(ip, host)) { DEBUG_debug(" IP is in room: ", i->name); temp = url; ListContainer *lc; if (i->sitelist) { lc = i->sitelist; if (inSiteList(temp, lc, false, false, match)) { DEBUG_debug(" room site exception found: "); *isexception = true; room = i->name; return true; } } temp = url; if (i->urllist && inURLList(temp, i->urllist, false, false, match)) { DEBUG_debug(" room url exception found: "); *isexception = true; room = i->name; return true; } if (i->block) { *block = true; *part_block = i->part_block; room = i->name; DEBUG_debug(" room blanket block active: "); return true; } else { DEBUG_debug(" room - no url/site exception or block found: "); return false; } } } return false; } LOptionContainer::~LOptionContainer() { reset(); } void LOptionContainer::reset() { deleteFilterGroups(); deleteRooms(); conffile.clear(); --o.LC_cnt; } void LOptionContainer::deleteFilterGroups() { for (int i = 0; i < numfg; i++) { if (fg[i] != NULL) { DEBUG_debug("In deleteFilterGroups loop"); delete fg[i]; // delete extra FOptionContainer objects fg[i] = NULL; } } if (numfg > 0) { delete[] fg; numfg = 0; } } void LOptionContainer::deleteFilterGroupsJustListData() { for (int i = 0; i < numfg; i++) { if (fg[i] != NULL) { fg[i]->resetJustListData(); } } } #ifdef NOTDEF bool LOptionContainer::read(std::string& filename, int type, std::string& exception_ip_list_location, std::string& banned_ip_list_location) { E2LOGGER_TRACE(filename); conffilename = filename; // all sorts of exceptions could occur reading conf files try { std::string linebuffer; String temp; // for tempory conversion and storage std::ifstream conffiles(filename.c_str(), std::ios::in); // e2guardian.conf if (!conffiles.good()) { E2LOGGER_error("error reading e2guardian.conf"); return false; } while (!conffiles.eof()) { getline(conffiles, linebuffer); if (!conffiles.eof() && linebuffer.length() != 0) { if (linebuffer[0] != '#') { // i.e. not commented out temp = (char *) linebuffer.c_str(); if (temp.contains("#")) { temp = temp.before("#"); } temp.removeWhiteSpace(); // get rid of spaces at end of line linebuffer = temp.toCharArray(); conffile.push_back(linebuffer); // stick option in deque } } } conffiles.close(); if (type == 0 || type == 2) { if (type == 0) { return true; } } if (((per_room_directory_location = findoptionS("perroomdirectory")) != "") || ((per_room_directory_location = findoptionS("perroomblockingdirectory")) != "")) { loadRooms(true); } // filter_groups_list_location = findoptionS("filtergroupslist"); // std::string banned_ip_list_location(findoptionS("bannediplist")); // std::string exception_ip_list_location(findoptionS("exceptioniplist")); // group_names_list_location = findoptionS("groupnamesfile"); // std::string language_list_location(languagepath + "messages"); { std::deque dq = findoptionM("iplist"); DEBUG_debug("iplist deque is size ", dq.size()); LMeta.load_type(LIST_TYPE_IP, dq); } { std::deque dq = findoptionM("sitelist"); DEBUG_debug("sitelist deque is size ", dq.size()); LMeta.load_type(LIST_TYPE_SITE, dq); } { std::deque dq = findoptionM("ipsitelist"); DEBUG_debug("ipsitelist deque is size ", dq.size()); LMeta.load_type(LIST_TYPE_IPSITE, dq); } { std::deque dq = findoptionM("urllist"); DEBUG_debug("urllist deque is size ", dq.size()); LMeta.load_type(LIST_TYPE_URL, dq); } if (!StoryA.readFile(o.storyboard_location.c_str(), LMeta, true)) return false; if (!StoryA.setEntry1("pre-authcheck")) { E2LOGGER_error("Required storyboard entry function 'pre-authcheck' is missing"); return false; } if (!readFilterGroupConf()) { E2LOGGER_error("Error reading filter group conf file(s)."); return false; } } catch (std::exception &e) { E2LOGGER_error(e.what()); return false; } return true; } #endif // TODO: Filter rules should migrate to FOptionContainer.cpp ? -- No, these are not filtergroup rules but nmaybe to their own cpp?? void LOptionContainer::loadRooms(bool throw_error) { if (!throw_error && (per_room_directory_location == "")) return; DIR *d = opendir(per_room_directory_location.c_str()); if (d == NULL) { if (throw_error) { E2LOGGER_error("Could not open room definitions directory: ", strerror(errno)); exit(1); } else { return; } } struct dirent *f; while ((f = readdir(d))) { if (f->d_name[0] == '.') continue; std::string filename(per_room_directory_location); filename.append(f->d_name); DEBUG_debug("Room file found : ", filename); std::ifstream infile(filename.c_str(), std::ios::in); if (!infile.good()) { E2LOGGER_error(" Could not open file room definitions "); exit(1); } DEBUG_debug("Opened room file : ", filename); std::string roomname; DEBUG_debug(" Reading room file : ", filename); getline(infile, roomname); if (infile.eof()) { E2LOGGER_error(" Unexpected EOF ", filename); exit(1); } if (infile.fail()) { E2LOGGER_error(" Unexpected failure on read: ", filename);; exit(1); } if (infile.bad()) { E2LOGGER_error(" Unexpected badbit failure on read: ", filename); exit(1); } if (!infile.good()) { E2LOGGER_error(" Could not open file room definitions: ", filename); exit(1); } DEBUG_debug(" Room name is: ", roomname); roomname = roomname.substr(1); room_item this_room; this_room.name = roomname; this_room.block = false; this_room.part_block = false; this_room.sitelist = NULL; this_room.urllist = NULL; IPList *contents = new IPList(); contents->ifsreadIPMelangeList(&infile, true, "#ENDLIST"); this_room.iplist = contents; if (infile.eof()) { // is old style room block this_room.block = true; this_room.sitelist = NULL; this_room.urllist = NULL; } else { std::string linestr; String temp; while (infile.good()) { std::getline(infile, linestr); if (infile.eof()) break; temp = linestr; if (temp.startsWith("#SITELIST")) { ListContainer *sitelist = new ListContainer(); if (sitelist->ifsReadSortItemList(&infile, "", "", true, "#ENDLIST", false, false, 0, filename.c_str())) { this_room.sitelist = sitelist; } else { delete sitelist; } } else if (temp.startsWith("#URLLIST")) { ListContainer *urllist = new ListContainer(); if (urllist->ifsReadSortItemList(&infile,"", "", true, "#ENDLIST", false, true, 0, filename.c_str())) { this_room.urllist = urllist; } else { delete urllist; } } else if (temp.startsWith("#BLOCK")) { this_room.block = true; } } } if (this_room.block && (this_room.sitelist || this_room.urllist)) this_room.part_block = true; rooms.push_back(this_room); infile.close(); if (roomname.size() <= 2) { E2LOGGER_error( "Could not read room from definitions file \"", filename, '"'); exit(1); } roomname = roomname.substr(1); // remove leading '#' } if (closedir(d) != 0) { if (errno != EINTR) { E2LOGGER_error("Could not close room definitions directory: ", strerror(errno)); exit(1); } } } void LOptionContainer::deleteRooms() { for (std::list::iterator i = rooms.begin(); i != rooms.end(); ++i) { delete i->iplist; if (i->sitelist != NULL) delete i->sitelist; if (i->urllist != NULL) delete i->urllist; } rooms.clear(); } long int LOptionContainer::findoptionI(const char *option) { long int res = String(findoptionS(option).c_str()).toLong(); return res; } std::string LOptionContainer::findoptionS(const char *option) { // findoptionS returns a found option stored in the deque String temp; String temp2; String o(option); for (std::deque::iterator i = conffile.begin(); i != conffile.end(); i++) { if ((*i).empty()) continue; temp = (*i).c_str(); temp2 = temp.before("="); while (temp2.endsWith(" ")) { // get rid of tailing spaces before = temp2.chop(); } if (o == temp2) { temp = temp.after("="); while (temp.startsWith(" ")) { // get rid of heading spaces temp.lop(); } if (temp.startsWith("'")) { // inverted commas temp.lop(); } while (temp.endsWith(" ")) { // get rid of tailing spaces temp.chop(); } if (temp.endsWith("'")) { // inverted commas temp.chop(); } return temp.toCharArray(); } } return ""; } std::deque LOptionContainer::findoptionM(const char *option) { // findoptionS returns all the matching options String temp; String temp2; String o(option); std::deque results; for (std::deque::iterator i = conffile.begin(); i != conffile.end(); i++) { if ((*i).empty()) continue; temp = (*i).c_str(); temp2 = temp.before("="); while (temp2.endsWith(" ")) { // get rid of tailing spaces before = temp2.chop(); } if (o == temp2) { temp = temp.after("="); while (temp.startsWith(" ")) { // get rid of heading spaces temp.lop(); } if (temp.startsWith("'")) { // inverted commas temp.lop(); } while (temp.endsWith(" ")) { // get rid of tailing spaces temp.chop(); } if (temp.endsWith("'")) { // inverted commas temp.chop(); } results.push_back(temp); } } return results; } #ifdef NOTDEF bool LOptionContainer::realitycheck(long int l, long int minl, long int maxl, const char *emessage) { // realitycheck checks an amount for certain expected criteria // so we can spot problems in the conf files easier if ((l < minl) || ((maxl > 0) && (l > maxl))) { E2LOGGER_error("Config problem; check allowed values for ", emessage); return false; } return true; } #endif bool LOptionContainer::readFilterGroupConf() { String prefix(o.config.configfile); prefix = prefix.before(".conf"); prefix += "f"; String file; ConfigVar groupnamesfile; String groupname; bool need_html = false; bool read_errors = false; DEBUG_config("read FilterGroups"); if (o.filter.use_group_names_list) { int result = groupnamesfile.readVar(group_names_list_location.c_str(), "="); if (result != 0) { E2LOGGER_error("Error opening group names file: ", group_names_list_location); return false; } } for (int i = 1; i <= o.filter.filter_groups; i++) { file = prefix; file += String(i); file += ".conf"; if (o.filter.use_group_names_list) { std::ostringstream groupnum; groupnum << i; groupname = groupnamesfile[groupnum.str().c_str()]; if (groupname.length() == 0) { E2LOGGER_error("Group names file too short: ", group_names_list_location); return false; } DEBUG_debug("Group name: ", groupname); } if (!readAnotherFilterGroupConf(file.toCharArray(), groupname.toCharArray(), need_html, i)) { E2LOGGER_warning("Error(s) opening or within filter group config: ", file); read_errors = true; } } if (read_errors && o.lists.abort_on_missing_list) { return false; } return true; } bool LOptionContainer::readAnotherFilterGroupConf(const char *filename, const char *groupname, bool &need_html, int fg_no) { DEBUG_debug("adding filter group: ", numfg, " ", filename); // array of pointers to FOptionContainer typedef FOptionContainer *PFOptionContainer; FOptionContainer **temp = new PFOptionContainer[numfg + 1]; for (int i = 0; i < numfg; i++) { temp[i] = fg[i]; } if (numfg > 0) { delete[] fg; } fg = temp; fg[numfg] = new FOptionContainer; DEBUG_debug("added filter group: ", numfg, " ", filename); // pass all the vars from OptionContainer needed (*fg[numfg]).weighted_phrase_mode = o.naughty.weighted_phrase_mode; (*fg[numfg]).force_quick_search = o.lists.force_quick_search; (*fg[numfg]).reverse_lookups = o.story.reverse_lookups; // pass in the group name (*fg[numfg]).name = groupname; // pass in the group number (*fg[numfg]).filtergroup = fg_no; // pass in the reporting level - can be overridden (*fg[numfg]).reporting_level = reporting_level; DEBUG_debug("passed variables to filter group: ", numfg, " ", filename); bool rc = (*fg[numfg]).read(filename); DEBUG_debug("reading filter group: ", numfg, " ", filename, " return is ", rc); numfg++; if (!rc) { return false; } return true; } int LOptionContainer::getFgFromName(String &name) { for (int i = 0; i < o.filter.numfg ; i++) { if (name == (*fg[i]).name) return i; } // not found return -1; } e2guardian-5.5.8r/src/LOptionContainer.hpp000066400000000000000000000067431477372360500205130ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_LOPTIONCONTAINER #define __HPP_LOPTIONCONTAINER // INCLUDES #include "ContentScanner.hpp" #include "DownloadManager.hpp" #include "String.hpp" #include "HTMLTemplate.hpp" #include "ListContainer.hpp" #include "ListManager.hpp" #include "FOptionContainer.hpp" #include "LanguageContainer.hpp" #include "ImageContainer.hpp" #include "RegExp.hpp" #include "Auth.hpp" #include "IPList.hpp" #include "Queue.hpp" #include #include "CertificateAuthority.hpp" // DECLARATIONS struct room_item { std::string name; IPList *iplist; ListContainer *sitelist; ListContainer *urllist; bool block; bool part_block; }; class LOptionContainer { public: //Queue* log_Q; //Queue* http_worker_Q; // all our options std::string name_suffix; // Unused ??? HTMLTemplate html_template; ListContainer filter_groups_list; IPList exception_ip_list; IPList banned_ip_list; ListMeta LMeta; StoryBoard StoryA; //ListManager lm; FOptionContainer **fg = nullptr; bool loaded_ok = true; bool is_fatal = false; bool sb_loaded_ok = true; int reload_id; int numfg = 0; String start_time; // access denied domain (when using the CGI) String access_denied_domain; void deleteFilterGroups(); void deleteFilterGroupsJustListData(); int getFgFromName(String &name); // returns -1 if not found //...and the functions that read them LOptionContainer(); LOptionContainer(int reload_id); ~LOptionContainer(); bool read(std::string& filename, int type, std::string& exception_ip_list_location, std::string& banned_ip_list_location); void reset(); bool inExceptionIPList(const std::string *ip, std::string *&host); //bool inBannedIPList(const std::string *ip, std::string *&host); bool readFilterGroupConf(); // public so fc_controlit can reload filter group config files // per-room blocking and URL whitelisting: see if given IP is in a room; if it is, return true and put the room name in "room" bool inRoom(const std::string &ip, std::string &room, std::string *host, bool *block, bool *part_block, bool *isexception, String url); void loadRooms(bool throw_error); void deleteRooms(); bool inSiteList(String &url, ListContainer *lc, bool swsort, bool ip, String &match); //char *inURLList(String &url, ListContainer *lc, bool swsort, bool ip, String &match); bool inURLList(String &url, ListContainer *lc, bool swsort, bool ip, String &match); String ISTag() { // unused ?? return start_time; } private: std::string per_room_directory_location; std::deque conffile; std::string conffilename; int reporting_level = 0; std::string html_template_location; std::string group_names_list_location; bool precompileregexps(); long int findoptionI(const char *option); std::string findoptionS(const char *option); //bool realitycheck(long int l, long int minl, long int maxl, const char *emessage); bool readAnotherFilterGroupConf(const char *filename, const char *groupname, bool &need_html, int fg_no); std::deque findoptionM(const char *option); //bool inIPList(const std::string *ip, ListContainer &list, std::string *&host); std::list rooms; }; #endif e2guardian-5.5.8r/src/LanguageContainer.cpp000066400000000000000000000042471477372360500206420ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "LanguageContainer.hpp" #include "RegExp.hpp" #include "String.hpp" #include "Logger.hpp" #include #include #include #include #include #include #include #include // GLOBALS // IMPLEMENTATION // wipe loaded language file void LanguageContainer::reset() { keys.clear(); values.clear(); } // look for the translated string corresponding to the given key //const char *LanguageContainer::getTranslation(const unsigned int index) //char *LanguageContainer::getTranslation(const unsigned int index) std::string LanguageContainer::getTranslation(const unsigned int index) { int i; int s = keys.size(); for (i = 0; i < s; i++) { if (keys[i] == index) { return (std::string) values[i]; } } String t = "TRANSLATION KEY "; String m(index); t += m; t += " MISSING "; return (std::string) t; } // open a language file, containing message names (keys) and translated messages (values) bool LanguageContainer::readLanguageList(const char *filename) { std::string linebuffer; // a string line buffer ;) String v; String line; unsigned int k; std::ifstream languagefile(filename, std::ios::in); // open the file for reading if (!languagefile.good()) { E2LOGGER_error("Error opening messages file (does it exist?): ", filename); return false; } while (!languagefile.eof()) { // keep going until end of file getline(languagefile, linebuffer); // grab a line if (linebuffer.length() == 0) { continue; } line = linebuffer.c_str(); k = line.after("\"").before("\",\"").toInteger(); v = line.after("\",\"").before("\""); if (k >= 0 && v.length() > 0) { keys.push_back(k); values.push_back(v); } } languagefile.close(); return true; // successful read } e2guardian-5.5.8r/src/LanguageContainer.hpp000066400000000000000000000011531477372360500206400ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_LANGUAGECONTAINER #define __HPP_LANGUAGECONTAINER // INCLUDES #include #include "String.hpp" // DECLARATIONS class LanguageContainer { public: void reset(); bool readLanguageList(const char *filename); //const char *getTranslation(const unsigned int index); std::string getTranslation(const unsigned int index); private: std::deque keys; std::deque values; }; #endif e2guardian-5.5.8r/src/ListContainer.cpp000066400000000000000000002352471477372360500200400ustar00rootroot00000000000000// ListContainer - class for both item and phrase lists // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include #include #include #include "ListContainer.hpp" #include "OptionContainer.hpp" #include "RegExp.hpp" #include "Logger.hpp" #include #include #include #include #include #include #include #include #include #include // GLOBALS extern OptionContainer o; // DEFINES #define ROOTNODESIZE 260 #define MAXROOTLINKS ROOTNODESIZE - 4 #define GRAPHENTRYSIZE 64 #define MAXLINKS GRAPHENTRYSIZE - 4 #define ROOTOFFSET ROOTNODESIZE - GRAPHENTRYSIZE // IMPLEMENTATION // Constructor - set default values ListContainer::ListContainer() { } // delete the memory block when the class is destryed ListContainer::~ListContainer() { reset(); } // for both types of list - clear & reset all values void ListContainer::reset() { if (data != nullptr) free(data); if (graphused && realgraphdata != nullptr) free(realgraphdata); // dereference this and included lists // - but not if the reason we're being // deleted is due to deletion (this will // only happen due to list garbage // collection, at which point the list // ref should already be zero) if (refcount > 0) { --refcount; // DEBUG_debug("de-reffing ", sourcefile, " due to manual list reset, refcount: ", refcount); for (size_t i = 0; i < morelists.size(); ++i) o.lm.deRefList(morelists[i]); } data = NULL; realgraphdata = NULL; maxchildnodes = 0; graphitems = 0; data_length = 0; data_memory = 0; items = 0; isSW = false; issorted = false; graphused = false; force_quick_search = 0; /*sthour = 0; stmin = 0; endhour = 0; endmin = 0; days = ""; timetag = "";*/ category = ""; istimelimited = false; combilist.clear(); slowgraph.clear(); list.clear(); lengthlist.clear(); weight.clear(); itemtype.clear(); timelimitindex.clear(); morelists.clear(); timelimits.clear(); listcategory.clear(); categoryindex.clear(); used = false; parent = false; bannedpfile = ""; exceptionpfile = ""; weightedpfile = ""; bannedpfiledate = 0; exceptionpfiledate = 0; weightedpfiledate = 0; read_errors = false; if (is_iplist) { iplist.clear(); iprangelist.clear(); } } // for item lists - during a config reload, can we simply retain the already loaded list? bool ListContainer::previousUseItem(const char *filename, bool startswith, int filters) { String f(filename); if (f == sourcefile && startswith == sourcestartswith && filters == sourcefilters) { return true; } return false; } // for phrase lists - read in the given file, which may be an exception list // inherit category and time limits from parent bool ListContainer::readPhraseList(const char *filename, bool isexception, int catindex, int timeindex, bool incref, int nlimit) { // only increment refcount on first read, not read of included files // (includes get amalgamated, unlike item lists) if (incref) ++refcount; sourcefile = filename; sourceisexception = isexception; std::string linebuffer; // a string line buffer ;) String temp; // a String for temporary manipulation String line; String lcat; size_t len = 0; try { len = getFileLength(filename); } catch (std::runtime_error &e) { E2LOGGER_error("Error reading file (does it exist?) ", filename, ": ", e.what()); o.config_error = true; return false; } if (len < 2) { return true; // its blank - perhaps due to webmin editing // just return } filedate = getFileDate(filename); if (!increaseMemoryBy(len + 2)) { // Allocate some memory to hold file E2LOGGER_error("Memory allocation failed"); return false; }; std::ifstream listfile(filename, std::ios::in); // open the file for reading if (!listfile.good()) { E2LOGGER_error("Error opening file (does it exist?): ", filename); o.config_error = true; return false; } lcat = ""; bool caseinsensitive = true; int line_no = 0; while (!listfile.eof()) { // keep going until end of file getline(listfile, linebuffer); // grab a line line_no++; if (linebuffer.length() != 0) { // sanity checking line = linebuffer.c_str(); line.removeWhiteSpace(); // convert to lowercase - unless this is, for example, // a phraselist in an odd character encoding which has // been marked as not to be converted if (caseinsensitive) line.toLower(); if (line.startsWith("<")) readPhraseListHelper(line, isexception, catindex, timeindex, nlimit); // handle included list files else if (line.startsWith(".")) { temp = line.after(".include<").before(">"); if (temp.length() > 0) { if (!readPhraseList(temp.toCharArray(), isexception, catindex, timeindex, false, nlimit)) { E2LOGGER_error(" at line ", line_no, " of ", filename); continue; // listfile.close(); // return false; } } } // phrase lists can be categorised (but not time limited) else if (line.startsWith("#listcategory:")) { //use the original line so as to preserve case in category names temp = linebuffer.c_str(); lcat = temp.after("\"").before("\""); // this serves two purposes: returning the index of the category string // if it is already in our category list, and adding it to the list (also // returning index) if it is not. catindex = getCategoryIndex(&lcat); DEBUG_debug("List category: ", lcat, "Category list index: ", catindex); } // phrase lists can also be marked as not to be case-converted, // to aid support for exotic character encodings else if (line.startsWith("#noconvert")) { DEBUG_debug("List flagged as not to be case-converted"); caseinsensitive = false; } // Read in time tags; set timeindex to the ID of the new tag else if (line.startsWith("#time: ")) { // see if we have a time tag TimeLimit tl; if (!readTimeTag(&line, tl)) { return false; } timelimits.push_back(tl); timeindex = timelimits.size() - 1; DEBUG_debug("Found time limit on phrase list. Now have ", timelimits.size(), " limits on this list (including parents)."); continue; } } } listfile.close(); return true; // sucessful read } // for phrase lists - helper function for readPhraseList void ListContainer::readPhraseListHelper(String line, bool isexception, int catindex, int timeindex, int &nlimit) { // read in weighting value, if there // 1st check for % weighting int weighting = line.after("><").before("%>").toInteger(); if (weighting != 0) // it is a % { weighting = (weighting * nlimit) / 100; } else { // check for normal weighting weighting = line.after("><").before(">").toInteger(); } // defaults to 0 int type; if (weighting != 0) { // this is a weighted phrase type = 1; line = line.before("><"); line += ">"; } else { if (isexception) { // this is an exception phrase type = -1; } else { type = 0; } } if (line.after(">,").length() > 2) { // push type & weighting for all phrases on this line onto the combi list while (line.length() > 2) { line = line.after("<"); // combination banned, weighted, or exception readPhraseListHelper2(line.before(">"), type + 11, weighting, catindex, timeindex); line = line.after(">,"); } // end of combi marker readPhraseListHelper2("", type + 21, weighting, catindex, timeindex); } else { line = line.after("<").before(">"); // push type & weighting for this individual phrase onto combi list (not combination phrase) readPhraseListHelper2(line, type, weighting, catindex, timeindex); } } // for phrase lists - push phrase, type, weighting & category onto combi list void ListContainer::readPhraseListHelper2(String phrase, int type, int weighting, int catindex, int timeindex) { // -1=exception // 0=banned // 1=weighted // 10 = combination exception // 11 = combination banned // 12 = combination weighted // 20,21,22 = end of combi marker if (type > 19) { combilist.push_back(-2); // mark an end of a combi combilist.push_back(type - 21); // store the combi type combilist.push_back(timeindex); // store the combi timtime limitt combilist.push_back(weighting); // store the combi weight combilist.push_back(catindex); // store the combi category return; } phrase.removePunctuation(); if (phrase.length() > 127) { E2LOGGER_error("Phrase length too long, truncating: ", phrase); phrase = phrase.subString(0, 127); } if (phrase.length() < 1) { // its too small to use return; } if (type < 10) { if (!addToItemListPhrase(phrase.toCharArray(), phrase.length(), type, weighting, false, catindex, timeindex)) { E2LOGGER_error("Duplicate phrase, dropping: ", phrase ); } return; } // must be a combi or end marker if got here // must be a combi if got here addToItemListPhrase(phrase.toCharArray(), phrase.length(), type, weighting, true, catindex, timeindex); } // for item lists - add phrases to list proper bool ListContainer::addToItemListPhrase(const char *s, size_t len, int type, int weighting, bool combi, int catindex, int timeindex) { list.push_back(data_length); lengthlist.push_back(len); for (size_t i = 0; i < len; i++) { data[data_length + i] = s[i]; } data[data_length + len] = 0; data_length += len + 1; if (combi) { // if this is a combination item, store the ID of the current item on the combi list combilist.push_back(items); } items++; weight.push_back(weighting); itemtype.push_back(type); categoryindex.push_back(catindex); timelimitindex.push_back(timeindex); return true; } //bool ListContainer::ifsreadItemList(std::istream *input, const char *list_pwd, int len, bool checkendstring, const char *endstring, bool do_includes, bool startswith, int filters) bool ListContainer::ifsreadItemList(const char *filename, std::istream *input, String basedir, const char *list_pwd, int len, bool checkendstring, const char *endstring, bool do_includes, bool startswith, int filters) { unsigned int mem_used = 2; RegExp re; re.comp("^.*\\:[0-9]+\\/.*"); RegResult Rre; if (is_iplist) { #ifdef HAVE_PCRE matchIP.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$"); matchSubnet.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$"); matchCIDR.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,2}$"); matchRange.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}-\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$"); #else matchIP.comp("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"); matchSubnet.comp("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"); matchCIDR.comp("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2}$"); matchRange.comp("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}-[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"); #endif } if (filters != 32) DEBUG_config("Converting to lowercase"); if (!increaseMemoryBy(len + 2)) { E2LOGGER_error("Memory allocation failed"); return false; }; // Allocate some memory to hold file String temp, inc, hostname, url; //char linebuffer[2048]; char linebuffer[20000]; // increased to allow checking of line length while (!input->eof()) { input->getline(linebuffer, sizeof(linebuffer)); temp = linebuffer; if (temp.length() < 2) continue; // its jibberish if (temp.length() > 2048) { temp.limitLength(100); E2LOGGER_error("Line too long in list file - ignored ", temp ); continue; } //item lists (URLs, domains) can be both categorised and time-limited if (linebuffer[0] == '#') { if (temp.startsWith("#time: ")) { // see if we have a time tag if (!readTimeTag(&temp, listtimelimit)) { return false; } continue; } else if (temp.startsWith("#listcategory:")) { category = temp.after("\"").before("\""); DEBUG_config("found item list category: ", category); continue; } else if (checkendstring && temp.startsWith(endstring)) { break; } continue; // it's a comment } // Strip off comments that don't necessarily start at the beginning of a line // - but not regular expression comments // - or '#' within a URL // So only strings starting with ' #' are regarded as comments // if the '#' is not at start of line // // Amended by Philip Pearce - included in e2g first release 2013: std::string::size_type commentstart = 1; while ((commentstart = temp.find_first_of('#', commentstart)) != std::string::npos) { // Don't treat "(?#...)" as a E2 comment - it's a regex comment // if (temp[commentstart - 1] != '?') // Changed to only treat ' #' as an embeded comment if (temp[commentstart - 1] == ' ') { temp = temp.substr(0, commentstart); break; } ++commentstart; } temp.removeWhiteSpace(); // tidy up and make it handle CRLF files if (temp.startsWith(".Include<")) { // see if we have another list if (do_includes) { inc = temp.after(".Include<"); // to include inc = inc.before(">"); if (inc.contains("__LISTDIR__")) { String inc2 = inc.before("__LISTDIR__"); inc2 += list_pwd; inc2 += inc.after("__LISTDIR__"); inc = inc2; } inc.fullPath(basedir); int list_no = -1; if (!readAnotherItemList(inc.toCharArray(), list_pwd, startswith, filters, list_no)) { // read it read_errors = true; // return false; } else { if ( list_no > -1 && o.lm.l[list_no]->read_errors ) { read_errors = true; } } } continue; } if (!(is_iplist || is_timelist)) { if (filters != 32) { 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.before("/").contains(":")) { // quicker than full regexp if (re.match(temp.toCharArray(), Rre)) { hostname = temp.before(":"); url = temp.after("/"); temp = hostname; temp += "/"; temp += url; } } } if (filters != 32) { temp.toLower(); // tidy up - but don't make regex lists lowercase! } } if (temp.length() > 0) { mem_used += temp.length() + 1; if (is_iplist) { addToIPList(temp); } else if (is_timelist) { if (!addToTimeList(temp)) return false; } else if (is_map) { addToDataMap(temp); } else { if (mem_used > data_memory) { if(!increaseMemoryBy(20000)) { E2LOGGER_error("Memory allocation error"); return false; }; } addToItemList(temp.toCharArray(), temp.length()); // add to unsorted list } } } if (is_iplist) { if (is_map) { std::stable_sort(ipmaplist.begin(), ipmaplist.end()); std::stable_sort(ipmaprangelist.begin(), ipmaprangelist.end()); issorted = true; // temp code for testing if (false) { std::cerr << "ipmaplist size is " << ipmaplist.size() << std::endl; std::vector iplist2{ "10.81.64.5", "10.81.76.23", "10.81.65.12", "10.81.65.16", "10.81.65.22", "10.81.65.25", "10.81.65.29", "10.81.65.31", "10.81.65.33", "10.81.65.35", "10.81.65.36", "10.81.65.37", "10.81.65.39", "10.81.65.40", "10.81.65.41", "10.81.65.45", "10.81.65.48", "10.81.65.49", "10.81.65.50", "10.81.65.51", "10.81.65.52", "10.81.65.55", "10.81.65.56", "10.81.65.57", "10.81.65.58", "10.81.65.59", "10.81.65.63", "10.81.65.60", "10.81.65.93", "10.81.65.159", "10.81.66.13", "10.81.66.18", "10.81.66.20", "10.81.66.25", "10.81.66.26", "10.81.66.28", "10.81.66.34", "10.81.66.36", "10.81.66.37", "10.81.66.39", "10.81.66.40", "10.81.66.42", "10.81.66.43", "10.81.66.44", "10.81.66.47", "10.81.66.51", "10.81.66.58", "10.81.66.64", "10.81.66.75", "10.81.67.10", "10.81.67.11", "10.81.67.12", "10.81.69.10", "10.81.70.13", "10.81.70.14", "192.168.206.27", "192.168.206.30", "192.168.206.35", "192.168.206.36", }; for (auto item: iplist2) { String res = getIPMapData(item); if (res.empty()) { std::cerr << "IP " << item << " NOT found" << std::endl; } else { std::cerr << "IP " << item << " found group " << res << std::endl; } } } } else { std::stable_sort(iplist.begin(), iplist.end()); std::stable_sort(iprangelist.begin(), iprangelist.end()); issorted = true; } } else if (is_map) { std::stable_sort(datamaplist.begin(), datamaplist.end()); issorted = true; if(false) { // make true for testing String match, result; for (auto item : datamaplist) { std::cerr << item.key << " mapped to " << item.group << std::endl; } std::cerr << "End of list" << std::endl; String t = "philip"; String tg = getMapData(t, match, result); std::cerr << "Search for philip got " << tg.c_str() << std::endl; } } if ((mem_used + 20) < data_memory) { // free up unused memory data = (char *) realloc(data, (mem_used + 20) * sizeof(char)); if (data == nullptr) return false; } if(read_errors) { E2LOGGER_warning("**List Error** There are read failures in some lists included in ", filename); } return true; // sucessful read } bool ListContainer::ifsReadSortItemList(std::ifstream *input, String basedir, const char *list_pwd, bool checkendstring, const char *endstring, bool do_includes, bool startswith, int filters, const char *filename) { size_t len = 0; try { len = getFileLength(filename); } catch (std::runtime_error &e) { E2LOGGER_error("Error reading file ", filename, ": ", e.what()); return false; } bool ret; ret = ifsreadItemList(filename,input, basedir, list_pwd, len, checkendstring, endstring, do_includes, startswith, filters); if (ret) { doSort(startswith); return true; } return false; } // for item lists - read item list from file. checkme - what is startswith? is it used? what is filters? bool ListContainer::readItemList(const char *filename, const char *list_pwd, bool startswith, int filters, bool isip, bool istime, bool ismap) { ++refcount; sourcefile = filename; sourcestartswith = startswith; sourcefilters = filters; if (isip) is_iplist = true; else is_iplist = false; if (istime) is_timelist = true; else is_timelist = false; if (ismap) is_map = true; else is_map = false; if (sourcefile.startsWithLower("memory:")) return readStdinItemList(startswith, filters, filename); std::string linebuffer; DEBUG_config(filename); //struct stat s; filedate = getFileDate(filename); size_t len = 0; if (filedate == 0) return false; try { len = getFileLength(filename); } catch (std::runtime_error &e) { E2LOGGER_error("*List Error* opening file ", filename, ": ", e.what()); return false; } if (len < 2) { return true; // its blank - perhaps due to webmin editing // just return } std::ifstream listfile(filename, std::ios::in); if (!listfile.good()) { E2LOGGER_error("*List Error* opening: ", filename); return false; } String base_dir(filename); base_dir.baseDir(); if (!ifsreadItemList(filename,&listfile, base_dir, list_pwd, len, false, NULL, true, startswith, filters)) { listfile.close(); E2LOGGER_error("*List Error* while reading: ", filename); return false; } listfile.close(); return true; // sucessful read } // for stdin item lists - read item list from stdin bool ListContainer::readStdinItemList(bool startswith, int filters, const char *filename) { if (filters != 32) DEBUG_config("Converting to lowercase"); std::string linebuffer; RegExp re; re.comp("^.*\\:[0-9]+\\/.*"); RegResult Rre; size_t len = 2046; if (!increaseMemoryBy(20000)) { // Allocate some memory to hold list E2LOGGER_error("Memory allocation failed"); return false; }; if (!std::cin.good()) { E2LOGGER_error("Error reading stdin: "); return false; } if (!ifsreadItemList(filename, &std::cin, "", "", len, true, "#ENDLIST", false, startswith, filters)) { E2LOGGER_error("Error reading stdin: "); return false; } else return true; } // for item lists - read nested item lists bool ListContainer::readAnotherItemList(const char *filename, const char *list_pwd, bool startswith, int filters, int &result) { result = o.lm.newItemList(filename, list_pwd, startswith, filters, false, is_iplist, is_timelist, is_map); if (result < 0) { //E2LOGGER_error("*List Error* unable to read file: ", filename); return false; } morelists.push_back((unsigned) result); return true; } // for item lists - is this item in the list? bool ListContainer::inList(const char *string, String &lastcategory) { String match, result; if (findInList(string, lastcategory, match, result)) { return true; } return false; } // for item lists - is an item in the list that ends with this string? bool ListContainer::inListEndsWith(const char *string, String &lastcategory) { if (isNow()) { if (items > 0) { if (search(&ListContainer::greaterThanEW, 0, items - 1, string) >= 0) { lastcategory = category; return true; } } bool rc; for (unsigned int i = 0; i < morelists.size(); i++) { rc = (*o.lm.l[morelists[i]]).inListEndsWith(string, lastcategory); if (rc) { // lastcategory = (*o.lm.l[morelists[i]]).lastcategory; return true; } } } return false; } // for item lists - is an item in the list that starts with this string? bool ListContainer::inListStartsWith(const char *string, String &lastcategory) { if (isNow()) { if (items > 0) { if (search(&ListContainer::greaterThanSW, 0, items - 1, string) >= 0) { lastcategory = category; return true; } } bool rc; for (unsigned int i = 0; i < morelists.size(); i++) { rc = (*o.lm.l[morelists[i]]).inListStartsWith(string, lastcategory); if (rc) { //lastcategory = (*o.lm.l[morelists[i]]).lastcategory; return true; } } } return false; } // find pointer to the part of the data array containing this string bool ListContainer::findInList(const char *string, String &lastcategory, String &match, String &result) { if (isNow()) { if (is_iplist) { if (is_map) { std::string sstring = string; String rcs = getIPMapData(sstring); if (rcs != "") { result = rcs; return true; } } else if (inIPList(string, match)) { lastcategory = category; return true; } } else if (is_map) { String sstring; sstring = string; auto rcs = getMapData(sstring, match, result); if (rcs) { return true; } } else if (items > 0) { int r; if (isSW) { r = search(&ListContainer::greaterThanSWF, 0, items - 1, string); } else { r = search(&ListContainer::greaterThanEWF, 0, items - 1, string); } if (r >= 0) { lastcategory = category; match = (data + list[r]); return true; } } bool rc; for (unsigned int i = 0; i < morelists.size(); i++) { rc = (*o.lm.l[morelists[i]]).findInList(string, lastcategory, match, result); if (rc) { //lastcategory = (*o.lm.l[morelists[i]]).lastcategory; return rc; } } } return false; } // find an item in the list which starts with this bool ListContainer::findStartsWith(const char *string, String &lastcategory, String &match) { if (isNow()) { if (items > 0) { int r = search(&ListContainer::greaterThanSW, 0, items - 1, string); if (r >= 0) { lastcategory = category; match = (data + list[r]); return true; } } bool rc; for (unsigned int i = 0; i < morelists.size(); i++) { rc = (*o.lm.l[morelists[i]]).findStartsWith(string, lastcategory, match); if (rc) { return rc; } } } return false; } bool ListContainer::findEndsWith(const char *string, String &lastcategory, String &match) { if (isNow()) { if (items > 0) { int r = search(&ListContainer::greaterThanEW, 0, items - 1, string); if (r >= 0) { lastcategory = category; match = (data + list[r]); return true; } } bool rc; for (unsigned int i = 0; i < morelists.size(); i++) { rc = (*o.lm.l[morelists[i]]).findEndsWith(string, lastcategory, match); if (rc) { return rc; } } } return false; } // For phrase lists - grab the text, score and type of a given phrase, based on item number within list std::string ListContainer::getItemAtInt(int index) { std::string s(data + list[index], lengthlist[index]); return s; } int ListContainer::getWeightAt(unsigned int index) { return weight[index]; } int ListContainer::getTypeAt(unsigned int index) { return itemtype[index]; } // Phrase lists - check whether the current time is within the limit imposed upon the given phrase bool ListContainer::checkTimeAt(unsigned int index) { if (timelimitindex[index] == -1) { return true; } return isNow(timelimitindex[index]); } bool ListContainer::checkTimeAtD(int index) { if (index == -1) { return true; } return isNow(index); } // The following comparator function will result in sort in descending order - which is counter-intuitive!!! // Sorts backwards within string - used for site lists which are more significant to the right struct lessThanEWF : public std::binary_function { bool operator()(const size_t &aoff, const size_t &boff) { const char *a = data + aoff; const char *b = data + boff; size_t alen = strlen(a); size_t blen = strlen(b); size_t apos = alen - 1; size_t bpos = blen - 1; for (size_t maxlen = ((alen < blen) ? alen : blen); maxlen > 0; apos--, bpos--, maxlen--) { if (a[apos] > b[bpos]) return true; else if (a[apos] < b[bpos]) return false; } if (alen >= blen) return true; else //if (alen < blen) return false; //return true; // both equal }; char *data; }; // The following comparator function will result in sort in descending order - which is counter-intuitive!!! // Sorts forwards within string - used for url and other lists which are more significant to the left struct lessThanSWF : public std::binary_function { bool operator()(const size_t &aoff, const size_t &boff) { const char *a = data + aoff; const char *b = data + boff; size_t alen = strlen(a); size_t blen = strlen(b); size_t maxlen = (alen < blen) ? alen : blen; for (size_t i = 0; i < maxlen; i++) { if (a[i] > b[i]) return true; else if (a[i] < b[i]) return false; } if (alen >= blen) return true; else //if (alen > blen) return false; }; char *data; }; void ListContainer::doSort(const bool startsWith) { // sort by ending of line for (size_t i = 0; i < morelists.size(); i++) (*o.lm.l[morelists[i]]).doSort(startsWith); if (is_iplist) { if (is_map) std::stable_sort(ipmaplist.begin(), ipmaplist.end()); else std::stable_sort(iplist.begin(), iplist.end()); return; } if (is_map) { // deal with datamaplist std::stable_sort(datamaplist.begin(), datamaplist.end()); return; } if (items < 2 || issorted) return; if (startsWith) { lessThanSWF lts; lts.data = data; std::stable_sort(list.begin(), list.end(), lts); } else { lessThanEWF lte; lte.data = data; std::stable_sort(list.begin(), list.end(), lte); } isSW = startsWith; issorted = true; #ifdef DEBUG_LOW self_check(); #endif return; } bool ListContainer::makeGraph(bool fqs) { force_quick_search = fqs; if (data_length == 0) return true; long int i; // Quick search has been forced on - put all items on the "slow" list and be done with it if (force_quick_search) { for (i = 0; i < items; i++) { // Check to see if the item is a duplicate std::string thisphrase = getItemAtInt(i); bool found = false; unsigned int foundindex = 0; for (std::vector::iterator j = slowgraph.begin(); j != slowgraph.end(); j++) { if (getItemAtInt(*j) == thisphrase) { found = true; foundindex = *j; break; } } if (!found) { // Not a duplicate - store it slowgraph.push_back(i); } else { // Duplicate - resolve the collision // // Existing entry must be a combi AND // new entry is not a combi so we overwrite the // existing values as combi values and types are // stored in the combilist // OR // both are weighted phrases and the new phrase is higher weighted // OR // the existing phrase is weighted and the new phrase is banned // OR // new phrase is an exception; exception phrases take precedence if ((itemtype[foundindex] > 9 && itemtype[i] < 10) || (itemtype[foundindex] == 1 && itemtype[i] == 1 && (weight[i] > weight[foundindex])) || (itemtype[foundindex] == 1 && itemtype[i] == 0) || itemtype[i] == -1) { itemtype[foundindex] = itemtype[i]; weight[foundindex] = weight[i]; categoryindex[foundindex] = categoryindex[i]; timelimitindex[foundindex] = timelimitindex[i]; } } } return true; } std::string s; std::string lasts; graphused = true; #ifdef DEBUG_LOW DEBUG_debug("Bytes needed for phrase tree in worst-case scenario: ", (sizeof(int) * ((GRAPHENTRYSIZE * data_length) + ROOTOFFSET)), ", starting off with allocation of ", (sizeof(int) * ((GRAPHENTRYSIZE * ((data_length / 3) + 1)) + ROOTOFFSET)) ); prolificroot = false; secondmaxchildnodes = 0; #endif // Make a conservative guess at how much memory will be needed - call realloc() as necessary to change what is actually taken current_graphdata_size = (GRAPHENTRYSIZE * ((data_length / 3) + 1)) + ROOTOFFSET; realgraphdata = (int *) calloc(current_graphdata_size, sizeof(int)); if (realgraphdata == NULL) { E2LOGGER_error("Cannot allocate memory for phrase tree: ", strerror(errno)); return false; } graphitems++; std::deque sizelist; for (i = 0; i < items; i++) { sizelist.push_back(i); } graphSizeSort(0, items - 1, &sizelist); for (i = 0; i < items; i++) { graphAdd(String(data + list[sizelist[i]], lengthlist[sizelist[i]]), 0, sizelist[i]); } DEBUG_config("Bytes actually needed for phrase tree: ", (sizeof(int) * ((GRAPHENTRYSIZE * graphitems) + ROOTOFFSET)) ); DEBUG_config("Most prolific node has ", maxchildnodes, " children"); DEBUG_config("It ", (prolificroot ? "is" : "is not"), " the root node"); DEBUG_config("Second most prolific node has ", secondmaxchildnodes, " children"); realgraphdata = (int *) realloc(realgraphdata, sizeof(int) * ((GRAPHENTRYSIZE * graphitems) + ROOTOFFSET)); if (realgraphdata == NULL) { E2LOGGER_error("Cannot reallocate memory for phrase tree: ", strerror(errno)); return false; } int ml = realgraphdata[2]; int branches; for (i = ml - 1; i >= 0; i--) { branches = graphFindBranches(realgraphdata[4 + i]); if (branches < 12) { // quicker to use B-M on node with few branches graphCopyNodePhrases(realgraphdata[4 + i]); // remove link to this node and so effectively remove all nodes // it links to but don't recover the memory as its not worth it for (int j = i; j < ml; j++) { realgraphdata[4 + j] = realgraphdata[4 + j + 1]; } realgraphdata[2]--; } } return true; } void ListContainer::graphSizeSort(int l, int r, std::deque *sizelist) { if (r <= l) return; size_t e; int k; size_t v = getItemAtInt((*sizelist)[r]).length(); int i = l - 1, j = r, p = i, q = r; for (;;) { while (getItemAtInt((*sizelist)[++i]).length() < v); while (v < getItemAtInt((*sizelist)[--j]).length()) { if (j == l) break; } if (i >= j) break; e = (*sizelist)[i]; (*sizelist)[i] = (*sizelist)[j]; (*sizelist)[j] = e; if (v == getItemAtInt((*sizelist)[i]).length()) { p++; e = (*sizelist)[p]; (*sizelist)[p] = (*sizelist)[i]; (*sizelist)[i] = e; } if (v == getItemAtInt((*sizelist)[j]).length()) { q--; e = (*sizelist)[q]; (*sizelist)[q] = (*sizelist)[j]; (*sizelist)[j] = e; } } e = (*sizelist)[i]; (*sizelist)[i] = (*sizelist)[r]; (*sizelist)[r] = e; j = i - 1; i++; for (k = l; k <= p; k++, j--) { e = (*sizelist)[k]; (*sizelist)[k] = (*sizelist)[j]; (*sizelist)[j] = e; } for (k = r - 1; k >= q; k--, i++) { e = (*sizelist)[k]; (*sizelist)[k] = (*sizelist)[i]; (*sizelist)[i] = e; } graphSizeSort(l, j, sizelist); graphSizeSort(i, r, sizelist); } // find the total number of children a node has, along all branches int ListContainer::graphFindBranches(unsigned int pos) { int branches = 0; int *graphdata; if (pos == 0) graphdata = realgraphdata; else graphdata = realgraphdata + ROOTOFFSET; int links = graphdata[pos * GRAPHENTRYSIZE + 2]; for (int i = 0; i < links; i++) { branches += graphFindBranches(graphdata[pos * GRAPHENTRYSIZE + 4 + i]); } if (links > 1) { branches += links - 1; } return branches; } // copy all phrases starting from a given root link into the slowgraph void ListContainer::graphCopyNodePhrases(unsigned int pos) { int *graphdata; if (pos == 0) graphdata = realgraphdata; else graphdata = realgraphdata + ROOTOFFSET; int links = graphdata[pos * GRAPHENTRYSIZE + 2]; int i; for (i = 0; i < links; i++) { graphCopyNodePhrases(graphdata[pos * GRAPHENTRYSIZE + 4 + i]); } bool found = false; unsigned int foundindex = 0; unsigned int phrasenumber = graphdata[pos * GRAPHENTRYSIZE + 3]; std::string thisphrase = getItemAtInt(phrasenumber); for (std::vector::iterator i = slowgraph.begin(); i != slowgraph.end(); i++) { if (getItemAtInt(*i) == thisphrase) { found = true; foundindex = *i; break; } } if (!found) { slowgraph.push_back(phrasenumber); } else { // Duplicate - resolve the collision // // Existing entry must be a combi AND // new entry is not a combi so we overwrite the // existing values as combi values and types are // stored in the combilist // OR // both are weighted phrases and the new phrase is higher weighted // OR // the existing phrase is weighted and the new phrase is banned // OR // new phrase is an exception; exception phrases take precedence if ((itemtype[foundindex] > 9 && itemtype[phrasenumber] < 10) || (itemtype[foundindex] == 1 && itemtype[phrasenumber] == 1 && (weight[phrasenumber] > weight[foundindex])) || (itemtype[foundindex] == 1 && itemtype[phrasenumber] == 0) || itemtype[phrasenumber] == -1) { itemtype[foundindex] = itemtype[phrasenumber]; weight[foundindex] = weight[phrasenumber]; categoryindex[foundindex] = categoryindex[phrasenumber]; timelimitindex[foundindex] = timelimitindex[phrasenumber]; } } } int ListContainer::bmsearch(char *file, off_t fl, const std::string &s) { off_t pl = s.length(); if (fl < pl) return 0; // reality checking if (pl > 126) return 0; // reality checking // must match all off_t j, l; // counters int p; // to hold precalcuated value for speed bool match; // flag int qsBc[256]; // Quick Search Boyer Moore shift table (256 alphabet) char *k; // pointer used in matching int count = 0; char *phrase = new char[pl + 1]; for (j = 0; j < pl; j++) { phrase[j] = s[j]; } phrase[pl] = 0; // For speed we append the phrase to the end of the memory block so it // is always found, thus eliminating some checking. This is possible as // we know an extra 127 bytes have been provided by NaughtyFilter.cpp // and also the OptionContainer does not allow phrase lengths greater // than 126 chars k = file + fl; for (j = 0; j < pl; j++) { k[j] = s[j]; } // Next we need to make the Quick Search Boyer Moore shift table p = pl + 1; for (j = 0; j < 256; j++) { // Preprocessing qsBc[j] = p; } for (j = 0; j < pl; j++) { // Preprocessing qsBc[(unsigned char) phrase[j]] = pl - j; } // Now do the searching! for (j = 0;;) { k = file + j; match = true; for (l = 0; l < pl; l++) { // quiv, but faster, memcmp() if (k[l] != phrase[l]) { match = false; break; } } if (match) { if (j >= fl) { break; // is the end of file marker } count++; } j += qsBc[(unsigned char) file[j + pl]]; // shift } delete[] phrase; return count; } // Format of the data is each entry has GRAPHENTRYSIZE int values with format of: // [letter][last letter flag][num links][from phrase][link0][link1]... void ListContainer::graphSearch(std::map > &result, char *doc, off_t len) { off_t i, j, k; std::map >::iterator existingitem; //do standard quick search on short branches (or everything, if force_quick_search is on) for (std::vector::iterator i = slowgraph.begin(); i != slowgraph.end(); i++) { std::string phrase = getItemAtInt(*i); j = bmsearch(doc, len, phrase); for (k = 0; k < j; k++) { existingitem = result.find(phrase); if (existingitem == result.end()) { result[phrase] = std::pair(*i, 1); } else { existingitem->second.second++; } } } if (force_quick_search || graphitems == 0) { #ifdef DEBUG_LOW DEBUG_debug("Map (quicksearch) start"); for (std::map >::iterator i = result.begin(); i != result.end(); i++) { DEBUG_debug("Map: ", i->first, " ", i->second.second); } DEBUG_debug("Map (quicksearch) end"); #endif return; } off_t sl; off_t ppos; off_t currnode = 0; int *graphdata = realgraphdata; off_t ml; char p; off_t pos; off_t depth; // number of links from root node to first letter of phrase ml = graphdata[2] + 4; // iterate over entire document for (i = 0; i < len; i++) { // iterate over all children of the root node for (j = 4; j < ml; j++) { // grab the endpoint of this link pos = realgraphdata[j]; sl = 0; // now comes the main graph search! // this is basically a depth-first tree search depth = 0; while (true) { // get the address of the link endpoint and the data actually stored at it // note that this only works for GRAPHENTRYSIZE == 64 ppos = pos << 6; if (ppos == 0) graphdata = realgraphdata; else graphdata = realgraphdata + ROOTOFFSET; p = graphdata[ppos]; // does the character at this string depth match the relevant character in the node we're currently looking at? if (p == doc[i + depth]) { // it does! // is this graph node marked as being the end of a phrase? if (graphdata[ppos + 1] == 1) { // it is, so store the pointer to the matched phrase. std::string phrase = getItemAtInt(graphdata[ppos + 3]); existingitem = result.find(phrase); if (existingitem == result.end()) { result[phrase] = std::pair(graphdata[ppos + 3], 1); } else { existingitem->second.second++; } DEBUG_debug("Found this phrase: ", phrase); } // grab this node's number of children sl = graphdata[ppos + 2]; if (sl > 0) { // this is now the node we're interested in looking at the children of currnode = ppos; // zip straight to the first child of the matched node // (this is the magic that makes it depth first) pos = graphdata[ppos + 4]; depth++; continue; } // if we just matched a node that has no children, // we can stop searching. there should be no case in // which the node was not also marked as end of phrase. else break; } if ((--sl) > 0) { // if we get here, we have discounted one child, but // we still have more children to examine from the last matched node. // we don't keep more than one current interesting node - no backtracking // is necessary, as there is only ever one occurrence of a given character as // a branch of a given node. backtracking would therefore never // trigger a match down a different route than has been taken thus far, so // don't bother. pos = graphdata[currnode + 4 + (graphdata[currnode + 2] - sl)]; continue; } // if we get here, we've discounted all branches at this depth, and the search is over. break; } } } #ifdef DEBUG_LOW DEBUG_debug("Map start"); for (std::map >::iterator i = result.begin(); i != result.end(); i++) { DEBUG_debug("Map: ", i->first, " ", i->second.second); } DEBUG_debug("Map end"); #endif } void ListContainer::graphAdd(String s, const int inx, int item) { unsigned char p = s.charAt(0); unsigned char c; bool found = false; String t; int i, px; int numlinks; int *graphdata; int *graphdata2 = realgraphdata + ROOTOFFSET; if (inx == 0) graphdata = realgraphdata; else graphdata = realgraphdata + ROOTOFFSET; //iterate over the input node's immediate children for (i = 0; i < graphdata[inx * GRAPHENTRYSIZE + 2]; i++) { //grab the character from this child c = (unsigned char) graphdata2[(graphdata[inx * GRAPHENTRYSIZE + 4 + i]) * GRAPHENTRYSIZE]; if (p == c) { //it matches the first char of our string! //keep searching, starting from here, to see if the entire phrase is already in the graph t = s; t.lop(); if (t.length() > 0) { graphAdd(t, graphdata[inx * GRAPHENTRYSIZE + 4 + i], item); return; } found = true; // this means the phrase is already there // as part of an existing phrase //check the end of word flag on the child px = graphdata2[(graphdata[inx * GRAPHENTRYSIZE + 4 + i]) * GRAPHENTRYSIZE + 1]; if (px == 1) { // the exact phrase is already there px = graphdata2[(graphdata[inx * GRAPHENTRYSIZE + 4 + i]) * GRAPHENTRYSIZE + 3]; // -1=exception // 0=banned // 1=weighted // 10 = combination exception // 11 = combination banned // 12 = combination weighted // 20,21,22 = end of combi marker if ((itemtype[px] > 9 && itemtype[item] < 10) || (itemtype[px] == 1 && itemtype[item] == 1 && (weight[item] > weight[px])) || (itemtype[px] == 1 && itemtype[item] == 0) || itemtype[item] == -1) { // exists as a combi entry already // if got here existing entry must be a combi AND // new entry is not a combi so we overwrite the // existing values as combi values and types are // stored in the combilist // OR // both are weighted phrases and the new phrase is higher weighted // OR // the existing phrase is weighted and the new phrase is banned // OR // new phrase is an exception; exception phrases take precedence itemtype[px] = itemtype[item]; weight[px] = weight[item]; categoryindex[px] = categoryindex[item]; timelimitindex[px] = timelimitindex[item]; } } } } // the phrase wasn't already in the list, so add it if (!found) { i = graphitems; graphitems++; // Reallocate memory if we're running out if (current_graphdata_size < ((GRAPHENTRYSIZE * graphitems) + ROOTOFFSET)) { int new_current_graphdata_size = (GRAPHENTRYSIZE * (graphitems + 256)) + ROOTOFFSET; realgraphdata = (int *) realloc(realgraphdata, sizeof(int) * new_current_graphdata_size); if (realgraphdata == NULL) { E2LOGGER_error("Cannot reallocate memory for phrase tree: ", strerror(errno)); exit(1); } memset(realgraphdata + current_graphdata_size, 0, sizeof(int) * (new_current_graphdata_size - current_graphdata_size)); current_graphdata_size = new_current_graphdata_size; graphdata2 = realgraphdata + ROOTOFFSET; if (inx == 0) graphdata = realgraphdata; else graphdata = realgraphdata + ROOTOFFSET; } numlinks = graphdata[inx * GRAPHENTRYSIZE + 2]; if ((inx == 0) ? ((numlinks + 1) > MAXROOTLINKS) : ((numlinks + 1) > MAXLINKS)) { E2LOGGER_error("Cannot load phraselists from this many languages/encodings simultaneously. (more than ", ((inx == 0) ? MAXROOTLINKS : MAXLINKS), " links from this node! [1])"); exit(1); } if ((numlinks + 1) > maxchildnodes) { maxchildnodes = numlinks + 1; #ifdef DEBUG_LOW prolificroot = (inx == 0); #endif } #ifdef DEBUG_LOW else if ((numlinks + 1) > secondmaxchildnodes) secondmaxchildnodes = numlinks + 1; #endif graphdata[inx * GRAPHENTRYSIZE + 2]++; graphdata[inx * GRAPHENTRYSIZE + 4 + numlinks] = i; graphdata2[i * GRAPHENTRYSIZE] = p; graphdata2[i * GRAPHENTRYSIZE + 3] = item; s.lop(); // iterate over remaining characters and add child nodes while (s.length() > 0) { numlinks = graphdata2[i * GRAPHENTRYSIZE + 2]; if ((inx == 0) ? ((numlinks + 1) > MAXROOTLINKS) : ((numlinks + 1) > MAXLINKS)) { E2LOGGER_error("Cannot load phraselists from this many languages/encodings simultaneously. (more than ", ((inx == 0) ? MAXROOTLINKS : MAXLINKS), " links from this node! [2])" ); exit(1); } if ((numlinks + 1) > maxchildnodes) { maxchildnodes = numlinks + 1; #ifdef DEBUG_LOW prolificroot = (inx == 0); #endif } #ifdef DEBUG_LOW else if ((numlinks + 1) > secondmaxchildnodes) secondmaxchildnodes = numlinks + 1; #endif graphdata2[i * GRAPHENTRYSIZE + 2]++; graphdata2[i * GRAPHENTRYSIZE + 4 + numlinks] = i + 1; i++; graphitems++; // Reallocate memory if we're running out if (current_graphdata_size < ((GRAPHENTRYSIZE * graphitems) + ROOTOFFSET)) { int new_current_graphdata_size = (GRAPHENTRYSIZE * (graphitems + 256)) + ROOTOFFSET; realgraphdata = (int *) realloc(realgraphdata, sizeof(int) * new_current_graphdata_size); if (realgraphdata == NULL) { E2LOGGER_error("Cannot reallocate memory for phrase tree: ", strerror(errno)); exit(1); } memset(realgraphdata + current_graphdata_size, 0, sizeof(int) * (new_current_graphdata_size - current_graphdata_size)); current_graphdata_size = new_current_graphdata_size; graphdata2 = realgraphdata + ROOTOFFSET; if (inx == 0) graphdata = realgraphdata; else graphdata = realgraphdata + ROOTOFFSET; } p = s.charAt(0); graphdata2[i * GRAPHENTRYSIZE] = p; graphdata2[i * GRAPHENTRYSIZE + 3] = item; s.lop(); } graphdata2[i * GRAPHENTRYSIZE + 1] = 1; } } void ListContainer::addToItemList(const char *s, size_t len) { list.push_back(data_length); lengthlist.push_back(len); for (size_t i = 0; i < len; i++) { data[data_length + i] = s[i]; } data[data_length + len] = 0; data_length += len + 1; items++; } bool ListContainer::addToTimeList(String &line) { TimeLimit tl; if (readTimeBand(line, tl)) { timelist.push_back(tl); return true; } return false; } void ListContainer::addToIPList(String &line) { if (is_map) return addToIPMap(line); RegResult Rre; // store the IP address (numerically, not as a string) and filter group in either the IP list or range list if (matchIP.match(line.toCharArray(), Rre)) { struct in_addr address; if (inet_aton(line.toCharArray(), &address)) { uint32_t addr = ntohl(address.s_addr); iplist.push_back(addr); } } else if (matchSubnet.match(line.toCharArray(), Rre)) { struct in_addr address; struct in_addr addressmask; String subnet(line.before("/")); String mask(line.after("/")); if (inet_aton(subnet.toCharArray(), &address) && inet_aton(mask.toCharArray(), &addressmask)) { ipl_rangestruct s; s.startaddr = ntohl(address.s_addr); uint32_t imask = ntohl(addressmask.s_addr); s.endaddr = s.startaddr | ~imask; iprangelist.push_back(s); } } else if (matchCIDR.match(line.toCharArray(), Rre)) { struct in_addr address; struct in_addr addressmask; String subnet(line.before("/")); String cidr(line.after("/")); int m = cidr.toInteger(); int host_part = 32 - m; if (host_part > -1) { String mask = (0xFFFFFFFF << host_part); if (inet_aton(subnet.toCharArray(), &address) && inet_aton(mask.toCharArray(), &addressmask)) { ipl_rangestruct s; s.startaddr = ntohl(address.s_addr); uint32_t imask = ntohl(addressmask.s_addr); s.endaddr = s.startaddr | ~imask; iprangelist.push_back(s); } } } else if (matchRange.match(line.toCharArray(), Rre)) { struct in_addr addressstart; struct in_addr addressend; String start(line.before("-")); String end(line.after("-")); if (inet_aton(start.toCharArray(), &addressstart) && inet_aton(end.toCharArray(), &addressend)) { ipl_rangestruct r; r.startaddr = ntohl(addressstart.s_addr); r.endaddr = ntohl(addressend.s_addr); iprangelist.push_back(r); } else { DEBUG_config("Not adding to any IP list:", line); } } } void ListContainer::addToDataMap(String &line) { String key, value; // split into key & value if (line.contains("=")) { key = line.before("="); key.removeWhiteSpace(); value = line.after("="); value.removeWhiteSpace(); if (value.startsWith("filter")) value = value.after("filter"); } else { E2LOGGER_error("No filter group given; entry %s in %s", line.toCharArray(), sourcefile.c_str()); //warn = true; return; } DEBUG_debug("key: ", key, " value: ", value); datamap d(key,value); datamaplist.push_back(d); } void ListContainer::addToIPMap(String &line) { RegResult Rre; String key, value; // split into key & value if (line.contains("=")) { key = line.before("="); key.removeWhiteSpace(); value = line.after("="); value.removeWhiteSpace(); if (value.startsWith("filter")) value = value.after("filter"); } else { E2LOGGER_error("No filter group given; entry %s in %s", line, sourcefile ); //warn = true; return; } DEBUG_debug("key: ", key, " value: ", value); if ((value.toInteger() < 1) || (value.toInteger() > o.filter.filter_groups)) { E2LOGGER_error("Filter group out of range; entry ", line, " in ", sourcefile); //warn = true; return; } // store the IP address (numerically, not as a string) and filter group in either the IP list, subnet list or range list if (matchIP.match(key.toCharArray(), Rre)) { // std::cerr << "Is straigth IP " << key << std::endl; struct in_addr address; auto aton_res = inet_aton(key.toCharArray(), &address); // std::cerr << "aton returned " << aton_res << std::endl; if (aton_res != 0) { ipmap tmap(ntohl(address.s_addr), value); ipmaplist.push_back(tmap); } } else if (matchSubnet.match(key.toCharArray(), Rre)) { // std::cerr << "Is subnet IP " << key << std::endl; struct in_addr address; struct in_addr addressmask; String subnet(key.before("/")); String mask(key.after("/")); if (inet_aton(subnet.toCharArray(), &address) && inet_aton(mask.toCharArray(), &addressmask)) { rangestruct s; s.startaddr = ntohl(address.s_addr); uint32_t imask = ntohl(addressmask.s_addr); s.endaddr = s.startaddr | ~imask; s.group = value; ipmaprangelist.push_back(s); } } else if (matchCIDR.match(key.toCharArray(), Rre)) { // std::cerr << "Is CIDR " << key << std::endl; struct in_addr address; struct in_addr addressmask; String subnet(key.before("/")); String cidr(key.after("/")); int m = cidr.toInteger(); int host_part = 32 - m; if (host_part > -1) { String mask = (0xFFFFFFFF << host_part); if (inet_aton(subnet.toCharArray(), &address) && inet_aton(mask.toCharArray(), &addressmask)) { rangestruct s; s.startaddr = ntohl(address.s_addr); uint32_t imask = ntohl(addressmask.s_addr); s.endaddr = s.startaddr | ~imask; s.group = value; ipmaprangelist.push_back(s); } } } else if (matchRange.match(key.toCharArray(), Rre)) { // std::cerr << "Is IP range " << key << std::endl; struct in_addr addressstart; struct in_addr addressend; String start(key.before("-")); String end(key.after("-")); if (inet_aton(start.toCharArray(), &addressstart) && inet_aton(end.toCharArray(), &addressend)) { rangestruct r; r.startaddr = ntohl(addressstart.s_addr); r.endaddr = ntohl(addressend.s_addr); r.group = value; ipmaprangelist.push_back(r); } } // hmmm. the key didn't match any of our regular expressions. output message & return a warning value. else { E2LOGGER_error( "Entry ", line, " in ", sourcefile, " was not recognised as an IP address, subnet or range"); //warn = true; } } // binary search list for given IP & return filter group, or -1 on failure String ListContainer::searchIPMap(int a, int s, const uint32_t &ip) { // change to serial search for testing if (false) { for (auto item : ipmaplist) { if (item.addr == ip) { return item.group; } } return ""; } if (true) { if (a > s) return ""; int m = (a + s) / 2; if (ipmaplist[m].addr == ip) return ipmaplist[m].group; if (ipmaplist[m].addr < ip) return searchIPMap(m + 1, s, ip); if (a == s) return ""; return searchIPMap(a, m - 1, ip); } } // search subnet list for given IP & return filter group or -1 //String ListContainer::inSubnetMap(const uint32_t &ip) { //for (std::list::const_iterator i = ipmapsubnetlist.begin(); i != ipmapsubnetlist.end(); ++i) { //if (i->maskedaddr == (ip & i->mask)) { //return i->group; //} //} //return ""; //} // search range list for a range containing given IP & return filter group or -1 String ListContainer::inIPRangeMap(const uint32_t &ip) { if (!ipmaprangelist.empty()) { rangestruct t; t.startaddr = ip; auto one_above = std::upper_bound(ipmaprangelist.begin(), ipmaprangelist.end(),t); if (one_above != ipmaprangelist.begin()) { auto i = one_above; i--; // move pointer to record which is the highest value that is less or equal to ip. if ((ip >= i->startaddr) && (ip <= i->endaddr)) { return i->group; } } } return ""; } String ListContainer::inIPMap(const uint32_t &ip) { if (ipmaplist.size() > 0) { return searchIPMap(0, ipmaplist.size() - 1, ip); } return ""; } // binary search list for given key & return filter group, or -1 on failure String ListContainer::searchDataMap(int a, int s, const String &key) { // change to serial search for testing if (false) { for (auto item : datamaplist) { if (item.key== key) { return item.group; } } return ""; } if (true) { if (a > s) return ""; int m = (a + s) / 2; if (datamaplist[m].key == key) return datamaplist[m].group; if (datamaplist[m].key < key) return searchDataMap(m + 1, s, key); if (a == s) return ""; return searchDataMap(a, m - 1, key); } } bool ListContainer::getMapData(String &key, String &match, String &result) { if(datamaplist.empty()) return false; result = searchDataMap(0,datamaplist.size() - 1,key); return true; } String ListContainer::getIPMapData(std::string &ip) { struct in_addr sin; inet_aton(ip.c_str(), &sin); uint32_t addr = ntohl(sin.s_addr); String fgs; String rfg; // check straight IPs, subnets, and ranges fgs = inIPMap(addr); if (fgs != "") { rfg = fgs; DEBUG_debug("Matched IP ", ip, " to straight IP list"); return rfg; } //fgs = inSubnetMap(addr); //if (fgs != "") { //rfg = fgs; //DEBUG_debug("Matched IP ", ip, " to subnet"); //return rfg; //} fgs = inIPRangeMap(addr); if (fgs != "") { rfg = fgs; DEBUG_debug("Matched IP ", ip, " to range"); return rfg; } DEBUG_debug("Matched IP ", ip, " to nothing"); return ""; } // Note: searches a reverse ordered (descending) list int ListContainer::search(int (ListContainer::*comparitor)(const char *a, const char *b), int a, int s, const char *p) { if (a > s) return (-1 - a); int m = (a + s) / 2; int r = (this->*comparitor)(p, data + list[m]); if (r == 0) return m; if (r == -1) return search(comparitor, m + 1, s, p); if (a == s) return (-1 - a); return search(comparitor, a, m - 1, p); } // The following comparator function will result in a search within a list sorted in descending order - which is counter-intuitive!!! int ListContainer::greaterThanEWF(const char *a, const char *b) { int alen = strlen(a); int blen = strlen(b); int apos = alen - 1; int bpos = blen - 1; for (int maxlen = (alen < blen ? alen : blen); maxlen > 0; apos--, bpos--, maxlen--) if (a[apos] > b[bpos]) return 1; else if (a[apos] < b[bpos]) return -1; if (alen > blen) return 1; else if (alen < blen) return -1; return 0; // both equal } // The following comparator function will result in a search within a list sorted in descending order - which is counter-intuitive!!! int ListContainer::greaterThanSWF(const char *a, const char *b) { int alen = strlen(a); int blen = strlen(b); int maxlen = alen < blen ? alen : blen; for (int i = 0; i < maxlen; i++) if (a[i] > b[i]) return 1; else if (a[i] < b[i]) return -1; if (alen > blen) return 1; else if (alen < blen) return -1; return 0; // both equal } int ListContainer::greaterThanSW(const char *a, const char *b) { int alen = strlen(a); int blen = strlen(b); int maxlen = alen < blen ? alen : blen; for (int i = 0; i < maxlen; i++) if (a[i] > b[i]) return 1; else if (a[i] < b[i]) return -1; // if the URLs didn't match and the one the user is browsing to is longer // than what we just compared against, we need to compare against longer URLs, // but only if the next character is actually part of a folder name rather than a separator. // ('cos if it *is* a separator, it doesn't matter that the overall URL is longer, the // beginning of it matches a banned URL.) if ((alen > blen) && !(a[blen] == '/' || a[blen] == '?' || a[blen] == '&' || a[blen] == '=')) return 1; // if the banned URL is longer than the URL we're checking, the two // can't possibly match. else if (blen > alen) return -1; return 0; // both equal } int ListContainer::greaterThanEW(const char *a, const char *b) { int alen = strlen(a); int blen = strlen(b); int apos = alen - 1; int bpos = blen - 1; for (int maxlen = (alen < blen ? alen : blen); maxlen > 0; apos--, bpos--, maxlen--) if (a[apos] > b[bpos]) return 1; else if (a[apos] < b[bpos]) return -1; if (blen > alen) return -1; return 0; // both equal } bool ListContainer::increaseMemoryBy(size_t bytes) { if (data_memory > 0) { data = (char *) realloc(data, (data_memory + bytes) * sizeof(char)); if (data == nullptr) return false; memset(data + data_memory, 0, bytes * sizeof(char)); data_memory += bytes; } else { free(data); data = (char *) calloc(bytes, sizeof(char)); if (data == nullptr) return false; memset(data + data_memory, 0, bytes * sizeof(char)); data_memory = bytes; } return true; } size_t getFileLength(const char *filename) { struct stat status; int rc = stat(filename, &status); if (rc < 0) throw std::runtime_error(strerror(errno)); return status.st_size; } time_t getFileDate(const char *filename) { struct stat status; int rc = stat(filename, &status); if (rc != 0) { if (errno == ENOENT) { E2LOGGER_error("*List Error* opening ",filename, ":", strerror(errno)); return 0; } // If there are permission problems, just reload the file (CN) if (errno == EACCES) { E2LOGGER_error("*List Error* opening ", filename, ". Check directory and file permissions and ownership. They should be 750 and 640 and readable by the e2guardian user: ", strerror(errno)); return 0; } else { E2LOGGER_error("*List Error* opening ", filename, ". Check directory and file permissions and ownership. They should be 750 and 640 and readable by the e2guardian user: ", strerror(errno)); return 0; //return sysv_kill(o.pid_filename); } } return status.st_mtime; } #ifdef NODEF time_t getFileDate(const char *filename) { struct stat status; int rc = stat(filename, &status); if (rc != 0) { if (errno == ENOENT) return 0; else throw std::runtime_error(strerror(errno)); } return status.st_mtime; } #endif bool ListContainer::upToDate() { if (sourcefile.startsWith("memory:")) return true; if (getFileDate(sourcefile.toCharArray()) > filedate) { return false; } for (unsigned int i = 0; i < morelists.size(); i++) { if (!(*o.lm.l[morelists[i]]).upToDate()) { return false; } } return true; } bool ListContainer::readTimeTag(String *tag, TimeLimit &tl) { DEBUG_config("Found a time tag"); String temp((*tag).after("#time: ")); return readTimeBand(temp, tl); } bool ListContainer::readTimeBand(String &tag, TimeLimit &tl) { String temp(tag); unsigned int tsthour, tstmin, tendhour, tendmin; temp.removeWhiteSpace(); tsthour = temp.before(" ").toInteger(); temp = temp.after(" "); temp.removeWhiteSpace(); tstmin = temp.before(" ").toInteger(); temp = temp.after(" "); temp.removeWhiteSpace(); tendhour = temp.before(" ").toInteger(); temp = temp.after(" "); temp.removeWhiteSpace(); tendmin = temp.before(" ").toInteger(); String tdays(temp.after(" ")); tdays.removeWhiteSpace(); if (tsthour > 23) { E2LOGGER_error("Time Tag Start Hour over bounds."); return false; } if (tendhour > 23) { E2LOGGER_error("Time Tag End Hour over bounds."); return false; } if (tstmin > 59) { E2LOGGER_error("Time Tag Start Min over bounds."); return false; } if (tendmin > 59) { E2LOGGER_error("Time Tag End Min over bounds."); return false; } if (tdays.length() > 7) { E2LOGGER_error("Time Tag Days over bounds."); return false; } istimelimited = true; tl.sthour = tsthour; tl.stmin = tstmin; tl.endhour = tendhour; tl.endmin = tendmin; tl.days = tdays; tl.timetag = tag; return true; } // Returns true if the current time is within the limits specified on this list. // For phrases, the time limit list index must be passed in - // included lists don't have their own ListContainer, so time limits are stored differently. bool ListContainer::isNow(int index) { if (!istimelimited) { return true; } TimeLimit tl = listtimelimit; if (index > -1) { tl = timelimits[index]; } return isNow(tl); } // Used for timelists only - returns true if current time is in list bool ListContainer::isNowInTimelist() { if (timelist.size() > 0) { for (unsigned int i = 0; i < timelist.size(); i++) { if (isNow(timelist[i])) return true; } } return false; } bool ListContainer::isNow(TimeLimit &tl) { time_t tnow; // to hold the result from time() struct tm *tmnow; // to hold the result from localtime() unsigned int hour, min, wday; time(&tnow); // get the time after the lock so all entries in order tmnow = localtime(&tnow); // convert to local time (BST, etc) hour = tmnow->tm_hour; min = tmnow->tm_min; wday = tmnow->tm_wday; // wrap week to start on Monday if (wday == 0) { wday = 7; } wday--; unsigned char cday = '0' + wday; bool matchday = false; for (unsigned int i = 0; i < tl.days.length(); i++) { if (tl.days[i] == cday) { matchday = true; break; } } if (!matchday) { return false; } if (hour < tl.sthour) { return false; } if (hour > tl.endhour) { return false; } if (hour == tl.sthour) { if (min < tl.stmin) { return false; } } if (hour == tl.endhour) { if (min > tl.endmin) { return false; } } DEBUG_debug("time match ", tl.sthour, ":", tl.stmin, "-", tl.endhour, ":", tl.endmin, " ", hour, ":", min, " ", sourcefile); return true; } int ListContainer::getCategoryIndex(String *lcat) { // where in the category list is our category? if nowhere, add it. if ((*lcat).length() < 2) { DEBUG_debug("blank entry index"); return 0; // blank entry index } int l = (signed) listcategory.size(); int i; for (i = 0; i < l; i++) { if ((*lcat) == listcategory[i]) { return i; } } listcategory.push_back((*lcat)); return l; } String ListContainer::getListCategoryAt(unsigned int index, unsigned int *catindex) { //category index of -1 indicates uncategorised list if ((index >= categoryindex.size()) || (categoryindex[index] < 0)) { return ""; } //return category index (allows naughtyfilter to do numeric comparison between categories //when doing duplicate search; much faster than string comparison) if (catindex != NULL) { (*catindex) = categoryindex[index]; } return listcategory[categoryindex[index]]; } String ListContainer::getListCategoryAtD(unsigned int index) { //category index of -1 indicates uncategorised list if ((index < 0) || (index >= listcategory.size())) { return ""; } return listcategory[index]; } // search for IP in list of individual IPs, ranges, subnets bool ListContainer::inIPList(const std::string &ipstr, String &match) { // const char *ListContainer::inIPList(const std::string &ipstr) { struct in_addr addr; inet_aton(ipstr.c_str(), &addr); uint32_t ip = ntohl(addr.s_addr); // start with individual IPs if ((iplist.size() > 0) && std::binary_search(iplist.begin(), iplist.end(), ip)) { // only return a hostname if that's what we matched against match = hIPtoChar(ip); return true; } // ranges if (!iprangelist.empty()) { ipl_rangestruct t; t.startaddr = ip; auto one_above = std::upper_bound(iprangelist.begin(), iprangelist.end(),t); if (one_above != iprangelist.begin()) { auto i = one_above; i--; // move pointer to record which is the highest value that is less or equal to ip. if ((ip >= i->startaddr) && (ip <= i->endaddr)) { match = hIPtoChar(i->startaddr); match += "-"; match += hIPtoChar(i->endaddr); return true; } } } //if (iprangelist.size() > 0) { //for (std::vector::const_iterator i = iprangelist.begin(); i != iprangelist.end(); ++i) { //if ((ip >= i->startaddr) && (ip <= i->endaddr)) { //String ret = hIPtoChar(i->startaddr); //ret += "-"; //ret += hIPtoChar(i->endaddr); //match = ret; //return true; //} //} //} // subnets //if (ipsubnetlist.size() > 0) { //for (std::list::const_iterator i = ipsubnetlist.begin(); i != ipsubnetlist.end(); ++i) { //if (i->maskedaddr == (ip & i->mask)) { //String ret = hIPtoChar(i->maskedaddr); //ret += "/"; //ret += hIPtoChar(i->mask); //match = ret; //return true; //} //} //} DEBUG_debug("inIPList ", category, " no match for ", ipstr); return false; } const char *ListContainer::hIPtoChar(uint32_t ip) { struct in_addr addr; addr.s_addr = htonl(ip); return inet_ntoa(addr); } void ListContainer::dump_data() { DEBUG_debug("List size is ",list.size()); if(list.size() > 0) { for (std::vector::const_iterator i = list.begin(); i != list.end(); ++i) { DEBUG_debug("LD:",data + *i); } } } bool ListContainer::self_check() { DEBUG_debug("List size is ", list.size()); int ok_cnt = 0, cnt = 0; if (list.size() > 0) { for (std::vector::const_iterator i = list.begin(); i != list.end(); ++i) { // DEBUG_debug("LD:", data + *i); int r; if (isSW) { r = search(&ListContainer::greaterThanSWF, 0, items - 1, data + *i); } else { r = search(&ListContainer::greaterThanEWF, 0, items - 1, data + *i); } if (r >= 0) { ok_cnt++; } cnt++; } if (ok_cnt < cnt) { DEBUG_config("LC: NOTOK ", ok_cnt, "/", cnt); return true; } else { DEBUG_config("LC: OK ", ok_cnt, "/", cnt); return false; } } return true; } e2guardian-5.5.8r/src/ListContainer.hpp000066400000000000000000000213171477372360500200340ustar00rootroot00000000000000// ListContainer class - for item and phrase lists // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_LISTCONTAINER #define __HPP_LISTCONTAINER // INLCUDES #include #include #include #include #include #include #include "String.hpp" #include "RegExp.hpp" #include "IPList.hpp" // DECLARATIONS // time limit information struct TimeLimit { unsigned int sthour, stmin, endhour, endmin; String days, timetag; }; // class for linking IPs to filter groups, complete with comparison operators // allowing standard C++ sort to work class ipmap { public: ipmap(uint32_t a, String g) { addr = a; group = g; }; uint32_t addr; String group; int operator<(const ipmap &a) const { return addr < a.addr; }; int operator<(const uint32_t &a) const { return addr < a; }; int operator==(const uint32_t &a) const { return a == addr; }; }; class datamap { public: datamap(String &k, String &g){ key = k; group = g; }; String key; String group; int operator<(const datamap &a) const { if (key.compare(a.key) < 0) return 1; return 0; }; int operator==(const String &a) const { if( key.compare(a) == 0) return 1; return 0; }; }; // structs linking subnets and IP ranges to filter groups //struct subnetstruct { //uint32_t maskedaddr; //uint32_t mask; //String group; //}; class rangestruct { public: uint32_t startaddr; uint32_t endaddr; String group; int operator<(const rangestruct &a) const { if (startaddr < a.startaddr) return 1; return 0; } }; time_t getFileDate(const char *filename); size_t getFileLength(const char *filename); class ListContainer { public: std::vector combilist; bool is_iplist = false; bool is_timelist = false; bool is_map = false; int refcount = 0; bool parent = false; time_t filedate; bool used = false; String bannedpfile; String exceptionpfile; String weightedpfile; int naughtynesslimit = 0; // used for phrase lists only time_t bannedpfiledate; time_t exceptionpfiledate; time_t weightedpfiledate; String sourcefile; // used for non-phrase lists only String category; //String lastcategory; std::vector morelists; // has to be non private as reg exp compiler needs to access these bool read_errors = false; ListContainer(); ~ListContainer(); void reset(); bool readPhraseList(const char *filename, bool isexception, int catindex = -1, int timeindex = -1, bool incref = true, int nlimit=0); bool ifsreadItemList(const char *filename,std::istream *input, String basedir, const char *list_pwd, int len, bool checkendstring, const char *endstring, bool do_includes, bool startswith, int filters); bool ifsReadSortItemList(std::ifstream *input, String basedir, const char *list_pwd, bool checkendstring, const char *endstring, bool do_includes, bool startswith, int filters, const char *filename); bool readItemList(const char *filename, const char *pwd, bool startswith, int filters, bool isip = false, bool istime = false, bool ismap = false); bool readStdinItemList(bool startswith, int filters, const char *filename); bool inList(const char *string, String &lastcategory); bool inListEndsWith(const char *string, String &lastcategory); bool inListStartsWith(const char *string, String &lastcategory); bool findInList(const char *string, String &lastcategory, String &match, String &result); void dump_data(); bool findEndsWith(const char *string, String &lastcategory, String &match); bool findStartsWith(const char *string, String &lastcategory, String &match); char *findStartsWithPartial(const char *string, String &lastcategory, String &match); String searchIPMap(int a, int s, const uint32_t &ip); String searchDataMap(int a, int s, const String &key); // String inSubnetMap(const uint32_t &ip); String inIPRangeMap(const uint32_t &ip); int getListLength() { return items; } std::string getItemAtInt(int index); int getWeightAt(unsigned int index); int getTypeAt(unsigned int index); void doSort(const bool startsWith); //bool createCacheFile(); bool makeGraph(bool fqs); bool previousUseItem(const char *filename, bool startswith, int filters); bool upToDate(); String getListCategoryAt(unsigned int index, unsigned int *catindex = NULL); String getListCategoryAtD(unsigned int index); void graphSearch(std::map > &result, char *doc, off_t len); bool isNow(int index = -1); // used for normal and phrase lists bool isNowInTimelist(); // used for timelists bool isNow(TimeLimit &tl); bool checkTimeAt(unsigned int index); bool checkTimeAtD(int index); //bool blanketblock; //bool blanket_ip_block; //bool blanketsslblock; //bool blanketssl_ip_block; private: bool sourceisexception = false; bool sourcestartswith = false; int sourcefilters = 0; char *data = nullptr; // Format of the data is each entry has 64 int values with format of: // [letter][last letter flag][num links][from phrase][link0][link1]... int *realgraphdata = nullptr; int current_graphdata_size = 0; #ifdef DEBUG_LOW bool prolificroot = false; int secondmaxchildnodes = 0; #endif int maxchildnodes = 0; int graphitems = 0; std::vector slowgraph; size_t data_length = 0; size_t data_memory = 0; long int items = 0; bool isSW = false; bool issorted = false; bool graphused = false; std::vector list; std::vector lengthlist ; std::vector weight; std::vector itemtype; // 0=banned, 1=weighted, -1=exception bool force_quick_search = false; //time-limited lists - only items (sites, URLs), not phrases TimeLimit listtimelimit; bool istimelimited = false; bool self_check(); //categorised lists - both phrases & items std::vector listcategory; std::vector categoryindex; // set of time limits for phrase lists std::vector timelimitindex; std::vector timelimits; RegExp matchIP, matchSubnet, matchRange, matchCIDR; //iplists std::vector iplist; std::vector iprangelist; //std::list ipsubnetlist; std::vector ipmaplist; std::vector ipmaprangelist; // std::list ipmapsubnetlist; //std::list datamaplist; std::vector datamaplist; //timelists std::vector timelist; bool readAnotherItemList(const char *filename, const char *list_pwd, bool startswith, int filters, int &list_no); void readPhraseListHelper(String line, bool isexception, int catindex, int timeindex, int &nlimit); void readPhraseListHelper2(String phrase, int type, int weighting, int catindex, int timeindex); bool addToItemListPhrase(const char *s, size_t len, int type, int weighting, bool combi, int catindex, int timeindex); void graphSizeSort(int l, int r, std::deque *sizelist); void graphAdd(String s, const int inx, int item); int graphFindBranches(unsigned int pos); void graphCopyNodePhrases(unsigned int pos); int bmsearch(char *file, off_t fl, const std::string &s); bool readProcessedItemList(const char *filename, bool startswith, int filters); void addToItemList(const char *s, size_t len); void addToIPList(String &line); void addToIPMap(String &line); void addToDataMap(String &line); bool addToTimeList(String &line); int greaterThanEWF(const char *a, const char *b); // full match int greaterThanEW(const char *a, const char *b); // partial ends with int greaterThanSWF(const char *a, const char *b); // full match int greaterThanSW(const char *a, const char *b); // partial starts with int search(int (ListContainer::*comparitor)(const char *a, const char *b), int a, int s, const char *p); bool increaseMemoryBy(size_t bytes); //categorised & time-limited lists support bool readTimeTag(String *tag, TimeLimit &tl); bool readTimeBand(String &tag, TimeLimit &tl); int getCategoryIndex(String *lcat); bool inIPList(const std::string &ipstr ,String &match); String getIPMapData(std::string &ip); const char *hIPtoChar(uint32_t ip); String inIPMap(const uint32_t &ip); bool getMapData(String &key, String &match, String &result); }; #endif e2guardian-5.5.8r/src/ListManager.cpp000066400000000000000000000167431477372360500174660ustar00rootroot00000000000000// ListManager - contains the ListContainers for all item and phrase lists, and can create new ones // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "ListManager.hpp" #include "Logger.hpp" #include #include #include // GLOBALS extern thread_local std::string thread_id; // IMPLEMENTATION ListManager::~ListManager() { for (unsigned int i = 0; i < l.size(); i++) { if (l[i] != NULL) { delete l[i]; l[i] = NULL; } } } void ListManager::clear() { for (unsigned int i = 0; i < l.size(); i++) { if (l[i] != NULL) { delete l[i]; l[i] = NULL; } } } // find an unused list in our collection of lists int ListManager::findNULL() { for (unsigned int i = 0; i < l.size(); i++) { if (l[i] == NULL) { DEBUG_debug("found free list:", std::to_string(i)); // std::cerr << thread_id << "found free list:" << i << std::endl; return (signed)i; } } return -1; } // delete all lists with zero reference count void ListManager::garbageCollect() { for (unsigned int i = 0; i < l.size(); i++) { if (l[i] != NULL) { if ((*l[i]).refcount < 1) { DEBUG_debug("deleting zero ref list: ", String(i), " ", String(l[i]->refcount) ); delete l[i]; l[i] = NULL; } } } } void ListManager::deRefList(size_t i) { if (l[i] == NULL) return; l[i]->refcount--; for (size_t j = 0; j < l[i]->morelists.size(); ++j) deRefList(l[i]->morelists[j]); } void ListManager::refList(size_t i) { l[i]->refcount++; DEBUG_debug("referencing list ref: ", String(i), ", refcount: ", String(l[i]->refcount), " (", l[i]->sourcefile, ")" ); for (size_t j = 0; j < l[i]->morelists.size(); ++j) refList(l[i]->morelists[j]); } // load the given list, or increase refcount on list if it's already been loaded. int ListManager::newItemList(const char *filename, const char *pwd, bool startswith, int filters, bool parent, bool isip, bool istime, bool ismap) { for (size_t i = 0; i < l.size(); i++) { if (l[i] == NULL) { continue; } if ((*l[i]).previousUseItem(filename, startswith, filters)) { // this upToDate check also checks all .Included files if ((*l[i]).upToDate()) { DEBUG_debug("Using previous item: ", String(i), " ", filename); refList(i); return i; } } } // find an empty list slot, create a new listcontainer, and load the list int free = findNULL(); if (free > -1) { l[free] = new ListContainer; } else { l.push_back(new ListContainer); free = l.size() - 1; } (l[free])->parent = parent; if (!(l[free]->readItemList(filename, pwd, startswith, filters, isip, istime, ismap))) { delete l[free]; l[free] = NULL; return -1; } return free; } #ifdef NOTDEF // load list from stdin int ListManager::newStdinItemList(bool startswith, int filters, bool parent) { char *filename; // find an empty list slot, create a new listcontainer, and load the list int free = findNULL(); if (free > -1) { l[free] = new ListContainer; } else { l.push_back(new ListContainer); free = l.size() - 1; } (*l[free]).parent = parent; if (!(*l[free]).readStdinItemList(startswith, filters, filename)) { delete l[free]; l[free] = NULL; return -1; } return free; } #endif // create a new phrase list. check dates on top-level list files to see if a reload is necessary. // note: unlike above, doesn't automatically call readPhraseList. // pass in exception, banned, and weighted phrase lists all at once. int ListManager::newPhraseList(const char *exception, const char *banned, const char *weighted, int nlimit) { if ( !strlen(exception) ) { E2LOGGER_error("missing exception phrase file "); return -1; } if ( !strlen(banned) ) { E2LOGGER_error("missing banned phrase file "); return -1; } if ( !strlen(weighted) ) { E2LOGGER_error("missing weighted phrase file "); return -1; } time_t bannedpfiledate = getFileDate(banned); time_t exceptionpfiledate = getFileDate(exception); time_t weightedpfiledate = getFileDate(weighted); for (size_t i = 0; i < l.size(); i++) { if (l[i] == NULL) { continue; } if ((*l[i]).exceptionpfile == String(exception) && (*l[i]).bannedpfile == String(banned) && (*l[i]).weightedpfile == String(weighted) && (*l[i]).naughtynesslimit == nlimit) { if (bannedpfiledate <= (*l[i]).bannedpfiledate && exceptionpfiledate <= (*l[i]).exceptionpfiledate && weightedpfiledate <= (*l[i]).weightedpfiledate) { // Known limitation - only weighted, exception, banned phrase // list checked for changes - not the included files. // //need to check all files that were included for phrase list //so when phrases read in in list container it needs to store //all the file names and if a single one has changed needs a //complete regenerate DEBUG_debug("Using previous phrase: ", exception, " - ", banned, " - ", weighted); refList(i); return i; } } } int free = findNULL(); if (free > -1) { l[(unsigned)free] = new ListContainer; } else { l.push_back(new ListContainer); free = l.size() - 1; } (*l[(unsigned)free]).parent = true; // all phrase lists are parent as // there are no sub lists (*l[(unsigned)free]).bannedpfiledate = bannedpfiledate; (*l[(unsigned)free]).exceptionpfiledate = exceptionpfiledate; (*l[(unsigned)free]).weightedpfiledate = weightedpfiledate; (*l[(unsigned)free]).exceptionpfile = exception; (*l[(unsigned)free]).bannedpfile = banned; (*l[(unsigned)free]).weightedpfile = weighted; return (unsigned)free; } bool ListManager::readbplfile(const char *banned, const char *exception, const char *weighted, unsigned int &list, bool force_quick_search, int nlimit) { bool return_error = false; int res = newPhraseList(exception, banned, weighted, nlimit); if (res < 0) { E2LOGGER_error("Error opening phraselists"); return_error = true; // return false; } if (!(*l[res]).used) { DEBUG_debug("Reading new phraselists"); bool result = (*l[res]).readPhraseList(exception, true); if (!result) { E2LOGGER_error("Error opening exceptionphraselist"); return_error = true; // return false; } result = (*l[res]).readPhraseList(banned, false, -1, -1, false,nlimit); if (!result) { E2LOGGER_error("Error opening bannedphraselist"); return_error = true; // return false; } result = (*l[res]).readPhraseList(weighted, false, -1, -1, false,nlimit); if (!result) { E2LOGGER_error("Error opening weightedphraselist"); return_error = true; //return false; } if (return_error) return false; if (!(*l[res]).makeGraph(force_quick_search)) return false; (*l[res]).used = true; } list = res; return true; } e2guardian-5.5.8r/src/ListManager.hpp000066400000000000000000000030121477372360500174540ustar00rootroot00000000000000// ListManager - for creating & containing all ListContainers of item & phrase lists // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_LISTMANAGER #define __HPP_LISTMANAGER // INCLUDES #include "String.hpp" #include "ListContainer.hpp" #include // DECLARATION class ListManager { public: // the lists we manage std::deque l; ~ListManager(); void clear(); // create a new item list. re-uses existing lists if a reload is not necessary. // calls readItemList. int newItemList(const char *filename, const char *list_pwd, bool startswith, int filters, bool parent, bool isip =false, bool istime = false, bool ismap = false); //int newStdinItemList(bool startswith, int filters, bool parent ); // create a new phrase list. re-uses existing lists, but cannot check nested lists (known limitation). // does not call readPhraseList. (checkme: why?) int newPhraseList(const char *exception, const char *banned, const char *weighted, int nlimit); bool readbplfile(const char *banned, const char *exception, const char *weighted, unsigned int &list, bool force_quick_search, int nlimit); void deRefList(size_t item); // delete lists with refcount zero void garbageCollect(); private: // find an empty slot in our collection of listcontainters int findNULL(); void refList(size_t item); }; #endif e2guardian-5.5.8r/src/ListMeta.cpp000066400000000000000000000726471477372360500170070ustar00rootroot00000000000000// ListMeta - super-class for both item and phrase lists // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "ListContainer.hpp" #include "ListMeta.hpp" #include "OptionContainer.hpp" #include "RegExp.hpp" #include "Logger.hpp" #include #include #include #include #include #include #include #include #include #include #include // GLOBALS extern OptionContainer o; // DEFINES // Constructor - set default values ListMeta::ListMeta() { } // delete the memory block when the class is destryed ListMeta::~ListMeta() { reset(); } String ListMeta::list_type(int type) { if (type > LIST_TYPE_ERROR || type < 0) type = LIST_TYPE_ERROR; return type_map[type]; } // clear & reset all values void ListMeta::reset() { for (std::vector::iterator i = list_vec.begin(); i != list_vec.end(); i++) { o.lm.deRefList(i->list_ref); i->comp.clear(); i->reg_list_ref.clear(); } } bool ListMeta::load_type(int type, std::deque &list) { unsigned int method_type = 0; switch (type) { case LIST_TYPE_IP : method_type = LIST_METHOD_IP; break; case LIST_TYPE_IPSITE : method_type = LIST_METHOD_IP; break; case LIST_TYPE_SITE : method_type = LIST_METHOD_READF_EWS; break; case LIST_TYPE_URL : method_type = LIST_METHOD_READF_SWS; break; case LIST_TYPE_SEARCH: method_type = LIST_METHOD_READF_SWS; break; case LIST_TYPE_REGEXP_BOOL: method_type = LIST_METHOD_REGEXP_BOOL; break; case LIST_TYPE_REGEXP_REP : method_type = LIST_METHOD_REGEXP_REPL; break; case LIST_TYPE_FILE_EXT: method_type = LIST_METHOD_READF_EWS; break; case LIST_TYPE_MIME: method_type = LIST_METHOD_READF_SWS; break; case LIST_TYPE_TIME: method_type = LIST_METHOD_TIME; break; case LIST_TYPE_MAP: method_type = LIST_METHOD_MAP; break; case LIST_TYPE_IPMAP: method_type = LIST_METHOD_IPMAP; break; case LIST_TYPE_CATEGORY: method_type = LIST_METHOD_READF_SWS; break; // PhraseList types to be added } bool errors = false; int dq_size = list.size(); for (int i = dq_size - 1; i > -1; i--) { // search backward thru list // parse line String t; t = list[i]; DEBUG_debug("reading ", t); String nm, fpath, pwd; bool anonlog = false; bool sitewild = true; unsigned int m_no = 0, log_m_no = 0; t.removeWhiteSpace(); t.removeChar('\''); t += ","; while (t.length() > 0) { if (t.startsWith("name=")) { nm = t.after("=").before(","); } else if (t.startsWith("messageno=")) { m_no = t.after("=").before(",").toInteger(); } else if (t.startsWith("logmessageno=")) { log_m_no = t.after("=").before(",").toInteger(); } else if (t.startsWith("path=")) { fpath = t.after("=").before(","); } else if (t.startsWith("listdir=")) { pwd = t.after("=").before(","); } else if (t.startsWith("anonlog=true")) { anonlog = true; } else if (t.startsWith("sitewild=false")) { sitewild = false; } t = t.after(","); t.removeWhiteSpace(); } if (list_exists(nm, type)) { E2LOGGER_info("List name '", nm, "' of this type already defined - ignoring", t); errors = true; continue; } list_info rec; rec.type = type; rec.method_type = method_type; rec.name = nm; rec.pwd = pwd; rec.mess_no = m_no; rec.anon_log = anonlog; rec.site_wild = sitewild; if (log_m_no) { rec.log_mess_no = log_m_no; } else { rec.log_mess_no = m_no; } DEBUG_debug("List name = ", nm, " m_no=", m_no, " log_m_no=", rec.log_mess_no, " path=", fpath); switch (method_type) { case LIST_METHOD_IP: if (readFile(fpath.toCharArray(), pwd.toCharArray(), &rec.list_ref, false, nm.toCharArray(), true)) { list_vec.push_back(rec); if (o.lm.l[rec.list_ref]->read_errors) errors = true; } else { E2LOGGER_error("Unable to read ", fpath); errors = true; }; break; case LIST_METHOD_IPMAP: if (readFile(fpath.toCharArray(), pwd.toCharArray(), &rec.list_ref, false, nm.toCharArray(), true, false, true)) { list_vec.push_back(rec); if (o.lm.l[rec.list_ref]->read_errors) errors = true; } else { E2LOGGER_error("Unable to read ", fpath); errors = true; }; break; case LIST_METHOD_MAP: if (readFile(fpath.toCharArray(), pwd.toCharArray(), &rec.list_ref, false, nm.toCharArray(), false, false, true)) { list_vec.push_back(rec); if (o.lm.l[rec.list_ref]->read_errors) errors = true; } else { E2LOGGER_error("Unable to read ", fpath); errors = true; }; break; case LIST_METHOD_TIME: if (readFile(fpath.toCharArray(), pwd.toCharArray(),&rec.list_ref, false, nm.toCharArray(), false, true)) { list_vec.push_back(rec); if (o.lm.l[rec.list_ref]->read_errors) errors = true; } else { E2LOGGER_error("Unable to read ", fpath); errors = true; }; break; case LIST_METHOD_READF_EWS : if (readFile(fpath.toCharArray(), pwd.toCharArray(), &rec.list_ref, false, nm.toCharArray())) { list_vec.push_back(rec); if (o.lm.l[rec.list_ref]->read_errors) errors = true; } else { E2LOGGER_error("Unable to read ", fpath); errors = true; }; break; case LIST_METHOD_READF_SWS : if (readFile(fpath.toCharArray(), pwd.toCharArray(), &rec.list_ref, true, nm.toCharArray())) { // test dump //DEBUG_debug(fpath, " read "); //o.lm.l[rec.list_ref]->dump_data(); // temp test only list_vec.push_back(rec); if (o.lm.l[rec.list_ref]->read_errors) errors = true; } else { E2LOGGER_error("Unable to read ", fpath); errors = true; }; break; case LIST_METHOD_REGEXP_BOOL : if (readRegExMatchFile(fpath.toCharArray(), pwd.toCharArray(), nm.toCharArray(), rec.list_ref, rec.comp, rec.source, rec.reg_list_ref)) { list_vec.push_back(rec); if (o.lm.l[rec.list_ref]->read_errors) errors = true; } else { E2LOGGER_error("Unable to read ", fpath); errors = true; }; break; case LIST_METHOD_REGEXP_REPL : if (readRegExReplacementFile(fpath.toCharArray(), pwd.toCharArray(),nm.toCharArray(), rec.list_ref, rec.replace, rec.comp)) { list_vec.push_back(rec); if (o.lm.l[rec.list_ref]->read_errors) errors = true; } else { E2LOGGER_error("Unable to read ", fpath); errors = true; }; break; } } if (errors) { return false; } // if (errors && o.lists.abort_on_missing_list) return false; return true; } bool ListMeta::list_exists(String name, int type) { if (findList(name, (int) type).name != "") return true; else return false; } ListMeta::list_info ListMeta::findList(String name, int tp) { list_info t; list_info *tptr; tptr = findListPtr(name, tp); if (tptr) { t = *tptr; } else t.list_ref = 0; return t; } ListMeta::list_info *ListMeta::findListPtr(String name, int tp) { list_info *t = nullptr; unsigned int type = (unsigned int) tp; //DEBUG_debug("Looking for ", name, " type ", type, " in listmeta"); for (std::vector::iterator i = list_vec.begin(); i != list_vec.end(); i++) { if (i->name == name && i->type == type) { //DEBUG_debug("Found ", i->name, " type ", i->type, " in listmeta"); t = &(*i); return t; } } //DEBUG_debug("Not Found ", name, " type ", type, " in listmeta"); return t; } unsigned int ListMeta::findListId(String name, int type) { list_info t; t.list_ref = 0; t = findList(name, type); return t.list_ref; } bool ListMeta::inList(String name, int type, String &tofind, list_result &res) { list_info info = findList(name, type); return inList(info, tofind, res); } bool ListMeta::inList(list_info &info, std::deque &header, list_result &res) { // this is only used for checking headers if (info.name == "") return false; int type = info.type; //char *match; switch (type) { case LIST_TYPE_REGEXP_BOOL : { int rc = inHeaderRegExp(info, header, res, res.category); if (rc == -1) { return false; } else { res.category = o.lm.l[info.reg_list_ref[rc]]->category; res.match = info.source[rc]; res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } } case LIST_TYPE_REGEXP_REP: { if (info.comp.size() == 0) return false; if (headerRegExpReplace(info, header, res)) { res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } return false; } } return false; } bool ListMeta::inList(list_info &info, String &tofind, list_result &res) { if (info.name == "") return false; int type = info.type; //const char *match; bool match; // String match; switch (type) { case LIST_TYPE_IP: match = o.lm.l[info.list_ref]->findInList(tofind.toCharArray(), res.category, res.match, res.result); if (!match) { return false; } else { // res.match = match; res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } break; case LIST_TYPE_IPMAP: case LIST_TYPE_MAP: match = o.lm.l[info.list_ref]->findInList(tofind.toCharArray(), res.category, res.match, res.result); if (!match) { return false; } else { // res.result = match; res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } break; case LIST_TYPE_TIME: if (o.lm.l[info.list_ref]->isNowInTimelist()) { res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } return false; break; case LIST_TYPE_IPSITE: match = o.lm.l[info.list_ref]->findInList(tofind.toCharArray(), res.category, res.match, res.result); if (!match) { return false; } else { //res.match = match; res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } break; case LIST_TYPE_SITE : match = inSiteList(tofind, info.list_ref, res.category,info.site_wild, res.match, res.result); if (!match) { return false; } else { //res.match = match; res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } break; case LIST_TYPE_URL: match = inURLList(tofind, info.list_ref, res.category,info.site_wild, res.match, res.result); if (!match) { return false; } else { //res.match = match; res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } break; case LIST_TYPE_SEARCH : match = inSearchList(tofind, info.list_ref, res.category, res.match, res.result); if (!match) { return false; } else { // res.match = match; res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } break; case LIST_TYPE_MIME: case LIST_TYPE_CATEGORY: match = o.lm.l[info.list_ref]->findInList(tofind.toCharArray(), res.category, res.match, res.result); if (!match) { return false; } else { // res.match = match; res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } break; case LIST_TYPE_FILE_EXT: match = o.lm.l[info.list_ref]->findEndsWith(tofind.toCharArray(), res.category, res.match); if (!match) { return false; } else { res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } break; case LIST_TYPE_REGEXP_BOOL : { int rc = inRegExpURLList(tofind, info.comp, info.reg_list_ref, info.list_ref, res.category); if (rc == -1) { return false; } else { res.category = o.lm.l[info.reg_list_ref[rc]]->category; res.match = info.source[rc]; res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } } break; case LIST_TYPE_REGEXP_REP: { if (info.comp.size() == 0) return false; String modified = tofind; if (regExp(modified, info.comp, info.replace)) { res.result = modified; res.mess_no = info.mess_no; res.log_mess_no = info.log_mess_no; res.anon_log = info.anon_log; return true; } return false; } break; } return false; } // read in the given file, write the list's ID into the given identifier, // sort using startsWith or endsWith depending on sortsw, and create a cache file if desired. // listname is used in error messages. bool ListMeta::readFile(const char *filename, const char *pwd, unsigned int *whichlist, bool sortsw, const char *listname, bool isip, bool istime, bool ismap) { DEBUG_config("read File: ", filename); if (strlen(filename) < 3) { E2LOGGER_error("Required Listname ", listname, " is not defined"); return false; } int res = o.lm.newItemList(filename, pwd, sortsw, 1, true, isip, istime, ismap); if (res < 0) { E2LOGGER_error("Error opening ", listname ); return false; } (*whichlist) = (unsigned) res; if (!(*o.lm.l[(*whichlist)]).used) { if(!istime) { if (sortsw) (*o.lm.l[(*whichlist)]).doSort(true); else (*o.lm.l[(*whichlist)]).doSort(false); } (*o.lm.l[(*whichlist)]).used = true; } return true; } bool ListMeta::inSiteList(String &urlp, unsigned int list, String &lastcategory, bool &site_wild, String &match, String &result) { String url = urlp; bool i; if (site_wild) { while (url.contains(".")) { i = (*o.lm.l[list]).findInList(url.toCharArray(), lastcategory, match, result); if (i) { return i; // exact match } url = url.after("."); // check for being in higher level domains } } if ((!url.contains(".")) && url.length() > 1) { // allows matching of .tld url = "." + url; } if(url.length() > 2) { i = (*o.lm.l[list]).findInList(url.toCharArray(), lastcategory, match, result); if (i) { return i; // exact match } } return false; } bool ListMeta::inSearchList(String &words, unsigned int list, String &lastcategory, String &match, String &result) { bool i = (*o.lm.l[list]).findInList(words.toCharArray(), lastcategory, match, result); if (i) { return i; // exact match } return false; } // look in given URL list for given URL bool ListMeta::inURLList(String &urlp, unsigned int list, String &lc, bool &site_wild, String &match, String &result) { String url = urlp; unsigned int fl; //char *i; bool i; String foundurl; DEBUG_debug("inURLList: ", url); // 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 } DEBUG_debug("inURLList (processed): ", url); while (url.before("/").contains(".")) { i = (*o.lm.l[list]).findStartsWith(url.c_str(), lc, match); if (i) { foundurl = match; fl = foundurl.length(); DEBUG_debug("foundurl: ", foundurl, foundurl.length()); DEBUG_debug("url: ", url, fl); if (url.length() > fl) { if (url[fl] == '/' || url[fl] == '?' || url[fl] == '&' || url[fl] == '=') { return true; // matches /blah/ or /blah/foo but not /blahfoo } } else { return true;// exact match } } if (!site_wild) break; url = url.after("."); // check for being in higher level domains } return false; } bool ListMeta::isIPHostname(String url) { RegResult Rre; if (!isiphost.match(url.toCharArray(), Rre)) { return true; } return false; } bool ListMeta::precompileregexps() { if (!isiphost.comp(".*[a-z|A-Z].*")) { E2LOGGER_error("Error compiling RegExp isiphost."); return false; } return true; } // read regexp url list bool ListMeta::readRegExMatchFile(const char *filename,const char *list_pwd, const char *listname, unsigned int &listref, std::deque &list_comp, std::deque &list_source, std::deque &list_ref) { int result = o.lm.newItemList(filename, list_pwd, true, 32, true); if (result < 0) { E2LOGGER_error("Error opening ", listname); return false; } listref = (unsigned) result; return compileRegExMatchFile(listref, list_comp, list_source, list_ref); } // NOTE TO SELF - MOVE TO LISTCONTAINER TO SOLVE FUE2E // compile regexp url list bool ListMeta::compileRegExMatchFile(unsigned int list, std::deque &list_comp, std::deque &list_source, std::deque &list_ref) { for (unsigned int i = 0; i < (*o.lm.l[list]).morelists.size(); i++) { if (!compileRegExMatchFile((*o.lm.l[list]).morelists[i], list_comp, list_source, list_ref)) { return false; } } RegExp r; bool rv = true; int len = (*o.lm.l[list]).getListLength(); String source; for (int i = 0; i < len; i++) { source = (*o.lm.l[list]).getItemAtInt(i).c_str(); rv = r.comp(source.toCharArray()); if (rv == false) { E2LOGGER_error("Error compiling regexp:", source); return false; } list_comp.push_back(r); list_source.push_back(source); list_ref.push_back(list); } (*o.lm.l[list]).used = true; return true; } // content and URL regular expression replacement files bool ListMeta::readRegExReplacementFile(const char *filename, const char *list_pwd, const char *listname, unsigned int &listid, std::deque &list_rep, std::deque &list_comp) { int result = o.lm.newItemList(filename,list_pwd, true, 32, true); if (result < 0) { E2LOGGER_error("Error opening ", listname); return false; } listid = (unsigned) result; if (!(*o.lm.l[listid]).used) { //(*o.lm.l[listid]).doSort(true); (*o.lm.l[listid]).used = true; } RegExp r; bool rv = true; String regexp; String replacement; for (int i = 0; i < (*o.lm.l[listid]).getListLength(); i++) { regexp = (*o.lm.l[listid]).getItemAtInt(i).c_str(); replacement = regexp.after("\"->\""); while (!replacement.endsWith("\"")) { if (replacement.length() < 2) { break; } replacement.chop(); } replacement.chop(); regexp = regexp.after("\"").before("\"->\""); if (regexp.length() < 1) { // allow replace with nothing continue; } rv = r.comp(regexp.toCharArray()); if (rv == false) { E2LOGGER_error("Error compiling regexp: ", (*o.lm.l[listid]).getItemAtInt(i) ); return false; } list_comp.push_back(r); list_rep.push_back(replacement); } return true; } // is this URL in the given regexp URL list? int ListMeta::inRegExpURLList(String &urlin, std::deque &list_comp, std::deque &list_ref, unsigned int list, String &lastcategory) { DEBUG_regexp("inRegExpURLList: ", urlin); // check parent list's time limit if (o.lm.l[list]->isNow()) { RegResult Rre; String url = urlin; url.removeWhiteSpace(); // just in case of weird browser crap url.toLower(); // whilst it would be nice to have regexes be able to match the PTP, // it has been assumed for too long that the URL string does not start with one, // and we don't want to break regexes that look explicitly for the start of // the string. changes here have therefore been reverted. 2005-12-07 url.removePTP(); 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 } // re-add the PTP /*if (ptp.length() > 0) url = ptp + "//" + url;*/ DEBUG_regexp("inRegExpURLList (processed): ", url); unsigned int i = 0; for (std::deque::iterator j = list_comp.begin(); j != list_comp.end(); j++) { if (o.lm.l[list_ref[i]]->isNow()) { if (j->match(url.toCharArray(), Rre)) return i; } else DEBUG_regexp("Outside included regexp list's time limit"); i++; } } else { DEBUG_regexp("Outside top level regexp list's time limit"); } return -1; } // Does a regexp search and replace. // urlRegExp Code originally from from Ton Gorter 2004 bool ListMeta::regExp(String &line, std::deque ®exp_list, std::deque &replacement_list) { RegExp *re; RegResult Rre; String replacement; String repstr; String newLine; bool linemodified = false; unsigned int i; unsigned int j, k; unsigned int s = regexp_list.size(); unsigned int matches, submatches; unsigned int match; unsigned int srcoff; unsigned int nextoffset; unsigned int matchlen; unsigned int oldlinelen; if ((line.empty()) || line.length() < 3) return false; // iterate over our list of precompiled regexes for (i = 0; i < s; i++) { newLine = ""; re = &(regexp_list[i]); if (re->match(line.toCharArray(), Rre)) { repstr = replacement_list[i]; matches = Rre.numberOfMatches(); srcoff = 0; for (j = 0; j < matches; j++) { nextoffset = Rre.offset(j); matchlen = Rre.length(j); // copy next chunk of unmodified data if (nextoffset > srcoff) { newLine += line.subString(srcoff, nextoffset - srcoff); srcoff = nextoffset; } // Count number of submatches (brackets) in replacement string for (submatches = 0; j + submatches + 1 < matches; submatches++) if (Rre.offset(j + submatches + 1) + Rre.length(j + submatches + 1) > srcoff + matchlen) break; // \1 and $1 replacement replacement = ""; for (k = 0; k < repstr.length(); k++) { // find \1..\9 and $1..$9 and fill them in with submatched strings if ((repstr[k] == '\\' || repstr[k] == '$') && repstr[k + 1] >= '1' && repstr[k + 1] <= '9') { match = repstr[++k] - '0'; if (match <= submatches) { replacement += Rre.result(j + match).c_str(); } } else { // unescape \\ and \$, and add non-backreference characters to string if (repstr[k] == '\\' && (repstr[k + 1] == '\\' || repstr[k + 1] == '$')) k++; replacement += repstr.subString(k, 1); } } // copy filled in replacement string newLine += replacement; srcoff += matchlen; j += submatches; } oldlinelen = line.length(); if (srcoff < oldlinelen) { newLine += line.subString(srcoff, oldlinelen - srcoff); } DEBUG_regexp("Line modified! (", line, " -> ", newLine, ")" ); // copy newLine into line and continue with other regexes line = newLine; linemodified = true; } } return linemodified; } bool ListMeta::headerRegExpReplace(ListMeta::list_info &listi, std::deque &header, list_result &res) { // exit immediately if list is empty if (not listi.comp.size()) return false; bool result = false; for (std::deque::iterator i = header.begin(); i != header.end(); i++) { DEBUG_regexp("Starting header reg exp replace: ", *i); bool chop = false; if (i->endsWith("\r")) { i->chop(); chop = true; } result |= regExp(*i, listi.comp, listi.replace); if (chop) i->append("\r"); } for (std::deque::iterator i = header.begin(); i != header.end(); i++) DEBUG_regexp("Starting header reg exp replace result: ", *i); return result; } int ListMeta::inHeaderRegExp(list_info &listi, std::deque &header, list_result &res, String &lastcategory) { // exit immediately if list is empty if (not listi.comp.size()) return false; int result = -1; for (std::deque::iterator i = header.begin(); i != header.end(); i++) { DEBUG_regexp("Starting header reg exp check ", *i); bool chop = false; if (i->endsWith("\r")) { i->chop(); chop = true; } result = inRegExpURLList(*i, listi.comp, listi.reg_list_ref, listi.list_ref, lastcategory); if (chop) i->append("\r"); if (result > -1) { res.category = lastcategory; //res.match = TODO add the info break; } } return result; } e2guardian-5.5.8r/src/ListMeta.hpp000066400000000000000000000132521477372360500167770ustar00rootroot00000000000000// ListMeta - super-class for both item and phrase lists // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_LISTMETA #define __HPP_LISTMETA // INLCUDES #include #include #include #include #include #include "String.hpp" #include "RegExp.hpp" #include "ListContainer.hpp" #define LIST_TYPE_IP 1 #define LIST_TYPE_SITE 2 #define LIST_TYPE_IPSITE 3 #define LIST_TYPE_URL 4 #define LIST_TYPE_SEARCH 5 #define LIST_TYPE_REGEXP_BOOL 6 #define LIST_TYPE_REGEXP_REP 7 #define LIST_TYPE_PHRASE_BANNED 8 #define LIST_TYPE_PHRASE_WEIGHTED 9 #define LIST_TYPE_PHRASE_EXCEPTION 10 #define LIST_TYPE_MIME 11 #define LIST_TYPE_FILE_EXT 12 #define LIST_TYPE_TIME 13 #define LIST_TYPE_MAP 14 #define LIST_TYPE_IPMAP 15 #define LIST_TYPE_CATEGORY 16 #define LIST_TYPE_ERROR 17 #define LIST_TYPE_TOP 18 #define LIST_METHOD_READF_SWS 1 #define LIST_METHOD_READF_EWS 2 #define LIST_METHOD_REGEXP_BOOL 3 #define LIST_METHOD_REGEXP_REPL 4 #define LIST_METHOD_PHRASES 5 #define LIST_METHOD_IP 6 #define LIST_METHOD_TIME 7 #define LIST_METHOD_MAP 8 #define LIST_METHOD_IPMAP 9 // DECLARATIONS class ListMeta { public: int items; bool reverse_lookups = false; struct list_info { String name; String pwd; unsigned int type; unsigned int method_type; unsigned int list_ref; std::deque comp; std::deque source; std::deque replace; std::deque reg_list_ref; unsigned int mess_no; unsigned int log_mess_no; bool anon_log; bool site_wild; bool used = false; }; struct list_result { String match; // to hold match from list String category; // holds list category String result; // to hold any modified Sting int mess_no = 0; int log_mess_no = 0; bool anon_log = false; }; std::vector list_vec; String list_type(int type); String type_map[LIST_TYPE_TOP] = { "", "iplist", "sitelist", "ipsitelist", "urllist", "searchlist", "regexpboollist", "regexpreplacelist", "phrasebannedlist", "phraseweightedlist", "phraseexceptionlist", "mimelist", "fileextlist", "timelist", "maplist", "ipmaplist", "categorylist", "unknown list" }; ListMeta(); ~ListMeta(); void reset(); bool load_type(int type, std::deque &list); struct list_info findList(String name, int type); struct list_info *findListPtr(String name, int type); unsigned int findListId(String name, int type); bool list_exists(String name, int type); bool inList(String name, int type, String &tofind, list_result &res); bool inList(list_info &list, String &tofind, list_result &res); bool inList(list_info &info, std::deque &header, list_result &res); bool readFile(const char *filename, const char *pwd, unsigned int *whichlist, bool sortsw, const char *listname,bool isip = false, bool istime = false, bool is_map = false); bool readRegExReplacementFile(const char *filename, const char *pwd, const char *listname, unsigned int &listid, std::deque &list_rep, std::deque &list_comp); private: bool inURLList(String &url, unsigned int list, String &lastcategory, bool &site_wild, String &match, String &result); bool inSiteList(String &url, unsigned int list, String &lastcategory, bool &site_wild, String &match, String &result); bool inSearchList(String &words, unsigned int list,String &lastcategory, String &match, String &result); int inRegExpURLList(String &url, std::deque &list_comp, std::deque &list_ref, unsigned int list, String &lastcategory); bool regExp(String &line, std::deque ®exp_list, std::deque &replacement_list); bool headerRegExpReplace(ListMeta::list_info &listi, std::deque &header, list_result &res ); int inHeaderRegExp(list_info &listi, std::deque &header, list_result &res, String &lastcategory ); bool isIPHostname(String url); char *testBlanketBlock(unsigned int list, bool ip, bool ssl, String &lastcategory); RegExp isiphost; bool precompileregexps(); bool readRegExMatchFile(const char *filename, const char *pwd, const char *listname, unsigned int &listref, std::deque &list_comp, std::deque &list_source, std::deque &list_ref); bool compileRegExMatchFile(unsigned int list, std::deque &list_comp, std::deque &list_source, std::deque &list_ref); }; #endif e2guardian-5.5.8r/src/Logger.cpp000066400000000000000000000612631477372360500164740ustar00rootroot00000000000000// Logger class - for central logging to console/syslog/file // // Author : Klaus-Dieter Gundermann // Created : 24.06.2020 // // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include #include #include #include #include #include #include #include #include "Logger.hpp" extern bool is_daemonised; // ------------------------------------------------------------- // --- Global Logger // ------------------------------------------------------------- Logger e2logger; // ------------------------------------------------------------- // --- Constructor // ------------------------------------------------------------- Logger::Logger() { setSyslogName(PACKAGE); // set up default output formats // bools are no_format, show_tag, show_func, func_last setMultiFormat(&working_logs,true,false,false,false,false,false); setMultiFormat(&working_messages,false,false,false,false, true, true); setMultiFormat(&debug_messages,false,false,true, true, true, true); // this can be overwritten by the debuglevel option setFormat(LoggerSource::storytrace,false,false,false,false,true, true); setLogOutput(LoggerSource::info, LoggerDestination::syslog, "LOG_INFO"); setLogOutput(LoggerSource::error, LoggerDestination::syslog, "LOG_ERR"); setLogOutput(LoggerSource::warning, LoggerDestination::syslog, "LOG_WARNING"); setLogOutput(LoggerSource::storytrace, LoggerDestination::syslog,"LOG_INFO", false); setLogOutput(LoggerSource::debug, LoggerDestination::STDOUT, "", false); setLogOutput(LoggerSource::trace, LoggerDestination::STDOUT, "", false); } Logger::~Logger() { closelog(); if (!Files.empty()) { for (std::vector::iterator i = Files.begin(); i != Files.end(); i++) { if (*i != nullptr) { if ((*i)->open) { ((*i)->file_stream)->close(); delete ((*i)->file_stream); } delete *i; } } } if (!Udps.empty()) { for (std::vector::iterator i = Udps.begin(); i != Udps.end(); i++) { if (*i != nullptr) { if ((*i)->open) { ((*i)->socket)->close(); delete ((*i)->socket); } delete *i; } } } } // ------------------------------------------------------------- // --- Helper // ------------------------------------------------------------- struct Logger::Helper { static std::string build_message(SourceRec *rec, std::string &prepend, const std::string &func, const std::string &file, const int &line, std::string what) { // TODO: add time properly in separate commit PIP std::string message; if (rec->show_timestamp_active) { time_t t = time(NULL); message = (std::to_string((long)t)); message += " "; } if (rec->show_source_category) { ((message = "(") += prepend) += ") "; } if (rec->show_thread_id) message += thread_id; if (rec->show_funct_line && !rec->funct_line_last) { message.append(func).append("():").append(file).append(":").append(std::to_string(line)).append(" "); } message += what; if (rec->show_funct_line && rec->funct_line_last) { message.append(" ").append(func).append("():").append(file).append(":").append(std::to_string(line)); } if( rec->destination == LoggerDestination::udp) { message.append("\n"); } return message; } }; class Logger::Udp { public: }; bool UdpRec::send(std::string &msg) { if (socket == nullptr) { std::cerr << "socket is null" << std::endl; } else { //std::cerr << "socket is not null" << std::endl; if (!socket->writeString(msg)) { std::cerr << "udp write to " << host << " failed"; return false; }; //std::cerr << "udp write to " << host << " OK"; } return true; } bool FileRec::write(std::string &msg) { if (file_stream == nullptr) { std::cerr << "file_stream is null" << std::endl; } else { // std::cerr << "file_stream is not null" << std::endl; *file_stream << msg << std::endl; if (file_stream->fail()) { //std::cerr << "log write to " << filename << " failed"; return false; } // std::cerr << "log write to " << filename << " OK"; } return true; } bool FileRec::flush() { if(file_stream == nullptr) return false; file_stream->flush(); return true; } bool FileRec::rotate() { // this must only be called by a single thread which controls all output to this file if(file_stream == nullptr) return false; std::string rfn = filename; mode_t old_umask; old_umask = umask(S_IWGRP | S_IWOTH); rfn += ".old"; if(link(filename.c_str(), rfn.c_str()) == 0) { unlink(filename.c_str()); file_stream->close(); delete file_stream; file_stream = new std::ofstream(filename.c_str(), std::ios::app); umask(old_umask); return true; } umask(old_umask); return false; } // ------------------------------------------------------------- // --- static Functions // ------------------------------------------------------------- LoggerSource Logger::string2source(std::string source) { for (int i = 0; i < static_cast(LoggerSource::__Max_Value); i++) { if (source == Sources[i]) return static_cast(i); } return LoggerSource::none; } LoggerDestination Logger::string2dest(std::string destination) { for (int i = 0; i < static_cast(LoggerDestination::__Max_Value); i++) { if (Destinations[i] == destination) return static_cast(i); } return LoggerDestination::none; } std::string Logger::source2string(LoggerSource source) { return Sources[static_cast(source)]; } std::string Logger::dest2string(LoggerDestination dest) { return Destinations[static_cast(dest)]; } // ------------------------------------------------------------- // --- Properties // ------------------------------------------------------------- void Logger::setSyslogName(const std::string logname) { _logname = logname; closelog(); openlog(logname.c_str(), LOG_PID | LOG_CONS, LOG_USER); }; void Logger::enable(const LoggerSource source) { sourceRecs[static_cast(source)].enabled = true; }; void Logger::enable(const char *source) { enable(string2source(std::string(source))); } void Logger::disable(const LoggerSource source) { sourceRecs[static_cast(source)].enabled = false; }; void Logger::disable(const char *source) { disable(string2source(std::string(source))); } bool Logger::isEnabled(const LoggerSource source) { return sourceRecs[static_cast(source)].enabled; }; bool Logger::isEnabled(const char *source) { return isEnabled(string2source(std::string(source))); } bool Logger::rotate(const LoggerSource source) { // this must only be called by a single thread which controls all output to this file if(sourceRecs[static_cast(source)].destination != LoggerDestination::file) return false; if(sourceRecs[static_cast(source)].fileRec == nullptr) return false; return sourceRecs[static_cast(source)].fileRec->rotate(); }; bool Logger::flush(const LoggerSource source) { // this must only be called by a single thread which controls all output to this file if(sourceRecs[static_cast(source)].destination != LoggerDestination::file) return false; if(sourceRecs[static_cast(source)].fileRec == nullptr) return false; return sourceRecs[static_cast(source)].fileRec->flush(); }; bool Logger::setLogOutput(const LoggerSource source, const LoggerDestination destination, const std::string filename, const bool alsoEnable) { if (destination == LoggerDestination::file) { if (!setFilename(source, filename)) return false; } else if (sourceRecs[static_cast(source)].destination == LoggerDestination::file) { // unlink file if previously set rmFileLink(sourceRecs[static_cast(source)].fileRec); sourceRecs[static_cast(source)].fileRec = nullptr; } if (destination == LoggerDestination::udp) { if (!setUdpname(source, filename)) return false; } else if (sourceRecs[static_cast(source)].destination == LoggerDestination::udp) { // unlink file if previously set rmUdpLink(sourceRecs[static_cast(source)].udpRec); sourceRecs[static_cast(source)].udpRec = nullptr; } // if (destination == LoggerDestination::udp) { // if (!setUdpDestination(source, filename)) // return false; // } if (destination == LoggerDestination::syslog) { setSyslogLevel(source, filename); sourceRecs[static_cast(source)].show_timestamp_active = false; } else { if (sourceRecs[static_cast(source)].show_timestamp) { sourceRecs[static_cast(source)].show_timestamp_active = true; } }; setDestination(source, destination); if (destination == LoggerDestination::none) disable(source); else if (alsoEnable) enable(source); return true; } void Logger::setFormat(const LoggerSource source, bool no_format, bool show_tag, bool show_func, bool func_last, bool show_thread_id, bool show_timestamp) { sourceRecs[static_cast(source)].no_format = no_format; // used for logs that do not required any further formating sourceRecs[static_cast(source)].show_source_category = show_tag; sourceRecs[static_cast(source)].show_funct_line = show_func; sourceRecs[static_cast(source)].funct_line_last = func_last; sourceRecs[static_cast(source)].show_thread_id = show_thread_id; sourceRecs[static_cast(source)].show_timestamp = show_timestamp; }; void Logger::setMultiFormat(std::vector *source_list, bool no_format, bool show_tag, bool show_func, bool func_last, bool show_thread_id, bool show_timestamp) { for (std::vector::iterator i = source_list->begin(); i != source_list->end(); i++) { setFormat(*i, no_format, show_tag, show_func, func_last, show_thread_id, show_timestamp); } } FileRec *Logger::findFileRec(std::string filename) { if (!Files.empty()) { for (std::vector::iterator i = Files.begin(); i != Files.end(); i++) { if ((*i)->filename == filename) return (*i); } } return nullptr; } void Logger::deleteFileEntry(std::string filename) { if (!Files.empty()) { for (std::vector::iterator i = Files.begin(); i != Files.end(); i++) { if ((*i)->filename == filename) { if ((*i)->file_stream != nullptr) { ((*i)->file_stream)->close(); delete (*i)->file_stream; (*i)->file_stream = nullptr; } delete *i; Files.erase(i); return; } } } } FileRec *Logger::addFile(std::string filename) { FileRec *fileRec = findFileRec(filename); if (fileRec == nullptr) { // new unique filename - add to Files and open FileRec* fileRec1 = new FileRec; fileRec1->filename = filename; fileRec1->link_count = 1; Files.push_back(fileRec1); fileRec = findFileRec(filename); if (fileRec == nullptr) { std::cerr << "failure to find new Files record for " << filename << std::endl; return nullptr; } mode_t old_umask; old_umask = umask(S_IWGRP | S_IWOTH); // fileRec->file_stream = fopen(filename.c_str(), "a"); fileRec->file_stream = new std::ofstream(filename.c_str(), std::ios::app); if (!fileRec->file_stream) { std::cerr << "Failed to open/create logfile: " << filename << " (check ownership and access rights)" << std::endl; deleteFileEntry(filename); umask(old_umask); return nullptr; } umask(old_umask); //std::cerr << "Opened new file: " << filename << std::endl; //std::cerr << "Opened new filename in record : " << fileRec->filename << std::endl; fileRec->open = true; //std::cerr << "File link count is " << fileRec->link_count << std::endl; } else { fileRec->link_count++; //std::cerr << "File link count is " << fileRec->link_count << std::endl; } return fileRec; }; void Logger::rmFileLink(FileRec *fileRec) { if(fileRec == nullptr) return; if (fileRec->link_count > 1) { fileRec->link_count--; //std::cerr << "rmFileLink File link count is " << fileRec->link_count << std::endl; return; } // link count will now be zero, close file, delete stream and remove record //std::cerr << "Close and delete " << fileRec->filename << std::endl; //fclose(fileRec->file_stream); if (fileRec->file_stream != nullptr) { fileRec->file_stream->close(); delete fileRec->file_stream; fileRec->file_stream = nullptr; } deleteFileEntry(fileRec->filename); } void Logger::setDockerMode() { // docker stdout/stderr are not in sync // so for debugging send everything to stderr (unbuffered) setDestination(LoggerSource::info, LoggerDestination::STDERR); setDestination(LoggerSource::error, LoggerDestination::STDERR); setDestination(LoggerSource::warning, LoggerDestination::STDERR); setDestination(LoggerSource::accesslog, LoggerDestination::STDOUT); } // ------------------------------------------------------------- // --- Public Functions // ------------------------------------------------------------- void Logger::log(const LoggerSource source, std::string message) { log(source,(const std::string)"",(const std::string)"",(int)0, message); } void Logger::log(const LoggerSource source, const std::string func, const std::string file, const int line, std::string message) { SourceRec *sourceRec = &(sourceRecs[static_cast(source)]); if (sourceRec->no_format && sourceRec->destination == LoggerDestination::file) { // following check is dane in sendMessage so remove // if (sourceRec->fileRec == nullptr ) { // std::cerr << "Error: in Log: filename is nullptr" << std::endl; // } else { // } //std::cerr << "in Log: filename is " << sourceRec->fileRec->filename << std::endl; sendMessage(source, message); } else { std::string tag; std::string msg; tag = source2string(source); msg = Helper::build_message(sourceRec, tag, func, file, line, message); sendMessage(source, msg); } }; // ------------------------------------------------------------- // --- Private Functions // ------------------------------------------------------------- void Logger::sendMessage(const LoggerSource source, std::string &message) { SourceRec *srec = &(sourceRecs[static_cast(source)]); LoggerDestination destination = srec->destination; //std::cerr << "sendMessage source " << source2string(source) << " dest " << dest2string(destination) << std::endl; switch (destination) { case LoggerDestination::none: break; case LoggerDestination::STDOUT: std::cout << message << std::endl; break; case LoggerDestination::STDERR: std::cerr << message << std::endl; break; case LoggerDestination::syslog: if (g_is_starting) std::cerr << message << std::endl; // show to console as well as syslog when not daemonised syslog(srec->syslog_flag, "%s", message.c_str()); break; case LoggerDestination::file: if (srec->fileRec == nullptr) { std::cerr << "dest fileRec is nullptr" << std::endl; } else { if (!srec->fileRec->open ) std::cerr << "Log file output stream is closed"; else { //std::cerr << "Log filename is " << srec->fileRec->filename << std::endl; //std::cerr << "log msg: " << message << std::endl; srec->fileRec->write(message); } } break; case LoggerDestination::udp: if (srec->udpRec == nullptr) { std::cerr << "dest udpRec is nullptr" << std::endl; } else { // std::cerr << "Log udp is " << srec->udpRec->host << std::endl; if(srec->udpRec->send(message)) { // std::cerr << "udp message sent" << std::endl; } else { std::cerr << "udp message failed" << std::endl; } } break; case LoggerDestination::__Max_Value: break; } } void Logger::setDestination(const LoggerSource source, const LoggerDestination destination) { sourceRecs[static_cast(source)].destination = destination; // std::cerr << "Logger::setDestination " << source2string(source) << " -> " << dest2string(destination) << std::endl; } bool Logger::setSyslogLevel(const LoggerSource source, const std::string filename) { int loglevel = LOG_INFO; if (filename == "LOG_ERR") loglevel = LOG_ERR; else if (filename == "LOG_WARNING") loglevel = LOG_WARNING; else if (filename == "LOG_INFO") loglevel = LOG_INFO; else if (filename == "LOG_DEBUG") loglevel = LOG_DEBUG; else if (filename == "LOG_ALERT") loglevel = LOG_ALERT; else if (filename == "LOG_CRIT") loglevel = LOG_CRIT; else if (filename == "LOG_NOTICE") loglevel = LOG_NOTICE; else if (filename == "LOG_EMERG") loglevel = LOG_EMERG; sourceRecs[static_cast(source)].syslog_flag = loglevel; return true; } bool Logger::setFilename(const LoggerSource source, const std::string filename) { std::string name; if (filename.empty()) { return false; } if (filename.front() == '/') // absolute path name = filename; else // relative to __LOGLOCATION name = std::string(__LOGLOCATION) + filename; if (sourceRecs[static_cast(source)].destination == LoggerDestination::file) { // rmFileLink(sourceRecs[static_cast(source)].fileRec); sourceRecs[static_cast(source)].fileRec = nullptr; } FileRec *file_rec = addFile(name); if (file_rec == nullptr) { std::cerr << "Null returned from addFile()" << std::endl; return false; } sourceRecs[static_cast(source)].fileRec = file_rec; // std::cerr << "Lookup filename after added to source_dests is " // << source_dests[static_cast(source)].fileRec->filename << std::endl; return true; } bool Logger::setUdpname(const LoggerSource source, const std::string filename) { std::string host=""; String port; String temp = filename; if (filename.empty()) { return false; } host = temp.before(":"); port = temp.after(":"); if (sourceRecs[static_cast(source)].destination == LoggerDestination::udp) { // rmUdpLink(sourceRecs[static_cast(source)].udpRec); sourceRecs[static_cast(source)].udpRec = nullptr; } UdpRec *udp_rec = addUdp(host, port.toInteger()); if (udp_rec == nullptr) { std::cerr << "Null returned from addUdp()" << std::endl; return false; } sourceRecs[static_cast(source)].udpRec = udp_rec; // std::cerr << "Lookup filename after added to source_dests is " << // source_dests[static_cast(source)].fileRec->filename << std::endl; return true; } #ifdef NOT_DEF bool Logger::setUdpDestination(const LoggerSource source, const std::string udp_destination) { std::string host=""; std::string port=""; std::size_t pos = udp_destination.find_last_of(':'); if ( pos > 0 ) { host = udp_destination.substr(0,pos); port = udp_destination.substr(pos+1); } if (host.empty() || port.empty()) { std::cerr << "missing host and port for UDP destination " << udp_destination << std::endl; return false; } sourceRecs[static_cast(source)].host = host; sourceRecs[static_cast(source)].port = port; return true; } #endif UdpRec *Logger::addUdp(std::string host, int port) { UdpRec *udpRec = findUdpRec(host, port); if (udpRec == nullptr) { // new unique udpname - add to Udps and open UdpRec *udpRec1 = new UdpRec; udpRec1->host = host; udpRec1->port = port; udpRec1->link_count = 1; Udps.push_back(udpRec1); udpRec = findUdpRec(host, port); if (udpRec == nullptr) { std::cerr << "failure to find new Udps record for " << host << ":" << port << std::endl; return nullptr; } int rc = -1; String shost(host); if (shost.isIp()) { // std::cerr << "host is an ip" << std::endl; udpRec->socket = new UdpSocket; rc = udpRec->socket->connect(host, port); } else { // do dns lookup // std::cerr << "host is NOT an ip" << std::endl; struct addrinfo hints, *addrs, *addr; memset(&hints, 0, sizeof(addrinfo)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; String sport = port; int status = getaddrinfo(host.c_str(), NULL, &hints, &addrs); if (status != 0) { std::cerr << "can not find host " << host << std::endl; return nullptr; } char t[256]; for (addr = addrs; addr != nullptr; addr = addr->ai_next) { getnameinfo(addr->ai_addr, addr->ai_addrlen, t, sizeof(t), NULL, 0, NI_NUMERICHOST); udpRec->socket = new UdpSocket; if ((rc = udpRec->socket->connect(t, port)) < 0) continue; else break; } } if (rc < 0) { // so not able to connect on any ip std::cerr << "can not open socket to " << host << std::endl; deleteUdpEntry(host, port); return nullptr; } //std::cerr << "Opened new udpname in record : " << udpRec->host << " port " <port << std::endl; udpRec->open = true; //std::cerr << "Udp link count is " << udpRec->link_count << std::endl; } else { udpRec->link_count++; //std::cerr << "Udp link count is " << udpRec->link_count << std::endl; }; return udpRec; } void Logger::rmUdpLink(UdpRec *udpRec) { if (udpRec == nullptr) return; if (udpRec->link_count > 1) { udpRec->link_count--; //std::cerr << "rmUdpLink Udp link count is " << udpRec->link_count << std::endl; return; } // link count will now be zero, close udp, delete stream and remove record //std::cerr << "Close and delete " << udpRec->udpname << std::endl; //fclose(udpRec->udp_stream); if (udpRec->socket != nullptr) { udpRec->socket->close(); delete udpRec->socket; udpRec->socket = nullptr; } deleteUdpEntry(udpRec->host, udpRec->port); } UdpRec *Logger::findUdpRec(std::string host, int port) { if (!Udps.empty()) { for (std::vector::iterator i = Udps.begin(); i != Udps.end(); i++) { if (((*i)->host == host) && ((*i)->port == port)) return (*i); } } return nullptr; } void Logger::deleteUdpEntry(std::string host, int port) { if (!Udps.empty()) { for (std::vector::iterator i = Udps.begin(); i != Udps.end(); i++) { if (((*i)->host == host) && ((*i)->port == port)) { if ((*i)->socket != nullptr) { ((*i)->socket)->close(); delete (*i)->socket; (*i)->socket = nullptr; } delete *i; Udps.erase(i); return; } } } } e2guardian-5.5.8r/src/Logger.hpp000066400000000000000000000265121477372360500164770ustar00rootroot00000000000000// Logger class - for central logging to console/syslog/file // // Author : Klaus-Dieter Gundermann // Created : 24.06.2020 // // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_LOGGING #define __HPP_LOGGING #include //#include #include #include #include #include #include #include #include #include "String.hpp" #include "UdpSocket.hpp" // only C++14 : using namespace std::string_literals; class FileRec { public: std::string filename; std::ofstream *file_stream = nullptr; int link_count = 0; bool open = false; bool write(std::string &msg); bool rotate(); bool flush(); }; class UdpRec { public: std::string host = ""; int port = 0; std::string ip = ""; UdpSocket *socket = nullptr; int link_count = 0; bool open = false; bool send(std::string &msg); }; enum class LoggerSource { none, // used in production: info, error, warning, accesslog, requestlog, storytrace, dstatslog, responselog, alertlog, // only usable when compiled with DEBUG_LOW: debug, trace, network, story, regexp, config, content, // only usable when compiled with DEBUG_HIGH: icap, avscan, auth, dwload, proxy, thttps, __Max_Value }; enum class LoggerDestination { // stdout and stderr refactored to upper case to avoid conflict with library stdout stderr macros none, STDOUT, STDERR, syslog, file, udp, __Max_Value }; class Logger { public: Logger(); // constructor ~Logger(); // destructor void setSyslogName(const std::string logname); // enable/disable Logging Sources void enable(const LoggerSource source); void enable(const char *source); void disable(const LoggerSource source); void disable(const char *source); bool isEnabled(const LoggerSource source); bool isEnabled(const char *source); bool rotate(const LoggerSource source); bool flush(const LoggerSource source); bool setLogOutput(const LoggerSource source, const LoggerDestination destination, const std::string filename = "", const bool alsoEnable = true); void setDockerMode(); void log(const LoggerSource source, std::string message); void log(const LoggerSource source, const std::string func, const std::string file, const int line, std::string message); // Conversion Enum <-> string std::vector Sources = {"none", "info", "error", "warning", "accesslog", "requestlog", "storytrace", "dstatslog", "responselog", "alertlog", // only usable when compiled with DEBUG_LOW: "debug", "trace", "network", "story", "regexp", "config", "content", // only usable when compiled with DEBUG_HIGH: "icap", "avscan", "auth", "dwload", "proxy", "thttps"}; std::vector Destinations = {"none", "stdout", "stderr", "syslog", "file", "udp"}; std::vector working_messages = { LoggerSource::info, LoggerSource::error, LoggerSource::warning, }; std::vector working_logs = { LoggerSource::accesslog, LoggerSource::requestlog, LoggerSource::dstatslog, LoggerSource::responselog, LoggerSource::alertlog, }; std::vector debug_messages = { LoggerSource::trace, LoggerSource::config, LoggerSource::debug, LoggerSource::avscan, LoggerSource::icap, LoggerSource::network, LoggerSource::regexp, LoggerSource::story, LoggerSource::auth, LoggerSource::dwload, LoggerSource::proxy, LoggerSource::thttps, LoggerSource::content, }; LoggerSource string2source(std::string source); LoggerDestination string2dest(std::string destination); std::string source2string(LoggerSource source); std::string dest2string(LoggerDestination dest); void setFormat(const LoggerSource source, bool no_format, bool show_tag = false, bool show_func = false, bool func_last = true, bool show_thread_id = true, bool show_timestamp = true); void setMultiFormat(std::vector *source_list, bool no_format, bool show_tag, bool show_func, bool func_last, bool show_thread_id = true, bool show_timestamp = true); // Variable Args template void cat_vars(std::stringstream &mess, T e) { mess << e; } template void cat_vars(std::stringstream &mess, T e, Args... args) { mess << e; cat_vars(mess, args...); } template std::string cat_all_vars(Args... args) { std::stringstream mess; cat_vars(mess, args...); return mess.str(); } template void vlog(const LoggerSource source, const std::string func, const std::string file, const int line, Args... args) { log(source, func, file, line, cat_all_vars(args...)); }; private: std::vector Files; std::vector Udps; struct SourceRec { bool enabled = false; LoggerDestination destination = LoggerDestination::none; FileRec *fileRec = nullptr; UdpRec *udpRec = nullptr; std::string host = ""; std::string port = ""; int syslog_flag = LOG_INFO; bool show_funct_line = false; bool funct_line_last = true; bool show_source_category = false; bool show_thread_id = true; bool no_format = false; bool show_timestamp = true; bool show_timestamp_active = true; }; SourceRec sourceRecs[static_cast(LoggerSource::__Max_Value)]; FileRec *findFileRec(std::string filename); FileRec *addFile(std::string filename); void rmFileLink(FileRec *fileRec); void deleteFileEntry(std::string filename); UdpRec *findUdpRec(std::string host, int port); UdpRec *addUdp(std::string host, int port); void rmUdpLink(UdpRec *udpRec); void deleteUdpEntry(std::string host, int port); std::string _logname; // arrays below replaced with array of source_rec //bool _enabled[static_cast(LoggerSource::__Max_Value)]; //LoggerDestination _destination[static_cast(LoggerSource::__Max_Value)]; //std::string _filename[static_cast(LoggerSource::__Max_Value)]; struct Helper; class Udp; void sendMessage(const LoggerSource source, std::string &message); void setDestination(const LoggerSource source, const LoggerDestination destination); bool setFilename(const LoggerSource source, const std::string filename); bool setUdpname(const LoggerSource source, const std::string filename); bool setUdpDestination(const LoggerSource source, const std::string udp_destination); bool setSyslogLevel(const LoggerSource source, const std::string filename); }; extern thread_local std::string thread_id; extern std::atomic g_is_starting; extern Logger e2logger; #define E2LOGGER_info(...) \ if (e2logger.isEnabled(LoggerSource::info)) \ e2logger.vlog(LoggerSource::info, __func__, __FILE__,__LINE__, __VA_ARGS__) #define E2LOGGER_error(...) \ if (e2logger.isEnabled(LoggerSource::error)) \ e2logger.vlog(LoggerSource::error, __func__, __FILE__,__LINE__, __VA_ARGS__) #define E2LOGGER_warning(...) \ if (e2logger.isEnabled(LoggerSource::warning)) \ e2logger.vlog(LoggerSource::warning, __func__, __FILE__,__LINE__, __VA_ARGS__) #define E2LOGGER_accesslog(STR) \ if (e2logger.isEnabled(LoggerSource::accesslog)) \ e2logger.log(LoggerSource::accesslog, STR) #define E2LOGGER_requestlog(STR) \ if (e2logger.isEnabled(LoggerSource::requestlog)) \ e2logger.log(LoggerSource::requestlog, STR) #define E2LOGGER_alertlog(STR) \ if (e2logger.isEnabled(LoggerSource::alertlog)) \ e2logger.log(LoggerSource::alertlog, STR) #define E2LOGGER_responselog(STR) \ if (e2logger.isEnabled(LoggerSource::responselog)) \ e2logger.log(LoggerSource::responselog, STR) #define E2LOGGER_dstatslog(STR) \ if (e2logger.isEnabled(LoggerSource::dstatslog)) \ e2logger.log(LoggerSource::dstatslog, STR) #define E2LOGGER_storytrace(...) \ if (e2logger.isEnabled(LoggerSource::storytrace)) \ e2logger.vlog(LoggerSource::storytrace, (const std::string) "", (const std::string) "", (int) 0, __VA_ARGS__) #ifdef DEBUG_HIGH #define DEBUG_icap(...) \ if (e2logger.isEnabled(LoggerSource::icap)) \ e2logger.vlog(LoggerSource::icap, __func__, __FILE__,__LINE__, __VA_ARGS__) #define DEBUG_avscan(...) \ if (e2logger.isEnabled(LoggerSource::avscan)) \ e2logger.vlog(LoggerSource::avscan, __func__, __FILE__,__LINE__, __VA_ARGS__) #define DEBUG_auth(...) \ if (e2logger.isEnabled(LoggerSource::auth)) \ e2logger.vlog(LoggerSource::auth, __func__, __FILE__,__LINE__, __VA_ARGS__) #define DEBUG_dwload(...) \ if (e2logger.isEnabled(LoggerSource::dwload)) \ e2logger.vlog(LoggerSource::dwload, __func__, __FILE__,__LINE__, __VA_ARGS__) #define DEBUG_proxy(...) \ if (e2logger.isEnabled(LoggerSource::proxy)) \ e2logger.vlog(LoggerSource::proxy, __func__, __FILE__,__LINE__, __VA_ARGS__) #define DEBUG_thttps(...) \ if (e2logger.isEnabled(LoggerSource::thttps)) \ e2logger.vlog(LoggerSource::thttps, __func__, __FILE__,__LINE__, __VA_ARGS__) #else #define DEBUG_icap(...) #define DEBUG_avscan(...) #define DEBUG_auth(...) #define DEBUG_dwload(...) #define DEBUG_proxy(...) #define DEBUG_thttps(...) #endif #ifdef DEBUG_LOW #define DEBUG_debug(...) \ if (e2logger.isEnabled(LoggerSource::debug)) \ e2logger.vlog(LoggerSource::debug, __func__, __FILE__,__LINE__, __VA_ARGS__) #define DEBUG_trace(...) \ if (e2logger.isEnabled(LoggerSource::trace)) \ e2logger.vlog(LoggerSource::trace, __func__, __FILE__,__LINE__, __VA_ARGS__) #define DEBUG_network(...) \ if (e2logger.isEnabled(LoggerSource::network)) \ e2logger.vlog(LoggerSource::network, __func__, __FILE__,__LINE__, __VA_ARGS__) #define DEBUG_regexp(...) \ if (e2logger.isEnabled(LoggerSource::regexp)) \ e2logger.vlog(LoggerSource::regexp, __func__, __FILE__,__LINE__, __VA_ARGS__) #define DEBUG_story(...) \ if (e2logger.isEnabled(LoggerSource::story)) \ e2logger.vlog(LoggerSource::story, __func__, __FILE__,__LINE__, __VA_ARGS__) #define DEBUG_config(...) \ if (e2logger.isEnabled(LoggerSource::config)) \ e2logger.vlog(LoggerSource::config, __func__, __FILE__,__LINE__, __VA_ARGS__) #define DEBUG_content(...) \ if (e2logger.isEnabled(LoggerSource::content)) \ e2logger.vlog(LoggerSource::content, __func__, __FILE__,__LINE__, __VA_ARGS__) #else #define DEBUG_debug(...) #define DEBUG_trace(...) #define DEBUG_network(...) #define DEBUG_regexp(...) #define DEBUG_story(...) #define DEBUG_config(...) #define DEBUG_content(...) #define DEBUG_low_icap(...) #define DEBUG_low_avscan(...) #define DEBUG_low_auth(...) #define DEBUG_low_dwload(...) #define DEBUG_low_proxy(...) #define DEBUG_low_thttps(...) #endif #endif e2guardian-5.5.8r/src/LoggerConfigurator.cpp000066400000000000000000000125151477372360500210530ustar00rootroot00000000000000 // LoggerConfigurator - reading strings for configuring the Logger // // Author : Klaus-Dieter Gundermann // Created : 30.06.2020 // // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include // #include #include "LoggerConfigurator.hpp" #include "String.hpp" // ------------------------------------------------------------- // --- Constructor // ------------------------------------------------------------- LoggerConfigurator::LoggerConfigurator(Logger *logger) { _logger = logger; }; LoggerConfigurator::~LoggerConfigurator() { _logger = NULL; }; const String LoggerConfigurator::Prefix("log_"); // ------------------------------------------------------------- // --- Public Functions // ------------------------------------------------------------- bool LoggerConfigurator::configure(LoggerSource source, const std::string option) { String line(option); line.removeChar(' '); std::string destination; String filename; if(line.contains(":")) { destination = line.before(":"); filename = line.after(":"); filename.removeChar('\''); } else { destination = line; } LoggerDestination dst = _logger->string2dest(destination); DEBUG_config("LoggerConfig:", " source:", _logger->source2string(source), " destination:", destination, " filename:", filename); return _logger->setLogOutput(source, dst, filename,true); } bool LoggerConfigurator::configure(const std::string option) { String line(option); line.removeChar(' '); if (!line.startsWith(Prefix)) return false; size_t pos1 = line.find("=", 0); if (pos1 == std::string::npos) return false; std::string source = line.substr(Prefix.size(), pos1 - Prefix.size()); std::string reduced_option; reduced_option = line.substr(pos1 + 1); LoggerSource src = _logger->string2source(source); return configure(src, reduced_option); }; bool LoggerConfigurator::debuglevel(const std::string option) { String line(option); line.removeChar(' '); String types, dest, file; LoggerDestination dst = LoggerDestination::none; if (line.contains(":")) { types = line.before(":"); dest = line.after(":"); if (dest.contains(":")) { file = dest.after(":"); dest = dest.before(":"); } dst = _logger->string2dest(dest); } else { types = line; } //std::cerr << "types:" << types << " dest:" << dest << " LoggerDestination " << _logger->dest2string(dst) << " file:" << file << std::endl; if (types.contains("ALL")) { String temp = types.before("ALL"); temp += "HIGH,LOW"; temp += types.after("ALL"); types = temp; } if (types.contains("HIGH")) { String temp = types.before("HIGH"); temp += "icap,avscan,auth,dwload,proxy,thttps"; temp += types.after("HIGH"); types = temp; } if (types.contains("LOW")) { String temp = types.before("LOW"); temp += "debug,trace,network,regexp,story,config,content"; temp += types.after("LOW"); types = temp; } types.toLower(); //std::cerr << types << std::endl; while (!types.empty()) { String temp; if (types.contains(",")) { temp = types.before(","); types = types.after(","); } else { temp = types; types = ""; } // std::cerr << "dealing with " << temp << " types left are " << types << std::endl; bool neg = temp.startsWith("-"); if (neg) temp = temp.after("-"); LoggerSource src = _logger->string2source(temp.c_str()); if (src == LoggerSource::none) { std::cerr << "Error: unknown debuglevel " << temp << std::endl; return false; }; if (neg) { _logger->disable(src); } else { if (dst != LoggerDestination::none) { // E2LOGGER_info("LoggerConfig:", " source:", src, " destination:", dest, " filename:", file ); _logger->setLogOutput(src, dst, file); // std::cerr << _logger->source2string(src) << " " << _logger->dest2string(dst) << " " << file << std::endl; } else { // assume useing defaults so just enable _logger->enable(src); } } } return true; } void LoggerConfigurator::debugformat(int fmt) { switch (fmt) { case 1: _logger->setMultiFormat(&_logger->debug_messages, false, false, true, true, true); break; case 2: _logger->setMultiFormat(&_logger->debug_messages, false, false, true, false, true); break; case 3: _logger->setMultiFormat(&_logger->debug_messages, false, false, false, false, true); break; case 4: _logger->setMultiFormat(&_logger->debug_messages, false, true, true, true, true); break; case 5: _logger->setMultiFormat(&_logger->debug_messages, false, true, true, false, true); break; case 6: _logger->setMultiFormat(&_logger->debug_messages, false, true, false, false, true); break; } } e2guardian-5.5.8r/src/LoggerConfigurator.hpp000066400000000000000000000015741477372360500210630ustar00rootroot00000000000000// LoggerConfigurator - reading strings for configuring the Logger // // Author : Klaus-Dieter Gundermann // Created : 30.06.2020 // // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_LOGGER_CONFIG #define __HPP_LOGGER_CONFIG #include "Logger.hpp" #include "String.hpp" class LoggerConfigurator { public: LoggerConfigurator(Logger *logger); ~LoggerConfigurator(); const static String Prefix; // option: log_{source}={output}[, filename] bool configure(const std::string option); bool configure(LoggerSource source, const std::string option); // debuglevel option debuglevel = 'ALL,-ICAP:destination:file bool debuglevel(const std::string option); void debugformat(int fmt); private: Logger *_logger; }; #endife2guardian-5.5.8r/src/Makefile.am000077500000000000000000000100771477372360500166050ustar00rootroot00000000000000clean_SUBDIRS= . downloadmanagers contentscanners authplugins DISTCLEANFILES = Makefile.in if ENABLE_CLAMD CLAMDSCAN_SOURCE = contentscanners/clamdscan.cpp else CLAMDSCAN_SOURCE = endif if ENABLE_AVASTD AVASTDSCAN_SOURCE = contentscanners/avastdscan.cpp else AVASTDSCAN_SOURCE = endif if ENABLE_ICAP ICAPSCAN_SOURCE = contentscanners/icapscan.cpp else ICAPSCAN_SOURCE = endif if ENABLE_KAVD KAVDSCAN_SOURCE = contentscanners/kavdscan.cpp else KAVDSCAN_SOURCE = endif if ENABLE_COMMANDLINE COMMANDLINE_SOURCE = contentscanners/commandlinescan.cpp else COMMANDLINE_SOURCE = endif DEFAULTDM_SOURCE = downloadmanagers/default.cpp #FANCYDM_SOURCE = d$downloadmanagers/fancy.cpp FANCYDM_SOURCE = TRICKLEDM_SOURCE = downloadmanagers/trickle.cpp IDENTAUTH_SOURCE = authplugins/ident.cpp IPAUTH_SOURCE = authplugins/ip.cpp PORTAUTH_SOURCE = authplugins/port.cpp HEADERAUTH_SOURCE = authplugins/header.cpp PFBASICAUTH_SOURCE = authplugins/ProxyFirstBasic.cpp BEARERBASICAUTH_SOURCE = authplugins/BearerBasic.cpp NTLMAUTH_SOURCE = if PRT_DNSAUTH DNSAUTH_SOURCE = authplugins/dnsauth.cpp else DNSAUTH_SOURCE = endif sbin_PROGRAMS = e2guardian e2guardian_CXXFLAGS = $(PCRE_CFLAGS) $(AM_CXXFLAGS) -Wall e2guardian_LDADD = $(PCRE_LIBS) $(AM_LIBS) e2guardian_CPPFLAGS = -D__LOGLOCATION='"$(E2LOGLOCATION)/"' \ -D__PIDDIR='"$(E2PIDDIR)"' \ -D__PROXYUSER='"$(E2PROXYUSER)"' \ -D__PROXYGROUP='"$(E2PROXYGROUP)"' \ -D__CONFDIR='"$(E2CONFDIR)"' \ $(AM_CPPFLAGS) e2guardian_SOURCES = String.cpp String.hpp \ FDTunnel.cpp FDTunnel.hpp \ ConnectionHandler.cpp ConnectionHandler.hpp \ DataBuffer.cpp DataBuffer.hpp \ HTTPHeader.cpp HTTPHeader.hpp \ ICAPHeader.cpp ICAPHeader.hpp \ NaughtyFilter.cpp NaughtyFilter.hpp \ BackedStore.cpp BackedStore.hpp\ RegExp.cpp RegExp.hpp \ BaseSocket.cpp BaseSocket.hpp \ Socket.cpp Socket.hpp \ UdpSocket.cpp UdpSocket.hpp \ FatController.cpp FatController.hpp \ UDSocket.cpp UDSocket.hpp \ SysV.cpp SysV.hpp \ ListContainer.cpp ListContainer.hpp \ ListMeta.cpp ListMeta.hpp \ StoryBoard.cpp StoryBoard.hpp \ SBFunction.cpp SBFunction.hpp \ HTMLTemplate.cpp HTMLTemplate.hpp \ LanguageContainer.cpp LanguageContainer.hpp \ DynamicURLList.cpp DynamicURLList.hpp \ DynamicIPList.cpp DynamicIPList.hpp \ ImageContainer.cpp ImageContainer.hpp \ IPList.cpp IPList.hpp \ OptionContainer.cpp OptionContainer.hpp \ FOptionContainer.cpp FOptionContainer.hpp \ ListManager.cpp ListManager.hpp \ md5.cpp md5.hpp \ DownloadManager.cpp DownloadManager.hpp \ ConfigVar.cpp ConfigVar.hpp \ ConfigReader.cpp ConfigReader.hpp \ ContentScanner.cpp ContentScanner.hpp \ SocketArray.cpp SocketArray.hpp \ e2guardian.cpp \ Plugin.hpp \ LOptionContainer.cpp LOptionContainer.hpp \ CertificateAuthority.cpp CertificateAuthority.hpp \ Queue.hpp, UrlRec.hpp, \ Logger.hpp Logger.cpp \ LoggerConfigurator.hpp LoggerConfigurator.cpp \ Auth.cpp Auth.hpp \ Utils/Path.hpp, Utils/Path.cpp \ \ $(ICAPSCAN_SOURCE) \ $(KAVDSCAN_SOURCE) $(CLAMDSCAN_SOURCE) \ $(AVASTDSCAN_SOURCE) \ $(COMMANDLINE_SOURCE) \ $(DEFAULTDM_SOURCE) $(FANCYDM_SOURCE) \ $(TRICKLEDM_SOURCE) \ $(IDENTAUTH_SOURCE) $(IPAUTH_SOURCE) \ $(DNSAUTH_SOURCE) $(PORTAUTH_SOURCE) \ $(BEARERBASICAUTH_SOURCE) \ $(HEADERAUTH_SOURCE) $(PFBASICAUTH_SOURCE) e2guardian-5.5.8r/src/NaughtyFilter.cpp000066400000000000000000001250221477372360500200340ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "String.hpp" #include "OptionContainer.hpp" #include "NaughtyFilter.hpp" #include "RegExp.hpp" #include "ListContainer.hpp" #include "Logger.hpp" #include #include // GLOBALS extern OptionContainer o; #ifdef HAVE_PCRE extern RegExp absurl_re, relurl_re; #endif // DECLARATIONS // category list entry class - stores category index & weight of all phrases // found so far that fall under this category. also includes a less-than // operator so that the STL sort algorithm can be applied to lists of these. class listent { public: listent() : weight(0), string(""){}; listent(const int &w, String &s) { weight = w; string = s; }; int weight; String string; int operator<(const listent &a) const { // sort in descending order of score return weight > a.weight ? 1 : 0; }; }; // IMPLEMENTATION // constructor - set up defaults NaughtyFilter::NaughtyFilter() { ch_isiphost.comp(",*[a-z|A-Z].*"); } NaughtyFilter::NaughtyFilter(HTTPHeader &request, HTTPHeader &response) { request_header = &request; response_header = &response; ch_isiphost.comp(",*[a-z|A-Z].*"); reset(); } NaughtyFilter::NaughtyFilter(HTTPHeader &request, HTTPHeader &response, auth_rec &authrecin) { request_header = &request; response_header = &response; authrec = &authrecin; ch_isiphost.comp(",*[a-z|A-Z].*"); reset(); } void NaughtyFilter::setURL(bool set_ismitm) { // do all of this normalisation etc just the once at the start. url = request_header->getUrl(false, false); baseurl = url; baseurl.removeWhiteSpace(); baseurl.toLower(); baseurl.removePTP(); logurl = request_header->getLogUrl(false, set_ismitm); urld = request_header->decode(url); urldomain = url.getHostname(); urldomain.toLower(); String temp; temp = request_header->getUrl(true,false); // get url with port urldomainport = temp.getHostname(); urldomainport.toLower(); connect_site = urldomain; isiphost = isIPHostnameStrip(urldomain); is_ssl = request_header->requestType().startsWith("CONNECT"); ishead = request_header->requestType().startsWith("HEAD"); isconnect = is_ssl; ismitm = set_ismitm; if (baseurl == urldomain) issiteonly = true; docsize = 0; } void NaughtyFilter::setURL(String &sni) { // do all of this normalisation etc just the once at the start. url = sni; baseurl = url; baseurl.removeWhiteSpace(); baseurl.toLower(); baseurl.removePTP(); logurl = url; urld = request_header->decode(url); urldomain = url.getHostname(); urldomain.toLower(); connect_site = urldomain; isiphost = isIPHostnameStrip(urldomain); if (baseurl == urldomain) issiteonly = true; docsize = 0; } void NaughtyFilter::reset() { anon_user = o.log.anonymise_logs; anon_url = false; logurl = ""; isItNaughty = false; isException = false; whatIsNaughty = ""; whatIsNaughtyLog = ""; whatIsNaughtyCategories = ""; tempfilename = ""; tempfilemime= ""; tempfiledis= ""; usedisplaycats = false; blocktype = 0; store = false; naughtiness = 0; isGrey = false; isSSLGrey = false; isSearch = false; message_no = 0; is_text = false; filtergroup = 0; deep_urls_checked = false; has_deep_urls = false; issiteonly = false; gomitm = false; nomitm = false; automitm = false; // resets from CH waschecked = false; // flags wasrequested = false; isexception = false; issemiexception = false; isourwebserver = false; wasclean = false; isbypass = false; iscookiebypass = false; isvirusbypass = false; isbypassallowed = false; isinfectionbypassallowed = false; bypasstimestamp = 0; isscanbypass = false; ispostblock = false; pausedtoobig = false; wasinfected = false; wasscanned = false; contentmodified = false; urlmodified = false; headermodified = false; headeradded = false; nocheckcert = false; noviruscheck = true; headersent = 0; message_no = 0; log_message_no = 0; urlredirect = false; logcategory = false; upfailure = false; isdirect = o.net.no_proxy; // always set if no proxy defined // authed = false; // isbanneduser = false; mimetype = "-"; docsize = 0; // to store the size of the returned document for logging orig_ip = ""; orig_port = 0; got_orig_ip = false; } // check the given document body for banned, weighted, and exception phrases (and PICS, and regexes, &c.) // also used for scanning search terms, which causes various features - PICS, META/TITLE extraction, etc. - to be disabled void NaughtyFilter::checkme(const char *rawbody, off_t rawbodylen, const String *url, const String *domain, FOptionContainer* &foc, unsigned int phraselist, int limit, bool searchterms) { if (searchterms) { DEBUG_content("Content flagged as search terms - disabling hex decoding, META/TITLE extraction & HTML removal"); } if (foc->weighted_phrase_mode == 0) { DEBUG_content("Weighted phrase mode 0 - not going any further."); return; } // hex-decoded data (not case converted) off_t hexdecodedlen = rawbodylen; const char *hexdecoded = rawbody; unsigned char c='\0'; // Hex decode content if desired // Do this now, as it's not especially case-sensitive, // and the case alteration should modify case post-decoding // Search terms are already hex decoded, as they need to be to strip URL decoding if (!searchterms && o.naughty.hex_decode_content) { // Mod suggested by AFN Tue 8th April 2003 DEBUG_content("Hex decoding is enabled"); char *hexdecoded_buf = new char[rawbodylen + 128 + 1]; memset(hexdecoded_buf, 0, rawbodylen + 128 + 1); unsigned char c1; unsigned char c2; unsigned char c3; char hexval[5] = "0x"; // Initializes a "hexadecimal string" hexval[4] = '\0'; //TODO: should this not be hexval[2]??? - No this is correct but inefficient char *ptr; // pointer required by strtol // make a copy of the escaped document char by char off_t i = 0; off_t j = 0; while (i < rawbodylen - 3) { // we lose 3 bytes but what the hell.. c1 = rawbody[i]; c2 = rawbody[i + 1]; c3 = rawbody[i + 2]; if (c1 == '%' && (((c2 >= '0') && (c2 <= '9')) || ((c2 >= 'a') && (c2 <= 'f')) || ((c2 >= 'A') && (c2 <= 'F'))) && (((c3 >= '0') && (c3 <= '9')) || ((c3 >= 'a') && (c3 <= 'f')) || ((c3 >= 'A') && (c3 <= 'F')))) { hexval[2] = c2; hexval[3] = c3; c = (unsigned char)strtol(hexval, &ptr, 0); i += 3; } else { c = c1; i++; } hexdecoded_buf[j] = c; j++; } // copy any remaining bytes while (i < rawbodylen) { hexdecoded_buf[j++] = rawbody[i++]; } hexdecoded_buf[j] = '\0'; hexdecodedlen = j; hexdecoded = hexdecoded_buf; } // scan twice, with & without case conversion (if desired) - aids support for exotic char encodings // TODO: move META/title sentinel location outside this loop, as they are not case sensitive operations bool preserve_case = (o.naughty.preserve_case != 0); bool do_raw = false; bool do_nohtml = false; if (o.naughty.preserve_case == 2) { // scanning twice *is* desired // first time round the loop, don't preserve case (non-exotic encodings) DEBUG_content("Filtering with/without case preservation is enabled"); preserve_case = false; } // Store for the lowercase (maybe) data // The extra 128 is used for various speed tricks to // squeeze as much speed as possible. char *bodylc = new char[hexdecodedlen + 128 + 1]; memset(bodylc, 0, hexdecodedlen + 128 + 1); // Store for the tag-stripped data // Don't bother tag stripping search terms char *bodynohtml = NULL; if (!searchterms && (o.naughty.phrase_filter_mode == 1 || o.naughty.phrase_filter_mode == 2)) { do_nohtml = true; bodynohtml = new char[hexdecodedlen + 128 + 1]; memset(bodynohtml, 0, hexdecodedlen + 128 + 1); } if ((o.naughty.phrase_filter_mode == 0 || o.naughty.phrase_filter_mode == 2 || o.naughty.phrase_filter_mode == 3)) do_raw = true; for (int loop = 0; loop < (o.naughty.preserve_case == 2 ? 2 : 1); loop++) { DEBUG_content("Preserve case: ", preserve_case ); off_t i, j; if (searchterms || do_raw) DEBUG_content("Raw content needed"); // use the one that's been hex decoded, but not stripped // make a copy of the document lowercase char by char if (preserve_case) { if (do_nohtml || o.naughty.phrase_filter_mode == 3) { for (i = 0; i < hexdecodedlen; i++) { c = hexdecoded[i]; // if (c == 13 || c == 9 || c == 10) { if (c < '/' || c == ':' || c == ';' || c == '=' || c == '?' || c == '@' || (c > 90 && c < 97)) { c = 32; // convert all whitespace and most punctuation marks to a space } bodylc[i] = c; } } else { // not being html striped so can remove < > now for (i = 0; i < hexdecodedlen; i++) { c = hexdecoded[i]; // if (c == 13 || c == 9 || c == 10) { if (c < '/' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a')) { c = 32; // convert all whitespace and most punctuation marks to a space } bodylc[i] = c; } } } else { DEBUG_content("Not preserving case of raw content"); if (do_nohtml || o.naughty.phrase_filter_mode == 3) { for (i = 0; i < hexdecodedlen; i++) { c = hexdecoded[i]; if (c >= 'A' && c <= 'Z') { c = 'a' + c - 'A'; } else if (c >= 192 && c <= 221) { // for accented chars c += 32; // 224 + c - 192 } else { //if (c == 13 || c == 9 || c == 10) { if (c < '/' || c == ':' || c == ';' || c == '=' || c == '?' || c == '@' || (c > 90 && c < 97)) { c = 32; // convert all whitespace and most punctuation marks to a space } } bodylc[i] = c; } } else { // not being html striped so can remove < > now for (i = 0; i < hexdecodedlen; i++) { c = hexdecoded[i]; if (c >= 'A' && c <= 'Z') { c = 'a' + c - 'A'; } else if (c >= 192 && c <= 221) { // for accented chars c += 32; // 224 + c - 192 } else { //if (c == 13 || c == 9 || c == 10) { if (c < '/' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a')) { c = 32; // convert all whitespace and most punctuation marks to a space } } bodylc[i] = c; } } } // filter meta tags & title only // based on idea from Nicolas Peyrussie if (!searchterms && (o.naughty.phrase_filter_mode == 3)) { DEBUG_content("Filtering META/title"); bool addit = false; // flag if we should copy this char to filtered version bool needcheck = false; // flag if we actually find anything worth filtering off_t bodymetalen; // find or as end of search range char *endhead = strstr(bodylc, "' i += 7; c = bodylc[i]; } } // meta tags end at a > // title tags end at the next < (opening of ) if (addit && ((c == '>') || (c == '<'))) { // stop ading data addit = false; // add a space before the next word in the check buffer bodymeta[j++] = 32; } if (addit) { // if we're in "record" mode (i.e. inside a title/metatag), strip certain characters out // of the data (to sanitise metatags & aid filtering of titles) //if (c == ',' || c == '=' || c == '"' || c == '\'' // || c == '(' || c == ')' || c == '.') { // // replace with a space // c = 32; //} // don't bother duplicating spaces if ((c != 32) || (c == 32 && (bodymeta[j - 1] != 32))) { bodymeta[j++] = c; // copy it to the filtered copy } } } if (needcheck) { bodymeta[j++] = '\0'; DEBUG_content(bodymeta); bodymetalen = j; checkphrase(bodymeta, bodymetalen, NULL, NULL, foc, phraselist, limit, searchterms); } else DEBUG_content("Nothing to filter"); delete[] bodymeta; // surely the intention is to search *only* meta/title, so always exit delete[] bodylc; delete[] bodynohtml; if (hexdecoded != rawbody) delete[] hexdecoded; return; } if (do_nohtml) { // if we fell through to here, use the one that's been hex decoded AND stripped // Strip HTML DEBUG_content("\"Smart\" filtering is enabled"); // we need this extra byte * bool inhtml = false; // to flag if our pointer is within a html <> bool addit; // flag if we should copy this char to filtered version j = 1; bodynohtml[0] = 32; // * for this for (off_t i = 0; i < hexdecodedlen; i++) { addit = true; c = bodylc[i]; if (c == '<') { inhtml = true; // flag we are inside a html <> } if (c == '>') { // flag we have just left a html <> inhtml = false; c = 32; } if (inhtml) { addit = false; } if (c == 32) { if (bodynohtml[j - 1] == 32) { // * and this addit = false; } } if (addit) { // if it passed the filters bodynohtml[j++] = c; // copy it to the filtered copy } } DEBUG_content("Checking smart content"); checkphrase(bodynohtml, j - 1, NULL, NULL, foc, phraselist, limit, searchterms); if (isItNaughty || isException) { delete[] bodylc; delete[] bodynohtml; if (hexdecoded != rawbody) delete[] hexdecoded; return; // Well there is no point in continuing is there? } } if (!do_raw) { delete[] bodylc; delete[] bodynohtml; if (hexdecoded != rawbody) delete[] hexdecoded; return; // only doing nohtml mode filtering } else { DEBUG_content("Checking raw content"); if (do_nohtml) { // already removed if not! // replace html tag start and finish with space so that Start and finish words are detected for (i = 0; i < hexdecodedlen; i++) { c = bodylc[i]; if (c == '>' || c == '<') bodylc[i] = 32; } } // check unstripped content checkphrase(bodylc, hexdecodedlen, url, domain, foc, phraselist, limit, searchterms); if (isItNaughty || isException) { delete[] bodylc; delete[] bodynohtml; if (hexdecoded != rawbody) delete[] hexdecoded; return; // Well there is no point in continuing is there? } } // second time round the case loop (if there is a second time), // do preserve case (exotic encodings) preserve_case = true; } delete[] bodylc; delete[] bodynohtml; if (hexdecoded != rawbody) delete[] hexdecoded; } // check the phrase lists void NaughtyFilter::checkphrase(char *file, off_t filelen, const String *url, const String *domain, FOptionContainer* &foc, unsigned int phraselist, int limit, bool searchterms) { int weighting = 0; unsigned int cat; RegResult Rre; std::string weightedphrase; String lastcategory; // checkme: translate this? String currcat("Embedded URLs"); // found categories list & reusable iterators std::map listcategories; // check for embedded references to banned sites/URLs. // have regexes that check for URLs in pages (look for attributes (src, href, javascript location) // or look for protocol strings (in which case, incl. ftp)?) and extract them. // then check the extracted list against the banned site/URL lists. // ADs category lists do not want to add to the possibility of a site being banned. // Exception lists are not checked. // Do not do full-blown category retrieval/duplicate checking; simply add the // "Embedded URLs" category. // Put a warning next to the option in the config file that this will take lots of CPU. // Support phrase mode 1/2 distinction (duplicate sites/URLs). // Have weight configurable per filter group, not globally or with a list directive - // a weight of 0 will disable the option, effectively making this functionality per-FG itself. // todo: if checkphrase is passed the domain & existing URL, it can create full URLs from relative ones. // if a src/href URL starts with a /, append it to the domain; otherwise, append it to the existing URL. // chop off anything after a ?, run through realPath, then put through the URL lists. #ifdef HAVE_PCRE // if weighted phrases are enabled, and we have been passed a URL and domain, and embedded URL checking is enabled... // then check for embedded URLs! #ifdef LEAVE_OUT_FOR_NOW // TODO - THIS SECTION DISABLED IN V5 - REVIISIT LATER IF NEEDED if (url != NULL && foc->embedded_url_weight > 0) { std::map::iterator ourcat; bool catinited = false; std::map found; std::map::iterator founditem; String u; char *j; // check for absolute URLs if (absurl_re.match(file, Rre)) { // each match generates 2 results (because of the brackets in the regex), // we're only interested in the first DEBUG_content("Found ", Rre.numberOfMatches() / 2, " absolute URLs:"); for (int i = 0; i < Rre.numberOfMatches(); i += 2) { // chop off quotes u = Rre.result(i); u = u.subString(1, u.length() - 2); DEBUG_content(u); if ((((j = foc->inBannedSiteList(u, false, false, false, lastcategory)) != NULL) && !(lastcategory.contains("ADs"))) || (((j = foc->inBannedURLList(u, false, false,false, lastcategory )) != NULL) && !(lastcategory.contains("ADs")))) { // duplicate checking // checkme: this should really be being done *before* we search the lists. // but because inBanned* methods do some cleaning up of their own, we don't know the form to check against. // we actually want these cleanups do be done before passing to inBanned*/inException* - this would // speed up ConnectionHandler a bit too. founditem = found.find(j); if ((foc->weighted_phrase_mode == 2) && (founditem != found.end())) { founditem->second++; } else { // add the site to the found phrases list found[j] = 1; if (weightedphrase.length() == 0) weightedphrase = "["; else weightedphrase += " "; weightedphrase += j; if (!catinited) { listcategories[-1] = listent(foc->embedded_url_weight, currcat); ourcat = listcategories.find(-1); catinited = true; } else ourcat->second.weight += foc->embedded_url_weight; } } } } found.clear(); // check for relative URLs if (relurl_re.match(file,Rre)) { // we don't want any parameters on the end of the current URL, since we append to it directly // when forming absolute URLs from relative ones. we do want a / on the end, too. String currurl(*url); if (currurl.contains("?")) currurl = currurl.before("?"); if (currurl[currurl.length() - 1] != '/') currurl += "/"; // each match generates 2 results (because of the brackets in the regex), // we're only interested in the first DEBUG_content("Found ", Rre.numberOfMatches() / 2, " relative URLs:"); for (int i = 0; i < Rre.numberOfMatches(); i += 2) { u = Rre.result(i); // can't find a way to negate submatches in PCRE, so it is entirely possible // that some absolute URLs have made their way into this list. we don't want them. if (u.contains("://")) continue; DEBUG_content(u); // remove src/href & quotes u = u.after("="); u.removeWhiteSpace(); u = u.subString(1, u.length() - 2); // create absolute URL if (u[0] == '/') u = (*domain) + u; else u = currurl + u; DEBUG_content("absolute form: ", u ); if ((((j = foc->inBannedSiteList(u, false, false, false, lastcategory)) != NULL) && !(lastcategory.contains("ADs"))) || (((j = foc->inBannedURLList(u, false, false, false, lastcategory)) != NULL) && !(lastcategory.contains("ADs")))) { // duplicate checking // checkme: this should really be being done *before* we search the lists. // but because inBanned* methods do some cleaning up of their own, we don't know the form to check against. // we actually want these cleanups do be done before passing to inBanned*/inException* - this would // speed up ConnectionHandler a bit too. founditem = found.find(j); if ((foc->weighted_phrase_mode == 2) && (founditem != found.end())) { founditem->second++; } else { // add the site to the found phrases list found[j] = 1; if (weightedphrase.length() == 0) weightedphrase = "["; else weightedphrase += " "; weightedphrase += j; if (!catinited) { listcategories[-1] = listent(foc->embedded_url_weight, currcat); ourcat = listcategories.find(-1); catinited = true; } else ourcat->second.weight += foc->embedded_url_weight; } } } } if (catinited) { weighting = ourcat->second.weight; weightedphrase += "]"; DEBUG_content(weightedphrase); DEBUG_content("score from embedded URLs: ", ourcat->second.weight ); } } #endif // END OF LEAVE_OUT_FOR_NOW #endif std::string bannedphrase; std::string exceptionphrase; String bannedcategory; int type, index, weight, time; bool allcmatched = true, bannedcombi = false; std::string s1; // this line here searches for phrases contained in the list - the rest of the code is all sorting // through it to find the categories, weightings, types etc. of what has actually been found. std::map > found; o.lm.l[phraselist]->graphSearch(found, file, filelen); // cache reusable iterators std::map >::iterator foundend = found.end(); std::map >::iterator foundcurrent; // look for combinations first //if banned must wait for exception later std::string combifound; std::string combisofar; std::vector::iterator combicurrent = o.lm.l[phraselist]->combilist.begin(); std::map::iterator catcurrent; int lowest_occurrences = 0; while (combicurrent != o.lm.l[phraselist]->combilist.end()) { // Grab the current combination phrase part index = *combicurrent; // Do stuff if what we have is an end marker (end of one list of parts) if (index == -2) { // Were all the parts in this combination matched? if (allcmatched) { type = *(++combicurrent); // check this time limit against the list of time limits time = *(++combicurrent); if (not(o.lm.l[phraselist]->checkTimeAtD(time))) { // nope - so don't take any notice of it #ifdef DEBUG_LOW combicurrent++; cat = (*++combicurrent); DEBUG_content("Ignoring combi phrase based on time limits: ", combisofar, "; ", o.lm.l[phraselist]->getListCategoryAtD(cat)); #else combicurrent += 2; #endif combisofar = ""; } else if (type == -1) { // combination exception isItNaughty = false; isException = true; // Combination exception phrase found: // Combination exception search term found: message_no = searchterms ? 456 : 605; whatIsNaughtyLog = o.language_list.getTranslation(message_no); whatIsNaughtyLog += combisofar; whatIsNaughty = ""; ++combicurrent; cat = *(++combicurrent); whatIsNaughtyCategories = o.lm.l[phraselist]->getListCategoryAtD(cat); return; } else if (type == 1) { // combination weighting weight = *(++combicurrent); weighting += weight * (foc->weighted_phrase_mode == 2 ? 1 : lowest_occurrences); if (weight > 0) { cat = *(++combicurrent); //category index -1 indicates an uncategorised list if (cat >= 0) { //don't output duplicate categories catcurrent = listcategories.find(cat); if (catcurrent != listcategories.end()) { catcurrent->second.weight += weight * (foc->weighted_phrase_mode == 2 ? 1 : lowest_occurrences); } else { currcat = o.lm.l[phraselist]->getListCategoryAtD(cat); listcategories[cat] = listent(weight, currcat); } } } else { // skip past category for negatively weighted phrases combicurrent++; } if (weightedphrase.length() > 0) { weightedphrase += "+"; } weightedphrase += "("; if (weight < 0) { weightedphrase += "-" + combisofar; } else { weightedphrase += combisofar; } DEBUG_content("found combi weighted phrase (", foc->weighted_phrase_mode, "): ", combisofar, " x", lowest_occurrences, " (per phrase: ", weight, ", calculated: ", (weight * (foc->weighted_phrase_mode == 2 ? 1 : lowest_occurrences)), ")" ); weightedphrase += ")"; combisofar = ""; } else if (type == 0) { // combination banned bannedcombi = true; combifound += "(" + combisofar + ")"; combisofar = ""; combicurrent += 2; cat = *(combicurrent); bannedcategory = o.lm.l[phraselist]->getListCategoryAtD(cat); } } else { // We had an end marker, but not all the parts so far were matched. // Reset the match flag ready for the next chain, and advance to its first part. allcmatched = true; combicurrent += 4; lowest_occurrences = 0; } } else { // We didn't get an end marker - just an individual part. // If all parts in the current chain have been matched so far, look for this one as well. if (allcmatched) { s1 = o.lm.l[phraselist]->getItemAtInt(index); if ((foundcurrent = found.find(s1)) == foundend) { allcmatched = false; combisofar = ""; } else { if (combisofar.length() > 0) { combisofar += ", "; } combisofar += s1; // also track lowest number of times any one part occurs in the text // as this will correspond to the number of times the whole chain occurs if ((lowest_occurrences == 0) || (lowest_occurrences > foundcurrent->second.second)) { lowest_occurrences = foundcurrent->second.second; } } } } // Advance to the next part in the current chain combicurrent++; } // even if we already found a combi ban, we must still wait; there may be non-combi exceptions to follow // now check non-combi phrases foundcurrent = found.begin(); while (foundcurrent != foundend) { // check time for current phrase if (not o.lm.l[phraselist]->checkTimeAt(foundcurrent->second.first)) { DEBUG_content("Ignoring phrase based on time limits: ", foundcurrent->first, ", ", o.lm.l[phraselist]->getListCategoryAt(foundcurrent->second.first) ); foundcurrent++; continue; } // 0=banned, 1=weighted, -1=exception, 2=combi, 3=weightedcombi type = o.lm.l[phraselist]->getTypeAt(foundcurrent->second.first); if (type == 0) { // if we already found a combi ban, we don't need to know this stuff if (!bannedcombi) { isItNaughty = true; bannedphrase = foundcurrent->first; bannedcategory = o.lm.l[phraselist]->getListCategoryAt(foundcurrent->second.first, &cat); } } else if (type == 1) { // found a weighted phrase - either add one lot of its score, or one lot for every occurrence, depending on phrase filtering mode weight = o.lm.l[phraselist]->getWeightAt(foundcurrent->second.first) * (foc->weighted_phrase_mode == 2 ? 1 : foundcurrent->second.second); weighting += weight; if (weight > 0) { currcat = o.lm.l[phraselist]->getListCategoryAt(foundcurrent->second.first, &cat); if (cat >= 0) { //don't output duplicate categories catcurrent = listcategories.find(cat); if (catcurrent != listcategories.end()) { // add one or N times the weight to this category's score catcurrent->second.weight += weight * (foc->weighted_phrase_mode == 2 ? 1 : foundcurrent->second.second); } else { listcategories[cat] = listent(weight, currcat); } } } if (o.naughty.show_weighted_found) { if (weightedphrase.length() > 0) { weightedphrase += "+"; } if (weight < 0) { weightedphrase += "-"; } weightedphrase += foundcurrent->first; } DEBUG_content("found weighted phrase (", foc->weighted_phrase_mode, "): ", foundcurrent->first, " x", foundcurrent->second.second, " (per phrase: ", o.lm.l[phraselist]->getWeightAt(foundcurrent->second.first), ", calculated: ", weight, ")" ); } else if (type == -1) { isException = true; isItNaughty = false; // Exception phrase found: // Exception search term found: message_no = searchterms ? 457 : 604; whatIsNaughtyLog = o.language_list.getTranslation(message_no); whatIsNaughtyLog += foundcurrent->first; whatIsNaughty = ""; whatIsNaughtyCategories = o.lm.l[phraselist]->getListCategoryAt(foundcurrent->second.first, NULL); return; // no point in going further } foundcurrent++; } DEBUG_content("WEIGHTING: ", weighting ); // store the lowest negative weighting or highest positive weighting out of all filtering runs, preferring to store positive weightings. if ((weighting < 0 && naughtiness <= 0 && weighting < naughtiness) || (naughtiness >= 0 && weighting > naughtiness) || (naughtiness < 0 && weighting > 0)) { naughtiness = weighting; } DEBUG_content("NAUGHTINESS: ", naughtiness ); // *now* we can safely get down to the whole banning business! if (bannedcombi) { isItNaughty = true; // Banned combination phrase found: // Banned combination search term found: message_no = searchterms ? 453 : 401; log_message_no = searchterms ? 452 : 400; whatIsNaughtyLog = o.language_list.getTranslation(log_message_no); whatIsNaughtyLog += combifound; // Banned combination phrase found. // Banned combination search term found. whatIsNaughty = o.language_list.getTranslation(message_no ); whatIsNaughtyCategories = bannedcategory.toCharArray(); return; } if (isItNaughty) { // Banned phrase found: // Banned search term found: message_no = searchterms ? 450 : 300; whatIsNaughtyLog = o.language_list.getTranslation(message_no); whatIsNaughtyLog += bannedphrase; // Banned phrase found. // Banned search term found. whatIsNaughty = o.language_list.getTranslation(searchterms ? 451 : 301); whatIsNaughtyCategories = bannedcategory.toCharArray(); return; } bool log_it = o.naughty.show_all_weighted_found; auto mno = searchterms ? 454 : 402; if (weighting > limit) { log_it = true; isItNaughty = true; message_no = mno; whatIsNaughty = o.language_list.getTranslation(searchterms ? 455 : 403); } if(log_it) { // Weighted phrase limit of // Weighted search term limit of whatIsNaughtyLog = o.language_list.getTranslation(mno); whatIsNaughtyLog += String(limit).toCharArray(); whatIsNaughtyLog += " : "; whatIsNaughtyLog += String(weighting).toCharArray(); if (o.naughty.show_weighted_found) { whatIsNaughtyLog += " ("; whatIsNaughtyLog += weightedphrase; whatIsNaughtyLog += ")"; } // Weighted phrase limit exceeded. // Weighted search term limit exceeded. // Generate category list, sorted with highest scoring first. bool nonempty = false; bool belowthreshold = false; String categories; std::deque sortable_listcategories; catcurrent = listcategories.begin(); while (catcurrent != listcategories.end()) { sortable_listcategories.push_back(catcurrent->second); catcurrent++; } std::sort(sortable_listcategories.begin(), sortable_listcategories.end()); std::deque::iterator k = sortable_listcategories.begin(); while (k != sortable_listcategories.end()) { // if category display threshold is in use, apply it if (!belowthreshold && (foc->category_threshold > 0) && (k->weight < foc->category_threshold)) { whatIsNaughtyDisplayCategories = categories.toCharArray(); belowthreshold = true; usedisplaycats = true; } if (k->string.length() > 0) { if (nonempty) categories += ", "; categories += k->string; nonempty = true; } k++; // if category threshold is set to show only the top category, // everything after the first loop is below the threshold if (!belowthreshold && foc->category_threshold < 0) { whatIsNaughtyDisplayCategories = categories.toCharArray(); belowthreshold = true; usedisplaycats = true; } } whatIsNaughtyCategories = categories.toCharArray(); } return; // whatIsNaughty is what is displayed in the browser // whatIsNaughtyLog is what is logged in the log file if at all } // strip the URL down to just the IP/hostname, then do an isIPHostname on the result bool NaughtyFilter::isIPHostnameStrip(String url) { url = url.getHostname(); if(ch_isiphost.match(url.toCharArray(), Rch_isiphost)) return false; else return true; // return ldl->fg[0]->isIPHostname(url); } String NaughtyFilter::getFlags() { String flags = listen_port; flags += ":"; if (!(authrec == nullptr)) { if (authrec->is_transparent) flags += "T"; else if (authrec->is_icap) flags += "I"; else { if(authrec->is_tlsproxy) flags += "L"; else flags += "P"; } if (o.log.addECHtoFlags && hasECH) { flags += "E"; } if (ismitm) flags += "M"; else if (isconnect) flags += "S"; else if (!authrec->is_icap) flags += "H"; flags += ":"; flags += authrec->user_source; flags += ":"; flags += authrec->group_source; } return flags; } String NaughtyFilter::get_lastmatch() { if (anon_url) { return lastmatch.anonimise(); } return lastmatch; } String NaughtyFilter::get_logUrl() { if (anon_url) { String t1 = logurl.before("://"); String t2 = logurl.after("://"); t1 += "://"; t1 += t2.anonimise(); return t1; } return logurl; } String NaughtyFilter::main_category() { String temp = whatIsNaughtyCategories; if (temp.contains(",")) { temp = temp.before(","); } if (temp == "-") temp = ""; temp.toLower(); return temp; } e2guardian-5.5.8r/src/NaughtyFilter.hpp000066400000000000000000000151161477372360500200430ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_NAUGHTYFILTER #define __HPP_NAUGHTYFILTER // INCLUDES //#include "String.hpp" //#include "OptionContainer.hpp" //#include "DataBuffer.hpp" #include "HTTPHeader.hpp" //#include "FOptionContainer.hpp" #include "UrlRec.hpp" #include "Auth.hpp" class FOptionContainer; // DECLARATIONS bool isListCheck = false; class NaughtyFilter { public: // should the content be blocked? bool isItNaughty = false; // should the content bypass any further filtering? bool isException = false; // is the url/site in Greylist - forces content check bool isGrey = false; // is the url/site in SSLGreylist bool isSSLGrey = false; // is the url a search request bool isSearch = false; // is the url to be blocked bool isBlocked = false; bool hasSNI = false; // used in transparent https mode bool hasECH = false; // used in transparent https mode bool isTLS = false; // used in transparent https mode String orig_ip; // used in transparent mode int orig_port = 0; // used in transparent mode bool got_orig_ip = false; // used in transparent mode // bad certificat bool badcert = false; int listen_port = 0; // listening port struct auth_rec *authrec = nullptr; // return true or false? bool isReturn = false; bool reverse_checked = false; bool hasEmbededURL = false; std::deque embededURLs; std::deque reversedURLs; HTTPHeader* request_header; HTTPHeader* response_header; bool isIPHostnameStrip(String url); RegExp ch_isiphost; RegResult Rch_isiphost; bool gomitm = false; bool nomitm = false; bool automitm = false; bool alert = false; bool deep_urls_checked = false; bool has_deep_urls = false; std::deque deep_urls; bool anon_user = false; bool anon_url = false; // flags from ConnectionHandler bool upfailure = false; //set when problem with upstream connection (site or proxy) bool waschecked = false; bool wasrequested = false; bool isexception = false; bool issemiexception = false; bool isourwebserver = false; bool wasclean = false; bool cachehit = false; bool isbypass = false; bool iscookiebypass = false; bool isvirusbypass = false; bool isscanbypass = false; bool isbypassallowed = false; bool isinfectionbypassallowed = false; bool isscanbypassallowed = false; bool istoobigbypassallowed = false; bool ispostblock = false; bool pausedtoobig = false; bool wasinfected = false; bool wasscanned = false; bool contentmodified = false; bool urlmodified = false; bool headermodified = false; bool headeradded = false; bool isconnect = false; bool ishead = false; bool isiphost = false; bool scanerror = false; bool ismitmcandidate = false; bool is_ssl = false; bool is_ip = false; bool ismitm = false; bool isdone = false; bool nolog = false; bool nocheckcert = false; bool logcategory = false; bool noviruscheck = true; bool urlredirect = false; bool isdirect = false; // go direct if true via proxy if false bool tunnel_rest = false; bool tunnel_2way = false; bool is_text = false; bool issiteonly = false; int auth_result = 0; String search_words; String search_terms; struct timeval thestart; // 0=none,1=first line,2=all int headersent = 0; int bypasstimestamp = 0; std::string mimetype; String url; // the normalised url String baseurl; // url with 'http[s]://' removed String logurl; // url with called protocol String urld; // decoded url String urldomain; // the domain or site part of the url String urldomainport; // the domain or site part of the url with port number String connect_site; // the site to connect to - normally same as urldomain String user; // result of auth plug-in id - may be network log-in name or client IP or port String realuser; // real or authed user name String lastmatch; String result; String get_lastmatch(); String get_logUrl(); String main_category(); std::string exceptionreason; // to hold the reason for not blocking std::string exceptioncat; off_t docsize; // to store the size of the returned document for logging int filtergroup; String tempfilename; String tempfilemime; String tempfiledis; // should the browser use the categories string or the displaycategories string? // (related to category list thresholding) bool usedisplaycats = false; // blocked data type - 0 = response body, 1 = request body (POST data), // 2 = URL parameters (search terms) int blocktype = 0; // flag for use by ContentScanners to say whether data should be stored // for future inspection. storage only implemented for POST data. bool store = false; int message_no = 0; int log_message_no = 0; // the reason for banning, what to say about it in the logs, and the // categories under which banning has taken place std::string whatIsNaughty; std::string whatIsNaughtyLog; std::string whatIsNaughtyCategories; std::string whatIsNaughtyDisplayCategories; std::string clienthost; std::string clientip; NaughtyFilter(); NaughtyFilter(HTTPHeader &request, HTTPHeader &response); NaughtyFilter(HTTPHeader &request, HTTPHeader &response, auth_rec &authrecin); void reset(); void setURL(bool set_ismitm = false); void setURL(String &sni); void checkme(const char *rawbody, off_t rawbodylen, const String *url, const String *domain, FOptionContainer* &foc, unsigned int phraselist, int limit, bool searchterms = false); String getFlags(); // highest positive (or lowest negative) weighting out of // both phrase filtering passes (smart/raw) int naughtiness = 0; String lastcategory; private: // check the banned, weighted & exception lists // pass in both URL & domain to activate embedded URL checking // (this is made optional in this manner because it's pointless // trying to look for links etc. in "smart" filtering mode, i.e. // after HTML has been removed, and in search terms.) void checkphrase(char *file, off_t filelen, const String *url, const String *domain, FOptionContainer* &foc, unsigned int phraselist, int limit, bool searchterms); }; #define __HPP_NAUGHTYFILTER #endif e2guardian-5.5.8r/src/OptionContainer.cpp000066400000000000000000001141501477372360500203620ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include #include "ConfigVar.hpp" #include "OptionContainer.hpp" #include "LOptionContainer.hpp" #include "RegExp.hpp" #include "Logger.hpp" #include "LoggerConfigurator.hpp" #include #include #include #include #include #include #include #include // GLOBALS // IMPLEMENTATION OptionContainer::OptionContainer() { log.log_Q = new Queue; log.RQlog_Q = new Queue; } OptionContainer::~OptionContainer() { reset(); } void OptionContainer::reset() { plugins.deletePlugins(plugins.dmplugins); plugins.deletePlugins(plugins.csplugins); plugins.deletePlugins(plugins.authplugins); plugins.auth.auth_map.clear(); language_list.reset(); net.filter_ip.clear(); net.filter_ports.clear(); } // Purpose: reads all options from the main configuration file (e2guardian.conf) bool OptionContainer::read_config(const Path &filename, bool readFullConfig) { ConfigReader cr; // all sorts of exceptions could occur reading conf files try { Path list_pwd(__CONFDIR); list_pwd.append("/lists/common"); if (!cr.readConfig(filename, list_pwd)) return false; if ((proc.pid_filename = cr.findoptionS("pidfilename")) == "") { proc.pid_filename = std::string(__PIDDIR) + "/e2guardian.pid"; } if (!readFullConfig) { // pid_filename is the only thing needed to send signals return true; } if (!findProcOptions(cr)) return false; if (!findLoggerOptions(cr)) return false; if (!findAccessLogOptions(cr)) return false; if (!findConfigOptions(cr)) return false; if (!findDStatOptions(cr)) return false; if (!findCertificateOptions(cr)) return false; if (!findNetworkOptions(cr)) return false; if (!findConnectionHandlerOptions(cr)) return false; if (!findContentScannerOptions(cr)) return false; if (!findBlockPageOptions(cr)) return false; if (!findFilterGroupOptions(cr)) return false; if (!findHeaderOptions(cr)) return false; if (!findICAPOptions(cr)) return false; if (!findListsOptions(cr)) return false; if (!findMonitorOptions(cr)) return false; if (!findNaughtyOptions(cr)) return false; if (!findPluginOptions(cr)) return false; if (!findStoryBoardOptions(cr)) return false; #ifdef ENABLE_EMAIL // Email notification patch by J. Gauthier mailer = cr.findoptionS("mailer"); #endif // to remove in v5.5 // monitor_helper = findoptionS("monitorhelper"); // if (monitor_helper == "") { // monitor_helper_flag = false; // } else { // monitor_helper_flag = true; // } use_xforwardedfor = cr.findoptionB("usexforwardedfor"); per_room_directory_location = cr.findoptionS("perroomdirectory"); // recheck_replaced_urls = cr.findoptionB("recheckreplacedurls"); // soft_restart = (findoptionS("softrestart") == "on"); // Unused // if (cert.enable_ssl) { // if (!cert.generate_ca_certificate()) return false; // } } catch (std::exception &e) { E2LOGGER_error(e.what()); return false; } DEBUG_config("Done: read Configfile: ", filename.fullPath()); return true; } bool OptionContainer::readinStdin() { DEBUG_trace(""); if (!std::cin.good()) { E2LOGGER_error("Error reading stdin"); return false; } std::string linebuffer; String temp; while (!std::cin.eof()) { getline(std::cin, linebuffer); DEBUG_debug("Line in: ", linebuffer); if (linebuffer.length() < 2) continue; // its jibberish temp = linebuffer.c_str(); bool site_list = false; bool url_list = false; if (linebuffer[0] == '#') { if (temp.startsWith("#SITELIST:")) site_list = true; else if (temp.startsWith("#URLLIST:")) url_list = true; else continue; String param = temp.after(":"); String nm, fpath; String t = param; bool startswith; t.removeWhiteSpace(); t += ","; while (t.length() > 0) { if (t.startsWith("name=")) { nm = t.after("=").before(","); } else if (t.startsWith("path=")) { fpath = t.after("=").before(","); } t = t.after(","); } if (!fpath.startsWith("memory:")) { // syntax error return false; } if (nm.length() == 0) { // syntax error return false; } if (site_list) startswith = false; else startswith = true; int rc = lm.newItemList(fpath.c_str(), "", startswith, 1, true); if (rc < 0) return false; lm.l[rc]->doSort(url_list); if (site_list) lists.sitelist_dq.push_back(param); else lists.urllist_dq.push_back(param); } } return true; } // PRIVATE bool OptionContainer::findAccessLogOptions(ConfigReader &cr) { log.dns_user_logging_domain = cr.findoptionS("dnsuserloggingdomain"); // default of unlimited no longer allowed as could cause buffer overflow log.max_logitem_length = cr.findoptionIWithDefault("maxlogitemlength", 10, 32000, 2000); log.log_level = cr.findoptionIWithDefault("loglevel", 0, 3, 3); log.log_file_format = cr.findoptionIWithDefault("logfileformat", 1, 8, 8); log.anonymise_logs = cr.findoptionB("anonymizelogs"); log.log_ad_blocks = cr.findoptionB("logadblocks"); log.log_timestamp = cr.findoptionB("logtimestamp"); log.log_user_agent = cr.findoptionB("loguseragent"); log.use_dash_for_blanks = cr.findoptionB("usedashforblank"); log.log_client_host_and_ip = cr.findoptionB("logclientnameandip"); log.log_exception_hits = cr.findoptionIWithDefault("logexceptionhits", 0, 2, 2); log.addECHtoFlags = cr.findoptionB("addECHtoFlags"); log.log_client_hostnames = cr.findoptionB("logclienthostnames"); conn.reverse_client_ip_lookups = log.log_client_hostnames; // TODO: reverse_client_ip_lookups could be done in log thread log.logid_1 = cr.findoptionS("logid1"); if (log.logid_1.empty()) log.logid_1 = "-"; log.logid_2 = cr.findoptionS("logid2"); if (log.logid_2.empty()) log.logid_2 = "-"; #ifdef SG_LOGFORMAT log.prod_id = cr.findoptionS("productid"); if (log.prod_id.empty()) // SG '08 log.prod_id = "2"; #endif return true; } bool OptionContainer::findBlockPageOptions(ConfigReader &cr) { block.reporting_level = cr.findoptionIWithDefault("reportinglevel", -1, 3, 3); if ( !cr.findoptionB("usecustombannedimage") ) { block.use_custom_banned_image = false; } else { block.use_custom_banned_image = true; block.custom_banned_image_file = cr.findoptionS("custombannedimagefile"); if (block.custom_banned_image_file.empty()) { block.custom_banned_image_file = __DATADIR; block.custom_banned_image_file += "/transparent1x1.gif"; } block.banned_image.read(block.custom_banned_image_file.c_str()); } if ( !cr.findoptionB("usecustombannedflash") ) { block.use_custom_banned_flash = false; } else { block.use_custom_banned_flash = true; block.custom_banned_flash_file = cr.findoptionS("custombannedflashfile"); if (block.custom_banned_flash_file.empty()) { block.custom_banned_flash_file = __DATADIR; block.custom_banned_flash_file += "/blockedflash.swf"; } block.banned_flash.read(block.custom_banned_flash_file.c_str()); } return true; } bool OptionContainer::findCertificateOptions(ConfigReader &cr) { cert.ssl_certificate_path = cr.findoptionS("sslcertificatepath") + "/"; if (cert.ssl_certificate_path == "/") { cert.ssl_certificate_path = ""; // "" will enable default openssl certs } cert.enable_ssl = cr.findoptionB("enablessl"); if (cert.enable_ssl) { bool ret = true; if (cr.findoptionB("useopensslconf")) { cert.use_openssl_conf = true; cert.openssl_conf_path = cr.findoptionS("opensslconffile"); cert.have_openssl_conf = (!cert.openssl_conf_path.empty()) ; } else { cert.use_openssl_conf = false; }; cert.ca_certificate_path = cr.findoptionS("cacertificatepath"); if (cert.ca_certificate_path == "") { E2LOGGER_error("cacertificatepath is required when ssl is enabled"); ret = false; } cert.ca_private_key_path = cr.findoptionS("caprivatekeypath"); if (cert.ca_private_key_path == "") { E2LOGGER_error("caprivatekeypath is required when ssl is enabled"); ret = false; } cert.cert_private_key_path = cr.findoptionS("certprivatekeypath"); if (cert.cert_private_key_path == "") { E2LOGGER_error("certprivatekeypath is required when ssl is enabled"); ret = false; } // if (!cert.generate_ca_certificate()) return false; cert.generated_cert_path = cr.findoptionS("generatedcertpath") + "/"; if (cert.generated_cert_path == "/") { E2LOGGER_error("generatedcertpath is required when ssl is enabled"); ret = false; } //time_t def_start = 1417872951; // 6th Dec 2014 time_t def_start = 1711926000; // 1st Apr 2024 time_t ten_years = 315532800; time_t one_year = 31553280; //E2LOGGER_info("sizeof time_t is ", sizeof(time_t)); String temp = cr.findoptionS("generatedcertstart"); if (temp.empty() || temp == "auto") { cert.generated_auto_start_end = true; } else { cert.generated_auto_start_end = false; } if (!cert.generated_auto_start_end) { cert.gen_cert_start = cr.findoptionI("generatedcertstart"); if (cert.gen_cert_start < def_start) cert.gen_cert_start = def_start; cert.gen_cert_end = cr.findoptionI("generatedcertend"); if (cert.gen_cert_end < cert.gen_cert_start) cert.gen_cert_end = cert.gen_cert_start + ten_years; } else { // auto generation time_t now; time(&now); struct tm *timeinfo; timeinfo = localtime(&now); if (timeinfo->tm_mon > 5) { timeinfo->tm_mon = 6; } else { timeinfo->tm_mon = 0; } timeinfo->tm_hour = 0; timeinfo->tm_min = 0; timeinfo->tm_sec = 0; timeinfo->tm_mday = 0; cert.gen_cert_start = mktime(timeinfo); cert.gen_cert_end = cert.gen_cert_start + one_year; } if (!cert.generate_ca_certificate()) return false; // now check values against root ca cert and adjust if needed to fall in range //E2LOGGER_info("gcertstart: ",cert.gen_cert_start, " gcertend:", cert.gen_cert_end); cert.set_cipher_list = cr.findoptionS("setcipherlist"); if (cert.set_cipher_list == "") cert.set_cipher_list = "HIGH:!ADH:!MD5:!RC4:!SRP:!PSK:!DSS"; if (ret) { #ifdef NODEF cert.ca = new CertificateAuthority(cert.ca_certificate_path.c_str(), cert.ca_private_key_path.c_str(), cert.cert_private_key_path.c_str(), cert.generated_cert_path.c_str(), cert.gen_cert_start, cert.gen_cert_end); #endif return true; } else { return false; } } return true; } bool OptionContainer::findConfigOptions(ConfigReader &cr) { String t = cr.findoptionS("languagedir") + "/"; if (t == "/") { t = __DATADIR; t += "/languages"; } config.languagepath = t + "/" + cr.findoptionS("language") + "/"; std::string language_list_location(config.languagepath + "messages"); if (!language_list.readLanguageList(language_list_location.c_str())) { return false; } // messages language file return true; } bool OptionContainer::findConnectionHandlerOptions(ConfigReader &cr) { conn.logconerror = cr.findoptionB("logconnectionhandlingerrors"); conn.use_original_ip_port = cr.findoptionB("useoriginalip"); conn.reverse_client_ip_lookups = cr.findoptionB("reverseclientiplookups"); if ((conn.internal_test_url = cr.findoptionS("internaltesturl")).empty()) { conn.internal_test_url = "internal.test.e2guardian.org"; } if ((conn.internal_status_url = cr.findoptionS("internalstatusurl")).empty()) { conn.internal_status_url = "internal.status.e2guardian.org"; } return true; } bool OptionContainer::findContentScannerOptions(ConfigReader &cr) { content.max_content_filecache_scan_size = cr.findoptionIWithDefault("maxcontentfilecachescansize", 0, 0, 20000); content.max_content_filecache_scan_size *= 1024; content.max_content_ramcache_scan_size = cr.findoptionIWithDefault("maxcontentramcachescansize", 0, 0, 2048); content.max_content_ramcache_scan_size *= 1024; if (content.max_content_ramcache_scan_size == 0) { content.max_content_ramcache_scan_size = content.max_content_filecache_scan_size; } content.max_content_filter_size = cr.findoptionIWithDefault("maxcontentfiltersize", 0, 0, 2048); content.max_content_filter_size *= 1024; content.contentscanning = cr.findoptionM("contentscanner")->size() > 0; if (content.contentscanning) { if (content.max_content_filter_size > content.max_content_ramcache_scan_size) { E2LOGGER_error("maxcontentfiltersize can not be greater than maxcontentramcachescansize"); return false; } if (content.max_content_ramcache_scan_size > content.max_content_filecache_scan_size) { E2LOGGER_error("maxcontentramcachescansize can not be greater than maxcontentfilecachescansize"); return false; } content.trickle_delay = cr.findoptionIWithDefault("trickledelay", 1, 0, 10); content.initial_trickle_delay = cr.findoptionIWithDefault("initialtrickledelay", 1, 0, 20); content.content_scanner_timeout_sec = cr.findoptionIWithDefault("contentscannertimeout", 1, 0, 60); if (content.content_scanner_timeout_sec > 0) content.content_scanner_timeout = content.content_scanner_timeout_sec * 1000; else { content.content_scanner_timeout = net.pcon_timeout; content.content_scanner_timeout_sec = net.pcon_timeout_sec; } } // this needs to be known before loading CS plugins, // because ClamAV plugin makes use of it during init() content.download_dir = cr.findoptionS("filecachedir"); if (content.download_dir.empty()) { content.download_dir = "/tmp"; } content.delete_downloaded_temp_files = cr.findoptionB("deletedownloadedtempfiles"); return true; } bool OptionContainer::findFilterGroupOptions(ConfigReader &cr) { filter.filter_groups = cr.findoptionI("filtergroups"); if (filter.filter_groups == 0) filter.filter_groups = 1; filter.numfg = filter.filter_groups; filter.default_fg = cr.findoptionIWithDefault("defaultfiltergroup", 1, filter.filter_groups, 1); filter.default_fg--; // zero based index filter.default_trans_fg = cr.findoptionIWithDefault("defaulttransparentfiltergroup", 1, filter.filter_groups, 1); filter.default_trans_fg--; filter.default_icap_fg = cr.findoptionIWithDefault("defaulticapfiltergroup", 1, filter.filter_groups, 1); filter.default_icap_fg--; return true; } bool OptionContainer::findHeaderOptions(ConfigReader &cr) { header.forwarded_for = cr.findoptionB("forwardedfor"); if (cr.findoptionB("addforwardedfor")) { header.forwarded_for = true; } header.max_header_lines = cr.findoptionIWithDefault("maxheaderlines", 10, 2500, 2000); return true; } bool OptionContainer::findICAPOptions(ConfigReader &cr) { icap.icap_reqmod_url = cr.findoptionS("icapreqmodurl"); if (icap.icap_reqmod_url == "") icap.icap_reqmod_url = "request"; icap.icap_resmod_url = cr.findoptionS("icapresmodurl"); if (icap.icap_resmod_url == "") icap.icap_resmod_url = "response"; return true; } bool OptionContainer::findListsOptions(ConfigReader &cr) { lists.force_quick_search = cr.findoptionB("forcequicksearch"); lists.abort_on_missing_list = cr.findoptionB("abortiflistmissing"); lists.search_sitelist_for_ip = cr.findoptionB("searchsitelistforip"); lists.iplist_dq = *cr.findoptionM("iplist"); lists.sitelist_dq = *cr.findoptionM("sitelist"); lists.ipsitelist_dq = *cr.findoptionM("ipsitelist"); lists.urllist_dq = *cr.findoptionM("urllist"); lists.regexpboollist_dq = *cr.findoptionM("regexpboollist"); lists.maplist_dq = *cr.findoptionM("maplist"); lists.ipmaplist_dq = *cr.findoptionM("ipmaplist"); return true; } bool OptionContainer::findLoggerOptions(ConfigReader &cr) { LoggerConfigurator loggerConf(&e2logger); { std::deque *temp = cr.findoptionM("debuglevel"); if ( temp && !temp->empty()) { for (std::deque::iterator i = temp->begin(); i != temp->end(); i++) { loggerConf.debuglevel(*i); } } } logger.log_ssl_errors = cr.findoptionB("logsslerrors"); { std::string temp = cr.findoptionS("set_info"); if (!temp.empty()) { if (!loggerConf.configure(LoggerSource::info, temp)) return false; } } { std::string temp = cr.findoptionS("set_error"); if (!temp.empty()) { if (!loggerConf.configure(LoggerSource::error, temp)) return false; } } { std::string temp = cr.findoptionS("set_warning"); if (!temp.empty()) { if (!loggerConf.configure(LoggerSource::warning, temp)) return false; } } logger.udp_source_port = cr.findoptionIWithDefault("udp_source_port",6000,64000,39000); { if (cr.findoptionB("logsyslog")) { if ((logger.name_suffix = cr.findoptionS("namesuffix")) == "") { logger.name_suffix = ""; } e2logger.setSyslogName(config.prog_name + logger.name_suffix); } } { String temp = cr.findoptionS("set_accesslog"); if (!temp.empty()) { if (!loggerConf.configure(LoggerSource::accesslog, temp)) return false; } else if (!proc.is_dockermode) { log.log_location = cr.findoptionS("loglocation"); if (log.log_location.empty()) { log.log_location = __LOGLOCATION; log.log_location += "/access.log"; } if (!e2logger.setLogOutput(LoggerSource::accesslog, LoggerDestination::file, log.log_location)) return false; } } logger.debug_format = cr.findoptionIWithDefault("debugformat", 1, 6, 1); loggerConf.debugformat(logger.debug_format); if (cr.findoptionB("tag_logs")) { e2logger.setFormat(LoggerSource::accesslog, false, true, false, false, false, false); e2logger.setFormat(LoggerSource::requestlog, false, true, false, false, false, false); e2logger.setFormat(LoggerSource::responselog, false, true, false, false, false, false); e2logger.setFormat(LoggerSource::alertlog, false, true, false, false, false, false); } { String temp = cr.findoptionS("set_requestlog"); if (!temp.empty()) { if (!loggerConf.configure(LoggerSource::requestlog, temp)) return false; log.log_requests = true; } else { if ((log.RQlog_location = cr.findoptionS("rqloglocation")) == "") { log.log_requests = false; } else { log.log_requests = true; if (!e2logger.setLogOutput(LoggerSource::requestlog, LoggerDestination::file, log.RQlog_location)) return false; } } } { String temp = cr.findoptionS("set_responselog"); if (!temp.empty()) { if (!loggerConf.configure(LoggerSource::responselog, temp)) return false; log.log_responses = true; } else { log.log_responses = false; } } { String temp = cr.findoptionS("set_alertlog"); if (!temp.empty()) { if (!loggerConf.configure(LoggerSource::alertlog, temp)) return false; log.log_alerts = true; } else { log.log_alerts = false; } } { dstat.dstat_log_flag = false; String temp = cr.findoptionS("set_dstatslog"); if (!temp.empty()) { if (!loggerConf.configure(LoggerSource::dstatslog, temp)) return false; dstat.dstat_log_flag = true; } else { if ((dstat.dstat_location = cr.findoptionS("dstatlocation")) == "") { dstat.dstat_log_flag = false; } else { dstat.dstat_log_flag = true; if (!e2logger.setLogOutput(LoggerSource::dstatslog, LoggerDestination::file, dstat.dstat_location)) return false; } } } { std::string temp = cr.findoptionS("set_storytrace"); if (!temp.empty()) { if (!loggerConf.configure(LoggerSource::storytrace, temp)) return false; } } // unlike other set_* this does not enable output - // this has to be done with storyboardtrace // as this is not a source code trace // but a trace of the storyboard logic { if (cr.findoptionB("storyboardtrace")) { logger.SB_trace = true; e2logger.enable(LoggerSource::storytrace); E2LOGGER_warning("Storyboard tracing enabled!!"); } else { logger.SB_trace = false; } } // Not sure why this was here - prob for debuging - PIP //if ( logger.SB_trace ) { // DEBUG_config("Enable Storyboard tracing !!"); // e2logger.enable(LoggerSource::story); //} return true; } bool OptionContainer::findMonitorOptions(ConfigReader &cr) { // to remove in v5.5 // monitor_helper = findoptionS("monitorhelper"); // if (monitor_helper == "") { // monitor_helper_flag = false; // } else { // monitor_helper_flag = true; // } monitor.monitor_flag_prefix = cr.findoptionS("monitorflagprefix"); monitor.monitor_flag_flag = (monitor.monitor_flag_prefix != ""); return true; } bool OptionContainer::findNaughtyOptions(ConfigReader &cr) { if (cr.findoptionS("weightedphrasemode").empty()) { naughty.weighted_phrase_mode = 2; } else { naughty.weighted_phrase_mode = cr.findoptionIWithDefault("weightedphrasemode", 0, 2, 2); } if (cr.findoptionS("phrasefiltermode").empty()) { naughty.phrase_filter_mode = 2; } else { naughty.phrase_filter_mode = cr.findoptionIWithDefault("phrasefiltermode", 0, 3, 2); } naughty.preserve_case = cr.findoptionIWithDefault("preservecase", 0, 2, 0); naughty.hex_decode_content = (cr.findoptionB("hexdecodecontent")); naughty.show_weighted_found = (cr.findoptionB("showweightedfound")); naughty.show_all_weighted_found = (cr.findoptionB("showallweightedfound")); if (naughty.show_all_weighted_found) naughty.show_weighted_found = true; return true; } bool OptionContainer::findNetworkOptions(ConfigReader &cr) { net.server_name = cr.findoptionS("servername"); if (net.server_name == "") { char sysname[256]; int r; r = gethostname(sysname, 256); if (r == 0) { net.server_name = sysname; } } net.connect_timeout_sec = cr.findoptionIWithDefault("connecttimeout", 1, 100, 10); net.connect_timeout = net.connect_timeout_sec * 1000; net.connect_retries = cr.findoptionIWithDefault("connectretries", 1, 100, 1); net.proxy_ip = cr.findoptionS("proxyip"); net.no_proxy = (net.proxy_ip == ""); if (!net.no_proxy) { net.proxy_port = cr.findoptionIWithDefault("proxyport", 1, 65535, 3128); } net.proxy_timeout_sec = cr.findoptionIWithDefault("proxytimeout", 5, 100, 55); net.proxy_timeout = net.proxy_timeout_sec * 1000; net.pcon_timeout_sec = cr.findoptionIWithDefault("pcontimeout", 5, 300, 55); net.pcon_timeout = net.pcon_timeout_sec * 1000; net.exchange_timeout_sec = cr.findoptionIWithDefault("proxyexchange", 5, 300, 61); net.exchange_timeout = net.exchange_timeout_sec * 1000; // multiple listen IP support net.filter_ip = cr.findoptionMD("filterip",":"); if (net.filter_ip.empty()) net.filter_ip.push_back(""); if (net.filter_ip.size() > 127) { E2LOGGER_error("Can not listen on more than 127 IPs"); return false; } // multiple check IP support - used for loop checking net.check_ip = cr.findoptionMD("checkip",":"); if (net.check_ip.size() > 127) { E2LOGGER_error("Can not check on more than 127 IPs"); return false; } if (net.check_ip.empty()) { net.check_ip.push_back("127.0.0.1"); } net.filter_ports = cr.findoptionMD("filterports",":"); if (net.filter_ports.empty()) net.filter_ports.push_back("8080"); net.filter_port = net.filter_ports[0].toInteger(); if (!realitycheck(net.filter_port, 1, 65535, "filterport[0]")) { return false; } net.check_ports = cr.findoptionMD("extracheckports", ":"); // now add filter_ports to check_ports for (auto pt : net.filter_ports) { net.check_ports.push_back(pt); } net.TLS_filter_ports = cr.findoptionMD("tlsfilterports",":"); net.TLSproxyCN = cr.findoptionS("tlsproxycn"); if (net.TLSproxyCN.empty()) net.TLSproxyCN = net.server_name; { String temp = net.TLSproxyCN; int tno = temp.before(".").toInteger(); if ( tno > 0 && tno < 256 ) { net.TLSproxyCN_is_ip = true; } } net.transparenthttps_port = cr.findoptionI("transparenthttpsport"); if (!realitycheck(net.transparenthttps_port, 0, 65535, "transparenthttpsport")) { return false; } net.icap_port = cr.findoptionI("icapport"); if (!realitycheck(net.icap_port, 0, 65535, "icapport")) { return false; } if (net.icap_port > 0) { // add non-plugin auth for ICAP StoryBoardOptions::SB_entry_map sen; sen.entry_function = "auth_icap"; sen.entry_id = ENT_STORYA_AUTH_ICAP; story.auth_entry_dq.push_back(sen); } net.xforwardedfor_filter_ip = *cr.findoptionM("xforwardedforfilterip"); return true; } bool OptionContainer::findDStatOptions(ConfigReader &cr) { dstat.dstat_interval = cr.findoptionI("dstatinterval"); if (dstat.dstat_interval == 0) { dstat.dstat_interval = 300; // 5 mins } dstat.stats_human_readable = cr.findoptionB("statshumanreadable"); if (cr.findoptionB("tag_dstatlog")) { e2logger.setFormat(LoggerSource::dstatslog, false, true, false, false, false, false); } return true; } bool OptionContainer::findPluginOptions(ConfigReader &cr) { if (!plugins.loadDMPlugins(cr)) { E2LOGGER_error("Error loading DM plugins"); return false; } if (content.contentscanning) { if (!plugins.loadCSPlugins(cr)) { E2LOGGER_error("Error loading CS plugins"); return false; } } if (!plugins.loadAuthPlugins(cr)) { E2LOGGER_error("Error loading auth plugins"); return false; } return true; } bool OptionContainer::findProcOptions(ConfigReader &cr) { proc.no_daemon = cr.findoptionB("nodaemon"); if (cr.findoptionB("dockermode")) { proc.is_dockermode = true; proc.no_daemon = true; e2logger.setDockerMode(); } if ((proc.daemon_user_name = cr.findoptionS("daemonuser")) == "") { proc.daemon_user_name = __PROXYUSER; } if ((proc.daemon_group_name = cr.findoptionS("daemongroup")) == "") { proc.daemon_group_name = __PROXYGROUP; } proc.http_workers = cr.findoptionIWithDefault("httpworkers", 20, 20000, 500); return true; } bool OptionContainer::findStoryBoardOptions(ConfigReader &cr) { story.storyboard_location = cr.findoptionS("preauthstoryboard"); if (story.storyboard_location.empty()) { story.storyboard_location = __CONFDIR; story.storyboard_location += "/preauth.story"; } story.reverse_lookups = cr.findoptionB("reverseaddresslookups"); return true; } //#pragma region Plugins bool PluginOptions::loadDMPlugins(ConfigReader &cr) { DEBUG_config("load Download manager plugins"); std::deque dq = *cr.findoptionM("downloadmanager"); unsigned int numplugins = dq.size(); if (numplugins < 1) { E2LOGGER_error("There must be at least one download manager option"); return false; } String config; for (unsigned int i = 0; i < numplugins; i++) { config = dq[i]; DEBUG_debug("loading download manager config: ", config); DMPlugin *dmpp = dm_plugin_load(config.toCharArray()); if (dmpp == NULL) { E2LOGGER_error("dm_plugin_load() returned NULL pointer with config file: ", config); return false; } bool lastplugin = (i == (numplugins - 1)); int rc = dmpp->init(&lastplugin); if (rc < 0) { E2LOGGER_error("Download manager plugin init returned error value: ", rc); return false; } else if (rc > 0) { E2LOGGER_error("Download manager plugin init returned warning value: ", rc); } dmplugins.push_back(dmpp); } // cache reusable iterators dmplugins_begin = dmplugins.begin(); dmplugins_end = dmplugins.end(); return true; } bool PluginOptions::loadCSPlugins(ConfigReader &cr) { DEBUG_config("load Content scanner plugins"); std::deque dq = *cr.findoptionM("contentscanner"); unsigned int numplugins = dq.size(); if (numplugins < 1) { return true; // to have one is optional } String config; for (unsigned int i = 0; i < numplugins; i++) { config = dq[i]; // worth adding some input checking on config DEBUG_debug("loading content scanner config: ", config); CSPlugin *cspp = cs_plugin_load(config.toCharArray()); if (cspp == NULL) { E2LOGGER_error("cs_plugin_load() returned NULL pointer with config file: ", config); return false; } DEBUG_debug("Content scanner plugin is good, calling init..."); int rc = cspp->init(NULL); if (rc < 0) { E2LOGGER_error("Content scanner plugin init returned error value: ", rc); return false; } else if (rc > 0) { E2LOGGER_error("Content scanner plugin init returned warning value: ", rc); } csplugins.push_back(cspp); } // cache reusable iterators csplugins_begin = csplugins.begin(); csplugins_end = csplugins.end(); return true; } bool PluginOptions::loadAuthPlugins(ConfigReader &cr) { DEBUG_config("load Auth plugins"); std::deque dq = *cr.findoptionM("authplugin"); unsigned int numplugins = dq.size(); if (numplugins < 1) { return true; // to have one is optional } if ( cr.findoptionB("authrequiresuserandgroup") && (authplugins.size() > 1)) auth.auth_requires_user_and_group = true; String config; for (unsigned int i = 0; i < numplugins; i++) { config = dq[i]; // worth adding some input checking on config DEBUG_debug("loading auth plugin config: ", config); AuthPlugin *app = auth_plugin_load(config.toCharArray()); if (app == NULL) { E2LOGGER_error("auth_plugin_load() returned NULL pointer with config file: ", config); return false; } DEBUG_debug("Auth plugin is good, calling init..."); int rc = app->init(NULL); if (rc < 0) { E2LOGGER_error("Auth plugin init returned error value:", rc); return false; } else if (rc > 0) { E2LOGGER_error("Auth plugin init returned warning value: ", rc); } authplugins.push_back(app); } // cache reusable iterators authplugins_begin = authplugins.begin(); authplugins_end = authplugins.end(); return true; } void PluginOptions::deletePlugins(std::deque &list) { for (std::deque::iterator i = list.begin(); i != list.end(); i++) { if ((*i) != NULL) { (*i)->quit(); delete (*i); } } list.clear(); } //#pragma endregion bool OptionContainer::createLists(int load_id) { DEBUG_config("create Lists: ", load_id); std::shared_ptr temp(new LOptionContainer(load_id)); if (!(temp->is_fatal) && temp->sb_loaded_ok && (temp->loaded_ok || !lists.abort_on_missing_list)) { current_LOC = temp; return true; } return false; } //#pragma region ProcessOptions bool ProcessOptions::find_user_ids() { struct passwd *st; struct group *sg; int rc; root_user = geteuid(); // This is an important feature because we need to be able to create temp // files with suitable permissions for scanning by AV daemons - we do this // by becoming a member of a specified AV group and setting group read perms if ((sg = getgrnam(daemon_group_name.c_str())) != 0) { proxy_group = sg->gr_gid; } else { E2LOGGER_error( "Unable to getgrnam(): ", strerror(errno)); E2LOGGER_error("Check the group that e2guardian runs as (", daemon_group_name, ")"); return 1; } if ((st = getpwnam(daemon_user_name.c_str())) != 0) { // find uid for proxy user proxy_user = st->pw_uid; rc = setgid(proxy_group); // change to rights of proxy user group // i.e. low - for security if (rc == -1) { E2LOGGER_error("Unable to setgid()"); return false; // setgid failed for some reason so exit with error } } else { E2LOGGER_error("Unable to getpwnam() - does the proxy user exist?"); E2LOGGER_error("Proxy user looking for is '", daemon_user_name, "'" ); return false; // was unable to lockup the user id from passwd // for some reason, so exit with error } return true; } bool ProcessOptions::become_root_user() { int rc; #ifdef HAVE_SETREUID rc = setreuid((uid_t)-1, root_user); #else rc = seteuid(root_user); #endif if (rc == -1) { E2LOGGER_error("Unable to seteuid() to become root user"); return false; } return true; } bool ProcessOptions::become_proxy_user() { int rc; #ifdef HAVE_SETREUID rc = setreuid((uid_t)-1, proxy_user); #else rc = seteuid(proxy_user); // become low priv again #endif if (rc == -1) { E2LOGGER_error("Unable to re-seteuid() to become proxy user"); return false; } return true; } // Fork ourselves off into the background bool ProcessOptions::daemonise() { if (no_daemon) { return true; } #ifdef DEBUG_LOW return true; // if debug mode is enabled we don't want to detach #endif if (is_daemonised) { return true; // we are already daemonised so this must be a // reload caused by a HUP } int nullfd = -1; if ((nullfd = open("/dev/null", O_WRONLY, 0)) == -1) { E2LOGGER_error("Couldn't open /dev/null"); return false; } pid_t pid; if ((pid = fork()) < 0) { // Error!! close(nullfd); return false; } else if (pid != 0) { // parent goes... if (nullfd != -1) { close(nullfd); } exit(0); // bye-bye } // child continues dup2(nullfd, 0); // stdin dup2(nullfd, 1); // stdout dup2(nullfd, 2); // stderr close(nullfd); setsid(); // become session leader if (chdir("/") != 0) {// change working directory E2LOGGER_error(" Can't change / directory !"); return false; } umask(0); // clear our file mode creation mask umask(S_IWGRP | S_IWOTH); // set to mor sensible setting?? is_daemonised = true; return true; } //#pragma endregion bool CertificateOptions::generate_ca_certificate() { if (!enable_ssl) return true; DEBUG_thttps("in gen_ca_cert gen_cert_start is ",gen_cert_start, " gen_cert_end is ",gen_cert_end); DEBUG_config("enable SSL"); if (ca_certificate_path != "") { ca = new CertificateAuthority(ca_certificate_path.c_str(), ca_private_key_path.c_str(), cert_private_key_path.c_str(), generated_cert_path.c_str(), gen_cert_start, gen_cert_end); } else { E2LOGGER_error( "Error - Valid cacertificatepath, caprivatekeypath and generatedcertpath must given when using MITM."); return false; } return true; } std::shared_ptr OptionContainer::currentLists() { return current_LOC; } // realitycheck checks an amount for certain expected criteria // so we can spot problems in the conf files easier bool OptionContainer::realitycheck(long int l, long int minl, long int maxl, const char *emessage) { if ((l < minl) || ((maxl > 0) && (l > maxl))) { E2LOGGER_error("Config problem; check allowed values for ", emessage, "( ", l, " should be >= ", minl, " <=", maxl, ")"); return false; } return true; } e2guardian-5.5.8r/src/OptionContainer.hpp000066400000000000000000000251761477372360500204000ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_OPTIONCONTAINER #define __HPP_OPTIONCONTAINER // INCLUDES #include "Auth.hpp" #include "CertificateAuthority.hpp" #include "ConfigReader.hpp" #include "FOptionContainer.hpp" #include "DownloadManager.hpp" #include "ContentScanner.hpp" #include "String.hpp" #include "HTMLTemplate.hpp" #include "ImageContainer.hpp" #include "ListContainer.hpp" #include "ListManager.hpp" #include "LanguageContainer.hpp" #include "LOptionContainer.hpp" #include "RegExp.hpp" #include "IPList.hpp" #include "Queue.hpp" #include // DECLARATIONS struct AccessLogOptions { std::string log_location; std::string RQlog_location; std::string RSlog_location; std::string ALlog_location; Queue* log_Q; Queue* RQlog_Q; //Queue log_Q; //Queue RQlog_Q; int log_level = 0; int log_file_format = 0; int log_exception_hits = 0; bool log_requests = false; bool log_responses = false; bool log_alerts = false; bool log_client_hostnames = false; bool log_client_host_and_ip = false; // todo: unused - this IS used - PP bool anonymise_logs = false; bool log_ad_blocks = false; bool log_timestamp = false; bool log_user_agent = false; bool use_dash_for_blanks = true; bool addECHtoFlags = false; unsigned int max_logitem_length = 2000; std::string dns_user_logging_domain; // TODO: not documented ?? bool dns_user_logging() { return !dns_user_logging_domain.empty(); }; // Hardware/organisation/etc. IDs std::string logid_1; std::string logid_2; std::string prod_id; }; struct AuthPluginOptions { bool auth_requires_user_and_group = false; std::map auth_map; }; struct BlockPageOptions { int reporting_level = 0; bool use_custom_banned_image = false; std::string custom_banned_image_file; ImageContainer banned_image; bool use_custom_banned_flash = false; std::string custom_banned_flash_file; ImageContainer banned_flash; }; struct CertificateOptions { bool enable_ssl = false; std::string ssl_certificate_path; std::string ca_certificate_path; std::string ca_private_key_path; std::string cert_private_key_path; std::string generated_cert_path; std::string generated_link_path; std::string openssl_conf_path; CertificateAuthority *ca; time_t gen_cert_start, gen_cert_end; bool use_openssl_conf = false; bool have_openssl_conf = false; bool generated_auto_start_end = true; std::string set_cipher_list; bool generate_ca_certificate(); }; struct ConfigOptions { std::string prog_name; // (default e2guardian) std::string configfile; // Main Configfile (default ${e2sysconfdir}/e2guardian.conf) std::string languagepath; char benchmark = '\0'; bool total_block_list = false; }; struct ConnectionHandlerOptions { bool use_original_ip_port = false; // only for transparent and no upstream proxy bool logconerror = false; bool reverse_client_ip_lookups = false; // internal test urls std::string internal_test_url; std::string internal_status_url; }; struct ContentScannerOptions { bool contentscanning = false; off_t max_content_filter_size; off_t max_content_ramcache_scan_size; off_t max_content_filecache_scan_size; bool scan_clean_cache = false; // Check: Not used? bool content_scan_exceptions = false; // Check: Not used? (There is another one in FOptionContainer) int initial_trickle_delay = 0; int trickle_delay = 0; int content_scanner_timeout = 0; int content_scanner_timeout_sec = 0; std::string download_dir; bool delete_downloaded_temp_files = false; }; struct DStatOptions { std::string dstat_location; bool dstat_log_flag = false; bool stats_human_readable = false; int dstat_interval = 300; }; struct FilterGroupOptions { int filter_groups = 0; int numfg = 0; int default_fg = 0; int default_trans_fg = 0; int default_icap_fg = 0; bool use_group_names_list = false; // Never set ?!?! std::string filter_groups_list_location; ListContainer filter_groups_list; }; struct HTTPHeaderOptions { std::string ident_header_value; bool forwarded_for = false; unsigned int max_header_lines = 0; }; struct ICAPOptions { std::string icap_reqmod_url; std::string icap_resmod_url; }; struct ListsOptions { bool read_from_stdin = false; // unused ?? kdg 22.12.2020 bool force_quick_search = false; bool abort_on_missing_list = false; bool search_sitelist_for_ip = false; std::deque iplist_dq; std::deque sitelist_dq; std::deque ipsitelist_dq; std::deque urllist_dq; std::deque regexpboollist_dq; std::deque maplist_dq; std::deque ipmaplist_dq; }; struct LoggerOptions { int debug_format = 1; bool log_ssl_errors = false; bool SB_trace = false; int udp_source_port = 39000; std::string name_suffix; // for SyslogName, where configured ?? }; struct MonitorOptions { std::string monitor_flag_prefix; bool monitor_flag_flag = false; }; struct NetworkOptions { std::string server_name; std::string proxy_ip; bool no_proxy = true; std::string TLSproxyCN; bool TLSproxyCN_is_ip = false; std::deque filter_ip; std::deque check_ip; std::deque xforwardedfor_filter_ip; std::deque filter_ports; std::deque check_ports; std::deque TLS_filter_ports; int filter_port = 0; int proxy_port = 0; int transparenthttps_port = 0; int icap_port = 0; int connect_timeout = 0; int connect_timeout_sec = 0; int connect_retries = 0; int proxy_timeout = 0; int proxy_timeout_sec = 0; int pcon_timeout = 0; int pcon_timeout_sec = 0; int exchange_timeout = 0; int exchange_timeout_sec = 0; int proxy_failure_log_interval = 0; // or in LogOptions ?? int number_of_fds_neded(); }; struct NaughtyOptions { int phrase_filter_mode = 0; int weighted_phrase_mode = 0; // PIP added in - not sure if still required (There is another on in FOption Container) bool show_weighted_found = false; bool show_all_weighted_found = false; // logs weighted less than limit int preserve_case = 0; bool hex_decode_content = false; }; struct PluginOptions { std::deque dmplugins; std::deque csplugins; std::deque authplugins; std::deque::iterator dmplugins_begin; std::deque::iterator dmplugins_end; std::deque::iterator csplugins_begin; std::deque::iterator csplugins_end; std::deque::iterator authplugins_begin; std::deque::iterator authplugins_end; bool loadDMPlugins(ConfigReader &cr); bool loadCSPlugins(ConfigReader &cr); bool loadAuthPlugins(ConfigReader &cr); void deletePlugins(std::deque &list); AuthPluginOptions auth; // for Auth Plugin }; struct ProcessOptions { int root_user = 0; int proxy_user = 0; int proxy_group = 0; //std::string daemon_user; //std::string daemon_group; std::string daemon_user_name; std::string daemon_group_name; std::string pid_filename; int http_workers = 0; bool no_daemon = false; bool is_daemonised = false; bool is_dockermode = false; bool find_user_ids(); bool become_root_user(); bool become_proxy_user(); bool daemonise(); // Fork ourselves off into the background }; struct StoryBoardOptions { struct SB_entry_map { int entry_id = 0; String entry_function; }; std::string storyboard_location; // better: preauth_location ?? std::deque auth_entry_dq; std::deque dm_entry_dq; bool reverse_lookups = false; }; class OptionContainer { public: // all our many, many options AccessLogOptions log; BlockPageOptions block; CertificateOptions cert; ConfigOptions config; ConnectionHandlerOptions conn; ContentScannerOptions content; DStatOptions dstat; FilterGroupOptions filter; HTTPHeaderOptions header; ICAPOptions icap; ListsOptions lists; LoggerOptions logger; MonitorOptions monitor; NaughtyOptions naughty; NetworkOptions net; PluginOptions plugins; ProcessOptions proc; StoryBoardOptions story; LanguageContainer language_list; ListManager lm; Queue http_worker_Q; bool config_error = false; bool use_xforwardedfor = false; #ifdef ENABLE_EMAIL // Email notification patch by J. Gauthier std::string mailer; #endif HTMLTemplate html_template; // unused ?? never set, not mentioned in e2guardian.conf but used in FOptionContainer::getHTMLTemplate std::string per_room_directory_location; // not used but in LOptionContainer ?? OptionContainer(); ~OptionContainer(); bool read_config(const Path &filename, bool readFullConfig=true); void reset(); bool readinStdin(); bool createLists(int load_id); std::atomic LC_cnt; std::shared_ptr current_LOC; std::shared_ptr currentLists(); private: bool findAccessLogOptions(ConfigReader &cr); bool findBlockPageOptions(ConfigReader &cr); bool findCertificateOptions(ConfigReader &cr); bool findConfigOptions(ConfigReader &cr); bool findConnectionHandlerOptions(ConfigReader &cr); bool findContentScannerOptions(ConfigReader &cr); bool findDStatOptions(ConfigReader &cr); bool findFilterGroupOptions(ConfigReader &cr); bool findHeaderOptions(ConfigReader &cr); bool findICAPOptions(ConfigReader &cr); bool findListsOptions(ConfigReader &cr); bool findLoggerOptions(ConfigReader &cr); bool findMonitorOptions(ConfigReader &cr); bool findNaughtyOptions(ConfigReader &cr); bool findNetworkOptions(ConfigReader &cr); bool findPluginOptions(ConfigReader &cr); bool findProcOptions(ConfigReader &cr); bool findStoryBoardOptions(ConfigReader &cr); bool realitycheck(long int l, long int minl, long int maxl, const char *emessage); // bool readAnotherFilterGroupConf(const char *filename, const char *groupname, bool &need_html); }; #endif e2guardian-5.5.8r/src/Plugin.hpp000066400000000000000000000010251477372360500165060ustar00rootroot00000000000000// the Plugin interface - inherit this to define new plugin types // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_PLUGIN #define __HPP_PLUGIN // INCLUDES // DECLARATIONS class Plugin { public: virtual ~Plugin(){}; // plugin initialise/quit routines. // return 0 for OK, < 0 for error, > 0 for warning virtual int init(void *args) = 0; virtual int quit() = 0; }; #endif e2guardian-5.5.8r/src/Queue.hpp000066400000000000000000000023611477372360500163400ustar00rootroot00000000000000// // Created by philip on 9/28/15. // #ifndef V4_0_QUEUE_HPP #define V4_0_QUEUE_HPP //#define __GXX_EXPERIMENTAL_CXX0X__ #include #include #include #include #include #include template class Queue { private: std::mutex d_mutex; std::condition_variable d_condition; std::queue Q; public: // void push(T const& value) { void push(T value) { std::lock_guard lock(d_mutex); Q.push(value); d_condition.notify_one(); }; T pop(void) { std::unique_lock lock(d_mutex); d_condition.wait(lock, [=] { return !Q.empty(); }); T rc = Q.front(); Q.pop(); return rc; }; int size() { std::lock_guard lock(d_mutex); return Q.size(); } }; struct LQ_rec { Socket *sock; unsigned int ct_type; }; // CT_TYPE DEFS #define CT_PROXY 1 // Normal proxy connection also handles tranpartent http #define CT_THTTPS 2 // Transparent https connection #define CT_ICAP 4 // ICAP connection #define CT_PROXY_TLS 8 // Normal proxy connection over https #endif //V4_0_QUEUE_HPP e2guardian-5.5.8r/src/RegExp.cpp000066400000000000000000000160141477372360500164410ustar00rootroot00000000000000// RegExp class - search text using regular expressions // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "RegExp.hpp" #include "Logger.hpp" #include #include RegResult::RegResult() : imatched(false) { } // destructor - free regex if compiled RegResult::~RegResult() { } // return the i'th match result std::string RegResult::result(int i) { if (i >= (signed)results.size() || i < 0) { // reality check return ""; // maybe exception? } return results[i]; } // get the position of the i'th match result in the overall text unsigned int RegResult::offset(int i) { if (i >= (signed)offsets.size() || i < 0) { // reality check return 0; // maybe exception? } return offsets[i]; } // get the length of the i'th match unsigned int RegResult::length(int i) { if (i >= (signed)lengths.size() || i < 0) { // reality check return 0; // maybe exception? } return lengths[i]; } // how many matches did the last run generate? int RegResult::numberOfMatches() { int i = (signed)results.size(); return i; } // did it, in fact, generate any? bool RegResult::matched() { return imatched; // regexp matches only - not search/replace } // constructor - set defaults RegExp::RegExp() : reg(), wascompiled(false) { } // copy constructor RegExp::RegExp(const RegExp &r) { // rs.results.clear(); //nrs.offsets.clear(); //nrs.lengths.clear(); //unsigned int i; //for (i = 0; i < rs.results.size(); i++) { //nrs.results.push_back(rs.results[i]); //} //for (i = 0; i < rs.offsets.size(); i++) { //nrs.offsets.push_back(rs.offsets[i]); //} //for (i = 0; i < rs.lengths.size(); i++) { //nrs.lengths.push_back(rs.lengths[i]); //} //nrs.imatched = rs.imatched; wascompiled = r.wascompiled; searchstring = r.searchstring; if (wascompiled == true) { #ifdef HAVE_PCRE if (regcomp(®, searchstring.c_str(), REG_ICASE | REG_EXTENDED | REG_DOTALL) != 0) { #else if (regcomp(®, searchstring.c_str(), REG_ICASE | REG_EXTENDED) != 0) { #endif regfree(®); //rs.imatched = false; wascompiled = false; } } } // destructor - free regex if compiled RegExp::~RegExp() { if (wascompiled) { regfree(®); } } // compile the given regular expression bool RegExp::comp(const char *exp) { if (wascompiled) { regfree(®); wascompiled = false; } DEBUG_regexp("Compiling ", exp); #ifdef HAVE_PCRE DEBUG_regexp("...with PCRE "); if (regcomp(®, exp, REG_ICASE | REG_EXTENDED | REG_DOTALL) != 0) { // compile regex #else DEBUG_regexp("...without PCRE "); if (regcomp(®, exp, REG_ICASE | REG_EXTENDED) != 0) { #endif regfree(®); return false; // need exception? } wascompiled = true; searchstring = exp; return true; } // match the given text against the pre-compiled expression bool RegExp::match(const char *text, RegResult &rs) { rs.results.clear(); rs.offsets.clear(); rs.lengths.clear(); rs.imatched = false; if (!wascompiled) { return false; // need exception? } if( text == NULL || *text == 0) // false if text is empty return false; char *pos = (char *)text; int i; unsigned int num_sub_expressions = MAX_SUB_EXPRESSIONS; if (reg.re_nsub < num_sub_expressions) num_sub_expressions = reg.re_nsub; regmatch_t *pmatch = new regmatch_t[num_sub_expressions + 1]; // to hold result if (!pmatch) { // if it failed delete[] pmatch; rs.imatched = false; return false; // exception? } if (regexec(®, pos, num_sub_expressions + 1, pmatch, 0)) { // run regexdelete[]pmatch; delete[] pmatch; rs.imatched = false; DEBUG_regexp("no match for:", searchstring); return false; // if no match } size_t matchlen; char *submatch; unsigned int largestoffset; int error = 0; while (error == 0) { largestoffset = 0; for (i = 0; i <= (signed)num_sub_expressions; i++) { if (pmatch[i].rm_so != -1) { matchlen = pmatch[i].rm_eo - pmatch[i].rm_so; submatch = new char[matchlen + 1]; strncpy(submatch, pos + pmatch[i].rm_so, matchlen); submatch[matchlen] = '\0'; rs.results.push_back(std::string(submatch)); rs.offsets.push_back(pmatch[i].rm_so + (pos - text)); rs.lengths.push_back(matchlen); delete[] submatch; if ((pmatch[i].rm_so + matchlen) > largestoffset) { largestoffset = pmatch[i].rm_so + matchlen; } } } if (largestoffset > 0) { pos += largestoffset; error = regexec(®, pos, num_sub_expressions + 1, pmatch, REG_NOTBOL); } else { error = -1; } } rs.imatched = true; delete[] pmatch; DEBUG_regexp("match(s) for:", searchstring); return true; // match(s) found } // My own version of STL::search() which seems to be 5-6 times faster char *RegExp::search(char *file, char *fileend, char *phrase, char *phraseend) { int j, l; // counters int p; // to hold precalcuated value for speed bool match; // flag int qsBc[256]; // Quick Search Boyer Moore shift table (256 alphabet) char *k; // pointer used in matching int pl = phraseend - phrase; // phrase length int fl = (int)(fileend - file) - pl; // file length that could match if (fl < pl) return fileend; // reality checking if (pl > 126) return fileend; // reality checking // For speed we append the phrase to the end of the memory block so it // is always found, thus eliminating some checking. This is possible as // we know an extra 127 bytes have been provided by NaughtyFilter.cpp // and also the OptionContainer does not allow phrase lengths greater // than 126 chars for (j = 0; j < pl; j++) { fileend[j] = phrase[j]; } // Next we need to make the Quick Search Boyer Moore shift table p = pl + 1; for (j = 0; j < 256; j++) { // Preprocessing qsBc[j] = p; } for (j = 0; j < pl; j++) { // Preprocessing qsBc[(unsigned char)phrase[j]] = pl - j; } // Now do the searching! for (j = 0;;) { k = file + j; match = true; for (l = 0; l < pl; l++) { // quiv, but faster, memcmp() if (k[l] != phrase[l]) { match = false; break; } } if (match) { return (j + file); // match found at offset j (but could be the // copy put at fileend) } j += qsBc[(unsigned char)file[j + pl]]; // shift } return fileend; // should never get here as it should always match } e2guardian-5.5.8r/src/RegExp.hpp000066400000000000000000000041151477372360500164450ustar00rootroot00000000000000// RegExp class - search text using regular expressions // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_REGEXP #define __HPP_REGEXP #define MAX_SUB_EXPRESSIONS 1024 // INCLUDES #include // needed for size_t used in regex.h #ifdef HAVE_PCRE #include #else #include #endif #include #include // DECLARATIONS class RegResult { public: // constructor - set sensible defaults RegResult(); // destructor - delete regexp if compiled ~RegResult(); // how many matches did the last run generate? int numberOfMatches(); // did it generate any at all? bool matched(); // the i'th match from the last run std::string result(int i); // position of the i'th match in the overall text unsigned int offset(int i); // length of the i'th match unsigned int length(int i); // the match results, their positions in the text & their lengths std::deque results; std::deque offsets; std::deque lengths; // have we matched something yet? bool imatched; }; class RegExp { public: // constructor - set sensible defaults RegExp(); // destructor - delete regexp if compiled ~RegExp(); // copy constructor RegExp(const RegExp &r ); // compile the given regular expression bool comp(const char *exp); // match the given text against the pre-compiled expression bool match(const char *text, struct RegResult& rs); // how many matches did the last run generate? // did it generate any at all? // faster equivalent of STL::Search char *search(char *file, char *fileend, char *phrase, char *phraseend); private: // the expression itself regex_t reg; // whether it's been pre-compiled bool wascompiled; // the uncompiled form of the expression (checkme: is this only used // for debugging purposes?) std::string searchstring; }; #endif e2guardian-5.5.8r/src/SBFunction.cpp000066400000000000000000000121761477372360500172660ustar00rootroot00000000000000// SBFunction - container for StoryBoard function // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "ListContainer.hpp" #include "SBFunction.hpp" #include "OptionContainer.hpp" #include "RegExp.hpp" #include "Logger.hpp" #include #include #include #include #include #include #include #include #include #include #include // GLOBALS extern OptionContainer o; // DEFINES // Constructor - set default values SBFunction::SBFunction() { } // delete the memory block when the class is destryed SBFunction::~SBFunction() { reset(); } // clear & reset all values void SBFunction::reset() { } bool SBFunction::start(String & sname, unsigned int id, unsigned int& line_no, String filename) { name = sname; fn_id = id; file_lineno = line_no; file_name = filename; comm_dq.clear(); return true; } bool SBFunction::end() { return true; } bool SBFunction::addline(String command, String params, String action, unsigned int line_no) { com_rec rec; if (command == "if") { rec.action_id = SB_COM_IF; rec.isif = true; } else if (command == "ifnot") { rec.action_id = SB_COM_IFNOT; rec.isif = false; } else { E2LOGGER_error("StoryBoard error: Invalid command ", command, "at line ", String(line_no), " of ", file_name); return false; } rec.file_lineno = line_no; // process params DEBUG_story("CLine ", params, " Action ", action); String state; String temp; String temp2; if (params.contains(",")) { state = params.before(","); DEBUG_story("CLine state is ", state); temp = params.after(","); } else { state = params; } state.removeWhiteSpace(); rec.state = getStateID(state); if (rec.state == 0) { E2LOGGER_error("StoryBoard error: Invalid state ", state, " at line ", String(line_no), " of ", file_name); return false; } if ( temp.length() ) { if (temp.contains(",")) { temp2 = temp.before(","); temp = temp.after(","); } else { temp2 = temp; temp = ""; } temp2.removeWhiteSpace(); rec.list_name = temp2; DEBUG_story("CLine list is ", temp2); } if ( temp.length() ) { if (temp.contains(",")) { temp2 = temp.before(","); temp = temp.after(","); } else { temp2 = temp; temp = ""; } temp2.removeWhiteSpace(); rec.mess_no = temp2.toInteger(); DEBUG_story("CLine mess_no is ", temp2); } if ( temp.length() ) { if (temp.contains(",")) { temp2 = temp.before(","); temp = temp.after(","); } else { temp2 = temp; temp = ""; } temp2.removeWhiteSpace(); rec.log_mess_no = temp2.toInteger(); DEBUG_story("CLine log_mess_no is ", temp2); } if ( temp.length() ) { if (temp.contains(",")) { temp2 = temp.before(","); temp = temp.after(","); } else { temp2 = temp; temp = ""; } temp2.removeWhiteSpace(); if (temp2 == "optional") rec.optional = true; DEBUG_story("CLine optional is true"); } // check list and get list_ID - needs ListMeta object - done in StoryBook::readfile rec.return_after_action = false; rec.return_after_action_is_true = false; if (action.startsWith("return ")) { rec.return_after_action = true; action = action.after("return "); } else if (action.startsWith("returnif ")) { rec.return_after_action_is_true = true; action = action.after("returnif "); }; action.removeWhiteSpace(); rec.action_name = action; // will check this and get action_id later as function may not yet be defined. comm_dq.push_back(rec); return true; } unsigned int SBFunction::getStateID(String & state) { unsigned int i = 0; while (i < SB_STATE_MAP_SIZE) { if ( state == state_map[i]) { return ++i; } ++i; } return 0; } unsigned int SBFunction::getBIFunctID(String &action) { // get built-in function id unsigned int i = 0; while (i < SB_FUNC_MAP_SIZE) { if ( action == bi_funct_map.at(i)) { return ++i + SB_BI_FUNC_BASE; } ++i; } return 0; } String SBFunction::getState(unsigned int id) { // get condition statement from state_id if (--id < SB_STATE_MAP_SIZE) return state_map[id]; else return String(id); }; String SBFunction::getBIFunct(unsigned int &id) { // get built-in function (action) from funct_id if (id > 5000) { unsigned int i = id - 5001; if (i < SB_FUNC_MAP_SIZE) return bi_funct_map[i]; } return String(id); }; String SBFunction::getName() { return name; } e2guardian-5.5.8r/src/SBFunction.hpp000066400000000000000000000135171477372360500172730ustar00rootroot00000000000000// SBFunction class // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_SBFN #define __HPP_SBFN // INLCUDES #include #include #include #include #include "String.hpp" #include "RegExp.hpp" #include "ListMeta.hpp" // commands #define SB_COM_STARTFUNCTION 1 #define SB_COM_ENDFUNCTION 2 #define SB_COM_IF 3 #define SB_COM_IFNOT 4 // STATES indicates test to be performed // xIN states takes list as argument and checks if x is in list #define SB_STATE_URLIN 1 #define SB_STATE_SITEIN 2 #define SB_STATE_SEARCHIN 3 #define SB_STATE_EMBEDDEDIN 4 #define SB_STATE_REFERERIN 5 #define SB_STATE_HEADERIN 6 #define SB_STATE_FULLURLIN 7 #define SB_STATE_EXTENSIONIN 8 #define SB_STATE_MIMEIN 9 #define SB_STATE_CLIENTIN 10 #define SB_STATE_USERAGENTIN 11 #define SB_STATE_TIMEIN 12 #define SB_STATE_USERIN 13 #define SB_STATE_LISTEN_PORTIN 14 #define SB_STATE_RESHEADERIN 15 #define SB_STATE_CATEGORYIN 16 #define SB_STATE_TOPIN 17 // all below this require a valid list // Check type of request #define SB_STATE_CONNECT 17 #define SB_STATE_POST 18 #define SB_STATE_GET 19 #define SB_STATE_SITEISIP 20 #define SB_STATE_TLS 21 // xSET check setting of flag x #define SB_STATE_EXCEPTIONSET 22 #define SB_STATE_GREYSET 23 #define SB_STATE_BLOCKSET 24 #define SB_STATE_MITMSET 25 #define SB_STATE_DONESET 26 #define SB_STATE_RETURNSET 27 #define SB_STATE_TRUE 28 #define SB_STATE_HASSNI 29 #define SB_STATE_REDIRECTSET 30 #define SB_STATE_VIRUSCHECKSET 31 #define SB_STATE_BYPASSSET 32 #define SB_STATE_BYPASSALLOWEDSET 33 #define SB_STATE_INFECTIONBYPASSALLOWEDSET 34 #define SB_STATE_SEMIEXCEPTIONSET 35 #define SB_STATE_MAP_SIZE 35 // Storyboard defined functions IDs start at 1 - Built-in actions at 5001 #define SB_BI_FUNC_BASE 5000 // BUILT_IN functions #define SB_FUNC_SETEXCEPTION 5001 #define SB_FUNC_SETGREY 5002 #define SB_FUNC_SETBLOCK 5003 #define SB_FUNC_SETMODURL 5004 #define SB_FUNC_SETDONE 5005 #define SB_FUNC_SETTRUE 5006 #define SB_FUNC_SETFALSE 5007 #define SB_FUNC_SETGOMITM 5008 #define SB_FUNC_SETLOGCAT 5009 #define SB_FUNC_SETADDHEADER 5010 #define SB_FUNC_SETREDIRECT 5011 #define SB_FUNC_SETNOCHECKCERT 5012 #define SB_FUNC_SETSEARCHTERM 5013 #define SB_FUNC_SETMODHEADER 5014 #define SB_FUNC_SETGODIRECT 5015 #define SB_FUNC_SETNOLOG 5016 #define SB_FUNC_SETALERT 5017 #define SB_FUNC_UNSETVIRUSCHECK 5018 #define SB_FUNC_UNSETBYPASS 5019 #define SB_FUNC_SETCONNECTSITE 5020 #define SB_FUNC_UNSETBYPASSALLOW 5021 #define SB_FUNC_UNSETINFECTIONBYPASSALLOW 5022 #define SB_FUNC_SETNOMITM 5023 #define SB_FUNC_SETAUTOMITM 5024 #define SB_FUNC_UNSETAUTOMITM 5025 #define SB_FUNC_SETGROUP 5026 #define SB_FUNC_SETSEMIEXCEPTION 5027 #define SB_FUNC_UNSETSEMIEXCEPTION 5028 #define SB_FUNC_MAP_SIZE 28 // DECLARATIONS class SBFunction { private: String state_map[SB_STATE_MAP_SIZE] = { "urlin", "sitein", "searchin", "embeddedin", "refererin", "headerin", "fullurlin", "extensionin", "mimein", "clientin", "useragentin", "timein", "userin", "listenportin", "responseheaderin", "categoryin", "connect", "post", "get", "siteisip", "tls", "exceptionset", "greyset", "blockset", "mitmset", "doneset", "returnset", "true", "hassniset", "redirectset", "viruscheckset", "bypassset", "bypassallowset", "infectionbypassallowset", "semiexceptionset" }; String command_map[4] = { "function", "end", "if", "ifnot", }; std::vector bi_funct_map = { "setexception", "setgrey", "setblock", "setmodurl", "setdone", "true", "false", "setgomitm", "setlogcategory", "setaddheader", "setredirect", "setnocheckcert", "setsearchterm", "setmodheader", "setgodirect", "setnolog", "setalert", "unsetviruscheck", "unsetbypass", "setconnectsite", "unsetbypassallow", "unsetinfectionbypassallow", "setnomitm", "setautomitm", "unsetautomitm", "setgroup", "setsemiexception", "unsetsemiexception" }; public: int items; struct com_rec { bool isif = false; // true if if - false is ifnot unsigned int state; // what is being tested e.g. url site search etc std::deque list_id_dq; // holds ids of list(s) being used unsigned int mess_no = 0; // optional overide of list defaults unsigned int log_mess_no = 0; // optional overide of list defaults unsigned int action_id; // action to take if result true bool return_after_action = false; bool return_after_action_is_true = false; bool optional = false; // if true do not abort if lists do not exist unsigned int file_lineno; // used for debug output String action_name; // name of action String list_name; // name of list }; String name; // holds name of function unsigned int fn_id; std::deque comm_dq; String file_name; // holds source file path for debug unsigned int file_lineno; // used for debug output SBFunction(); ~SBFunction(); void reset(); bool start(String & name, unsigned int id, unsigned int& line_noi, String filename); bool end(); bool addline(String command, String params, String action, unsigned int line_no); unsigned int getStateID(String & state); unsigned int getBIFunctID(String & action); String getState(unsigned int id); String getBIFunct(unsigned int &id); String getName(); }; #endif e2guardian-5.5.8r/src/SNI.cpp000066400000000000000000000017461477372360500157060ustar00rootroot00000000000000char *get_TLS_SNI(unsigned char *bytes, int* len) { unsigned char *curr; unsigned char sidlen = bytes[43]; curr = bytes + 1 + 43 + sidlen; unsigned short cslen = ntohs(*(unsigned short*)curr); curr += 2 + cslen; unsigned char cmplen = *curr; curr += 1 + cmplen; unsigned char *maxchar = curr + 2 + ntohs(*(unsigned short*)curr); curr += 2; unsigned short ext_type = 1; unsigned short ext_len; while(curr < maxchar && ext_type != 0) { ext_type = ntohs(*(unsigned short*)curr); curr += 2; ext_len = ntohs(*(unsigned short*)curr); curr += 2; if(ext_type == 0) { curr += 3; unsigned short namelen = ntohs(*(unsigned short*)curr); curr += 2; *len = namelen; return (char*)curr; } else curr += ext_len; } if (curr != maxchar) throw std::exception("incomplete SSL Client Hello"); return NULL; //SNI was not present } e2guardian-5.5.8r/src/Socket.cpp000066400000000000000000001036601477372360500165030ustar00rootroot00000000000000// Socket class - implements BaseSocket for INET domain sockets // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "Socket.hpp" #include "Logger.hpp" #include #include #include #include #include #include #include #include #include #include "openssl/x509v3.h" #include "openssl/asn1.h" #include "openssl/ssl.h" #include "openssl/err.h" #include "String.hpp" #include "CertificateAuthority.hpp" extern bool reloadconfig; #ifndef X509_V_FLAG_TRUSTED_FIRST #warning "openssl X509_V_FLAG_TRUSTED_FIRST not available, certificate chain creation will be unreliable and will fail on some sites" #warning "To fix install a later version of openssl" #define X509_V_FLAG_TRUSTED_FIRST 0 #endif // IMPLEMENTATION // // destructor Socket::~Socket() { close(); } // constructor - create an INET socket & clear address structures Socket::Socket() { sck = socket(AF_INET, SOCK_STREAM, 0); if (sck < 0) { s_errno = errno; } else { memset(&my_adr, 0, sizeof my_adr); memset(&peer_adr, 0, sizeof peer_adr); my_adr.sin_family = AF_INET; peer_adr.sin_family = AF_INET; peer_adr_length = sizeof(struct sockaddr_in); int f = 1; if (sck > 0) setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, &f, sizeof(int)); my_port = 0; chunkError = false; ssl = NULL; ctx = NULL; isssl = false; issslserver = false; } } // create socket from pre-existing FD (address structs will be invalid!) Socket::Socket(int fd) : BaseSocket(fd) { memset(&my_adr, 0, sizeof my_adr); memset(&peer_adr, 0, sizeof peer_adr); my_adr.sin_family = AF_INET; peer_adr.sin_family = AF_INET; peer_adr_length = sizeof(struct sockaddr_in); int f = 1; int res = setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, &f, sizeof(int)); if (res < 0) s_errno = errno; my_port = 0; chunkError = false; ssl = NULL; ctx = NULL; isssl = false; issslserver = false; } // create socket from pre-existing FD, storing local & remote IPs Socket::Socket(int newfd, struct sockaddr_in myip, struct sockaddr_in peerip) : BaseSocket(newfd) { memset(&my_adr, 0, sizeof my_adr); // *** memset(&peer_adr, 0, sizeof peer_adr); // *** my_adr.sin_family = AF_INET; // *** Fix suggested by peer_adr.sin_family = AF_INET; // *** Christopher Weimann my_adr = myip; peer_adr = peerip; peer_adr_length = sizeof(struct sockaddr_in); int f = 1; int res = setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, &f, sizeof(int)); if (res < 0) s_errno = errno; my_port = 0; chunkError = false; ssl = NULL; ctx = NULL; isssl = false; issslserver = false; //fcntl(sck, F_SETFL, O_NONBLOCK); } // find the ip to which the client has connected std::string Socket::getLocalIP() { char res[INET_ADDRSTRLEN]; return inet_ntop(AF_INET,&my_adr.sin_addr, res, sizeof(res)); } // find the ip of the client connecting to us std::string Socket::getPeerIP() { if (!client_addr.empty()) { return client_addr; } else { char res[INET_ADDRSTRLEN]; return inet_ntop(AF_INET, &peer_adr.sin_addr, res, sizeof(res)); } } // find the port of the client connecting to us int Socket::getPeerSourcePort() { return ntohs(peer_adr.sin_port); } int Socket::getPort() { return my_port; } void Socket::setPort(int port) { my_port = port; } // return the address of the client connecting to us unsigned long int Socket::getPeerSourceAddr() { return (unsigned long int) ntohl(peer_adr.sin_addr.s_addr); } // close connection & wipe address structs void Socket::reset() { if (isssl) { stopSsl(); } this->baseReset(); sck = socket(AF_INET, SOCK_STREAM, 0); if (sck < 0) { s_errno = errno; return; } memset(&my_adr, 0, sizeof my_adr); memset(&peer_adr, 0, sizeof peer_adr); my_adr.sin_family = AF_INET; peer_adr.sin_family = AF_INET; peer_adr_length = sizeof(struct sockaddr_in); infds[0].fd = sck; outfds[0].fd = sck; chunkError = false; chunk_to_read = 0; } // connect to given IP & port (following default constructor) int Socket::connect(const std::string &ip, int port) { reset(); // do it anyway as we need sck to be allocated if (sck < 0) // socket creation error { return -1; } int len = sizeof my_adr; peer_adr.sin_port = htons(port); inet_aton(ip.c_str(), &peer_adr.sin_addr); my_port = port; int save_flags = fcntl(sck,F_GETFL); fcntl(sck, F_SETFL, save_flags | O_NONBLOCK); s_errno = 0; errno = 0; int ret = ::connect(sck, (struct sockaddr *) &peer_adr, len); if (ret < 0 && errno == EINPROGRESS) ret = 0; else s_errno = errno; if (ret == 0) { int rc = poll(outfds, 1, timeout); if (rc == 0) { timedout = true; ret = -1; } else if (rc < 0) { s_errno = errno; ret = -1; } else { int so_error; socklen_t len = sizeof so_error; getsockopt(sck, SOL_SOCKET, SO_ERROR, &so_error, &len); if (so_error != 0) { sockerr = true; s_errno = so_error; ret = -1; } else { ret = 0; } } } if (ret < 0) { close(); } else { save_flags = fcntl(sck,F_GETFL); fcntl(sck, F_SETFL, save_flags & ~O_NONBLOCK); } return ret; } // bind socket to given port int Socket::bind(int port) { int len = sizeof my_adr; int i = 1; setsockopt(sck, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); my_adr.sin_port = htons(port); my_port = port; return ::bind(sck, (struct sockaddr *) &my_adr, len); } // bind socket to given port & IP int Socket::bind(const std::string &ip, int port) { int len = sizeof my_adr; int i = 1; setsockopt(sck, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); my_adr.sin_port = htons(port); my_adr.sin_addr.s_addr = inet_addr(ip.c_str()); my_port = port; return ::bind(sck, (struct sockaddr *) &my_adr, len); } // accept incoming connections & return new Socket Socket *Socket::accept() { peer_adr_length = sizeof(struct sockaddr_in); s_errno = 0; errno = 0; int newfd = ::accept(sck, (struct sockaddr *) &peer_adr, &peer_adr_length); if (newfd > 0) { Socket *s = new Socket(newfd, my_adr, peer_adr); s->setPort(my_port); return s; } else { s_errno = errno; return NULL; } } //use this socket as an ssl client int Socket::startSslClient(const std::string &certificate_path, String hostname) { if (isssl) { stopSsl(); } ERR_clear_error(); #if OPENSSL_VERSION_NUMBER < 0x10100000L #error "openssl version 1.1 or greater is required" #else ctx = SSL_CTX_new(TLS_client_method()); #endif if (ctx == NULL) { log_ssl_errors("Error ssl context is null for ", hostname.c_str()); return -1; } //set the timeout for the ssl session if (SSL_CTX_set_timeout(ctx, 130l) < 1) { SSL_CTX_free(ctx); ctx = NULL; return -1; } //load certs ERR_clear_error(); if (certificate_path.length()) { if (!SSL_CTX_load_verify_locations(ctx, NULL, certificate_path.c_str())) { log_ssl_errors("couldnt load certificates from ", certificate_path.c_str()); //tidy up SSL_CTX_free(ctx); ctx = NULL; return -2; } } else if (!SSL_CTX_set_default_verify_paths(ctx)) //use default if no certPpath given { log_ssl_errors("couldnt load default certificates for ", hostname.c_str()); //tidy up SSL_CTX_free(ctx); ctx = NULL; return -2; } // add validation params ERR_clear_error(); X509_VERIFY_PARAM *x509_param = X509_VERIFY_PARAM_new(); if (!x509_param) { log_ssl_errors("couldnt add validation params for %s", hostname.c_str()); SSL_CTX_free(ctx); ctx = NULL; return -2; } // X509_V_FLAG_TRUSTED_FIRST is set by default in ossl v1.1 and above - so no need for this check //ERR_clear_error(); //if (!X509_VERIFY_PARAM_set_flags(x509_param, X509_V_FLAG_TRUSTED_FIRST)) { //log_ssl_errors("couldnt add validation params for %s", hostname.c_str()); //X509_VERIFY_PARAM_free(x509_param); //SSL_CTX_free(ctx); //ctx = NULL; //return -2; //} // ERR_clear_error(); // X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new(); // X509_VERIFY_PARAM_set_hostflags(param,X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS ); // moved from checkCertValid // kulge to check theory in use of illegal char in dns name //{ //String t = hostname; //if (t.contains("_")) { //t.swapChar('_','-'); //hostname = t; //} //} DEBUG_debug("Adding hostname to check:",hostname,":"); // moved from checkCertValid ERR_clear_error(); if (!X509_VERIFY_PARAM_set1_host(x509_param, hostname.c_str(), hostname.length())) { log_ssl_errors("couldnt add validation params for %s", hostname.c_str()); X509_VERIFY_PARAM_free(x509_param); SSL_CTX_free(ctx); ctx = NULL; return -2; } ERR_clear_error(); if (!SSL_CTX_set1_param(ctx, x509_param)) { log_ssl_errors("couldnt add validation params for %s", hostname.c_str()); X509_VERIFY_PARAM_free(x509_param); SSL_CTX_free(ctx); ctx = NULL; return -2; } X509_VERIFY_PARAM_free(x509_param); // try not freeing this as SSL_CTX_free seems to be trying to free it - PIP - 4/09/24 v5.5 seems OK now - valgrind complains if it is not freed. //hand socket over to ssl lib ERR_clear_error(); ssl = SSL_new(ctx); SSL_set_options(ssl, SSL_OP_ALL); SSL_set_connect_state(ssl); SSL_set_fd(ssl, this->getFD()); SSL_set_tlsext_host_name(ssl, hostname.c_str()); X509_VERIFY_PARAM_set1_host(SSL_get0_param(ssl), hostname.c_str(),0); int rc = 0; while (rc != 1) { ERR_clear_error(); rc = SSL_connect(ssl); DEBUG_network("ssl_connect returned ", rc ); if (rc != 1) { s_errno = SSL_get_error(ssl,rc); DEBUG_network("ssl_connect s_error is ", s_errno); switch (s_errno) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: if (ssl_poll_wait(s_errno, timeout)) continue; timedout = true; default: log_ssl_errors("ssl_connect failed to %s", hostname.c_str()); DEBUG_network("ssl_connect failed with error ", SSL_get_error(ssl, rc)); // tidy up SSL_free(ssl); ssl = NULL; SSL_CTX_free(ctx); ctx = NULL; return -3; } } } isssl = true; issslserver = false; return 0; } bool Socket::isSsl() { return isssl; } bool Socket::isSslServer() { return issslserver; } //shuts down the current ssl connection void Socket::stopSsl() { DEBUG_network("ssl stopping"); if(!isssl) return; isssl = false; if (ssl != NULL) { if (issslserver) { #ifdef DEBUG_LOW DEBUG_network("this is a server connection"); if (SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) { DEBUG_network("SSL_SENT_SHUTDOWN IS SET"); } if (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) { DEBUG_network("SSL_RECEIVED_SHUTDOWN IS SET"); } #endif DEBUG_network("calling 1st ssl shutdown"); int rc = 0; while (rc != 1) { ERR_clear_error(); rc = SSL_shutdown(ssl); DEBUG_network("SSL_shutdown returns ", rc); //if (rc == 0) continue; if (rc == 0) break; if (rc != 1) { s_errno = SSL_get_error(ssl,rc); DEBUG_network("s_errno ", s_errno); switch (s_errno) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: if (ssl_poll_wait(s_errno, timeout)) continue; DEBUG_network("timed out ", timeout); break; default: DEBUG_network("Error shutitng down SSL ", s_errno ); log_ssl_errors("ssl_shutdown failed ",""); break; } } break; } if (rc == 0) { DEBUG_network("Discarding extra data from client"); shutdown(SSL_get_fd(ssl), SHUT_WR); char junk[1024]; readFromSocket(junk, sizeof(junk), 0, 5); DEBUG_network("done"); } } else { #ifdef DEBUG_LOW DEBUG_network("this is a client connection"); if (SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) { DEBUG_network("SSL_SENT_SHUTDOWN IS SET"); } if (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) { DEBUG_network("SSL_RECEIVED_SHUTDOWN IS SET"); } DEBUG_network("calling ssl shutdown"); #endif int rc = 0; while (rc != 1) { ERR_clear_error(); rc = SSL_shutdown(ssl); DEBUG_network("SSL_shutdown returns ", rc); // if (rc == 0) continue; if (rc == 0) break; if (rc != 1) { s_errno = SSL_get_error(ssl,rc); DEBUG_network("s_errno ", s_errno); switch (s_errno) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: if (ssl_poll_wait(s_errno, timeout)) continue; break; default: DEBUG_network("Error shutitng down SSL", s_errno ); log_ssl_errors("ssl_shutdwon failed ",""); break; } } break; } DEBUG_network("done"); } } cleanSsl(); } void Socket::cleanSsl() { // called when failure in ssl set up functions and from stopSsl if (ssl != NULL) { SSL_free(ssl); ssl = NULL; } if (ctx != NULL ) { SSL_CTX_free(ctx); ctx = NULL; } issslserver = false; isssl = false; } //check that everything in this certificate is correct appart from the hostname long Socket::checkCertValid(String &hostname) { //check we have a certificate X509 *peerCert = SSL_get_peer_certificate(ssl); if (peerCert == NULL) { return -1; } X509_free(peerCert); // Moved to startSslClient // X509_VERIFY_PARAM *param; //param = X509_VERIFY_PARAM_new() ; //X509_VERIFY_PARAM_set1_host(param,hostname.c_str(), hostname.length()); //SSL_CTX_set1_param(ctx,param); //X509_VERIFY_PARAM_free(param); return SSL_get_verify_result(ssl); } //check the common name and altnames of a certificate against hostname int Socket::checkCertHostname(const std::string &_hostname) { return 0; //TODO } void Socket::close() { if (isssl) { stopSsl(); } BaseSocket::close(); } //use this socket as an ssl server int Socket::startSslServer(X509 *x, EVP_PKEY *privKey, std::string &set_cipher_list) { if (isssl) { stopSsl(); } // set ssl to NULL ssl = NULL; //setup the ssl server ctx ctx = SSL_CTX_new(TLS_server_method()); if (ctx == NULL) { DEBUG_network("Error ssl context is null (check that openssl has been inited)"); return -1; } //set the timeout to match firefox if (SSL_CTX_set_timeout(ctx, 130l) < 1) { cleanSsl(); return -1; } //set the ctx to use the certificate if (SSL_CTX_use_certificate(ctx, x) < 1) { DEBUG_network("Error using certificate"); cleanSsl(); return -1; } if (set_cipher_list.length() > 0) SSL_CTX_set_cipher_list(ctx, set_cipher_list.c_str()); //set the ctx to use the private key if (SSL_CTX_use_PrivateKey(ctx, privKey) < 1) { DEBUG_network("Error using private key"); cleanSsl(); return -1; } //setup the ssl session ERR_clear_error(); ssl = SSL_new(ctx); SSL_set_options(ssl, SSL_OP_ALL); // SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); SSL_set_accept_state(ssl); ERR_clear_error(); if(!SSL_set_fd(ssl, this->getFD())) { log_ssl_errors("ssl_set_fd failed to client", ""); cleanSsl(); return -1; }; int rc = 0; while (rc != 1) { ERR_clear_error(); rc = SSL_accept(ssl); DEBUG_network("accepting returns ", rc); if (rc != 1) { s_errno = SSL_get_error(ssl,rc); switch (s_errno) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: if (ssl_poll_wait(s_errno, timeout)) continue; timedout = true; return -1; default: DEBUG_network("Error accepting ssl connection error is ", s_errno ); log_ssl_errors("ssl_accept failed to client %s", ""); cleanSsl(); return -1; } } } ERR_clear_error(); if (SSL_do_handshake(ssl) < 0) { log_ssl_errors("ssl_handshake failed to client ", ""); cleanSsl(); return -1; } SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY); isssl = true; issslserver = true; return 0; } bool Socket::checkForInput(int timeout) { if (!isssl) { return BaseSocket::checkForInput(timeout); } //if(timeout == 0) // return false; DEBUG_network("checking for input on ssl connection"); if ((bufflen - buffstart) > 0) { DEBUG_network("found buffered input on ssl connection"); return true; } int rc = 0; int rc2 = 0; rc = s_checkPending(); if (rc < 1 ) { if (timeout == 0) { // poll ios handled by calling function DEBUG_network("no pending data on ssl connection pending ", rc); timedout = true; return false; } else { if ((rc2 = poll(infds, 1, timeout)) == 0) { DEBUG_network("timed out on pending data on ssl connection SSL_pending ", rc); timedout = true; return false; } if (infds[0].revents & POLLIN) return true; } } DEBUG_network("found data on ssl connection"); return true; } // read a line from the socket int Socket::getLine(char *buff, int size, int timeout, bool *chopped, bool *truncated) { try { if (!isssl) { return BaseSocket::getLine(buff, size, timeout, chopped, truncated); } // first, return what's left from the previous buffer read, if anything int i = 0; if ((bufflen - buffstart) > 0) { int tocopy = size - 1; if ((bufflen - buffstart) < tocopy) tocopy = bufflen - buffstart; char *result = (char *)memccpy(buff, buffer + buffstart, '\n', tocopy); if (result != NULL) { // indicate that a newline was chopped off, if desired if (chopped) *chopped = true; *(--result) = '\0'; buffstart += (result - buff) + 1; return result - buff; } else { i += tocopy; buffstart += tocopy; } } while (i < (size - 1)) { buffstart = 0; bufflen = 0; //int pend_ret = SSL_has_pending(ssl); //DEBUG_network("pending ret ", pend_ret); // may need checkForInput here !! if (!checkForInput(timeout)) { timedout = true; return -1; } bufflen = SSL_read(ssl, buffer, SCK_READ_BUFF_SIZE); DEBUG_network("read into buffer; bufflen: ", bufflen); if (bufflen < 1) { s_errno = SSL_get_error(ssl,bufflen); DEBUG_network("read into buffer; s_errno: ", s_errno); switch (s_errno) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: if (ssl_poll_wait(s_errno, timeout)) continue; timedout = true; return -1; case SSL_ERROR_ZERO_RETURN: // eof ishup = true; buff[i] = '\0'; // ...terminate string & return what read if (truncated) *truncated = true; return i; case SSL_ERROR_SYSCALL: // happens if other end stops ssl - line may or may not be HUPed ishup = true; return -1; default: log_ssl_errors("ssl_read failed %s", ""); DEBUG_network("SSL_read failed with error ", SSL_get_error(ssl, bufflen)); ishup = true; return -1; } } int tocopy = bufflen; if ((i + bufflen) > (size - 1)) tocopy = (size - 1) - i; char *result = (char *)memccpy(buff + i, buffer, '\n', tocopy); if (result != NULL) { // indicate that a newline was chopped off, if desired if (chopped) *chopped = true; *(--result) = '\0'; buffstart += (result - (buff + i)) + 1; return i + (result - (buff + i)); } i += tocopy; } // oh dear - buffer end reached before we found a newline buff[i] = '\0'; if (truncated) *truncated = true; return i; } catch (...) { return -1; } } // write line to socket bool Socket::writeString(const char *line) { int l = strlen(line); return writeToSocket(line, l, 0, timeout); } // write line to socket bool Socket::writeString(std::string line) { int l = line.length(); return writeToSocket(line.c_str(), l, 0, timeout); } // write data to socket bool Socket::writeToSocket(const char *buff, int len, unsigned int flags, int timeout) { if (len == 0) // nothing to write return true; if (!isssl) { return BaseSocket::writeToSocket(buff, len, flags, timeout); } int actuallysent = 0; int sent; while (actuallysent < len) { DEBUG_network("Ready to write ", len - actuallysent, " bytes"); ERR_clear_error(); sent = SSL_write(ssl, buff + actuallysent, len - actuallysent); if (sent < 1) { s_errno = SSL_get_error(ssl,sent); switch (s_errno) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: if (timeout > 0 && ssl_poll_wait(s_errno, timeout)) continue; DEBUG_network("Poll timeed out or error"); timedout = true; return false; case SSL_ERROR_ZERO_RETURN: // eof DEBUG_network("SSL returns eof on write???"); ishup = true; return false; default: String serr(s_errno); log_ssl_errors("ssl_write failed - error ",serr.c_str()); DEBUG_network("ssl_write failed ", s_errno, " failed to write"); ishup = true; return false; } } actuallysent += sent; } return true; } int Socket::readFromSocket(char *buff, int len, unsigned int flags, int timeout, bool ret_part ) { if (len == 0) // nothing to read return 0; if (!isssl) { return BaseSocket::readFromSocket(buff, len, flags, timeout, ret_part); } // first, return what's left from the previous buffer read, if anything int cnt = len; int tocopy = 0; if ((bufflen - buffstart) > 0) { DEBUG_network("Socket::readFromSocket: data already in buffer; bufflen: ", bufflen, " buffstart: ", buffstart ); tocopy = len; if ((bufflen - buffstart) < len) tocopy = bufflen - buffstart; memcpy(buff, buffer + buffstart, tocopy); cnt -= tocopy; buffstart += tocopy; buff += tocopy; if(ret_part) return tocopy; if (cnt == 0) return len; } //#ifdef DEBUG_LOW // int pend_stat = SSL_has_pending(ssl); // DEBUG_network("has_pending returns ", pend_stat); //#endif bool has_buffer = checkForInput(timeout); int rc; while (cnt > 0) { ERR_clear_error(); if(!has_buffer) { if (timeout > 0) { if (!checkForInput(timeout)) { timedout = true; if (len - cnt > 0) { return len - cnt; } else { return -1; } } } } else { has_buffer = false; } ERR_clear_error(); rc = SSL_read(ssl, buff, cnt); if (rc < 1) { s_errno = SSL_get_error(ssl,rc); DEBUG_network("Error SSL_read ", s_errno); switch (s_errno) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: if (timeout > 0 && ssl_poll_wait(s_errno, timeout)) continue; timedout = true; return -1; case SSL_ERROR_ZERO_RETURN: // eof ishup = true; return len - cnt; case SSL_ERROR_SYSCALL: // ssl stopped at remote end ishup = true; return len - cnt; default: log_ssl_errors("ssl_read failed %s", ""); DEBUG_network("ssl_read failed", s_errno, " failed to read ",cnt, " bytes"); ishup = true; return len - cnt; } } DEBUG_network("SSL read returned ", rc); buff += rc; cnt -= rc; if(ret_part) return len - cnt; } return len; } void Socket::resetChunk() { chunk_to_read = 0; chunked_trailer = ""; ieof = false; } bool Socket::writeChunk( char *buffout, int len, int timeout){ std::stringstream stm; stm << std::hex << len; std::string hexs (stm.str()); //int lw; hexs += "\r\n"; DEBUG_network("writeChunk size=", hexs); if(!writeString(hexs.c_str())) { DEBUG_network(("Error on chunked size write")); return false; } //else { // DEBUG_network("chunked size write ok"); //} if(!writeToSocket(buffout,len,0,timeout)) { DEBUG_network(("Error on chunked data write")); return false; } //else { // DEBUG_network("chunked data write ok"); //} if(!writeString("\r\n")) { DEBUG_network(("Error on chunked line end write")); return false; } else { DEBUG_network("chunked line end write ok"); } return true; }; bool Socket::writeChunkTrailer( String &trailer) { std::string hexs ("0\r\n"); DEBUG_network("writeChunk trailer size=", hexs); if(writeString(hexs.c_str()) && writeToSocket(trailer.c_str(),trailer.length(),0,timeout) && writeString("\r\n")) return true; return false; }; int Socket::readChunk( char *buffin, int maxlen, int timeout){ if (chunk_to_read == 0) // need to read chunk size { char size[40]; ieof = false; int len = getLine(size, 38, timeout); if (len < 2) { // min valid length is 2 i.e. "0\r" chunkError = true; return -1; } DEBUG_network("readChunk size=", size); String l = size; l.chop(); String t = l.before(";"); if (t.length() > 0) { if (l.endsWith("; ieof")) { ieof = true; } l = t; } chunk_to_read = l.hexToInteger(); DEBUG_network("readChunk chunk_to_read =", chunk_to_read); } int clen = chunk_to_read; if (clen > maxlen) { clen = maxlen; } int rc = 0; DEBUG_network("readChunk max_read =", clen); if(clen == 0) { chunked_trailer = ""; char trailer[32000]; int len = 3; while( len > 2) { len = getLine(trailer, 31900, timeout); if (len > 2) { chunked_trailer += trailer; chunked_trailer += "\n"; } } return 0; } if (clen > 0) { rc = readFromSocket(buffin, clen, 0, timeout); DEBUG_network("readChunk read ", rc); if (rc < 0) { chunkError = true; return -1; } chunk_to_read -= rc; } if (chunk_to_read > 0) // there is more to read in this chunk - so do not check for trailing \r\n return rc; char ts[2]; int len = readFromSocket(ts, 2, 0, timeout); if (len == 2 && ts[0] == '\r' && ts[1] == '\n') { return rc; } else { chunkError = true; DEBUG_network("readChunk - tail in error"); return -1; } } int Socket::loopChunk(int timeout) // reads chunks and sends back until 0 len chunk or timeout { char buff[32000]; int tot_size = 0; int csize = 1; while (csize > 0) { csize = readChunk(buff,32000, timeout); if (csize == 0) // end chunk { if (!writeChunkTrailer(chunked_trailer)) { #ifdef CHUNKDEBUG std::cerr << thread_id << "loopChunk - error in writing chunk trailer" << std::endl; #endif return -1; }; #ifdef CHUNKDEBUG std::cerr << thread_id << "loopChunk tot_size=" << tot_size << std::endl; #endif return tot_size; } if (!(csize > 0 && writeChunk(buff,csize,timeout))) { #ifdef CHUNKDEBUG std::cerr << thread_id << "loopChunk - error" << std::endl; #endif return -1; } tot_size += csize; } return -1; // should never get here! } int Socket::drainChunk(int timeout) // reads chunks until 0 len chunk or timeout { char buff[32000]; int tot_size = 0; int csize = 1; while (csize > 0) { csize = readChunk(buff,32000, timeout); if (csize < 0) { #ifdef CHUNKDEBUG std::cerr << thread_id << "drainChunk - error" << std::endl; #endif return -1; } tot_size += csize; } #ifdef CHUNKDEBUG std::cerr << thread_id << "drainChunk tot_size=" << tot_size << std::endl; #endif return tot_size; } bool Socket::getIeof() { return ieof; } bool Socket::ssl_poll_wait(int serr, int timeout) { // if(timeout == 0) { // timedout = true; // return false; //} int rc; s_errno = 0; DEBUG_network("sERR ", serr); if (serr == SSL_ERROR_WANT_READ) { if (isNoRead()) return false; errno = 0; rc = poll(infds, 1, timeout); if (rc == 0) { DEBUG_network("poll returned 0"); timedout = true; return false; //timeout } timedout = false; if (rc < 0) { DEBUG_network("poll returned ", s_errno); s_errno = errno; sockerr = true; return false; } if (infds[0].revents & POLLHUP) { DEBUG_network("poll returned HUP"); ishup = true; } if ((infds[0].revents & ( POLLIN))) { return true; } sockerr = true; return false; // must be POLLERR or POLLNVAL } else if(serr == SSL_ERROR_WANT_WRITE) { //must be SSL_ERROR_WANT_WRITE if (isNoWrite()) return false; errno = 0; rc = poll(outfds, 1, timeout); if (rc == 0) { timedout = true; DEBUG_network("poll returned 0"); return false; //timeout } timedout = false; if (rc < 0) { s_errno = errno; DEBUG_network("poll returned ", s_errno); sockerr = true; return false; } if (outfds[0].revents & POLLHUP) { ishup = true; DEBUG_network("poll returned HUP"); } if ((outfds[0].revents & ( POLLOUT))) { return true; } } return false; } short int Socket::get_wait_flag(bool write_flag) { if (!isssl) return BaseSocket::get_wait_flag(write_flag); if(timedout) { if (s_errno == SSL_ERROR_WANT_READ) { timedout = false; return POLLIN; } else if (s_errno == SSL_ERROR_WANT_WRITE) { timedout = false; return POLLOUT; } } return POLLIN; } void Socket::setClientAddr(std::string ip, int port) { client_addr = ip; client_port = port; } bool Socket::s_checkPending() { if(s_checkShutdown()&SSL_RECEIVED_SHUTDOWN) { DEBUG_network("SSL shudown in progress - returning false"); return (s_pending = false); } s_pending = SSL_pending(ssl); DEBUG_network("SSL_pending gives ", s_pending); #ifndef LIBRESSL_VERSION_NUMBER s_prev_has_pending = s_has_pending; s_has_pending = SSL_has_pending(ssl); DEBUG_network("SSL_has_pending gives ", s_has_pending); s_pending = s_pending || (s_has_pending && !s_prev_has_pending); #endif DEBUG_network("Returning ", s_pending); return s_pending; } int Socket::s_checkShutdown() { return SSL_get_shutdown(ssl); } e2guardian-5.5.8r/src/Socket.hpp000066400000000000000000000106151477372360500165050ustar00rootroot00000000000000// Socket class - implements BaseSocket for INET domain sockets // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_SOCKET #define __HPP_SOCKET // INCLUDES #include "BaseSocket.hpp" #include "String.hpp" #include #include #include "openssl/ssl.h" #include "String.hpp" // DECLARATIONS class Socket : public BaseSocket { friend class FDTunnel; public: // create INET socket & clear address structs Socket(); // create socket using pre-existing FD (address structs will be empty!) Socket(int fd); // create socket from pre-existing FD, storing given local & remote IPs Socket(int newfd, struct sockaddr_in myip, struct sockaddr_in peerip); ~Socket(); // connect to given IP & port (following default constructor) int connect(const std::string &ip, int port); // bind to given port int bind(int port); // bind to given IP & port, for machines with multiple NICs int bind(const std::string &ip, int port); // accept incoming connections & return new Socket Socket *accept(); // close socket & clear address structs void reset(); // get remote IP/port std::string getPeerIP(); int getPeerSourcePort(); int getPort(); void setPort(int port); void setClientAddr( std::string ip, int port); unsigned long int getPeerSourceAddr(); std::string down_thread_id; // get local IP std::string getLocalIP(); int getLocalPort(); // Chunked i/o String chunked_trailer; long int chunk_to_read; bool writeChunk( char *buffout, int len, int timeout); bool writeChunkTrailer( String &trailer); int readChunk( char *buffout, int maxlen, int timeout); int loopChunk(int timeout); int drainChunk(int timeout); void resetChunk(); short int get_wait_flag(bool write_flag); //flags for use with ssl only bool s_read_wants_read = false; bool s_read_wants_write = false; bool s_write_wants_read = false; bool s_write_wants_write = false; bool s_shutdown_wants_read = false; bool s_shutdown_wants_write = false; int s_pending = 0; bool s_has_pending = false; bool s_prev_has_pending = false; bool s_checkPending(); int s_checkShutdown(); bool chunkError; //use this socket as an ssl server int startSslClient(const std::string &certPath, String hostname); //is this a SSL connection bool isSsl(); bool isSslServer(); //shuts down the current ssl connection void stopSsl(); //cleans up ssl void cleanSsl(); //check that everything in this certificate is correct appart from the hostname long checkCertValid(String &hostname); //check the common name and altnames of a certificate against hostname int checkCertHostname(const std::string &hostame); void close(); //use this socket as an ssl server int startSslServer(X509 *x, EVP_PKEY *privKey, std::string &set_cipher); // blocking check, can break on config reloads //void readyForOutput(int timeout, bool honour_reloadconfig = false); // non-blocking check for input data bool checkForInput(int timeout = 20); //bool bcheckForInput(int timeout); bool ssl_poll_wait(int eno, int timeout); // get a line from the socket - can break on config reloads int getLine(char *buff, int size, int timeout, bool *chopped = NULL, bool *truncated = NULL) ; // write buffer to string bool writeString(const char *line); bool writeString(std::string line); // write buff to socket - blocking bool writeToSocket(const char *buff, int len, unsigned int flags, int timeout); // write buff to socket non-blocking - returns number of bytes written or 0 if would block or -1 on error int writeToSocketNB(const char *buff, int len, unsigned int flags); // read from socket, returning number of bytes read int readFromSocket(char *buff, int len, unsigned int flags, int timeout, bool ret_part = false); bool getIeof(); private: SSL *ssl = NULL; SSL_CTX *ctx = NULL; bool isssl = false; bool issslserver = false; // local & remote addresses struct sockaddr_in my_adr; struct sockaddr_in peer_adr; int my_port = 0; std::string my_addr; int client_port = 0; std::string client_addr; bool ieof = false; }; #endif e2guardian-5.5.8r/src/SocketArray.cpp000066400000000000000000000072071477372360500175020ustar00rootroot00000000000000// SocketArray - wrapper for clean handling of an array of Sockets // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "SocketArray.hpp" #include "Queue.hpp" #include "Logger.hpp" #include #include // GLOBALS // IMPLEMENTATION SocketArray::~SocketArray() { delete[] drawer; } void SocketArray::deleteAll() { delete[] drawer; drawer = NULL; socknum = 0; } // close all sockets & create new ones void SocketArray::reset(int sockcount) { delete[] drawer; drawer = new Socket[sockcount]; socknum = sockcount; } // bind our first socket to any IP int SocketArray::bindSingle(int port) { if (socknum < 1) { return -1; } DEBUG_debug("bindSingle binding port", port); lc_types.push_back(CT_PROXY); return drawer[0].bind(port); } int SocketArray::bindSingle(unsigned int index, int port, unsigned int type) { if (socknum <= index) { return -1; } DEBUG_debug("bindSingle binding port", port, " with type ", type); lc_types.push_back(type); return drawer[index].bind(port); } // bind our first socket to any IP and one or more ports int SocketArray::bindSingleM(std::deque &ports, int &index, int ct_type ) { if ( socknum < (index + ports.size())) { return -1; } for (auto i : ports) { DEBUG_debug("bindSingleM binding port", i); if (drawer[index].bind(i.toInteger())) { E2LOGGER_error("Error binding server socket: [", i, " ", index, "] (", strerror(errno), ")"); return -1; } lc_types.push_back(ct_type); index++; } return 0; } // return an array of our socket FDs int *SocketArray::getFDAll() { int *fds = new int[socknum]; for (unsigned int i = 0; i < socknum; i++) { DEBUG_debug("Socket ", i, " fd:", drawer[i].getFD() ); fds[i] = drawer[i].getFD(); } return fds; } // listen on all IPs with given kernel queue size int SocketArray::listenAll(int queue) { for (unsigned int i = 0; i < socknum; i++) { if (drawer[i].listen(queue)) { E2LOGGER_error("Error listening to socket"); return -1; } } return 0; } // bind all sockets to given IP list int SocketArray::bindAll(std::deque &ips, std::deque &ports, int &index, int ct_type) { if ((index + (ips.size() * ports.size())) > socknum) { return -1; } //for (unsigned int i = 0; i < socknum; i++) { for (auto i_ips : ips) { for (auto i_ports : ports) { DEBUG_debug("Binding server socket[", i_ports, " ", i_ips, " ", index, "]"); if (drawer[index].bind(i_ips.toCharArray(), i_ports.toInteger())) { E2LOGGER_error("Error binding server socket: [", i_ports, " ", i_ips, " ", index, "] (", strerror(errno), ")"); return -1; } lc_types.push_back(ct_type); index++; } } return 0; } // try connecting to all our sockets which are still open to allow tidy close void SocketArray::self_connect() { for (unsigned int i = 0; i < socknum; i++) { if (drawer[i].getFD() > -1) { std::string sip = drawer[i].getLocalIP(); int port = drawer[i].getPort(); Socket temp; temp.setTimeout(100); temp.connect(sip, port); temp.close(); } } } unsigned int SocketArray::getType(unsigned int ind) { return lc_types.at(ind); } e2guardian-5.5.8r/src/SocketArray.hpp000066400000000000000000000033741477372360500175100ustar00rootroot00000000000000// SocketArray - wrapper for clean handling of an array of Sockets // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_SOCKETARRAY #define __HPP_SOCKETARRAY // INCLUDES #include "Socket.hpp" #include "String.hpp" #include #include #include // DECLARATIONS class SocketArray { public: // set sensible defaults SocketArray() : drawer(NULL), socknum(0){}; // delete all sockets ~SocketArray(); // close all old socks & create specified amount of new ones void reset(int sockcount); // just delete the lot of 'em void deleteAll(); // bind our sockets to the given IPs int bindAll(std::deque &ips, std::deque &ports, int &index, int ct_type); // bind just the one, to all available IPs int bindSingle(int port); int bindSingle(unsigned int index, int port, unsigned int CT_type); int bindSingleM(std::deque &port, int &index, int ct_type); // set all sockets listening with given kernel queue length int listenAll(int queue); // shove all socket FDs into the given array (pass in unallocated) int *getFDAll(); unsigned int getType(unsigned int ind); // try connecting to all our sockets which are still open to allow tidy close void self_connect(); // array dereference operator Socket *operator[](int i) { return &(drawer[i]); }; private: // our sock collection container Socket *drawer; std::vector lc_types; // holds listening connection type i.e. LC_PROXY, LC_THTTPS, LC_ICAP // how many sockets we have unsigned int socknum; }; #endif e2guardian-5.5.8r/src/StoryBoard.cpp000066400000000000000000001155161477372360500173460ustar00rootroot00000000000000// ListContainer - class for both item and phrase lists // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "ListContainer.hpp" #include "StoryBoard.hpp" #include "OptionContainer.hpp" #include "RegExp.hpp" #include "Logger.hpp" #include #include // for gethostby #include #include #include #include #include #include #include #include #include #include // GLOBALS extern OptionContainer o; // DEFINES //#define SBDEBUG // Constructor - set default values StoryBoard::StoryBoard() { fnt_cnt = 0; } // delete the memory block when the class is destryed StoryBoard::~StoryBoard() { reset(); } // clear & reset all values void StoryBoard::reset() { } bool StoryBoard::readFile(const char *filename, ListMeta &LM, bool is_top) { if (strlen(filename) < 3) { E2LOGGER_error("Storyboard file", filename, " is not defined"); return false; } DEBUG_trace("Reading storyboard file ", filename); LMeta = &LM; std::string linebuffer; // a string line buffer ;) String temp; // a String for temporary manipulation String line; String command; String params; String action; SBFunction curr_function; int fnt_id = 0; bool overwrite = false; bool in_function = false; std::ifstream listfile(filename, std::ios::in); // open the file for reading if (!listfile.good()) { E2LOGGER_error("Error opening Storyboard file (does it exist?): ", filename); return false; } String base_dir(filename); base_dir.baseDir(); bool caseinsensitive = true; unsigned int line_no = 0; while (!listfile.eof()) { // keep going until end of file getline(listfile, linebuffer); // grab a line ++line_no; if (linebuffer.length() == 0) { // sanity checking continue; } DEBUG_story("Readline ", linebuffer); line = linebuffer.c_str(); line.removeWhiteSpace(); // handle included list files if (line.startsWith(".")) { temp = line.after(".Include<").before(">"); if (temp.length() > 0) { temp.fullPath(base_dir); if (!readFile(temp.toCharArray(), *LMeta, false)) { listfile.close(); return false; } else { continue; } } else { continue; } } if (line.startsWith("#")) continue; // ignore comment lines if (caseinsensitive) line.toLower(); command = line.before("("); command.removeWhiteSpace(); params = line.before(")").after("("); params.removeWhiteSpace(); action = line.after(")"); if (action.contains("#")) action = action.before("#"); // remove trailing comments action.removeWhiteSpace(); if (command == "function") { if (in_function) { // already in another function definition & so assume end of previous function curr_function.end(); // push function to list if (overwrite) funct_vec.at(--fnt_id) = curr_function; else funct_vec.push_back(curr_function); } in_function = true; String temp = params; int oldf = 0; if ((oldf = getFunctID(temp)) > 0) { if (oldf > SB_BI_FUNC_BASE) { // overloadng buildin action E2LOGGER_error("SB: error - reserved word used a function name - ", filename, " word ", temp); return false; } else { fnt_id = oldf; overwrite = true; } } else { fnt_id = ++fnt_cnt; overwrite = false; } curr_function.start(params, fnt_id, line_no, filename); continue; } if (command == "end") { if (in_function) { curr_function.end(); // push function to list if (overwrite) funct_vec.at(--fnt_id) = curr_function; else funct_vec.push_back(curr_function); in_function = false; } // otherwise ignore continue; } if (!curr_function.addline(command, params, action, line_no)) return false; } if (in_function) { // at eof so now end curr_function.end(); // push function to list if (overwrite) funct_vec.at(--fnt_id) = curr_function; else funct_vec.push_back(curr_function); } DEBUG_debug("Read storyboard file ", filename, " finished. ", " function vect size is ", String(funct_vec.size()), " is_top ", String(is_top)); if (!is_top) return true; // only do the 2nd pass once all file(s) have been read // in top file now do second pass to record action functions ids in command lines and add list_ids DEBUG_trace("Read storyboard start 2nd pass checking"); for (std::vector::iterator i = funct_vec.begin(); i != funct_vec.end(); i++) { for (std::deque::iterator j = i->comm_dq.begin(); j != i->comm_dq.end(); j++) { DEBUG_story("Line ", String(j->file_lineno), " state is ", String(j->state), " actionid ", String(j->action_id), " listname ", j->list_name, " function ", i->name, " id ", String(i->fn_id)); // check condition if (j->state < SB_STATE_TOPIN) { // is an *in condition and requires a list std::deque types; switch (j->state) { case SB_STATE_SITEIN: types = {LIST_TYPE_IPSITE, LIST_TYPE_SITE, LIST_TYPE_REGEXP_BOOL}; break; case SB_STATE_URLIN: types = {LIST_TYPE_IPSITE, LIST_TYPE_SITE, LIST_TYPE_URL, LIST_TYPE_FILE_EXT, LIST_TYPE_REGEXP_BOOL}; break; case SB_STATE_SEARCHIN: types = {LIST_TYPE_SEARCH}; break; case SB_STATE_EMBEDDEDIN: types = {LIST_TYPE_IPSITE, LIST_TYPE_SITE, LIST_TYPE_URL, LIST_TYPE_REGEXP_BOOL}; break; case SB_STATE_REFERERIN: types = {LIST_TYPE_IPSITE, LIST_TYPE_SITE, LIST_TYPE_URL, LIST_TYPE_REGEXP_BOOL, LIST_TYPE_REGEXP_REP}; break; case SB_STATE_FULLURLIN: types = {LIST_TYPE_REGEXP_REP}; break; case SB_STATE_HEADERIN: types = {LIST_TYPE_REGEXP_REP, LIST_TYPE_REGEXP_BOOL}; break; case SB_STATE_RESHEADERIN: types = {LIST_TYPE_REGEXP_REP, LIST_TYPE_REGEXP_BOOL}; break; case SB_STATE_CLIENTIN: types = {LIST_TYPE_IP, LIST_TYPE_SITE, LIST_TYPE_IPMAP}; break; case SB_STATE_USERIN: types = {LIST_TYPE_IPMAP, LIST_TYPE_MAP}; break; case SB_STATE_EXTENSIONIN: types = {LIST_TYPE_FILE_EXT}; break; case SB_STATE_MIMEIN: types = {LIST_TYPE_MIME}; break; case SB_STATE_USERAGENTIN: types = {LIST_TYPE_REGEXP_BOOL}; break; case SB_STATE_TIMEIN: types = {LIST_TYPE_TIME}; break; case SB_STATE_LISTEN_PORTIN: types = {LIST_TYPE_MAP}; break; case SB_STATE_CATEGORYIN: types = {LIST_TYPE_CATEGORY}; break; } bool found = false; for (std::deque::iterator k = types.begin(); k != types.end(); k++) { ListMeta::list_info *listiptr = LMeta->findListPtr(j->list_name, *k); ListMeta::list_info listi; if (listiptr) { listi = *listiptr; DEBUG_story("SB list reference ", String(listi.list_ref), " '", listi.name, "' found for ", j->list_name); if (listi.name.length()) { // std::cerr << "used is set " << "" << listi.list_ref << " '" << listi.name << ":" // << *k << std::endl; listiptr->used = true; j->list_id_dq.push_back(listi); found = true; } } } if (!found) { // warning message E2LOGGER_error("SB warning: Undefined list ", j->list_name, " used at line ", j->file_lineno, " of ", i->file_name, " (", filename, ")"); } else { DEBUG_story("SB ", j->list_name, " matches ", String(j->list_id_dq.size()), " types"); } } // check action if ((j->action_id = getFunctID(j->action_name)) == 0) { // warning message E2LOGGER_error("StoryBoard error: Action ", j->action_name, " not defined: ", filename, " at line ", String(j->file_lineno), " of ", i->file_name); } DEBUG_story("Line ", String(j->file_lineno), " state is ", String(j->state), " actionid ", String(j->action_id), " listname ", j->list_name); } } // check for required functions for (std::vector::iterator j = LMeta->list_vec.begin(); j != LMeta->list_vec.end(); j++) { if (!j->used) { E2LOGGER_error("SB warning: Defined list ", LMeta->list_type(j->type), ":", j->name, " is not referenced in the storyboard ", filename); } } return true; } unsigned int StoryBoard::getFunctID(String &fname) { unsigned int i = 0; // check built in functions first if (!funct_vec.empty()) { i = funct_vec[0].getBIFunctID(fname); if (i > 0) return i; } // check StoryBoard defined functions // std::cerr << "Looking for function " << fname << std::endl;; for (std::vector::iterator j = funct_vec.begin(); j != funct_vec.end(); j++) { if (j->name == fname) return j->fn_id; } return 0; } bool StoryBoard::runFunct(String &fname, NaughtyFilter &cm) { return runFunct(getFunctID(fname), cm); } bool StoryBoard::runFunct(unsigned int fID, NaughtyFilter &cm) { --fID; SBFunction *F = &(funct_vec[fID]); bool action_return = false; E2LOGGER_storytrace("SB:Entering ", F->getName(), " line: ", F->file_lineno, " of ", F->file_name); for (std::deque::iterator i = F->comm_dq.begin(); i != F->comm_dq.end(); i++) { bool isListCheck = false; bool isMultiListCheck = false; bool isHeaderCheck = false; HTTPHeader *targetheader = nullptr; bool state_result = false; ListMeta::list_result res; String target; String target2; String targetful; std::deque targetdq; switch (i->state) { case SB_STATE_SITEIN: isListCheck = true; target = cm.urldomain; target2 = target; targetful = cm.url; if (has_reverse_hosts(targetdq, cm)) isMultiListCheck = true; break; case SB_STATE_URLIN: isListCheck = true; target = cm.baseurl; target2 = cm.urldomain; targetful = cm.url; if (has_reverse_hosts(targetdq, cm)) isMultiListCheck = true; break; case SB_STATE_FULLURLIN: isListCheck = true; target = cm.baseurl; target2 = cm.urldomain; targetful = cm.url; if (has_reverse_hosts(targetdq, cm)) isMultiListCheck = true; break; case SB_STATE_SEARCHIN: if (cm.isSearch) { isListCheck = true; target = cm.search_words; } break; case SB_STATE_EMBEDDEDIN: if (!cm.deep_urls_checked) { cm.deep_urls = deep_urls(cm.baseurl, cm); if (cm.deep_urls.size() > 0) cm.hasEmbededURL = true; } if (cm.hasEmbededURL) { isMultiListCheck = true; targetdq = cm.deep_urls; target = "mutli"; } break; case SB_STATE_REFERERIN: isListCheck = true; target = cm.request_header->getReferer(); // needs spliting before?? target2 = target.getHostname(); break; case SB_STATE_HEADERIN: isHeaderCheck = true; targetheader = cm.request_header; break; case SB_STATE_RESHEADERIN: isHeaderCheck = true; targetheader = cm.response_header; break; case SB_STATE_CLIENTIN: isListCheck = true; target = cm.clientip; target2 = cm.clienthost; break; case SB_STATE_USERIN: isListCheck = true; cm.user.toLower(); target = cm.user; target2 = cm.clienthost; break; case SB_STATE_LISTEN_PORTIN: isListCheck = true; target = cm.listen_port; break; case SB_STATE_TIMEIN: isListCheck = true; break; case SB_STATE_USERAGENTIN: isListCheck = true; targetful = cm.request_header->userAgent(); // needs spliting before??target = ""; target2 = ""; break; case SB_STATE_EXTENSIONIN: target = cm.response_header->disposition(); if (target.length() > 4) isListCheck = true; target2 = ""; break; case SB_STATE_MIMEIN: target = cm.response_header->getContentType(); if (target.length() > 4) isListCheck = true; target2 = ""; break; case SB_STATE_CATEGORYIN: target = cm.main_category(); DEBUG_debug("CAT target is ", target); if (target.length() > 2) isListCheck = true; target2 = ""; break; case SB_STATE_CONNECT: state_result = cm.isconnect; break; case SB_STATE_GET: state_result = (cm.request_header->requestType() == "GET"); break; case SB_STATE_POST: state_result = (cm.request_header->requestType() == "POST"); break; case SB_STATE_EXCEPTIONSET: state_result = cm.isexception; break; case SB_STATE_SEMIEXCEPTIONSET: state_result = cm.issemiexception; break; case SB_STATE_GREYSET: state_result = cm.isGrey; break; case SB_STATE_BLOCKSET: state_result = cm.isBlocked; break; case SB_STATE_MITMSET: state_result = cm.ismitm; break; case SB_STATE_DONESET: state_result = cm.isdone; break; case SB_STATE_RETURNSET: state_result = cm.isReturn; break; case SB_STATE_REDIRECTSET: state_result = cm.urlredirect; break; case SB_STATE_VIRUSCHECKSET: state_result = !cm.noviruscheck; break; case SB_STATE_BYPASSSET: state_result = cm.isbypass; break; case SB_STATE_BYPASSALLOWEDSET: state_result = cm.isbypassallowed; break; case SB_STATE_INFECTIONBYPASSALLOWEDSET: state_result = cm.isinfectionbypassallowed; break; case SB_STATE_HASSNI: state_result = cm.hasSNI; break; case SB_STATE_TLS: state_result = cm.isTLS; break; case SB_STATE_SITEISIP: state_result = cm.isiphost; break; case SB_STATE_TRUE: state_result = true; break; } DEBUG_story("SB-Test ", " state: ", F->getState(i->state), " target: ", target, " target2: ", target2, " list_check: ", String(isListCheck), " targetfull: ", targetful, " isSearch: ", String(cm.isSearch), " state_result: ", String(state_result)); if (isHeaderCheck) { DEBUG_trace("HeaderCheck"); for (std::deque::iterator u = targetheader->header.begin(); u != targetheader->header.end(); u++) { // String t = *u; for (std::deque::iterator j = i->list_id_dq.begin(); j != i->list_id_dq.end(); j++) { DEBUG_story("checking ", j->name, " type ", j->type); if (LMeta->inList(*j, *u, res)) { //found state_result = true; if (i->isif) { cm.lastcategory = res.category; cm.whatIsNaughtyCategories = res.category; cm.message_no = res.mess_no; cm.log_message_no = res.log_mess_no; cm.lastmatch = res.match; cm.result = res.result; if (j->type == LIST_TYPE_REGEXP_REP) { *u = res.result; } if (o.log.anonymise_logs) cm.anon_user = true; if (res.anon_log) { cm.anon_url = true; } } DEBUG_story("SB lc", cm.lastcategory, " mess_no ", cm.message_no, " log_mess ", cm.log_message_no, " match ", res.match); break; } } if (state_result) break; } } if (isListCheck) { DEBUG_story("ListCheck:", i->list_name, " size:", i->list_id_dq.size()); for (std::deque::iterator j = i->list_id_dq.begin(); j != i->list_id_dq.end(); j++) { //ListMeta::list_result res; String t; if ((j->type >= LIST_TYPE_SITE) && (j->type < LIST_TYPE_URL)) { t = target2; } else if (j->type == LIST_TYPE_REGEXP_BOOL || j->type == LIST_TYPE_REGEXP_REP) { t = targetful; } else if ((j->type == LIST_TYPE_FILE_EXT) && target.contains("?")) { t = target.before("?"); // remove cgi part of url } else { t = target; } DEBUG_story("ListCheck ", j->name, "(type ", j->type, ")", " for ", t); if (cm.issiteonly && (j->type == LIST_TYPE_URL || j->type == LIST_TYPE_FILE_EXT)) continue; if (!(cm.isiphost) && j->type == LIST_TYPE_IPSITE) continue; if ((cm.isiphost) && j->type == LIST_TYPE_SITE && !o.lists.search_sitelist_for_ip) continue; // DEBUG_story("checking ", j->name, " type ", String(j->type)); if (LMeta->inList(*j, t, res)) { //found state_result = true; if (i->isif) { cm.result = res.result; } DEBUG_story("ListCheck lc", cm.lastcategory, " mess_no ", cm.message_no, " log_mess ", cm.log_message_no, " match ", res.match); break; } } DEBUG_debug("ListCheck:", i->list_name, " NOT FOUND"); } if (isMultiListCheck && !state_result) { DEBUG_trace("MultiListCheck"); for (std::deque::iterator u = targetdq.begin(); u != targetdq.end(); u++) { for (std::deque::iterator j = i->list_id_dq.begin(); j != i->list_id_dq.end(); j++) { //ListMeta::list_result res; String t; if ((j->type >= LIST_TYPE_SITE) && (j->type < LIST_TYPE_URL)) { t = u->urldomain; } else if (j->type == LIST_TYPE_REGEXP_BOOL || j->type == LIST_TYPE_REGEXP_REP) { t = u->fullurl; } else { t = u->baseurl; } if (u->is_siteonly && j->type == LIST_TYPE_URL) continue; if (!(u->site_is_ip) && j->type == LIST_TYPE_IPSITE) continue; if ((u->site_is_ip) && j->type == LIST_TYPE_SITE && !o.lists.search_sitelist_for_ip) continue; DEBUG_story("checking ", j->name, " type ", j->type, "Target ", t); if (LMeta->inList(*j, t, res)) { //found state_result = true; if (i->isif) { cm.result = res.result; } DEBUG_story("SB lc", cm.lastcategory, " mess_no ", cm.message_no, " log_mess ", cm.log_message_no, " match ", res.match); break; } } if (state_result) break; } } if (!i->isif) { state_result = !state_result; } DEBUG_story("SB-Result", " state: ", F->getState(i->state), " target: ", target, " target2: ", target2, " list_check: ", String(isListCheck), " targetfull: ", targetful, " isSearch: ", String(cm.isSearch), " state_result: ", String(state_result)); E2LOGGER_storytrace("SB: ", i->file_lineno, (i->isif ? " if(" : " ifnot("), F->getState(i->state), ",", i->list_name, ") is ", (state_result ? "true" : "false")); if (!state_result) { action_return = false; cm.isReturn = action_return; continue; // nothing to do so continue to next SB line } action_return = true; // if (i->mess_no > 0) cm.message_no = i->mess_no; // if (i->log_mess_no > 0) cm.log_message_no = i->log_mess_no; DEBUG_story("ACTION ", " lc: ", cm.lastcategory, " mess_no: ", String(cm.message_no), " log_mess: ", String(cm.log_message_no), " match: ", cm.whatIsNaughty, " action_id: ", F->getBIFunct(i->action_id)); if (i->action_id > SB_BI_FUNC_BASE) { // is built-in action switch (i->action_id) { case SB_FUNC_SETEXCEPTION: cm.isexception = true; cm.isGrey = false; cm.isBlocked = false; if (!cm.issemiexception) { // don't overwrite semi exception messages update_messages(cm, res); if (i->mess_no > 0) cm.message_no = i->mess_no; if (i->log_mess_no > 0) cm.log_message_no = i->log_mess_no; cm.whatIsNaughty = o.language_list.getTranslation(cm.message_no) + cm.lastmatch; if (cm.log_message_no == 0) cm.whatIsNaughtyLog = cm.whatIsNaughty; else cm.whatIsNaughtyLog = o.language_list.getTranslation(cm.log_message_no) + cm.lastmatch; cm.exceptioncat = cm.lastcategory; } cm.issemiexception = false; break; case SB_FUNC_SETSEMIEXCEPTION: cm.issemiexception = true; cm.isexception = false; cm.isGrey = false; cm.isBlocked = false; update_messages(cm, res); if (i->mess_no > 0) cm.message_no = i->mess_no; if (i->log_mess_no > 0) cm.log_message_no = i->log_mess_no; //cm.exceptionreason = o.language_list.getTranslation(cm.message_no); cm.whatIsNaughty = o.language_list.getTranslation(cm.message_no) + cm.lastmatch; if (cm.log_message_no == 0) cm.whatIsNaughtyLog = cm.whatIsNaughty; else cm.whatIsNaughtyLog = o.language_list.getTranslation(cm.log_message_no) + cm.lastmatch; cm.exceptioncat = cm.lastcategory; break; case SB_FUNC_UNSETSEMIEXCEPTION: cm.issemiexception = false; break; case SB_FUNC_SETGREY: cm.isGrey = true; cm.isexception = false; cm.issemiexception = false; cm.isBlocked = false; break; case SB_FUNC_SETBLOCK: cm.isBlocked = true; cm.isGrey = false; cm.isexception = false; cm.issemiexception = false; update_messages(cm, res); if (i->mess_no > 0) cm.message_no = i->mess_no; if (i->log_mess_no > 0) cm.log_message_no = i->log_mess_no; if (cm.message_no == 503) cm.whatIsNaughty = o.language_list.getTranslation(cm.message_no); else cm.whatIsNaughty = o.language_list.getTranslation(cm.message_no) + cm.get_lastmatch(); if (cm.log_message_no == 0) cm.whatIsNaughtyLog = o.language_list.getTranslation(cm.message_no) + cm.get_lastmatch(); else cm.whatIsNaughtyLog = o.language_list.getTranslation(cm.log_message_no) + cm.get_lastmatch(); cm.whatIsNaughtyCategories = cm.lastcategory; break; case SB_FUNC_SETMODURL: cm.urlmodified = true; cm.request_header->setURL(cm.result); cm.url = cm.result; cm.baseurl = cm.url; cm.baseurl.removeWhiteSpace(); cm.baseurl.toLower(); cm.baseurl.removePTP(); cm.logurl = cm.request_header->getLogUrl(false, cm.ismitm); cm.urld = cm.request_header->decode(cm.url); cm.urldomain = cm.url.getHostname(); cm.urldomain.toLower(); cm.connect_site = cm.urldomain; DEBUG_story("SB: URL modified to ", cm.url); break; case SB_FUNC_SETCONNECTSITE: cm.urlmodified = true; cm.logurl = cm.result; cm.connect_site = cm.result.getHostname(); DEBUG_story("SB: connect site changed to ", cm.connect_site); break; case SB_FUNC_SETLOGCAT: cm.logcategory = true; update_messages(cm, res); if (i->mess_no > 0) cm.message_no = i->mess_no; if (i->log_mess_no > 0) cm.log_message_no = i->log_mess_no; cm.whatIsNaughty = o.language_list.getTranslation(cm.message_no) + cm.lastmatch; if (cm.log_message_no == 0) cm.whatIsNaughtyLog = cm.whatIsNaughty; else cm.whatIsNaughtyLog = o.language_list.getTranslation(cm.log_message_no) + cm.lastmatch; cm.whatIsNaughtyCategories = cm.lastcategory; break; case SB_FUNC_SETREDIRECT: if (cm.result.size() > 0) { cm.request_header->redirect = cm.result; cm.urlredirect = true; } else { action_return = false; } break; case SB_FUNC_SETGOMITM: if (cm.ismitmcandidate && !cm.nomitm) { cm.gomitm = true; } else { action_return = false; } break; case SB_FUNC_SETNOMITM: cm.nomitm = true; cm.gomitm = false; cm.automitm = false; break; case SB_FUNC_SETAUTOMITM: if (!cm.nomitm) cm.automitm = true; break; case SB_FUNC_UNSETAUTOMITM: cm.automitm = false; break; case SB_FUNC_SETADDHEADER: cm.headeradded = true; cm.request_header->addHeader(cm.result); break; case SB_FUNC_SETMODHEADER: cm.headermodified = true; break; case SB_FUNC_SETNOCHECKCERT: cm.nocheckcert = true; break; case SB_FUNC_SETSEARCHTERM: if (cm.result.size() > 0) { cm.isSearch = true; cm.search_words = cm.result.sort_search(); cm.search_terms = cm.result; cm.search_terms.swapChar('+', ' '); }; break; case SB_FUNC_SETGODIRECT: cm.isdirect = true; break; case SB_FUNC_SETDONE: cm.isdone = true; break; case SB_FUNC_SETNOLOG: cm.nolog = true; break; case SB_FUNC_SETALERT: cm.alert = true; break; case SB_FUNC_SETGROUP: action_return = false; if (cm.result.size() > 0) { int g = cm.result.toInteger(); if (g > 0 && g <= o.filter.numfg) { cm.filtergroup = --g; cm.authrec->group_source = i->list_name; action_return = true; } }; break; case SB_FUNC_UNSETVIRUSCHECK: cm.noviruscheck = true; break; case SB_FUNC_UNSETBYPASS: cm.isbypass = false; cm.iscookiebypass = false; cm.isscanbypass = false; cm.isvirusbypass = false; cm.isexception = false; break; case SB_FUNC_UNSETBYPASSALLOW: if (cm.isbypassallowed) cm.isbypassallowed = false; else action_return = false; break; case SB_FUNC_UNSETINFECTIONBYPASSALLOW: if (cm.isinfectionbypassallowed) cm.isinfectionbypassallowed = false; else action_return = false; break; case SB_FUNC_SETTRUE: break; case SB_FUNC_SETFALSE: action_return = false; break; } // DEBUG_story("SB-ACTION: ", F->name, " : ", F->getBIFunct(i->action_id), " ", (action_return? "true" : "false" )); } else { // is SB defined function if (i->action_id > 0) { action_return = runFunct(i->action_id, cm); E2LOGGER_storytrace("SB:Resuming ", F->name, " in ", F->file_name); } } cm.isReturn = action_return; if (i->return_after_action) break; if (i->return_after_action_is_true && action_return) break; } E2LOGGER_storytrace("SB:Exiting ", F->getName(), " returned ", (action_return ? "true" : "false")); return action_return; } bool StoryBoard::setEntry(unsigned int index, String fname) { entrys[index] = getFunctID(fname); if (entrys[index] > 0) { return true; } return false; }; bool StoryBoard::runFunctEntry(unsigned int index, NaughtyFilter &cm) { DEBUG_trace(""); cm.isdone = false; // only has logical scope for a single call if (entrys[index] > 0) return runFunct(entrys[index], cm); else return false; }; std::deque StoryBoard::deep_urls(String &urld, NaughtyFilter &cm) { std::deque temp; String durl = urld; // Google web site translate checking // Format is domain with '.' replaced with '-' and '-' with '--' ending with .translate.goo // so www.merriam-webster.com -> www-merriam--webster-com.translate.goog if (cm.urldomain.endsWith(".translate.goog")) { // E2LOGGER_info("Found translate.goog ", durl ); String tem_dom = cm.urldomain.before("."); String new_url; while (tem_dom.contains("-")) { new_url += tem_dom.before("-"); tem_dom = tem_dom.after("-"); if (tem_dom.startsWith("-")) { // is a '--' so replace with '-' new_url += "-"; tem_dom = tem_dom.after("-"); } else { new_url += "."; // is a '-' so replace with '.' } // E2LOGGER_info(" GT embedd url is ", new_url); } new_url += tem_dom; // E2LOGGER_info(" GT embedd url is ", new_url); if (!cm.issiteonly) { //replace url part if needed new_url += "/"; new_url += urld.after("/"); } // E2LOGGER_info(" GT embedd url is ", new_url); // E2LOGGER_info(" GT tem_dom is ", tem_dom); url_rec t; t.baseurl = new_url; t.fullurl = new_url; t.urldomain = t.baseurl.getHostname(); if (t.baseurl == t.urldomain) t.is_siteonly = true; if (cm.isIPHostnameStrip(t.urldomain)) t.site_is_ip = true; temp.push_back(t); } while (durl.contains(":")) { durl = durl.after(":"); if (!durl.contains(".")) break; while (durl.startsWith(":'") || durl.startsWith("/")) { durl.lop(); } if (durl.size() > 5) { url_rec t; t.baseurl = durl; t.baseurl.removePTP(); t.fullurl = durl; if (durl.startsWith("http:") || durl.startsWith("https:")) durl = durl.after(":"); t.urldomain = t.baseurl.getHostname(); if (t.baseurl == t.urldomain) t.is_siteonly = true; if (cm.isIPHostnameStrip(t.urldomain)) t.site_is_ip = true; temp.push_back(t); } else { break; } } return temp; } // reverse DNS lookup on IP. be aware that this can return multiple results, unlike a standard lookup. std::deque StoryBoard::ipToHostname(NaughtyFilter &cm) { std::deque result; const char *ip = cm.urldomain.c_str(); String urlp = cm.urld.after("/"); //struct in_addr address, **addrptr; struct in_addr address; if (inet_aton(ip, &address)) { // convert to in_addr struct hostent *answer; answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET); if (answer) { // sucess in reverse dns url_rec t; t.urldomain = answer->h_name; t.baseurl = t.urldomain + "/" + urlp; t.fullurl = "http://" + t.baseurl; result.push_back(t); //for (addrptr = (struct in_addr **)answer->h_addr_list; *addrptr; addrptr++) { // result->push_back(String(inet_ntoa(**addrptr))); //} } } return result; } bool StoryBoard::has_reverse_hosts(std::deque &urec, NaughtyFilter &cm) { if (!(cm.isiphost && o.story.reverse_lookups)) return false; if (!cm.reverse_checked) { cm.reversedURLs = ipToHostname(cm); cm.reverse_checked = true; } if (cm.reversedURLs.size() > 0) { urec = cm.reversedURLs; return true; } return false; } void StoryBoard::update_messages(NaughtyFilter &cm, ListMeta::list_result &res) { cm.lastcategory = res.category; cm.whatIsNaughtyCategories = res.category; cm.message_no = res.mess_no; cm.log_message_no = res.log_mess_no; cm.lastmatch = res.match; cm.result = res.result; if (res.anon_log) { cm.anon_user = true; cm.anon_url = true; } } e2guardian-5.5.8r/src/StoryBoard.hpp000066400000000000000000000047731477372360500173550ustar00rootroot00000000000000// StoryBoard class // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_SB #define __HPP_SB // INLCUDES #include #include #include #include #include "String.hpp" #include "RegExp.hpp" #include "SBFunction.hpp" #include "ListMeta.hpp" #include "UrlRec.hpp" // #include "NaughtyFilter.hpp" //#ifndef __HPP_SB // Entry point codes for pre-auth.story #define ENT_STORYA_PRE_AUTH 1 #define ENT_STORYA_PRE_AUTH_THTTPS 2 #define ENT_STORYA_PRE_AUTH_ICAP 3 // auth plugin entry point codes #define ENT_STORYA_AUTH_IP 11 #define ENT_STORYA_AUTH_PORT 12 #define ENT_STORYA_AUTH_BASIC_PROXY 13 #define ENT_STORYA_AUTH_IDENT 14 #define ENT_STORYA_AUTH_HEADER 15 #define ENT_STORYA_AUTH_NTLM_PROXY 16 #define ENT_STORYA_AUTH_DIGEST_PROXY 17 #define ENT_STORYA_AUTH_ICAP 18 #define ENT_STORYA_AUTH_PF_BASIC 19 // Entry point codes for storyfn.story #define ENT_STORYB_PROXY_REQUEST 1 #define ENT_STORYB_PROXY_RESPONSE 2 #define ENT_STORYB_THTTPS_REQUEST 3 #define ENT_STORYB_ICAP_REQMOD 4 #define ENT_STORYB_ICAP_RESMOD 5 #define ENT_STORYB_LOG_CHECK 6 // download manager plugin entry point codes #define ENT_STORYB_DM_TRICKLE 11 #define ENT_STORYB_DM_FANCY 12 #define ENT_STORY_MAX_SIZE 20 class NaughtyFilter; // DECLARATIONS class StoryBoard { private: unsigned int entrys[ENT_STORY_MAX_SIZE]; public: int items; int fnt_cnt; ListMeta* LMeta; std::vector funct_vec; StoryBoard(); ~StoryBoard(); void reset(); bool readFile(const char *filename, ListMeta & LMeta, bool is_top = true); unsigned int getFunctID(String &fname); bool runFunct(String &fname, NaughtyFilter &cm); bool runFunct(unsigned int fID, NaughtyFilter &cm); //bool runFunctEntry1(NaughtyFilter &cm); //bool runFunctEntry2(NaughtyFilter &cm); bool runFunctEntry(unsigned int index, NaughtyFilter &cm); bool setEntry(unsigned int index, String fname); //bool setEntry1(String fname); //bool setEntry2(String fname); std::deque deep_urls(String & url, NaughtyFilter &cm); std::deque ipToHostname(NaughtyFilter &cm); bool has_reverse_hosts(std::deque & urec, NaughtyFilter &cm); void update_messages(NaughtyFilter &cm, ListMeta::list_result &res); }; //#endif #define __HPP_SB #endif e2guardian-5.5.8r/src/String.cpp000066400000000000000000000453041477372360500165210ustar00rootroot00000000000000// String - guess what: it's a string class! // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "String.hpp" #include "md5.hpp" #include "Logger.hpp" #if defined(__GNUC__) && __GNUC__ < 3 && __GNUC_MINOR__ < 96 #warning "Using strstream instead of sstream" #include #else #include #endif #include #include #include extern thread_local std::string thread_id; // construct string representations of ints/longs #if defined(__GNUC__) && __GNUC__ < 3 && __GNUC_MINOR__ < 96 String::String(const int num) { std::ostrstream buf; buf << num << std::ends; *this = buf.str(); // with side effect: it calls buf.freeze() } String::String(const long num) { std::ostrstream buf; buf << num << std::ends; *this = buf.str(); } String::String(const unsigned int num) { std::ostrstream buf; buf << num << std::ends; *this = buf.str(); } String::String(const long unsigned int num) { std::ostrstream buf; buf << num << std::ends; *this = buf.str(); } #ifndef OFFT_COLLISION // If large file support is not enabled (and possibly even if it is), // the type of off_t may be a typedef of a type for which we already // have a constructor. In that case, don't define one which takes an // off_t, or we get compiler errors. String::String(const off_t num) { std::ostrstream buf; buf << num << std::ends; *this = buf.str(); } #endif #else String::String(const int num) { std::stringstream buf; buf << num << std::ends; // std::string can contain a NULL byte within the counted length // - this happens here. Add a byte to the length when allocating // the buffer it's going to go into (to account for the appended // NULL), but subtract one when updating our idea of what the string // length is, since it counts the NULL byte in the stringstream's // own buffer. int l = buf.str().length(); char *data = new char[l + 1]; buf >> data; *this = data; delete[] data; } String::String(const long num) { std::stringstream buf; buf << num << std::ends; int l = buf.str().length(); char *data = new char[l + 1]; buf >> data; *this = data; delete[] data; } String::String(const unsigned int num) { std::stringstream buf; buf << num << std::ends; int l = buf.str().length(); char *data = new char[l + 1]; buf >> data; *this = data; delete[] data; } String::String(const long unsigned num) { std::stringstream buf; buf << num << std::ends; int l = buf.str().length(); char *data = new char[l + 1]; buf >> data; *this = data; delete[] data; } String::String(const bool flag) { if (flag) { char buff[2] = "1"; *this = buff; } else { char buff[2] = "0"; *this = buff; } } #ifndef OFFT_COLLISION // If large file support is not enabled (and possibly even if it is), // the type of off_t may be a typedef of a type for which we already // have a constructor. In that case, don't define one which takes an // off_t, or we get compiler errors. String::String(const off_t num) { std::stringstream buf; buf << num << std::ends; int l = buf.str().length(); char *data = new char[l + 1]; buf >> data; *this = data; delete[] data; } #endif #endif void String::replaceall(const char *what, const char *with) { std::string::size_type pos = 0; size_t whatlen = strlen(what); size_t withlen = strlen(with); while ((pos = this->find(what, pos)) != std::string::npos) { // replace charactrs in original string this->replace(pos, whatlen, with); // increment search position pos += withlen; } } // string-to-off_t conversion // This is horrible, horrible code, but the best I can come up with // which will work in both 32 and 64-bit file offset modes. :( off_t String::toOffset() { if (this->length() == 0) return 0; off_t t = 0; this->removeWhiteSpace(); #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64) sscanf(this->c_str(), "%lld", &t); #else sscanf(this->c_str(), "%ld", &t); #endif return t; } // string-to-integer conversion int String::toInteger() { if (this->length() == 0) { return 0; } return (atoi(this->c_str())); } // string-to-long-int conversion long int String::toLong() { if (this->length() == 0) { return 0; } return (atol(this->c_str())); } // return integer from hex string long int String::hexToInteger() { int n = 0; // position in string int m = 0; // position in digit[] to shift int count; // loop index long int intValue = 0; // integer value of hex string int digit[15]; // hold values to convert while (n < 14) { if ((*this)[n] == '\0') break; if ((*this)[n] >= '0' && (*this)[n] <= '9') digit[n] = (*this)[n] & 0x0f; else if (((*this)[n] >= 'a' && (*this)[n] <= 'f') || ((*this)[n] >= 'A' && (*this)[n] <= 'F')) digit[n] = ((*this)[n] & 0x0f) + 9; else break; n++; } count = n; m = n - 1; n = 0; while (n < count) { // digit[n] is value of hex digit at position n // (m << 2) is the number of positions to shift // OR the bits into return value intValue = intValue | (digit[n] << (m << 2)); m--; n++; } return (intValue); } // case conversions void String::toLower() { unsigned int l = this->length(); char *c = new char[l + 1]; const char *d = this->c_str(); for (unsigned int i = 0; i < l; i++) { c[i] = tolower(d[i]); } *this = String(c, l); delete[] c; } void String::toUpper() { unsigned int l = this->length(); char *c = new char[l + 1]; const char *d = this->c_str(); for (unsigned int i = 0; i < l; i++) { c[i] = toupper(d[i]); } *this = String(c, l); delete[] c; } // swap chars void String::swapChar(char old, char newc) { unsigned int l = this->length(); char *c = new char[l + 1]; const char *d = this->c_str(); for (unsigned int i = 0; i < l; i++) { if (d[i] == old) { c[i] = newc; } else { c[i] = d[i]; } } *this = String(c, l); delete [] c; } void String::removeChar(char old) { unsigned int l = this->length(); char *c = new char[l + 1]; unsigned int j = 0; const char *d = this->c_str(); for (unsigned int i = 0; i < l; i++) { if (d[i] != old) { c[j++] = d[i]; } } c[j] = 0; *this = String(c); delete [] c; } void String::baseDir() { size_t fnsize; if ((fnsize = this->find_last_of("/")) > 0) *this = this->subString(0,++fnsize); } void String::fullPath(String &base_dir) { if(!this->startsWith("/")) { String temp(base_dir); temp += *this; *this = temp; } } // decode %xx to individual characters (checkme: i'm sure this is duplicated elsewhere...) void String::hexDecode() //TODO: make this more efficient - use similar login to hex2integer { if (this->length() < 3) return; char *temp = new char[this->length() + 1]; const char *t = this->c_str(); unsigned char c; unsigned char c1; unsigned char c2; unsigned char c3; char hexval[5] = "0x"; // Initializes a "hexadecimal string" hexval[4] = '\0'; char *ptr; // pointer required by strtol ptr = NULL; unsigned int j = 0; unsigned int end = this->length() - 2; unsigned int i, k; for (i = 0; i < end;) { c1 = t[i]; c2 = t[i + 1]; c3 = t[i + 2]; if (c1 == '%' && (((c2 >= '0') && (c2 <= '9')) || ((c2 >= 'a') && (c2 <= 'f')) || ((c2 >= 'A') && (c2 <= 'F'))) && (((c3 >= '0') && (c3 <= '9')) || ((c3 >= 'a') && (c3 <= 'f')) || ((c3 >= 'A') && (c3 <= 'F')))) { hexval[2] = c2; hexval[3] = c3; c = (unsigned char)strtol(hexval, &ptr, 0); i += 3; } else { c = c1; i++; } temp[j++] = c; } k = this->length(); for (; i < k; i++) { temp[j++] = t[i]; // copy last 2 bytes if any// } temp[j] = '\0'; (*this) = String(temp, j); delete[] temp; } // does this string start with the given text? bool String::startsWith(const String &s) const { return (strncmp(this->c_str(), s.c_str(), s.length()) == 0); } // does this string end with the given text? bool String::endsWith(const String &s) const { if (s.length() > this->length()) { return false; } if (!strncmp((this->c_str() + this->length() - s.length()), s.c_str(), s.length())) { return true; } return false; } // does this string start with the given text after conversion to lowercase? // (pass the search string in in lowercase; only the text being searched // is converted) bool String::startsWithLower(const String &s) const { if (s.length() > this->length()) { return false; } for (unsigned int i = 0; i < s.length(); i++) { if (tolower((*this)[i]) != s[i]) return false; } return true; } // find the position of the given substring within the string int String::indexOf(const char *s) const { size_type i = this->find(s); if (i != std::string::npos) return i; return -1; } // does this string contain given substring? bool String::contains(const char *s) const { if (this->length() >= strlen(s)) { if (indexOf(s) != -1) { return true; } } return false; } // grab the part of the string that follows the first occurrence of given text String String::after(const char *bs) const { if (this->length() < strlen(bs)) return ""; size_type i = this->find(bs); if (i == std::string::npos) return ""; return this->substr(i + strlen(bs)); } // grab the part of the string that precedes the first occurrence of given text String String::before(const char *bs) const { if (this->length() < strlen(bs)) return ""; size_type i = this->find(bs); if (i == std::string::npos) return ""; return this->substr(0, i); } bool String::headerVal() { *this = this->after(":"); this->removeWhiteSpace(); if (this->length() > 0) return true; else return false; }; // remove characters from end/beginning void String::chop() { if (this->length() > 0) *this = this->substr(0, this->length() - 1); } void String::lop() { if (this->length() > 0) *this = this->substr(1); } // remove leading & trailing whitespace void String::removeWhiteSpace() { size_type start = this->find_first_not_of(" \t\r\n"); if (start == std::string::npos) start = 0; size_type end = this->find_last_not_of(" \t\r\n"); if (end == std::string::npos) end = this->length() - 1; *this = this->substr(start, (end - start) + 1); } void String::removePunctuation() { std::string temp; bool lastspace = false; unsigned char t; unsigned int l = this->length(); for (unsigned int i = 0; i < l; i++) { t = (*this)[i]; if (t < '/' || t == ':' || t == ';' || t == '=' || t == '?' || t == '@' || (t > 90 && t < 97)) { t = 32; // convert all whitespace and most punctuation marks to a space } if (t == 32) { if (lastspace) continue; lastspace = true; } else { lastspace = false; } temp += t; continue; } *this = temp; } // remove protocol specifier void String::removePTP() { if (this->startsWith("http://") || this->startsWith("https://") || this->startsWith("ftp://")) { *this = this->after("://"); } } // get hostname from string as url String String::getHostname() { String hostname; hostname = this->substr(0); if (hostname.contains("://")) hostname = hostname.after("://"); if (hostname.contains("/")) hostname = hostname.before("/"); if (hostname.contains("@")) // Contains a username:password combo hostname = hostname.after("@"); return hostname; } // limit string to given length int String::limitLength(unsigned int l) { *this = this->substr(0, l); return this->length(); } // remove repeated occurrences of given character void String::removeMultiChar(unsigned char c) { std::string temp; unsigned char t; bool wasslash = false; unsigned int l = this->length(); for (unsigned int i = 0; i < l; i++) { t = (*this)[i]; if (t != c) { // we didn't find the character - copy what we did find, // and clear repetition flag temp += t; wasslash = false; continue; } // we found the character if (wasslash) { // we found it repeated - don't copy it again continue; } // we found the character, without repetition flag set // - copy only first occurrence & set repetition flag wasslash = true; temp += t; } *this = temp; } // tidy up slashes, trailing dots, etc. in file paths void String::realPath() { if (this->length() < 3) { return; } char *temp = new char[this->length() + 1]; unsigned char b, c, d; unsigned int offset = 0, l = this->length(); for (unsigned int i = 0; i < l; i++) { b = (*this)[i]; if (b == '/') { if ((*this)[i + 1] == '/') { // ignore multiple slashes continue; } } if (b == '.') { c = (*this)[i + 1]; if (c == '\0' || c == '/') { continue; // ignore just dot } if (c == '.') { d = (*this)[i + 2]; if (d == '\0' || d == '/' || d == '\\') { if (offset > 0) { offset--; } while (offset > 0) { if (temp[--offset] == '/') { break; } if (temp[offset] == '\\') { break; } } i++; continue; } } } temp[offset++] = b; } temp[offset] = '\0'; *this = temp; delete[] temp; } // * // * // * Hashing functions // * // * String String::md5(const char *salt) { String newValue(*this); newValue += salt; return newValue.md5(); } String String::md5() { char *md5array = new char[16]; char *buf = new char[16]; int i; String ret; md5_buffer(this->c_str(), (size_t) this->length(), md5array); for (i = 0; i < 16; i++) { sprintf(buf, "%02X", (unsigned char)(md5array[i])); ret += buf; } delete[] md5array; delete[] buf; return ret; } String String::sort_search() { this->toLower(); int ln = this->length(); if (ln < 3) { return (*this); } //char *temp = new (std::nothrow) char[ln + 1]; //if (temp == NULL) { // E2LOGGER_error("Unable to create temp char[%d] in sort_search", ln); // return (*this); //} // strcpy(temp, (this)->c_str()); std::unique_ptr temp(new char[ln + 1]); std::strcpy(temp.get(), (this)->c_str()); int i = 0; int c = 0; // count '+' signs - gives number of words - 1 while (i < ln) { if (temp[i++] == '+') c++; }; if (c == 0) { // only one word - nothing to do //delete [] temp; return (*this); }; // split into words and index char *p[c + 1]; i = 0; int j = 0; char *ind = temp.get(); while (i <= c) { p[i] = ind + j; while ((j < ln) & !(ind[j] == '+')) { ++j; }; if ( j < ln) ind[j++] = 0; i++; }; #ifdef DEBUG_LOW int k = 0; while (k <= c) { // std::cerr << thread_id << "Search word " << k << " is " << p[k] << std::endl; DEBUG_debug("Search word ", k, " is ", p[k]); k++; } #endif // sort char *t; bool swap = true; while (swap) { swap = false; j = 0; while (j < c) { if (strcmp(p[j], p[j + 1]) > 0) { swap = true; t = p[j]; p[j] = p[j + 1]; p[j + 1] = t; } j++; }; }; #ifdef DEBUG_LOW k = 0; while (k <= c) { //std::cerr << thread_id << "Search word after sort" << k << " is " << p[k] << std::endl; DEBUG_debug("Search word after sort", k, " is ", p[k]); k++; } #endif String ret(p[0]); j = 1; while (j <= c) { if (ret.empty()) ret = p[j++]; else ret = ret + "+" + p[j++]; } DEBUG_debug("Search words after sort are ", ret); // delete [] temp; return ret; }; String String::CN() { if (this->length() < 64) return (*this); String ret = *this; while (ret.length() > 62) { ret = ret.after("."); } ret = "*." + ret; return ret; } //bool String::isNull() { // if (*this == NULL) // return true; // return false; //} String String::anonimise() { std::string temp; unsigned char t; unsigned int l = this->length(); for (unsigned int i = 0; i < l; i++) { t = (*this)[i]; if (!(t < '/' || t == ':' || t == ';' || t == '=' || t == '?' || t == '@' || (t > 90 && t < 97))) { t = 'x'; // convert all alphanumeric to 'x' } temp += t; continue; } String ts = temp; return ts; } bool String::isIp() { // Regex expression for validating IPv4 RegExp ipv4; ipv4.comp("(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"); RegResult rs; if(ipv4.match((*this).c_str(), rs)) return true; return false; // Regex expression for validating IPv6 // regex ipv6("((([0-9a-fA-F]){1,4})\\:){7}([0-9a-fA-F]){1,4}"); } bool String::is_valid_domain() { if (this->empty()) return false; unsigned int l = this->length(); if(l < 4) return false; // too short - can not possibly be a valid domain! const char *d = this->c_str(); if ((d[0] == '-')||(d[l - 1] == '-')) return false; // hyphens are not allowed at start or end of domain for (unsigned int i = 0; i < l; i++) { if (!((d[i] >= '0' && d[i] <= '9') || (d[i] >= 'A' && d[i] <= 'Z') || (d[i] >= 'a' && d[i] <= 'z') || (d[i] == '-')||(d[i] == '.'))) { return false; } } return true; } e2guardian-5.5.8r/src/String.hpp000066400000000000000000000105531477372360500165240ustar00rootroot00000000000000// String - guess what: it's a string class! Cut down version of Java string // class interface // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_STRING #define __HPP_STRING // INCLUDES #include #include #include #include "RegExp.hpp" // DECLARATIONS class String : public std::string { public: String() : std::string(){}; //~String(); // constructor from c-string String(const char *bs) : std::string(bs){}; // construct from c++ string String(const std::string &s) : std::string(s){}; // copy constructor String(const String &s) : std::string(s){}; // construct string represenations of numbers String(const int num); String(const long num); String(const long unsigned num); String(const unsigned int num); String(const bool flag); #ifndef OFFT_COLLISION // If large file support is not enabled (and possibly even if it is), // the type of off_t may be a typedef of a type for which we already // have a constructor. In that case, don't define one which takes an // off_t, or we get compiler errors. String(const off_t num); #endif // substring constructors String(const char *bs, int len) : std::string(bs, len){}; String(const char *bs, int start, int len) : std::string(bs + start, len){}; // operators String operator+(const int &i) { return (*this) + String(i); }; // return c-string const char *toCharArray() const { return (this->c_str()); }; // return substring of length l from start String subString(int start, int l) const { return this->substr(start, l); }; // convert to integer/long integer int toInteger(); long int toLong(); off_t toOffset(); // return integer from hex string long int hexToInteger(); // case conversions void toLower(); void toUpper(); // Swap characters void swapChar(char old, char newc); // decode %xx to characters (checkme: duplicate code?) void hexDecode(); // does the string start/end with this text? bool startsWith(const String &s) const; bool endsWith(const String &s) const; // does this string start with the given text after conversion to lowercase? // (pass the search string in in lowercase; only the text being searched // is converted) bool startsWithLower(const String &s) const; // return offset of substring s within the string int indexOf(const char *s) const; // does it contain this text? bool contains(const char *s) const; // chop to base dir (for file paths) void baseDir(); // convert relative path filename to fully pathed void fullPath(String &base_dir); // index operator mark 2 unsigned char charAt(int index) { return (*this)[index]; }; // return string following first occurrence of bs String after(const char *bs) const; // return string preceding first occurrence of bs String before(const char *bs) const; // search & replace void replaceall(const char *what, const char *with); // return header value (after ':' and any leading whitespace) - assumes header finishes with '\r' bool headerVal(); // remove character from end/beginning void chop(); void lop(); // remove leading & trailing whitespace void removeWhiteSpace(); // remove punctuation void removePunctuation(); // remove protocol prefix (e.g. http://) void removePTP(); // get hostname from string as url String getHostname(); // truncate to given length int limitLength(unsigned int l); // remove all occurrences of char from String void removeChar(char c); // remove repeated occurrences of this character void removeMultiChar(unsigned char c); // clean up slashes, trailing dots, etc. in file paths void realPath(); // generate MD5 hash of string (using given salt) String md5(); String md5(const char *salt); // Sort search words String sort_search(); // get CN name that is oK for cert from url String CN(); // bool isNull(); // redact string/url String anonimise(); bool isIp(); bool is_valid_domain(); }; #endif e2guardian-5.5.8r/src/SysV.cpp000066400000000000000000000130121477372360500161460ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "SysV.hpp" #include #include #include #include #include #include #include #include #include // GLOBALS extern OptionContainer o; // DECLARATIONS // get the PID from the given file, or by looking for "e2guardian" process by name - returns -1 if process not running pid_t getpid(std::string pidfile); // read process number from file (straight file read, no is-it-running check) pid_t getpidfromfile(std::string pidfile); // get PID from processes matching this command name (UNIMPLEMENTED) pid_t getpidfromcommand(const char *command); // confirm pid corresponds to a currently running process bool confirmname(pid_t p); // IMPLEMENTATION // grab the PID from the file & check it's running (returns -1 on failure) // (also checks process names if file method fails, but this is unimplemented) pid_t getpid(std::string pidfile) { pid_t p = getpidfromfile(pidfile); if (p > 1) { if (confirmname(p)) { // is that pid really E2 and running? return p; // it is so return it } } pid_t t = getpidfromcommand("e2guardian"); // pid file method failed // so try a search from the // command return t; // if it failed t will be -1 } // grab process number from file (no run check) pid_t getpidfromfile(std::string pidfile) { int handle = open(pidfile.c_str(), O_RDONLY); if (handle < 0) { // Unable to open the pid file. return -1; } char pidbuff[32]; int rc = read(handle, pidbuff, sizeof(pidbuff) - 1); if (rc < 1) { // pid file must be at least 1 byte long close(handle); return -1; } pidbuff[rc] = '\0'; close(handle); return atoi(pidbuff); // convert the string to a pid } // grab process number from processes matching the given command pid_t getpidfromcommand(const char *command) { return -1; // ******** NOT IMPLEMENTED YET ************ // only needed if the pid file gets deleted. //I KNOW HOW TO DO THIS IN A PORTABLE LINUX/BSD WAY //BUT I HAVE NOT HAD THE TIME TO ADD IT YET AS THIS //IS FUNCTIONAL ENOUGH TO WORK } // check the given PID is alive and running bool confirmname(pid_t p) { int rc = ::kill(p, 0); // just a test if (rc != 0) { if (errno == EPERM) { return true; // we got no perms to test it but it must be there } return false; // no process running at that pid } // ******** NOT FULLY IMPLEMENTED YET ************ //I KNOW HOW TO DO THIS IN A PORTABLE LINUX/BSD WAY //BUT I HAVE NOT HAD THE TIME TO ADD IT YET AS THIS //IS FUNCTIONAL ENOUGH TO WORK return true; } // kill process in the pidfile, optionally deleting the pidfile & URL cache/logger IPC sockets int sysv_kill(std::string pidfile, bool dounlink) { pid_t p = getpid(pidfile); if (p > 1) { int rc = ::kill(p, SIGTERM); if (rc == -1) { std::cerr << "Error trying to kill pid:" << p << std::endl; if (errno == EPERM) { std::cerr << "Permission denied." << std::endl; } return 1; } if (dounlink) { unlink(pidfile.c_str()); } return 0; } std::cerr << "No e2guardian process found." << std::endl; return 1; } // send HUP to process int sysv_hup(std::string pidfile) { pid_t p = getpid(pidfile); if (p > 1) { int rc = ::kill(p, SIGHUP); if (rc == -1) { std::cerr << "Error trying to hup pid:" << p << std::endl; if (errno == EPERM) { std::cerr << "Permission denied." << std::endl; } return 1; } return 0; } std::cerr << "No e2guardian process found." << std::endl; return 1; } // send USR1 to process int sysv_usr1(std::string pidfile) { pid_t p = getpid(pidfile); if (p > 1) { int rc = ::kill(p, SIGUSR1); if (rc == -1) { std::cerr << "Error trying to sig1 pid:" << p << std::endl; if (errno == EPERM) { std::cerr << "Permission denied." << std::endl; } return 1; } return 0; } std::cerr << "No e2guardian process found." << std::endl; return 1; } // show PID of running E2 process int sysv_showpid(std::string pidfile) { pid_t p = getpid(pidfile); if (p > 1) { std::cout << "Parent e2guardian pid:" << p << std::endl; return 0; } std::cerr << "No e2guardian process found." << std::endl; return 1; } // create a new pidfile int sysv_openpidfile(std::string pidfile) { unlink(pidfile.c_str()); return open(pidfile.c_str(), O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); } // write pid to file & close it int sysv_writepidfile(int pidfilefd, pid_t pid = 0) { if(pid == 0) pid = getpid(); char pidbuff[32]; sprintf(pidbuff, "%d", (int)pid); // Messy, but it works! int len = strlen(pidbuff) + 1; pidbuff[len - 1] = '\n'; int rc = write(pidfilefd, pidbuff, len); if (rc < len) { // failed to write close(pidfilefd); return 1; } close(pidfilefd); return 0; } // check process in pidfile is running bool sysv_amirunning(std::string pidfile) { if (getpid(pidfile) > 1) { return true; } return false; } e2guardian-5.5.8r/src/SysV.hpp000066400000000000000000000020631477372360500161570ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_SYSV #define __HPP_SYSV // INCLUDES #include "OptionContainer.hpp" #include #include // DECLARATIONS // Kill the process specified in the given pidfile, optionally deleting the pidfile while we're at it, // along with the UNIX domain sockets for the old logger & url cache int sysv_kill(std::string pidfile, bool dounlink = true); // show PID of running E2 process int sysv_showpid(std::string pidfile); // check that the process in the pidfile is running bool sysv_amirunning(std::string pidfile); // delete any existing file with this name, and create a new one with relevant mode flags int sysv_openpidfile(std::string pidfile); // write our pid to the given file & close it int sysv_writepidfile(int pidfilefd,pid_t master_pid); // send HUP or USR1 to the process in the pidfile int sysv_hup(std::string pidfile); int sysv_usr1(std::string pidfile); #endif e2guardian-5.5.8r/src/UDSocket.cpp000066400000000000000000000062501477372360500167310ustar00rootroot00000000000000// UDSocket class - implements BaseSocket for UNIX domain sockets // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "UDSocket.hpp" #include "Logger.hpp" #include #include #include #include #include #include #include #include #include // necessary for calculating size of sockaddr_un in a portable manner #ifndef offsetof #define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER) #endif // IMPLEMENTATION // constructor - creates default UNIX domain socket & clears address structs UDSocket::UDSocket() { sck = socket(PF_UNIX, SOCK_STREAM, 0); infds[0].fd = sck; outfds[0].fd = sck; memset(&my_adr, 0, sizeof my_adr); memset(&peer_adr, 0, sizeof peer_adr); my_adr.sun_family = AF_UNIX; strcpy(my_adr.sun_path, ""); peer_adr.sun_family = AF_UNIX; strcpy(peer_adr.sun_path, ""); my_adr_length = 0; } // create socket from pre-existing FD (address structs will be invalid!) UDSocket::UDSocket(int fd) : BaseSocket(fd) { memset(&my_adr, 0, sizeof my_adr); memset(&peer_adr, 0, sizeof peer_adr); my_adr.sun_family = AF_UNIX; strcpy(my_adr.sun_path, ""); peer_adr.sun_family = AF_UNIX; strcpy(peer_adr.sun_path, ""); my_adr_length = 0; } // create socket from given FD & local address (checkme: is it local or remote that gets passed in here?) UDSocket::UDSocket(int newfd, struct sockaddr_un myadr) : BaseSocket(newfd) { my_adr = myadr; my_adr_length = sizeof(my_adr.sun_family) + strlen(my_adr.sun_path); } // close socket & clear address structs void UDSocket::reset() { this->baseReset(); sck = socket(PF_UNIX, SOCK_STREAM, 0); infds[0].fd = sck; outfds[0].fd = sck; memset(&my_adr, 0, sizeof my_adr); memset(&peer_adr, 0, sizeof peer_adr); my_adr.sun_family = AF_UNIX; strcpy(my_adr.sun_path, ""); peer_adr.sun_family = AF_UNIX; strcpy(peer_adr.sun_path, ""); my_adr_length = 0; } // accept incoming connection & return new UDSocket UDSocket *UDSocket::accept() { my_adr_length = sizeof(my_adr.sun_family) + strlen(my_adr.sun_path); int newfd = this->baseAccept((struct sockaddr *)&my_adr, &my_adr_length); UDSocket *s = new UDSocket(newfd, my_adr); return s; } // connect to given server (following default constructor) int UDSocket::connect(const char *path) { if (strlen(path) > 108) return -1; DEBUG_network("uds connect:", path); strcpy(my_adr.sun_path, path); my_adr_length = offsetof(struct sockaddr_un, sun_path) + strlen(path); return ::connect(sck, (struct sockaddr *)&my_adr, my_adr_length); } // bind socket to given path int UDSocket::bind(const char *path) { // to bind a unix domain socket to a path if (strlen(path) > 108) return -1; unlink(path); strcpy(my_adr.sun_path, path); my_adr_length = offsetof(struct sockaddr_un, sun_path) + strlen(path); return ::bind(sck, (struct sockaddr *)&my_adr, my_adr_length); } e2guardian-5.5.8r/src/UDSocket.hpp000066400000000000000000000022671477372360500167420ustar00rootroot00000000000000// UDSocket class - implements BaseSocket for UNIX domain sockets // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_UDSOCKET #define __HPP_UDSOCKET // INCLUDES #include "BaseSocket.hpp" // DECLARATIONS class UDSocket : public BaseSocket { public: // create default UNIX domain socket & clear address structs UDSocket(); // create socket from pre-existing FD (address structs will be empty!) UDSocket(int fd); // create socket from FD & local path (checkme: is it actually local that gets passed?) UDSocket(int newfd, struct sockaddr_un myadr); // connect socket to given server (following default constructor) int connect(const char *path); // bind socket to given path (for creating servers) int bind(const char *path); // accept incoming connection & return new UDSocket UDSocket *accept(); // close connection & clear address structs void reset(); private: // local & remote address structs struct sockaddr_un my_adr; struct sockaddr_un peer_adr; socklen_t my_adr_length; }; #endif e2guardian-5.5.8r/src/UdpSocket.cpp000066400000000000000000000174131477372360500171540ustar00rootroot00000000000000// UdpSocket class - implements BaseSocket for INET domain sockets // Note: This class is only tested for UDP send at present other functions (such as listen, read etc may not currently work!!! PIP // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "UdpSocket.hpp" #include "Logger.hpp" #include #include #include #include #include #include #include #include #include #include "String.hpp" extern bool reloadconfig; // IMPLEMENTATION // // destructor UdpSocket::~UdpSocket() { close(); } // constructor - create an INET socket & clear address structures UdpSocket::UdpSocket() { sck = socket(AF_INET, SOCK_DGRAM, 0); if (sck < 0) { s_errno = errno; } else { memset(&my_adr, 0, sizeof my_adr); memset(&peer_adr, 0, sizeof peer_adr); my_adr.sin_family = AF_INET; peer_adr.sin_family = AF_INET; peer_adr_length = sizeof(struct sockaddr_in); int f = 1; if (sck > 0) setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, &f, sizeof(int)); my_port = 0; } } // create socket from pre-existing FD (address structs will be invalid!) UdpSocket::UdpSocket(int fd) : BaseSocket(fd) { memset(&my_adr, 0, sizeof my_adr); memset(&peer_adr, 0, sizeof peer_adr); my_adr.sin_family = AF_INET; peer_adr.sin_family = AF_INET; peer_adr_length = sizeof(struct sockaddr_in); int f = 1; int res = setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, &f, sizeof(int)); if (res < 0) s_errno = errno; my_port = 0; } // create socket from pre-existing FD, storing local & remote IPs UdpSocket::UdpSocket(int newfd, struct sockaddr_in myip, struct sockaddr_in peerip) : BaseSocket(newfd) { memset(&my_adr, 0, sizeof my_adr); // *** memset(&peer_adr, 0, sizeof peer_adr); // *** my_adr.sin_family = AF_INET; // *** Fix suggested by peer_adr.sin_family = AF_INET; // *** Christopher Weimann my_adr = myip; peer_adr = peerip; peer_adr_length = sizeof(struct sockaddr_in); int f = 1; int res = setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, &f, sizeof(int)); if (res < 0) s_errno = errno; my_port = 0; } // find the ip to which the client has connected std::string UdpSocket::getLocalIP() { char res[INET_ADDRSTRLEN]; return inet_ntop(AF_INET,&my_adr.sin_addr, res, sizeof(res)); } // find the ip of the client connecting to us std::string UdpSocket::getPeerIP() { if (!client_addr.empty()) { return client_addr; } else { char res[INET_ADDRSTRLEN]; return inet_ntop(AF_INET, &peer_adr.sin_addr, res, sizeof(res)); } } // find the port of the client connecting to us int UdpSocket::getPeerSourcePort() { return ntohs(peer_adr.sin_port); } int UdpSocket::getPort() { return my_port; } void UdpSocket::setPort(int port) { my_port = port; } // return the address of the client connecting to us unsigned long int UdpSocket::getPeerSourceAddr() { return (unsigned long int) ntohl(peer_adr.sin_addr.s_addr); } // close connection & wipe address structs void UdpSocket::reset() { this->baseReset(); sck = socket(AF_INET, SOCK_DGRAM, 0); if (sck < 0) { s_errno = errno; return; } memset(&my_adr, 0, sizeof my_adr); memset(&peer_adr, 0, sizeof peer_adr); my_adr.sin_family = AF_INET; peer_adr.sin_family = AF_INET; peer_adr_length = sizeof(struct sockaddr_in); infds[0].fd = sck; outfds[0].fd = sck; } // connect to given IP & port (following default constructor) int UdpSocket::connect(const std::string &ip, int port) { reset(); // do it anyway as we need sck to be allocated if (sck < 0) // socket creation error { return -1; } bind(39000); // otherwise source port allocated on each send confusing destination server. f/w etc int len = sizeof my_adr; peer_adr.sin_port = htons(port); inet_aton(ip.c_str(), &peer_adr.sin_addr); my_port = port; peer_adr_length = sizeof peer_adr; int save_flags = fcntl(sck,F_GETFL); fcntl(sck, F_SETFL, save_flags | O_NONBLOCK); s_errno = 0; errno = 0; int ret = ::connect(sck, (struct sockaddr *) &peer_adr, len); if (ret < 0 && errno == EINPROGRESS) ret = 0; else s_errno = errno; if (ret == 0) { int rc = poll(outfds, 1, timeout); if (rc == 0) { timedout = true; ret = -1; } else if (rc < 0) { s_errno = errno; ret = -1; } else { int so_error; socklen_t len = sizeof so_error; getsockopt(sck, SOL_SOCKET, SO_ERROR, &so_error, &len); if (so_error != 0) { sockerr = true; s_errno = so_error; ret = -1; } else { ret = 0; } } } if (ret < 0) { close(); } else { save_flags = fcntl(sck,F_GETFL); fcntl(sck, F_SETFL, save_flags & ~O_NONBLOCK); } return ret; } // bind socket to given port int UdpSocket::bind(int port) { int len = sizeof my_adr; int i = 1; setsockopt(sck, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); my_adr.sin_port = htons(port); my_port = port; return ::bind(sck, (struct sockaddr *) &my_adr, len); } // bind socket to given port & IP int UdpSocket::bind(const std::string &ip, int port) { int len = sizeof my_adr; int i = 1; setsockopt(sck, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); my_adr.sin_port = htons(port); my_adr.sin_addr.s_addr = inet_addr(ip.c_str()); my_port = port; return ::bind(sck, (struct sockaddr *) &my_adr, len); } // accept incoming connections & return new UdpSocket UdpSocket *UdpSocket::accept() { peer_adr_length = sizeof(struct sockaddr_in); s_errno = 0; errno = 0; int newfd = ::accept(sck, (struct sockaddr *) &peer_adr, &peer_adr_length); if (newfd > 0) { UdpSocket *s = new UdpSocket(newfd, my_adr, peer_adr); s->setPort(my_port); return s; } else { s_errno = errno; return NULL; } } void UdpSocket::close() { BaseSocket::close(); } // write line to socket bool UdpSocket::writeString(std::string line) { int l = line.length(); return writeToSocket(line.c_str(), l, 0, timeout); } // write data to socket bool UdpSocket::writeToSocket(const char *buff, int len, unsigned int flags, int timeout) { if (len == 0) // nothing to write return true; if (true) { return BaseSocket::writeToSocket(buff, len, flags, timeout); } return true; } int UdpSocket::readFromSocket(char *buff, int len, unsigned int flags, int timeout, bool ret_part ) { if (len == 0) // nothing to read return 0; if (true) { return BaseSocket::readFromSocket(buff, len, flags, timeout, ret_part); } // first, return what's left from the previous buffer read, if anything int cnt = len; int tocopy = 0; if ((bufflen - buffstart) > 0) { DEBUG_network("Socket::readFromSocket: data already in buffer; bufflen: ", bufflen, " buffstart: ", buffstart ); tocopy = len; if ((bufflen - buffstart) < len) tocopy = bufflen - buffstart; memcpy(buff, buffer + buffstart, tocopy); cnt -= tocopy; buffstart += tocopy; buff += tocopy; if(ret_part) return tocopy; if (cnt == 0) return len; } return len; } bool UdpSocket::getIeof() { return ieof; } void UdpSocket::setClientAddr(std::string ip, int port) { client_addr = ip; client_port = port; } e2guardian-5.5.8r/src/UdpSocket.hpp000066400000000000000000000051541477372360500171600ustar00rootroot00000000000000// UdpSocket class - implements BaseSocket for INET domain sockets // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_UDPSOCKET #define __HPP_UDPSOCKET // INCLUDES #include "BaseSocket.hpp" #include "String.hpp" #include #include //#include "openssl/ssl.h" #include "String.hpp" // DECLARATIONS class UdpSocket : public BaseSocket { //friend class FDTunnel; public: // create INET socket & clear address structs UdpSocket(); // create socket using pre-existing FD (address structs will be empty!) UdpSocket(int fd); // create socket from pre-existing FD, storing given local & remote IPs UdpSocket(int newfd, struct sockaddr_in myip, struct sockaddr_in peerip); ~UdpSocket(); // connect to given IP & port (following default constructor) int connect(const std::string &ip, int port); // bind to given port int bind(int port); // bind to given IP & port, for machines with multiple NICs int bind(const std::string &ip, int port); // accept incoming connections & return new UdpSocket UdpSocket *accept(); // close socket & clear address structs void reset(); // get remote IP/port std::string getPeerIP(); int getPeerSourcePort(); int getPort(); void setPort(int port); void setClientAddr( std::string ip, int port); unsigned long int getPeerSourceAddr(); //std::string down_thread_id; // get local IP std::string getLocalIP(); int getLocalPort(); short int get_wait_flag(bool write_flag); void close(); // blocking check, can break on config reloads //void readyForOutput(int timeout, bool honour_reloadconfig = false); // write buffer to string bool writeString(const char *line); bool writeString(std::string line); // write buff to socket - blocking bool writeToSocket(const char *buff, int len, unsigned int flags, int timeout); // write buff to socket non-blocking - returns number of bytes written or 0 if would block or -1 on error int writeToSocketNB(const char *buff, int len, unsigned int flags); // read from socket, returning number of bytes read int readFromSocket(char *buff, int len, unsigned int flags, int timeout, bool ret_part = false); bool getIeof(); private: // local & remote addresses struct sockaddr_in my_adr; struct sockaddr_in peer_adr; int my_port = 0; std::string my_addr; int client_port = 0; std::string client_addr; bool ieof = false; }; #endif e2guardian-5.5.8r/src/UrlRec.hpp000066400000000000000000000005651477372360500164540ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_URLREC #define __HPP_URLREC // INCLUDES struct url_rec { String fullurl; String urldomain; String baseurl; bool is_siteonly = false; bool site_is_ip = false; }; #endif e2guardian-5.5.8r/src/Utils/000077500000000000000000000000001477372360500156415ustar00rootroot00000000000000e2guardian-5.5.8r/src/Utils/Path.cpp000066400000000000000000000012111477372360500172340ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "Path.hpp" Path::~Path() { } Path Path::baseDir() const { size_t fnsize; if ((fnsize = _path.find_last_of("/")) > 0) return Path(_path.substr(0,++fnsize)); else return Path(""); } void Path::append(const Path &relative_path) { _path += relative_path._path; } Path Path::combine(const Path &relative_path) { Path temp(*this); temp.append(relative_path); return temp; } e2guardian-5.5.8r/src/Utils/Path.hpp000066400000000000000000000020461477372360500172500ustar00rootroot00000000000000// Class Path : methods on strings which represents the path of file or directory // // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. #ifndef __HPP_PATH #define __HPP_PATH // INCLUDES #include #include #include // DECLARATIONS class Path { private: std::string _path; public: Path() { _path = ""; }; ~Path(); // properties std::string fullPath() const { return _path; } // constructor from c-string Path(const char *bs) { _path = bs; }; // constructor from std-string Path(std::string ss) { _path = ss; }; // copy constructor Path(const Path &p) { _path = p._path; }; // return to base dir (for file paths) Path baseDir() const; // appends to this base dir a relative_path void append(const Path &relative_path); // combines this base dir and a relative_path Path combine(const Path &relative_path); }; #endif e2guardian-5.5.8r/src/authplugins/000077500000000000000000000000001477372360500171045ustar00rootroot00000000000000e2guardian-5.5.8r/src/authplugins/BearerBasic.cpp000066400000000000000000000136411477372360500217570ustar00rootroot00000000000000// BearerBasic auth plugin // // Plugin for use where e2g is sent auth details in 'bearer' format, but sent in via a Basic header // // Token is sent as username and signature as password // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../Auth.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" #include extern bool is_daemonised; extern OptionContainer o; extern thread_local std::string thread_id; // DECLARATIONS class JWT_token { public: // These are all required String sub; // username String fgr; // filter group name String aud; // target e2g server/tenant - may be used for checking, by super-proxy for forwarding destination, or by tenanted e2g to determine tenant. long exp = 0; // expiration timestamp // These are optional String cip; // client IP (similar to x-forwarded - may be used by super-proxy) String clid; // client id String jti; // Unique id of token bool load(String token,NaughtyFilter &cm) { // returns true if valid String temp = token.after("{"); int man_cnt = 0; while ( !temp.empty() ) { String line; if (temp.contains(",")) { line = temp.before(","); temp = temp.after(","); } else { line = temp.before("}"); temp = ""; } String key = line.before(":"); String data = line.after(":"); key.removeWhiteSpace(); key.removeChar('"'); data.removeWhiteSpace(); data.removeChar('"'); if (key == "sub") { sub = data; man_cnt++; } else if (key == "fgr") { fgr = data; man_cnt++; } else if (key == "aud") { aud = data; man_cnt++; } else if (key == "exp") { man_cnt++; exp = data.toLong(); } else if (key == "cip") { cip = data; } else if (key == "clid") { clid = data; } else if (key == "jti") { jti = data; } } if (man_cnt < 4) { DEBUG_auth("Mandatory token field missing"); return false; } if (cm.thestart.tv_sec > exp) { DEBUG_auth("Token has expired on:", exp); return false; } return true; } }; // class name is relevant! class bearer_basic_instance : public AuthPlugin { public: bearer_basic_instance(ConfigVar &definition) : AuthPlugin(definition) { client_ip_based = false; }; int identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user, auth_rec &authrec,NaughtyFilter &cm); int init(void *args); }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin AuthPlugin *bearer_basic_create(ConfigVar &definition) { return new bearer_basic_instance(definition); } // end of Class factory // proxy auth header username extraction int bearer_basic_instance::identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &username, bool &is_real_user, auth_rec &authrec,NaughtyFilter &cm) { // don't match for non-basic auth types String t(h.getAuthType()); t.toLower(); if (t == "basic") { // extract token String string; string = h.getAuthData(); //string = h.decodeb64(string); DEBUG_auth("decoded auth string:", string); DEBUG_auth("decoded auth string length:", string.length()); if (string.length() > 0) { String token, sig; token = string; sig = token.after(":"); //sig = h.decodeb64(sig); token = token.before(":"); //token = h.decodeb64(token); String tocheck = token; tocheck.append(bearer_secret); DEBUG_auth("tocheck:", tocheck); String gen_md5 = tocheck.md5(); gen_md5.toLower(); DEBUG_auth("tocheck.md5:", gen_md5); DEBUG_auth("sig sent", sig); if (gen_md5 == sig) { token += "="; token = h.decodeb64(token); DEBUG_auth("plain token:", token); JWT_token token_struct; if(token_struct.load(token,cm)) { authrec.user_name = token_struct.sub; username = token_struct.sub; authrec.fg_name = token_struct.fgr; authrec.user_source = "bearer_b"; authrec.group_source = "bearer_b"; is_real_user = true; return E2AUTH_OK_GOT_GROUP_NAME; } } else { DEBUG_auth("signature not valid"); } } else { DEBUG_auth("empty authdata"); } } else { DEBUG_auth("auth is not Basic or absent"); } // need to add logic to send 407 (and close??) String outmess = "HTTP/1.1 407 Proxy Authentication Required\r\nProxy-Authenticate: Basic realm=\""; outmess += bearer_realm; outmess += "\"\r\n\r\n"; peercon.writeString(outmess); return E2AUTH_407_SENT; } int bearer_basic_instance::init(void *args) { AuthPlugin::init(args); bearer_secret = cv["bearersecret"]; if (bearer_secret.empty()) { E2LOGGER_error("No bearersecret supplied in authplugin/BearerBasic.conf"); return -1; } bearer_realm= cv["realm"]; if (bearer_realm.empty()) { E2LOGGER_error("No realm supplied in authplugin/BearerBasic.conf"); return -1; } return 0; } e2guardian-5.5.8r/src/authplugins/ProxyFirstBasic.cpp000066400000000000000000000042341477372360500227060ustar00rootroot00000000000000// ProxyFirstBasic auth plugin // // Plugin for use where e2g is behind squid and squid sends user in basic // format // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../Auth.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" #include extern OptionContainer o; // DECLARATIONS // class name is relevant! class pf_basic_instance : public AuthPlugin { public: pf_basic_instance(ConfigVar &definition) : AuthPlugin(definition) { client_ip_based = false; }; int identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user, auth_rec &authrec,NaughtyFilter &cm); int init(void *args); }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin AuthPlugin *PF_basic_create(ConfigVar &definition) { return new pf_basic_instance(definition); } // end of Class factory // proxy auth header username extraction int pf_basic_instance::identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user, auth_rec &authrec,NaughtyFilter &cm) { // don't match for non-basic auth types String t(h.getAuthType()); t.toLower(); if (t != "basic") return E2AUTH_NOMATCH; // extract username string = h.getAuthData(); if (string.length() > 0) { string.resize(string.find_first_of(':')); authrec.user_name = string; authrec.user_source = "pf_basic"; is_real_user = true; return E2AUTH_OK; } return E2AUTH_NOMATCH; } int pf_basic_instance::init(void *args) { StoryBoardOptions::SB_entry_map sen; sen.entry_function = cv["story_function"]; if (sen.entry_function.length() > 0) { sen.entry_id = ENT_STORYA_AUTH_PF_BASIC; story_entry = sen.entry_id; o.story.auth_entry_dq.push_back(sen); read_def_fg(); return 0; } else { E2LOGGER_error("No story_function defined in proxy auth plugin config"); return -1; } } e2guardian-5.5.8r/src/authplugins/dnsauth.cpp000066400000000000000000000204201477372360500212540ustar00rootroot00000000000000// DNS auth plugin // 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 // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../Auth.hpp" #include "../RegExp.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" #include #include #include #include #include #include #include #include #include #include // GLOBALS extern OptionContainer o; extern int h_errno; // DECLARATIONS // user record struct struct userstruct { String ippath; String user; int group; }; String basedomain; String authurl; String authprefix; bool redirect_to_auth; // class name is relevant! class dnsauthinstance : public AuthPlugin { public: // keep credentials for the whole of a connection - IP isn't going to change. // not quite true - what about downstream proxy with x-forwarded-for? dnsauthinstance(ConfigVar &definition) : AuthPlugin(definition) { client_ip_based = true; if (!o.use_xforwardedfor) is_connection_based = true; }; int identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user,auth_rec &authrec,NaughtyFilter &cm); int determineGroup(std::string &user, int &fg, ListContainer &uglc); int init(void *args); int quit(); private: userstruct userst; bool getdnstxt(String &ippath); String dns_error(int herror); bool inAuthByPassLists(HTTPHeader &h); }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin AuthPlugin *dnsauthcreate(ConfigVar &definition) { return new dnsauthinstance(definition); } // end of Class factory // // Standard plugin funcs // // // plugin quit - clear IP, subnet & range lists int dnsauthinstance::quit() { return 0; } // plugin init - read in vars int dnsauthinstance::init(void *args) { AuthPlugin::init(args); basedomain = cv["basedomain"]; authurl = cv["authurl"]; authprefix = cv["prefix_auth"]; String t; t = cv["redirect_to_auth"]; if ((t.length() < 1) || (t == "yes")) { redirect_to_auth = true; } else { redirect_to_auth = false; }; if (basedomain.length() < 1) { E2LOGGER_error("No basedomain defined in DNS auth plugin config"); return -1; } if (authurl.length() < 1) { E2LOGGER_error("No authurl defined in DNS auth plugin config"); return -1; } if (authprefix.length() < 1) { E2LOGGER_error("No prefix_auth defined in DNS auth plugin config"); return -1; } read_def_fg(); DEBUG_auth( "basedomain is ", basedomain, " authurl is ", authurl); return 0; } // DNS-AUTH filter group determination // never actually return NOUSER from this, because we don't actually look in the filtergroupslist. // NOUSER stops ConnectionHandler from querying subsequent plugins. int dnsauthinstance::identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, /*int &fg,*/ std::string &string, bool &is_real_user,auth_rec &authrec,NaughtyFilter &cm) { String p1, p2, ippath; p1 = peercon.getPeerIP(); if (o.use_xforwardedfor) { // grab the X-Forwarded-For IP if available p2 = h.getXForwardedForIP(); if (p2.length() > 0) { ippath = p1 + "-" + p2; } else { ippath = p1; } } else { ippath = p1; } DEBUG_auth("IPPath is ", ippath); // change '.' to '-' ippath.swapChar('.', '-'); DEBUG_auth("IPPath is ", ippath); if (getdnstxt(ippath)) { string = userst.user; is_real_user = true; authrec.user_name = string; authrec.filter_group = userst.group; authrec.user_source = "dnsa"; return E2AUTH_OK_GOT_GROUP; } else { // redirect code if (redirect_to_auth) { // used to force log-in // dnsauth plug-in must be last // check if this is request to authurl or in authexception lists if (h.url().startsWith(authprefix) || inAuthByPassLists(h)) { string = "::auth::"; userst.user = string; userst.group = 0; return E2AUTH_OK_NOPERSIST; } else { string = authurl + "=" + h.URLEncode(); return E2AUTH_REDIRECT; } } else { return E2AUTH_NOMATCH; // used for log-in on demand // needs dnsauth plug-in to be first } } } int dnsauthinstance::determineGroup(std::string &user, int &fg, ListContainer &uglc) { fg = userst.group; DEBUG_auth("Matched user", user, " to group ", fg, " in cached DNS record"); return E2AUTH_OK; } bool dnsauthinstance::getdnstxt(String &ippath) { // get info from DNS union { HEADER hdr; u_char buf[NS_PACKETSZ]; } response; int responseLen; ns_msg handle; /* handle for response message */ responseLen = res_querydomain(ippath.c_str(), basedomain.c_str(), ns_c_in, ns_t_txt, (u_char *)&response, sizeof(response)); if (responseLen < 0) { DEBUG_auth("DNS query returned error ", dns_error(h_errno)); return false; } if (ns_initparse(response.buf, responseLen, &handle) < 0) { DEBUG_auth("ns_initparse returned error ", strerror(errno)); return false; } //int rrnum; /* resource record number */ ns_rr rr; /* expanded resource record */ //u_char *cp; //char ans[MAXDNAME]; int i = ns_msg_count(handle, ns_s_an); if (i > 0) { if (ns_parserr(&handle, ns_s_an, 0, &rr)) { DEBUG_auth("ns_paserr returned error ", strerror(errno)); return false; } else { if (ns_rr_type(rr) == ns_t_txt) { DEBUG_auth("ns_rr_rdlen returned ", ns_rr_rdlen(rr)); u_char *k = (u_char *)ns_rr_rdata(rr); char p[400]; unsigned int j = 0; for (unsigned int j1 = 1; j1 < ns_rr_rdlen(rr); j1++) { p[j++] = k[j1]; } p[j] = '\0'; DEBUG_auth("ns_rr_data returned ", p); String dnstxt(p); userst.user = dnstxt.before(","); userst.group = (dnstxt.after(",")).toInteger() - 1; return true; } } } return true; } String dnsauthinstance::dns_error(int herror) { String s; switch (herror) { case HOST_NOT_FOUND: s = "HOST_NOT_FOUND"; break; case TRY_AGAIN: s = "TRY_AGAIN - DNS server failure"; break; case NO_DATA: s = "NO_DATA - unexpected DNS error"; break; default: String S2(herror); s = "DNS - Unexpected error number " + S2; break; } return s; } bool dnsauthinstance::inAuthByPassLists(HTTPHeader &h) { String url = h.url(); String urld = h.decode(url); //FOptionContainer* foc = o.currentLists()->fg[0]; url.removePTP(); if (url.contains("/")) { url = url.before("/"); } // Find other way to do this using Storyboarding // bool is_ip = (*foc).isIPHostname(url); // bool is_ssl = h.requestType() == "CONNECT"; // if ((*foc).inAuthExceptionSiteList(urld, true, is_ip, is_ssl)) { // // exceptioncat = (*o.lm.l[(*o.fg[filtergroup]).exception_site_list]).lastcategory.toCharArray(); // return true; //} else if ((*foc).inAuthExceptionURLList(urld, true, is_ip, is_ssl)) { // // exceptioncat = (*o.lm.l[(*o.fg[filtergroup]).exception_url_list]).lastcategory.toCharArray(); // return true; //} return false; } e2guardian-5.5.8r/src/authplugins/header.cpp000066400000000000000000000037621477372360500210500ustar00rootroot00000000000000// Header auth plugin // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../Auth.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" // DECLARATIONS HTTPHeader *reqheader; // GLOBALS extern OptionContainer o; String fname = ""; // class name is relevant! class headerinstance : public AuthPlugin { public: headerinstance(ConfigVar &definition) : AuthPlugin(definition) { String fname(cv["header"]); o.header.ident_header_value = fname; client_ip_based = false; }; int identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user,auth_rec &authrec,NaughtyFilter &cm); int init(void *args); }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin AuthPlugin *headercreate(ConfigVar &definition) { return new headerinstance(definition); } // end of Class factory // proxy auth header username extraction int headerinstance::identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user,auth_rec &authrec,NaughtyFilter &cm) { if (fname.length() < 0) return E2AUTH_NOMATCH; string = h.getAuthHeader(); if (string.length() > 0) { authrec.user_name = string; authrec.user_source = "header"; is_real_user = false; return E2AUTH_OK; } return E2AUTH_NOMATCH; } int headerinstance::init(void *args) { AuthPlugin::init(args); StoryBoardOptions::SB_entry_map sen; sen.entry_function = cv["story_function"]; if (sen.entry_function.length() > 0) { sen.entry_id = ENT_STORYA_AUTH_HEADER; story_entry = sen.entry_id; o.story.auth_entry_dq.push_back(sen); return 0; } else { E2LOGGER_error("No story_function defined in header auth plugin config"); return -1; } } e2guardian-5.5.8r/src/authplugins/ident.cpp000066400000000000000000000102211477372360500207070ustar00rootroot00000000000000// Ident server auth plugin // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../Auth.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" // GLOBALS extern OptionContainer o; // DECLARATIONS // class name is relevant! class identinstance : public AuthPlugin { public: identinstance(ConfigVar &definition) : AuthPlugin(definition){ client_ip_based = true; // not sure if this is correct!! }; int identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user,auth_rec &authrec,NaughtyFilter &cm); int init(void *args); }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin AuthPlugin *identcreate(ConfigVar &definition) { return new identinstance(definition); } // end of Class factory // ident server username extraction // checkme: needs better error reporting int identinstance::identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user,auth_rec &authrec,NaughtyFilter &cm) { std::string clientip; bool use_xforwardedfor; use_xforwardedfor = false; if (o.use_xforwardedfor == 1) { if (o.net.xforwardedfor_filter_ip.size() > 0) { const char *ip = peercon.getPeerIP().c_str(); for (unsigned int i = 0; i < o.net.xforwardedfor_filter_ip.size(); i++) { if (strcmp(ip, o.net.xforwardedfor_filter_ip[i].c_str()) == 0) { use_xforwardedfor = true; break; } } } else { use_xforwardedfor = true; } } if (use_xforwardedfor) { // grab the X-Forwarded-For IP if available clientip = h.getXForwardedForIP(); // otherwise, grab the IP directly from the client connection if (clientip.length() == 0) clientip = peercon.getPeerIP(); } else { clientip = peercon.getPeerIP(); } int clientport = peercon.getPeerSourcePort(); int serverport = peercon.getPort(); DEBUG_auth("Connecting to: ", clientip, "to ask about: ", clientport); Socket iq; iq.setTimeout(5000); int rc = iq.connect(clientip.c_str(), 113); // ident port if (rc) { DEBUG_auth("Error connecting to obtain ident from: ", clientip); return E2AUTH_NOMATCH; } DEBUG_auth("Connected to:", clientip); std::string request; request = String(clientport).toCharArray(); request += ", "; request += String(serverport).toCharArray(); request += "\r\n"; DEBUG_auth("About to send:", request); if (!iq.writeToSocket((char *)request.c_str(), request.length(), 0, 5000)) { DEBUG_auth("Error writing to ident connection to: ", clientip); iq.close(); // close conection to client return -1; } DEBUG_auth("wrote ident request to:", clientip); char buff[8192]; try { iq.getLine(buff, 8192, 5000); } catch (std::exception &e) { return -2; } String temp; temp = buff; // convert to String DEBUG_auth("got ident reply: ", temp, " from: ", clientip); iq.close(); // close conection to client temp = temp.after(":"); if (!temp.before(":").contains("USERID")) { return -3; } temp = temp.after(":"); temp = temp.after(":"); temp.removeWhiteSpace(); if (temp.length() > 0) { string = temp.toCharArray(); authrec.user_name = string; authrec.user_source = "ident"; is_real_user = true; return E2AUTH_OK; } return E2AUTH_NOMATCH; } int identinstance::init(void *args) { AuthPlugin::init(args); StoryBoardOptions::SB_entry_map sen; sen.entry_function = cv["story_function"]; if (sen.entry_function.length() > 0) { sen.entry_id = ENT_STORYA_AUTH_IDENT; story_entry = sen.entry_id; o.story.auth_entry_dq.push_back(sen); return 0; } else { E2LOGGER_error("No story_function defined in ident auth plugin config"); return -1; } } e2guardian-5.5.8r/src/authplugins/ip.cpp000066400000000000000000000131451477372360500202240ustar00rootroot00000000000000// IP (range, subnet) auth plugin // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../Auth.hpp" #include "../RegExp.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" #include #include #include #include // GLOBALS extern OptionContainer o; // DECLARATIONS // structs linking subnets and IP ranges to filter groups //struct subnetstruct // uint32_t maskedaddr; //uint32_t mask; //int group; //}; //struct rangestruct { // uint32_t startaddr; // uint32_t endaddr; // int group; //}; // class for linking IPs to filter groups, complete with comparison operators // allowing standard C++ sort to work #ifdef NODEF class ip { public: ip(uint32_t a, int g) { addr = a; group = g; }; uint32_t addr; int group; int operator<(const ip &a) const { return addr < a.addr; }; int operator<(const uint32_t &a) const { return addr < a; }; int operator==(const uint32_t &a) const { return a == addr; }; }; #endif // class name is relevant! class ipinstance : public AuthPlugin { public: // keep credentials for the whole of a connection - IP isn't going to change. // not quite true - what about downstream proxy with x-forwarded-for? ipinstance(ConfigVar &definition) : AuthPlugin(definition) { if (!o.use_xforwardedfor) is_connection_based = true; client_ip_based = true; }; int identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user, auth_rec &authrec,NaughtyFilter &cm); //int determineGroup(std::string &user, int &fg, ListContainer &uglc); int init(void *args); int quit(); private: //std::vector iplist; //std::list ipsubnetlist; std::vector iprangelist; int readIPMelangeList(const char *filename); int searchList(int a, int s, const uint32_t &ip); int inList(const uint32_t &ip); int inSubnet(const uint32_t &ip); int inRange(const uint32_t &ip); }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin AuthPlugin *ipcreate(ConfigVar &definition) { return new ipinstance(definition); } // end of Class factory // // // Standard plugin funcs // // // plugin quit - clear IP, subnet & range lists int ipinstance::quit() { //iplist.clear(); //ipsubnetlist.clear(); //iprangelist.clear(); return 0; } // plugin init - read in ip melange list int ipinstance::init(void *args) { AuthPlugin::init(args); StoryBoardOptions::SB_entry_map sen; sen.entry_function = cv["story_function"]; if (sen.entry_function.length() > 0) { sen.entry_id = ENT_STORYA_AUTH_IP; story_entry = sen.entry_id; o.story.auth_entry_dq.push_back(sen); return 0; } else { E2LOGGER_error("No story_function defined in IP auth plugin config"); return -1; } } // IP-based filter group determination // never actually return NOUSER from this, because we don't actually look in the filtergroupslist. // NOUSER stops ConnectionHandler from querying subsequent plugins. int ipinstance::identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user, auth_rec &authrec,NaughtyFilter &cm) { // we don't get usernames out of this plugin, just a filter group // for now, use the IP as the username bool use_xforwardedfor; use_xforwardedfor = false; if (o.use_xforwardedfor == 1) { if (o.net.xforwardedfor_filter_ip.size() > 0) { const char *ip = peercon.getPeerIP().c_str(); for (unsigned int i = 0; i < o.net.xforwardedfor_filter_ip.size(); i++) { if (strcmp(ip, o.net.xforwardedfor_filter_ip[i].c_str()) == 0) { use_xforwardedfor = true; break; } } } else { use_xforwardedfor = true; } } if (use_xforwardedfor == 1) { // grab the X-Forwarded-For IP if available string = h.getXForwardedForIP(); // or try the client IP from the header if (string.length() == 0) string = h.getClientIP(); // otherwise, grab the IP directly from the client connection if (string.length() == 0) string = peercon.getPeerIP(); } else { string = h.getClientIP(); // otherwise, grab the IP directly from the client connection if (string.length() == 0) string = peercon.getPeerIP(); } authrec.user_name = string; authrec.user_source = "ip";; is_real_user = false; return E2AUTH_OK; } #ifdef NODEF int ipinstance::determineGroup(std::string &user, int &rfg, ListContainer &uglc) { struct in_addr sin; inet_aton(user.c_str(), &sin); uint32_t addr = ntohl(sin.s_addr); int fg; // check straight IPs, subnets, and ranges // fg = inList(addr); if (fg >= 0) { rfg = fg; DEBUG_auth("Matched IP ", user, " to straight IP list"); return E2AUTH_OK; } // fg = inSubnet(addr); if (fg >= 0) { rfg = fg; DEBUG_auth("Matched IP ", user, " to subnet"); return E2AUTH_OK; } // fg = inRange(addr); if (fg >= 0) { rfg = fg; DEBUG_auth("Matched IP ", user, " to range"); return E2AUTH_OK; } DEBUG_auth("Matched IP ", user, " to nothing"); return E2AUTH_NOMATCH; } #endif e2guardian-5.5.8r/src/authplugins/port.cpp000066400000000000000000000067011477372360500206000ustar00rootroot00000000000000// listening PORT auth plugin // 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 // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../Auth.hpp" #include "../RegExp.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" #include #include #include #include // GLOBALS extern OptionContainer o; // DECLARATIONS // structs linking ports to filter groups struct portstruct { unsigned long int port; int group; }; // class name is relevant! class portinstance : public AuthPlugin { public: // keep credentials for the whole of a connection - IP isn't going to change. // not quite true - what about downstream proxy with x-forwarded-for? portinstance(ConfigVar &definition) : AuthPlugin(definition) { is_connection_based = true; client_ip_based = false; } int identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user,auth_rec &authrec,NaughtyFilter &cm); //int determineGroup(std::string &user, int &fg, ListContainer &uglc); int init(void *args); int quit(); private: std::deque ipportlist; int readIPMelangeList(const char *filename); int searchList(int a, int s, const unsigned int &ip); int inList(const int &ip); }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin AuthPlugin *portcreate(ConfigVar &definition) { return new portinstance(definition); } // end of Class factory // // // Standard plugin funcs // // // plugin quit - clear IP, port & range lists int portinstance::quit() { ipportlist.clear(); return 0; } // plugin init - read in ip melange list int portinstance::init(void *args) { AuthPlugin::init(args); StoryBoardOptions::SB_entry_map sen; sen.entry_function = cv["story_function"]; if (sen.entry_function.length() > 0) { sen.entry_id = ENT_STORYA_AUTH_PORT; story_entry = sen.entry_id; o.story.auth_entry_dq.push_back(sen); return 0; } else { E2LOGGER_error("No story_function defined in port auth plugin config"); return -1; } } // Port-based filter group determination // never actually return NOUSER from this, because we don't actually look in the portgroupslist. // NOUSER stops ConnectionHandler from querying subsequent plugins. int portinstance::identify(Socket &peercon, Socket &proxycon, HTTPHeader &h, std::string &string, bool &is_real_user,auth_rec &authrec,NaughtyFilter &cm) { // we don't get usernames out of this plugin, just a filter group // for now, use the dest Port as the username String s(peercon.getPort()); string = s; authrec.user_name = string; authrec.user_source = "port"; is_real_user = false; return E2AUTH_OK; } e2guardian-5.5.8r/src/contentscanners/000077500000000000000000000000001477372360500177505ustar00rootroot00000000000000e2guardian-5.5.8r/src/contentscanners/avastdscan.cpp000066400000000000000000000263671477372360500226210ustar00rootroot00000000000000// AvastD content scanning plugin // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../String.hpp" #include "../ContentScanner.hpp" #include "../UDSocket.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" #include #include // GLOBALS extern OptionContainer o; // DECLARATIONS // class name is relevant! class avastdinstance : public CSPlugin { public: avastdinstance(ConfigVar &definition) : CSPlugin(definition), archivewarn(false){}; // we are not replacing scanTest or scanMemory // but for scanFile and the default scanMemory to work, we need a working scanFile implementation int scanFile(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc, const char *ip, const char *filename, NaughtyFilter *checkme, const String *disposition, const String *mimetype); int init(void *args); private: // AvastD UNIX domain socket path String udspath; // Whether or not to just issue a warning on archive limit/encryption warnings bool archivewarn; static String encode(const String &Str); // Set avastd protocol for new deamon version String avastprotocol; String scanreturncode; }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin CSPlugin *avastdcreate(ConfigVar &definition) { return new avastdinstance(definition); } // end of Class factory // initialise the plugin int avastdinstance::init(void *args) { int rc; if ((rc = CSPlugin::init(args)) != E2CS_OK) return rc; // read in AvastD UNIX domain socket path udspath = cv["avastdudsfile"]; if (udspath.length() < 3) { E2LOGGER_error("Error reading avastdudsfile option.", udspath); return E2CS_ERROR; // it would be far better to do a test connection to the file but // could not be arsed for now } archivewarn = cv["archivewarn"] == "on"; DEBUG_avscan("avastd configuration: archivewarn = ", archivewarn); avastprotocol = cv["avastprotocol"]; if (avastprotocol.length() < 3) { avastprotocol = "avast4"; E2LOGGER_error("avasd configuration missing avastprotocol: use ", avastprotocol); } if (avastprotocol.compare("avast4") != 0 && avastprotocol.compare("avast2014") != 0) { E2LOGGER_error("Error reading avastprotocol option."); return E2CS_ERROR; } DEBUG_avscan("avastd configuration: avastprotocol = ", avastprotocol); // set some parameter by avastd protocol version if (avastprotocol.compare("avast4") == 0) { scanreturncode = "200 "; } else scanreturncode = "210 "; return E2CS_OK; } // no need to replace the inheritied scanMemory() which just calls scanFile() // there is no capability to scan memory with avastdscan as we pass it // a file name to scan. So we save the memory to disk and pass that. // Then delete the temp file. int avastdinstance::scanFile(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc, const char *ip, const char *filename, NaughtyFilter *checkme, const String *disposition, const String *mimetype) { lastmessage = lastvirusname = String(); // mkstemp seems to only set owner permissions, so our AV daemon won't be // able to read the file, unless it's running as the same user as us. that's // not usually very convenient. so instead, just allow group read on the // file, and tell users to make sure the daemongroup option is friendly to // the AV daemon's group membership. // TODO? chmod can error out with EINTR, we may wish to ignore this if (chmod(filename, S_IRGRP | S_IRUSR) != 0) { lastmessage = "Error giving AvastD read access to temp file"; E2LOGGER_error("Could not change file ownership to give AvastD read access: ", strerror(errno)); return E2CS_SCANERROR; }; UDSocket stripedsocks; if (stripedsocks.getFD() < 0) { lastmessage = "Error opening socket to talk to AvastD"; E2LOGGER_error("Error creating socket for talking to AvastD"); return E2CS_SCANERROR; } if (stripedsocks.connect(udspath.toCharArray()) < 0) { lastmessage = "Error connecting to AvastD socket"; E2LOGGER_error("Error connecting to AvastD socket"); return E2CS_SCANERROR; } char buffer[4096]; int rc; bool infected = false; bool warning = false; bool truncated = false; try { // After connecting, the daemon sends the following welcome message: // 220 Welcome to avast! Virus scanning daemon x.x (VPS yy-yy dd.mm.yyyy) rc = stripedsocks.getLine(buffer, sizeof(buffer), o.content.content_scanner_timeout); DEBUG_avscan("Got from avastd: ", encode(buffer)); if (strncmp(buffer, "220 ", 4) != 0) { lastmessage = "Unexpected reply during AvastD handshake: "; String ebuffer(encode(buffer)); lastmessage += ebuffer; E2LOGGER_error(lastmessage); return E2CS_SCANERROR; } // Syntax: // SCAN FileName (with some escaping) String command("SCAN "); command += encode(filename); command += "\r\n"; DEBUG_avscan("avastd command: ", encode(command)); stripedsocks.writeString(command.toCharArray()); // Possible return codes: // One of the following: // 501 Syntax error in arguments // 451 Engine error %d // 200 OK rc = stripedsocks.getLine(buffer, sizeof(buffer), o.content.content_scanner_timeout); DEBUG_avscan("Got from avastd: ", encode(buffer)); if (strncmp(buffer, scanreturncode.toCharArray(), 4) != 0) { lastmessage = "Unexpected reply to scan command: "; String ebuffer(encode(buffer)); lastmessage += ebuffer; E2LOGGER_error(lastmessage); return E2CS_SCANERROR; } // Scan response format: // avast4: Filepath\t[Status]\tMoreInfo // avast2014: SCAN\sFilepath\t[Statos]\tMoreInfo\tVirusName // where: // \t is ASCII character 9 (tab) // FilePath is full path to the scanned file // [Status] is one of the following values // [+] - scan succeeded, the file is clean // [L] - scan succeeded, the file is infected, for more info see // Following these lines there is a blank line which signals the end of data // transter from the daemon side. for (rc = stripedsocks.getLine(buffer, sizeof(buffer), o.content.content_scanner_timeout, NULL, &truncated); rc > 0 && !truncated && buffer[0] != '\r'; rc = stripedsocks.getLine(buffer, sizeof(buffer), o.content.content_scanner_timeout, NULL, &truncated)) { DEBUG_avscan("Got from avastd: ", encode(buffer)); // If a line can't fit in our buffer, we're probably dealing with a zip bomb or // something similarly nasty. Let's consider it an error, whatever archivewarn says. if (buffer[rc - 1] != '\r') { lastmessage = "Error whilst reading AvastD socket: can't fit line in buffer."; E2LOGGER_error(lastmessage); return E2CS_SCANERROR; } // We're looking for this kind of string: ^[^\t]*\t\[.\](\t.*)?\r$ char *result = strchr(buffer, '\t'); if (strncmp(buffer, "200 ", 4) == 0 && avastprotocol.compare("avast2014") == 0) { DEBUG_avscan("ignore 200 SCAN OK and exit loop"); break; } else { if ((avastprotocol.compare("avast4") == 0 && (result == NULL || result[1] != '[' || result[1] == '\0' || result[3] != ']' || (result[4] != '\t' && result[4] != '\r'))) || (avastprotocol.compare("avast2014") == 0 && (result == NULL || result[1] != '[' || result[1] == '\0' || result[3] != ']'))) { lastmessage = "Unexpected reply in scan results: "; String ebuffer(encode(buffer)); lastmessage += ebuffer; E2LOGGER_error(lastmessage); return E2CS_SCANERROR; } *result = '\0'; result += 5; switch (result[-3]) { case '+': // Clean! DEBUG_avscan("avastd result: ", encode(buffer) "\tclean!"); break; case 'L': // Infected! DEBUG_avscan("avastd result: ", encode(buffer), "\tinfected with ", result); if (!lastvirusname.empty()) lastvirusname += " "; { char *r = strchr(result, '\r'); lastvirusname += r == NULL ? result : String(result, r - result); } infected = true; break; default: // Can't interpret result. DEBUG_avscan("avastd result: ", encode(buffer), "\tcan't analyze (", result, ")" ); if (!lastvirusname.empty()) lastvirusname += " "; lastvirusname += "Encrypted"; warning = true; break; } } } } catch (std::exception &e) { lastmessage = "Exception whilst reading AvastD socket: "; lastmessage += e.what(); E2LOGGER_error(lastmessage); return E2CS_SCANERROR; } DEBUG_avscan("avastd final result: infected: ", infected, "\twarning: ", warning, "\tlastvirusname: ", lastvirusname, "\ttruncated: ", truncated); // Socket unexpectedly closed. if (rc == 0 || truncated || (avastprotocol.compare("avast4") == 0 && buffer[0] != '\r')) { lastmessage = "Error whilst reading AvastD socket: truncated data."; E2LOGGER_error(lastmessage); return E2CS_SCANERROR; } if (infected || (warning && archivewarn)) { blockFile(NULL, NULL, checkme); return E2CS_INFECTED; } return E2CS_CLEAN; } String avastdinstance::encode(const String &Str) { char Enc[Str.length() * 2]; char *p = Enc; for (String::const_iterator i = Str.begin(); i != Str.end(); ++i) switch (*i) { case '\t': *(p++) = '\\'; *(p++) = 't'; break; case '\n': *(p++) = '\\'; *(p++) = 'n'; break; case '\r': *(p++) = '\\'; *(p++) = 'r'; break; case '\\': *(p++) = '\\'; *(p++) = '\\'; break; case '\0': // This shouldn't happen. DEBUG_avscan("Warning: '\\0' found in filename."); *(p++) = '\\'; *(p++) = '0'; break; default: *(p++) = *i; break; } // No need to allocate new memory if no escapes were inserted. if (p - Enc == Str.length()) return String(Str); else return String(Enc, p - Enc); } e2guardian-5.5.8r/src/contentscanners/clamdscan.cpp000066400000000000000000000143111477372360500224010ustar00rootroot00000000000000// ClamD content scanning plugin // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../String.hpp" #include "../ContentScanner.hpp" #include "../UDSocket.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" #include #include #include #include // GLOBALS extern OptionContainer o; // DECLARATIONS // class name is relevant! class clamdinstance : public CSPlugin { public: clamdinstance(ConfigVar &definition) : CSPlugin(definition), archivewarn(false){}; // we are not replacing scanTest or scanMemory // but for scanFile and the default scanMemory to work, we need a working scanFile implementation int scanFile(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc, const char *ip, const char *filename, NaughtyFilter *checkme, const String *disposition, const String *mimetype); int init(void *args); private: // ClamD UNIX domain socket path String udspath; // File path prefix for chrooted ClamD String pathprefix; // Whether or not to just issue a warning on archive limit/encryption warnings bool archivewarn; }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin CSPlugin *clamdcreate(ConfigVar &definition) { return new clamdinstance(definition); } // end of Class factory // initialise the plugin int clamdinstance::init(void *args) { int rc; if ((rc = CSPlugin::init(args)) != E2CS_OK) return rc; // read in ClamD UNIX domain socket path udspath = cv["clamdudsfile"]; if (udspath.length() < 3) { E2LOGGER_error("Error reading clamdudsfile option."); return E2CS_ERROR; // it would be far better to do a test connection to the file but // could not be arsed for now } // read in path prefix pathprefix = cv["pathprefix"]; archivewarn = cv["archivewarn"] == "on"; return E2CS_OK; } // no need to replace the inheritied scanMemory() which just calls scanFile() // there is no capability to scan memory with clamdscan as we pass it // a file name to scan. So we save the memory to disk and pass that. // Then delete the temp file. int clamdinstance::scanFile(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc , const char *ip, const char *filename, NaughtyFilter *checkme, const String *disposition, const String *mimetype) { lastmessage = lastvirusname = ""; // mkstemp seems to only set owner permissions, so our AV daemon won't be // able to read the file, unless it's running as the same user as us. that's // not usually very convenient. so instead, just allow group read on the // file, and tell users to make sure the daemongroup option is friendly to // the AV daemon's group membership. if (chmod(filename, S_IRGRP | S_IRUSR ) != 0) { lastmessage = "Error giving ClamD read access to temp file "; E2LOGGER_error(lastmessage, strerror(errno)); return E2CS_SCANERROR; }; String command("SCAN "); if (pathprefix.length()) { String fname(filename); command += fname.after(pathprefix.toCharArray()); } else { command += filename; } command += "\r\n"; DEBUG_avscan("clamdscan command:", command); UDSocket stripedsocks; if (stripedsocks.getFD() < 0) { lastmessage = "Error opening socket to talk to ClamD"; E2LOGGER_error(lastmessage); return E2CS_SCANERROR; } if (stripedsocks.connect(udspath.toCharArray()) < 0) { lastmessage = "Error connecting to ClamD socket"; E2LOGGER_error(lastmessage); stripedsocks.close(); return E2CS_SCANERROR; } if( ! stripedsocks.writeString(command.toCharArray())) { lastmessage = "Exception whilst writing to ClamD socket: "; String t = stripedsocks.getErrno(); lastmessage += t; if (stripedsocks.isTimedout()) lastmessage += " TimedOut"; if (stripedsocks.isHup()) lastmessage += " HUPed"; if (stripedsocks.isNoWrite()) lastmessage += " NotWritable"; E2LOGGER_error(lastmessage); DEBUG_avscan(lastmessage); stripedsocks.close(); return E2CS_SCANERROR; } char *buff = new char[4096]; int rc; rc = stripedsocks.getLine(buff, 4096, o.content.content_scanner_timeout); if (rc < 1) { delete[] buff; lastmessage = "Exception whist reading ClamD socket: "; String t = stripedsocks.getErrno(); lastmessage +=t; if (stripedsocks.isTimedout()) lastmessage += " TimedOut"; if (stripedsocks.isHup()) lastmessage += " HUPed"; if (stripedsocks.isNoRead()) lastmessage += " NotReadable"; E2LOGGER_error(lastmessage); DEBUG_avscan(lastmessage); stripedsocks.close(); return E2CS_SCANERROR; } String reply(buff); delete[] buff; reply.removeWhiteSpace(); DEBUG_avscan("Got from clamdscan: ", reply); stripedsocks.close(); if (reply.endsWith("ERROR")) { lastmessage = "ClamD error: " + reply; E2LOGGER_error(lastmessage); return E2CS_SCANERROR; } else if (reply.endsWith("FOUND")) { lastvirusname = reply.after(": ").before(" FOUND"); // format is: // /foo/path/file: foovirus FOUND DEBUG_avscan("clamdscan INFECTED! with: ", lastvirusname); if (archivewarn && (lastvirusname.contains(".Exceeded") || lastvirusname.contains(".Encrypted"))) { DEBUG_avscan("clamdscan: detected an ArchiveBlockMax \"virus\"; logging warning only"); lastmessage = "Archive not fully scanned: " + lastvirusname; return E2CS_WARNING; } blockFile(NULL, NULL, checkme); return E2CS_INFECTED; } // must be clean // Note: we should really check what the output of a "clean" message actually looks like, // and check explicitly for that, but the ClamD documentation is sparse on output formats. DEBUG_avscan("clamdscan - he say yes (clean)"); return E2CS_CLEAN; } e2guardian-5.5.8r/src/contentscanners/commandlinescan.cpp000066400000000000000000000260071477372360500236140ustar00rootroot00000000000000// Command line content scanning plugin // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../String.hpp" #include "../ContentScanner.hpp" #include "../UDSocket.hpp" #include "../OptionContainer.hpp" #include "../RegExp.hpp" #include "../Logger.hpp" #include #include #include #include #include #include #include #include // GLOBALS extern OptionContainer o; // IMPLEMENTATION // class name is relevant class commandlineinstance : public CSPlugin { public: commandlineinstance(ConfigVar &definition) : CSPlugin(definition), usevirusregexp(false), submatch(0), arguments(NULL), numarguments(0), infectedcodes(NULL), numinfectedcodes(0), cleancodes(NULL), numcleancodes(0), defaultresult(-1){}; int scanFile(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc , const char *ip, const char *filename, NaughtyFilter *checkme, const String *disposition, const String *mimetype); int init(void *args); ~commandlineinstance() { delete[] infectedcodes; delete[] cleancodes; for (int i = 0; i < numarguments; i++) delete arguments[i]; delete[] arguments; }; private: // regular expression for finding virus names in program output RegExp virusregexp; RegResult virusregexpres; // whether or not the above is in use bool usevirusregexp; // which sub-match to take from the match int submatch; // path to command-line scanning program (+ initial arguments) String progname; // argument array (the above must be split on space for passing to exec) char **arguments; int numarguments; // return code(s) for infected files int *infectedcodes; int numinfectedcodes; // return code(s) for uninfected files int *cleancodes; int numcleancodes; // optional default result - can be used to e.g. define only cleancodes, // and have everything else default to infected. int defaultresult; }; // class factory code *MUST* be included in every plugin CSPlugin *commandlinecreate(ConfigVar &definition) { return new commandlineinstance(definition); } // end of Class factory // initialise plugin int commandlineinstance::init(void *args) { int rc; if ((rc = CSPlugin::init(args)) != E2CS_OK) return rc; // read in program name progname = cv["progname"]; if (progname.length() == 0) { E2LOGGER_error("Command-line scanner: No program specified"); return E2CS_ERROR; } // split into an argument array std::list temparguments; char *tempprogname = new char[progname.length() + 1]; tempprogname[progname.length()] = '\0'; strncpy(tempprogname, progname.c_str(), progname.length()); char *result = strtok(tempprogname, " "); while (result) { temparguments.push_back(std::string(result)); result = strtok(NULL, " "); } delete[] tempprogname; for (int i = 0; i < numarguments; i++) delete arguments[i]; delete[] arguments; numarguments = temparguments.size(); arguments = new char *[numarguments + 2]; arguments[numarguments + 1] = NULL; int count = 0; for (std::list::iterator i = temparguments.begin(); i != temparguments.end(); i++) { char *newthing = new char[i->length()]; strcpy(newthing, i->c_str()); arguments[count++] = newthing; } progname = cv["progname"]; #ifdef DEBUG_HIGH DEBUG_avscan("Program and arguments: "); for (int i = 0; i < numarguments; i++) { DEBUG_avscan(arguments[i], ", "); } #endif // read in virus name regular expression String ucvirusregexp(cv["virusregexp"]); if (ucvirusregexp.length()) { usevirusregexp = true; if (!virusregexp.comp(ucvirusregexp.toCharArray())) { E2LOGGER_error("Command-line scanner: Could not compile regular expression for extracting virus names"); return E2CS_ERROR; } String ssubmatch(cv["submatch"]); if (ssubmatch.length()) submatch = ssubmatch.toInteger(); } // read in the lists of good and bad program return codes String sinfectedcodes(cv["infectedcodes"]); String scleancodes(cv["cleancodes"]); std::list tempinfectedcodes; std::list tempcleancodes; char *tempcodes = new char[sinfectedcodes.length() + 1]; tempcodes[sinfectedcodes.length()] = '\0'; strncpy(tempcodes, sinfectedcodes.c_str(), sinfectedcodes.length()); result = strtok(tempcodes, ","); DEBUG_avscan("Infected file return codes: "); while (result) { tempinfectedcodes.push_back(atoi(result)); DEBUG_avscan(tempinfectedcodes.back() ); result = strtok(NULL, ","); } delete[] tempcodes; tempcodes = new char[scleancodes.length() + 1]; tempcodes[scleancodes.length()] = '\0'; strncpy(tempcodes, scleancodes.c_str(), scleancodes.length()); result = strtok(tempcodes, ","); DEBUG_avscan("Clean file return codes: "); while (result) { tempcleancodes.push_back(atoi(result)); DEBUG_avscan(tempcleancodes.back()); result = strtok(NULL, ","); } delete[] tempcodes; // we need at least one of our three mechanisms (cleancodes, infectedcodes and virus names) // to be defined in order to make a decision about the nature of a scanning result. numcleancodes = tempcleancodes.size(); numinfectedcodes = tempinfectedcodes.size(); if (!(usevirusregexp || numcleancodes || numinfectedcodes)) { E2LOGGER_error("Command-line scanner requires some mechanism for interpreting results. Please define cleancodes, infectedcodes, and/or a virusregexp."); return E2CS_ERROR; } // Copy return code lists out into static arrays delete[] infectedcodes; delete[] cleancodes; infectedcodes = new int[numinfectedcodes]; cleancodes = new int[numcleancodes]; count = 0; for (std::list::iterator i = tempinfectedcodes.begin(); i != tempinfectedcodes.end(); i++) infectedcodes[count++] = *i; count = 0; for (std::list::iterator i = tempcleancodes.begin(); i != tempcleancodes.end(); i++) cleancodes[count++] = *i; // read in default result type String sdefaultresult(cv["defaultresult"]); if (sdefaultresult.length()) { if (sdefaultresult == "clean") { defaultresult = 1; } else if (sdefaultresult == "infected") { defaultresult = 0; } else { E2LOGGER_error("Command-line scanner: Default result value not understood"); return E2CS_WARNING; } } return E2CS_OK; } // no need to replace the inheritied scanMemory() which just calls scanFile() // there is no capability to scan memory with commandlinescan as we pass it // a file name to scan. So we save the memory to disk and pass that. // Then delete the temp file. // TODO Allow for placeholders in command line for inserting content-disposition & content-type? int commandlineinstance::scanFile(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc , const char *ip, const char *filename, NaughtyFilter *checkme, const String *disposition, const String *mimetype) { // create socket pairs for child (scanner) process's stdout & stderr int scannerstdout[2]; int scannerstderr[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, scannerstdout) == -1) { lastmessage = "Cannot create sockets for communicating with scanner"; E2LOGGER_error(lastmessage, " ", strerror(errno) ); return E2CS_SCANERROR; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, scannerstderr) == -1) { lastmessage = "Cannot create sockets for communicating with scanner" ; E2LOGGER_error(lastmessage, " ", strerror(errno) ); return E2CS_SCANERROR; } int f = fork(); if (f == 0) { DEBUG_avscan("Running: ", progname, " ", filename); // close read ends of sockets close(scannerstdout[0]); close(scannerstderr[0]); // bind stdout & stderr dup2(scannerstdout[1], 1); dup2(scannerstderr[1], 2); // execute scanner arguments[numarguments] = (char *)filename; execv(arguments[0], arguments); // if we get here, an error occurred! E2LOGGER_error("Cannot exec command-line scanner (command ", progname, " ",filename, "): ", strerror(errno)); _exit(255); } else if (f == -1) { lastmessage = "Cannot launch scanner"; E2LOGGER_error("Cannot fork to launch command-line scanner (command ", progname, " ",filename, "): ", strerror(errno)); return E2CS_SCANERROR; } // close write ends of sockets close(scannerstdout[1]); close(scannerstderr[1]); char buff[8192]; std::string result; FILE *readme = fdopen(scannerstdout[0], "r"); while (fgets(buff, 8192, readme) != NULL) { #ifndef DEBUG_LOW if (usevirusregexp) #endif result += buff; } fclose(readme); readme = fdopen(scannerstderr[0], "r"); while (fgets(buff, 8192, readme) != NULL) { #ifndef DEBUG_LOW if (usevirusregexp) #endif result += buff; } fclose(readme); // close read ends too now close(scannerstdout[0]); close(scannerstderr[0]); // wait for scanner to quit & retrieve exit status int returncode; returncode = WEXITSTATUS(returncode); if (waitpid(f, &returncode, 0) == -1) { lastmessage = "Cannot get scanner return code"; E2LOGGER_error(lastmessage, " ", strerror(errno)); return E2CS_SCANERROR; } DEBUG_avscan("Scanner result: ", (result), "Code: ", returncode); if (returncode == 255) { lastmessage = "Cannot get scanner return code"; E2LOGGER_error("Cannot get command-line scanner return code: scanner exec failed"); return E2CS_SCANERROR; } lastvirusname = "Unknown"; if (usevirusregexp) { virusregexp.match(result.c_str(), virusregexpres); if (virusregexpres.matched()) { lastvirusname = virusregexpres.result(submatch); blockFile(NULL, NULL, checkme); return E2CS_INFECTED; } } if (cleancodes) { for (int i = 0; i < numcleancodes; i++) { if (returncode == cleancodes[i]) return E2CS_CLEAN; } } if (infectedcodes) { for (int i = 0; i < numinfectedcodes; i++) { if (returncode == infectedcodes[i]) { blockFile(NULL, NULL, checkme); return E2CS_INFECTED; } } } if (defaultresult == 1) return E2CS_CLEAN; else if (defaultresult == 0) { blockFile(NULL, NULL, checkme); return E2CS_INFECTED; } if (returncode != 0) return E2CS_SCANERROR; return 0; } e2guardian-5.5.8r/src/contentscanners/icapscan.cpp000066400000000000000000000551541477372360500222470ustar00rootroot00000000000000// ICAP server content scanning plugin // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../String.hpp" #include "../ContentScanner.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" #include #include #include #include #include #include // for gethostby #include // DEFINES #define ICAP_CONTINUE E2CS_MAX + 1 #define ICAP_NODATA E2CS_MAX + 2 // GLOBALS extern OptionContainer o; // DECLARATIONS // class name is relevant! class icapinstance : public CSPlugin { public: icapinstance(ConfigVar &definition) : CSPlugin(definition), usepreviews(false), previewsize(0), supportsXIF(false), needsBody(false){}; int willScanRequest(const String &url, const char *user, FOptionContainer* &foc, const char *ip, bool post, bool reconstituted, bool exception, bool bypass); int scanMemory(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc, const char *ip, const char *object, unsigned int objectsize, NaughtyFilter *checkme, const String *disposition, const String *mimetype); int scanFile(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc, const char *ip, const char *filename, NaughtyFilter *checkme, const String *disposition, const String *mimetype); int init(void *args); private: // ICAP server hostname, IP and port String icaphost; String icapip; unsigned int icapport; // URL for the AV service String icapurl; // whether or not to send ICAP message previews, and the preview object size bool usepreviews; unsigned int previewsize; // supports X-Infection-Found and/or needs us to look at the whole body bool supportsXIF; bool needsBody; // Send ICAP request headers to server bool doHeaders(Socket &icapsock, HTTPHeader *reqheader, HTTPHeader *respheader, unsigned int objectsize); // Check data returned from ICAP server and return one of our standard return codes int doScan(Socket &icapsock, HTTPHeader *docheader, const char *object, unsigned int objectsize, NaughtyFilter *checkme); }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin CSPlugin *icapcreate(ConfigVar &definition) { return new icapinstance(definition); } // end of Class factory // don't scan POST data or reconstituted data - wouldn't work for multi-part posts // without faking request headers, as we are only passed a single part, not the whole request verbatim int icapinstance::willScanRequest(const String &url, const char *user, FOptionContainer* &foc, const char *ip, bool post, bool reconstituted, bool exception, bool bypass) { if (post || reconstituted) return E2CS_NOSCAN; else { return CSPlugin::willScanRequest(url, user, foc, ip, post, reconstituted, exception, bypass); } } // initialise the plugin - determine icap ip, port & url int icapinstance::init(void *args) { // always include these lists if (!readStandardLists()) { return E2CS_ERROR; } icapurl = cv["icapurl"]; // format: icap://icapserver:1344/avscan if (icapurl.length() < 3) { E2LOGGER_error("Error reading icapurl option."); return E2CS_ERROR; // it would be far better to do a test connection } icaphost = icapurl.after("//"); icapport = icaphost.after(":").before("/").toInteger(); if (icapport == 0) { icapport = 1344; } icaphost = icaphost.before("/"); if (icaphost.contains(":")) { icaphost = icaphost.before(":"); } struct hostent *host; if ((host = gethostbyname(icaphost.toCharArray())) == 0) { E2LOGGER_error("Error resolving icap host address."); return E2CS_ERROR; } icapip = inet_ntoa(*(struct in_addr *)host->h_addr_list[0]); DEBUG_avscan("ICAP server is ", icapip ); // try to connect to the ICAP server and perform an OPTIONS request Socket icapsock; try { if (icapsock.connect(icapip.toCharArray(), icapport) < 0) { throw std::runtime_error("Could not connect to server"); } String line("OPTIONS " + icapurl + " ICAP/1.0\r\nHost: " + icaphost + "\r\n\r\n"); icapsock.writeString(line.toCharArray()); // parse the response char buff[8192]; // first line - look for 200 OK icapsock.getLine(buff, 8192, o.content.content_scanner_timeout); line = buff; DEBUG_avscan("ICAP/1.0 OPTIONS response: ", line); if (line.after(" ").before(" ") != "200") { E2LOGGER_error("ICAP response not 200 OK"); return E2CS_WARNING; //throw std::runtime_error("Response not 200 OK"); } while (icapsock.getLine(buff, 8192, o.content.content_scanner_timeout) > 0) { line = buff; if (line.startsWith("\r")) { break; } else if (line.startsWith("Preview:")) { previewsize = line.after(": ").toInteger(); if (previewsize > 0) usepreviews = true; } else if (line.startsWith("Server:")) { if (line.contains("AntiVir-WebGate")) { needsBody = true; } } else if (line.startsWith("Service-ID:")) { if (line.contains("KAVIcap")) { needsBody = true; } } else if (line.startsWith("X-Allow-Out:")) { if (line.contains("X-Infection-Found")) { supportsXIF = true; } // Dr web bug ICAP response header without suportXIF } else if (line.startsWith("Service: Dr.Web")) { supportsXIF = true; } DEBUG_avscan("ICAP/1.0 OPTIONS response part: ", line); } icapsock.close(); } catch (std::exception &e) { E2LOGGER_error("ICAP server did not respond to OPTIONS request: ", e.what()); return E2CS_ERROR; } if (usepreviews){ DEBUG_avscan( "Message previews enabled; size: ", previewsize); } else { DEBUG_avscan("Message previews enabled; size: disabled"); } return E2CS_OK; } // send memory buffer to ICAP server for scanning int icapinstance::scanMemory(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc, const char *ip, const char *object, unsigned int objectsize, NaughtyFilter *checkme, const String *disposition, const String *mimetype) { lastvirusname = lastmessage = ""; Socket icapsock; if (not doHeaders(icapsock, requestheader, docheader, objectsize)) { icapsock.close(); return E2CS_SCANERROR; } if (usepreviews && (objectsize > previewsize)){ DEBUG_avscan("Sending memory date to icap preview first"); } unsigned int sent = 0; if (usepreviews && (objectsize > previewsize)) { try { if (!icapsock.writeToSocket(object, previewsize, 0, o.content.content_scanner_timeout)) { throw std::runtime_error("standard error"); } sent += previewsize; icapsock.writeString("\r\n0\r\n\r\n"); int rc = doScan(icapsock, docheader, object, objectsize, checkme); if (rc != ICAP_CONTINUE) return rc; // some servers send "continue" immediately followed by another response if (icapsock.checkForInput()) { int rc = doScan(icapsock, docheader, object, objectsize, checkme); if (rc != ICAP_NODATA) return rc; } char objectsizehex[32]; snprintf(objectsizehex, sizeof(objectsizehex), "%x\r\n", objectsize - previewsize); icapsock.writeString(objectsizehex); } catch (std::exception &e) { DEBUG_avscan("Exception sending message preview to ICAP: ", e.what()); // this *might* just be an early response & closed connection if (icapsock.checkForInput()) { int rc = doScan(icapsock, docheader, object, objectsize, checkme); if (rc != ICAP_NODATA) return rc; } icapsock.close(); lastmessage = "Exception sending message preview to ICAP"; E2LOGGER_error(lastmessage, e.what()); return E2CS_SCANERROR; } } try { if(icapsock.writeToSocket(object + sent, objectsize - sent, 0, o.content.content_scanner_timeout)) { DEBUG_avscan("total sent to icap: ", objectsize); icapsock.writeString("\r\n0\r\n\r\n"); // end marker DEBUG_avscan("memory was sent to icap"); } } catch (std::exception &e) { DEBUG_avscan("Exception sending memory file to ICAP: ", e.what()); // this *might* just be an early response & closed connection if (icapsock.checkForInput()) { int rc = doScan(icapsock, docheader, object, objectsize, checkme); if (rc != ICAP_NODATA) return rc; } icapsock.close(); lastmessage = "Exception sending memory file to ICAP"; E2LOGGER_error(lastmessage, e.what()); return E2CS_SCANERROR; } return doScan(icapsock, docheader, object, objectsize, checkme); } // send file contents for scanning int icapinstance::scanFile(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc, const char *ip, const char *filename, NaughtyFilter *checkme, const String *disposition, const String *mimetype) { lastmessage = lastvirusname = ""; int filefd = open(filename, O_RDONLY); if (filefd < 0) { DEBUG_avscan("Error opening file (", filename, "): ", strerror(errno)); lastmessage = "Error opening file to send to ICAP"; E2LOGGER_error(lastmessage, strerror(errno)); return E2CS_SCANERROR; } lseek(filefd, 0, SEEK_SET); unsigned int filesize = lseek(filefd, 0, SEEK_END); Socket icapsock; if (not doHeaders(icapsock, requestheader, docheader, filesize)) { icapsock.close(); close(filefd); return E2CS_SCANERROR; } lseek(filefd, 0, SEEK_SET); unsigned int sent = 0; char *data = new char[previewsize]; char *object = new char[100]; int objectsize = 0; DEBUG_avscan("About to send file data to icap"); if (usepreviews && (filesize > previewsize)) { try { while (sent < previewsize) { int rc = read(filefd, data, previewsize); if (rc < 0) { throw std::runtime_error("could not read from file"); } if (rc == 0) { break; // should never happen } if (!icapsock.writeToSocket(data, rc, 0, o.content.content_scanner_timeout)) { throw std::runtime_error("could not write to socket"); } memcpy(object, data, (rc > 100) ? 100 : rc); objectsize += (rc > 100) ? 100 : rc; sent += rc; } icapsock.writeString("\r\n0\r\n\r\n"); int rc = doScan(icapsock, docheader, object, objectsize, checkme); if (rc != ICAP_CONTINUE) { delete[] data; close(filefd); return rc; } // some servers send "continue" immediately followed by another response if (icapsock.checkForInput()) { int rc = doScan(icapsock, docheader, object, objectsize, checkme); if (rc != ICAP_NODATA) { delete[] data; close(filefd); return rc; } } char objectsizehex[32]; snprintf(objectsizehex, sizeof(objectsizehex), "%x\r\n", filesize - previewsize); icapsock.writeString(objectsizehex); } catch (std::exception &e) { icapsock.close(); lastmessage = "Exception sending message preview to ICAP"; E2LOGGER_error(lastmessage, e.what()); DEBUG_avscan(lastmessage, e.what()); delete[] data; close(filefd); // this *might* just be an early response & closed connection if (icapsock.checkForInput()) { int rc = doScan(icapsock, docheader, object, objectsize, checkme); if (rc != ICAP_NODATA) return rc; } return E2CS_SCANERROR; } } delete[] data; data = new char[256 * 1024]; // 256k try { while (sent < filesize) { int rc = read(filefd, data, 256 * 1024); DEBUG_avscan("reading icap file rc: ", rc); if (rc < 0) { DEBUG_avscan("error reading icap file so throwing exception"); throw std::runtime_error("could not read from file"); } if (rc == 0) { DEBUG_avscan("got zero bytes reading icap file"); break; // should never happen } memcpy(object + objectsize, data, (rc > (100 - objectsize)) ? (100 - objectsize) : rc); objectsize += (rc > (100 - objectsize)) ? (100 - objectsize) : rc; if (!icapsock.writeToSocket(data, rc, 0, o.content.content_scanner_timeout)) { throw std::runtime_error("Unable to write to socket"); }; sent += rc; } DEBUG_avscan("total sent to icap: ", sent); icapsock.writeString("\r\n0\r\n\r\n"); // end marker DEBUG_avscan("file was sent to icap"); } catch (std::exception &e) { DEBUG_avscan( "Exception sending file to ICAP: ", e.what()); lastmessage = "Exception sending file to ICAP"; E2LOGGER_error(lastmessage, e.what()); delete[] data; close(filefd); // this *might* just be an early response & closed connection if (icapsock.checkForInput()) { int rc = doScan(icapsock, docheader, object, objectsize, checkme); if (rc != ICAP_NODATA) return rc; } return E2CS_SCANERROR; } close(filefd); delete[] data; return doScan(icapsock, docheader, object, objectsize, checkme); } // send ICAP request headers, returning success or failure bool icapinstance::doHeaders(Socket &icapsock, HTTPHeader *reqheader, HTTPHeader *respheader, unsigned int objectsize) { int rc = icapsock.connect(icapip.toCharArray(), icapport); if (rc) { lastmessage = "Error connecting to ICAP server"; DEBUG_avscan(lastmessage); E2LOGGER_error(lastmessage); return false; } char objectsizehex[32]; // encapsulated HTTP request header: // use a dummy unless it proves absolutely necessary to do otherwise, // as using real data could lead to e.g. yet another source of password // leakage over the network. String encapsulatedheader("GET " + reqheader->getUrl() + " HTTP/1.0\r\n\r\n"); // body chunk size in hex - either full body, or just preview if (usepreviews && (objectsize > previewsize)) { snprintf(objectsizehex, sizeof(objectsizehex), "%x\r\n", previewsize); } else { snprintf(objectsizehex, sizeof(objectsizehex), "%x\r\n", objectsize); } // encapsulated HTTP response header: // use real data, because scanners can use this to aid the process /*String httpresponseheader; for (std::deque::iterator i = respheader->header.begin(); i != respheader->header.end(); i++) { httpresponseheader += (*i) + "\r\n"; } httpresponseheader += "\r\n";*/ String httpresponseheader("HTTP/1.0 200 OK\r\n\r\n"); // ICAP header itself String icapheader("RESPMOD " + icapurl + " ICAP/1.0\r\nHost: " + icaphost + "\r\nAllow: 204\r\nEncapsulated: req-hdr=0, res-hdr=" + String(encapsulatedheader.length()) + ", res-body=" + String(httpresponseheader.length() + encapsulatedheader.length())); if (usepreviews && (objectsize > previewsize)) { icapheader += "\r\nPreview: " + String(previewsize); } icapheader += "\r\n\r\n"; DEBUG_avscan("About to send icapheader:\n", icapheader, encapsulatedheader, httpresponseheader, objectsizehex); try { icapsock.writeString(icapheader.toCharArray()); icapsock.writeString(encapsulatedheader.toCharArray()); icapsock.writeString(httpresponseheader.toCharArray()); icapsock.writeString(objectsizehex); } catch (std::exception &e) { lastmessage = "Exception sending headers to ICAP"; DEBUG_avscan(lastmessage, e.what()); E2LOGGER_error(lastmessage, e.what()); return false; } return true; } // check data received from ICAP server and interpret as virus name & return value int icapinstance::doScan(Socket &icapsock, HTTPHeader *docheader, const char *object, unsigned int objectsize, NaughtyFilter *checkme) { char *data = new char[8192]; try { String line; int rc = icapsock.getLine(data, 8192, o.content.content_scanner_timeout); if (rc == 0) return ICAP_NODATA; line = data; DEBUG_avscan("reply from icap: ", line); // reply is of the format: // ICAP/1.0 204 No Content Necessary (etc) String returncode(line.after(" ").before(" ")); if (returncode == "204") { DEBUG_avscan("ICAP says clean!"); delete[] data; return E2CS_CLEAN; } else if (returncode == "100") { DEBUG_avscan("ICAP says continue!"); // discard rest of headers (usually just a blank line) // this is so we are in the right place in the data stream to // call doScan() again later, because people like Symantec seem // to think sending code 100 then code 204 one after the other // is not an abuse of the ICAP specification. while (icapsock.getLine(data, 8192, o.content.content_scanner_timeout) > 0) { if (data[0] == 13) break; } delete[] data; return ICAP_CONTINUE; } else if (returncode == "200") { DEBUG_avscan("ICAP says maybe not clean!"); while (icapsock.getLine(data, 8192, o.content.content_scanner_timeout) > 0) { if (data[0] == 13) // end marker break; line = data; // Symantec's engine gives us the virus name in the ICAP headers if (supportsXIF && line.startsWith("X-Infection-Found")) { DEBUG_avscan("ICAP says infected! (X-Infection-Found)"); lastvirusname = line.after("Threat=").before(";"); delete[] data; blockFile(NULL, NULL, checkme); return E2CS_INFECTED; } } // AVIRA's and KAV Antivir gives us 200 in all cases, so // - unfortunately - we must pay attention to the encapsulated // header/body. if (needsBody) { // grab & compare the HTTP return code from modified response // if it's been modified, assume there's an infection icapsock.getLine(data, 8192, o.content.content_scanner_timeout); line = data; DEBUG_avscan( "Comparing original return code to modified:", docheader->header.front(), " ", line); int respmodReturnCode = line.after(" ").before(" ").toInteger(); if (respmodReturnCode != docheader->returnCode()) { DEBUG_avscan("ICAP says infected! (returned header comparison)"); delete[] data; lastvirusname = "Unknown"; blockFile(NULL, NULL, checkme); return E2CS_INFECTED; } // ok - headers were identical, so look at encapsulated body // discard the rest of the encapsulated headers while (icapsock.getLine(data, 8192, o.content.content_scanner_timeout) > 0) { if (data[0] == 13) break; } // grab body chunk size icapsock.getLine(data, 8192, o.content.content_scanner_timeout); line = data; DEBUG_avscan("Comparing original body data to modified"); int bodysize = line.hexToInteger(); // get, say, the first 100 bytes and compare them to what we // originally sent to see if it has been modified unsigned int chunksize = (bodysize < 100) ? bodysize : 100; if (chunksize > objectsize) chunksize = objectsize; icapsock.readFromSocket(data, chunksize, 0, o.content.content_scanner_timeout); if (memcmp(data, object, chunksize) == 0) { DEBUG_avscan("ICAP says clean! (body byte comparison)"); delete[] data; return E2CS_CLEAN; } else { DEBUG_avscan("ICAP says infected! (body byte comparison)"); delete[] data; lastvirusname = "Unknown"; blockFile(NULL, NULL, checkme); return E2CS_INFECTED; } } // even if we don't find an X-Infection-Found header, // the file is still infected! DEBUG_avscan("ICAP says infected! (no further tests)"); delete[] data; lastvirusname = "Unknown"; blockFile(NULL, NULL, checkme); return E2CS_INFECTED; } else if (returncode == "404") { DEBUG_avscan("ICAP says no such service!"); lastmessage = "ICAP reports no such service"; E2LOGGER_error(lastmessage, " check your server URL"); delete[] data; return E2CS_SCANERROR; } else { lastmessage = "ICAP returned unrecognised response code."; DEBUG_avscan(lastmessage, returncode); E2LOGGER_error(lastmessage, returncode); delete[] data; return E2CS_SCANERROR; } delete[] data; } catch (std::exception &e) { lastmessage = "Exception getting reply from ICAP."; E2LOGGER_error(lastmessage, e.what()); DEBUG_avscan(lastmessage, e.what()); delete[] data; return E2CS_SCANERROR; } // it is generally NOT a good idea, when using virus scanning, // to continue as if nothing went wrong by default! return E2CS_SCANERROR; } e2guardian-5.5.8r/src/contentscanners/kavdscan.cpp000066400000000000000000000133761477372360500222600ustar00rootroot00000000000000// Kaspersky AV Daemon content scanning plugin // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../String.hpp" #include "../ContentScanner.hpp" #include "../UDSocket.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" #include #include #include #include // GLOBALS extern OptionContainer o; // IMPLEMENTATION // class name is relevant class kavdinstance : public CSPlugin { public: kavdinstance(ConfigVar &definition) : CSPlugin(definition){}; int scanFile(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc, const char *ip, const char *filename, NaughtyFilter *checkme, const String *disposition, const String *mimetype); int init(void *args); private: // UNIX domain socket path for KAVD String udspath; // File path prefix for chrooted KAVD String pathprefix; }; // class factory code *MUST* be included in every plugin CSPlugin *kavdcreate(ConfigVar &definition) { return new kavdinstance(definition); } // end of Class factory // initialise plugin int kavdinstance::init(void *args) { int rc; if ((rc = CSPlugin::init(args)) != E2CS_OK) return rc; udspath = cv["kavdudsfile"]; if (udspath.length() < 3) { E2LOGGER_error("Error reading kavdudsfile option."); return E2CS_ERROR; // it would be far better to do a test connection to the file but // could not be arsed for now } // read in path prefix pathprefix = cv["pathprefix"]; return E2CS_OK; } // no need to replace the inheritied scanMemory() which just calls scanFile() // there is no capability to scan memory with kavdscan as we pass it // a file name to scan. So we save the memory to disk and pass that. // Then delete the temp file. int kavdinstance::scanFile(HTTPHeader *requestheader, HTTPHeader *docheader, const char *user, FOptionContainer* &foc, const char *ip, const char *filename, NaughtyFilter *checkme, const String *disposition, const String *mimetype) { lastvirusname = lastmessage = ""; // mkstemp seems to only set owner permissions, so our AV daemon won't be // able to read the file, unless it's running as the same user as us. that's // not usually very convenient. so instead, just allow group read on the // file, and tell users to make sure the daemongroup option is friendly to // the AV daemon's group membership. // chmod can error with EINTR, ignore this? if (chmod(filename, S_IRGRP | S_IRUSR) != 0) { E2LOGGER_error("Could not change file ownership to give kavd read access: ", strerror(errno)); return E2CS_SCANERROR; }; String command("SCAN bPQRSTUW "); if (pathprefix.length()) { String fname(filename); command += fname.after(pathprefix.toCharArray()); } else { command += filename; } command += "\r\n"; DEBUG_avscan("kavdscan command:", command); UDSocket stripedsocks; if (stripedsocks.getFD() < 0) { E2LOGGER_error("Error creating socket for talking to kavdscan"); return E2CS_SCANERROR; } if (stripedsocks.connect(udspath.toCharArray()) < 0) { E2LOGGER_error("Error connecting to kavdscan socket"); stripedsocks.close(); return E2CS_SCANERROR; } char *buff = new char[4096]; memset(buff, 0, 4096); int rc; try { // read kaspersky kavdscan (AV Enging Server) - format: 2xx greeting rc = stripedsocks.getLine(buff, 4096, o.content.content_scanner_timeout); } catch (std::exception &e) { } if (buff[0] != '2') { delete[] buff; stripedsocks.close(); E2LOGGER_error("kavdscan did not return ok"); return E2CS_SCANERROR; } try { stripedsocks.writeString(command.toCharArray()); } catch (std::exception &e) { delete[] buff; stripedsocks.close(); E2LOGGER_error("unable to write to kavdscan"); return E2CS_SCANERROR; } try { rc = stripedsocks.getLine(buff, 4096, o.content.content_scanner_timeout); } catch (std::exception &e) { delete[] buff; stripedsocks.close(); E2LOGGER_error("Error reading kavdscan socket"); return E2CS_SCANERROR; } String reply(buff); DEBUG_avscan("Got from kavdscan:", reply); if (reply[0] == '2') { // clean DEBUG_avscan("kavdscan - clean"); delete[] buff; stripedsocks.close(); return E2CS_CLEAN; } if (reply.startsWith("322")) { // infected // patch to handle multiple virii in kavd response // originally submitted by cahya while (reply[0] != '2' && rc != 0) { reply.removeWhiteSpace(); lastvirusname = lastvirusname + " " + reply.after("322-").before(" "); try { rc = stripedsocks.getLine(buff, 4096, o.content.content_scanner_timeout); } catch (std::exception &e) { delete[] buff; stripedsocks.close(); E2LOGGER_error("Error reading kavdscan socket"); return E2CS_SCANERROR; } reply = buff; DEBUG_avscan("Got from kavdscan:", reply); } E2LOGGER_error("lastvirusname: ", lastvirusname); delete[] buff; stripedsocks.close(); // format: 322 nastyvirus blah blockFile(NULL, NULL, checkme); return E2CS_INFECTED; } delete[] buff; stripedsocks.close(); // must be an error then lastmessage = reply; return E2CS_SCANERROR; } e2guardian-5.5.8r/src/downloadmanagers/000077500000000000000000000000001477372360500200665ustar00rootroot00000000000000e2guardian-5.5.8r/src/downloadmanagers/default.cpp000066400000000000000000000130301477372360500222130ustar00rootroot00000000000000// Default download manager, used when no other plugin matches the user agent // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../DownloadManager.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" #include #include #include #include // GLOBALS extern OptionContainer o; // DECLARATIONS class dminstance : public DMPlugin { public: dminstance(ConfigVar &definition) : DMPlugin(definition) {}; int in(DataBuffer *d, Socket *sock, Socket *peersock, HTTPHeader *requestheader, HTTPHeader *docheader, bool wantall, int *headersent, bool *toobig); // default plugin is as basic as you can get - no initialisation, and uses the default // set of matching mechanisms. uncomment and implement these to override default behaviour. //int init(void* args); //bool willHandle(HTTPHeader *requestheader, HTTPHeader *docheader); }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin DMPlugin *defaultdmcreate(ConfigVar &definition) { DEBUG_trace("Creating default DM"); return new dminstance(definition); } // end of Class factory // uncomment these if you wish to replace the default inherited functions // < 0 = error // = 0 = ok // > 0 = warning //int dminstance::init(void* args) { // return 0; //} //int dminstance::quit(void) { // return 0; //} // download body for this request int dminstance::in(DataBuffer *d, Socket *sock, Socket *peersock, class HTTPHeader *requestheader, class HTTPHeader *docheader, bool wantall, int *headersent, bool *toobig) { //DataBuffer *d = where to stick the data back into //Socket *sock = where to read from //Socket *peersock = browser to send stuff to for keeping it alive //HTTPHeader *requestheader = header client used to request //HTTPHeader *docheader = header used for sending first line of reply //bool wantall = to determine if just content filter or a full scan //int *headersent = to use to send the first line of header if needed // or to mark the header has already been sent //bool *toobig = flag to modify to say if it could not all be downloaded DEBUG_trace("Inside default download manager plugin icap=", d->icap); // To access settings for the plugin use the following example: // std::cerr << "cvtest:" << cv["dummy"] << std::endl; // int rc = 0; d->got_all = false; d->bytes_toget = docheader->contentLength(); if (!d->icap) { DEBUG_dwload("tranencodeing is ", docheader->transferEncoding()); d->chunked = docheader->transferEncoding().contains("chunked"); } // if using non-persistent connections, some servers will not report // a content-length. in these situations, just download everything. d->geteverything = false; if ((d->bytes_toget < 0) || (d->chunked)) d->geteverything = true; d->swappedtodisk = false; d->doneinitialdelay = false; struct timeval themdays; struct timeval nowadays; gettimeofday(&themdays, NULL); // buffer size for streaming downloads off_t blocksize = 32768; // set to a sensible minimum if (!wantall && (blocksize > o.content.max_content_filter_size)) blocksize = o.content.max_content_filter_size; else if (wantall && (blocksize > o.content.max_content_ramcache_scan_size)) blocksize = o.content.max_content_ramcache_scan_size; DEBUG_dwload("blocksize: ", blocksize); while ((d->bytes_toget > 0) || d->geteverything) { DEBUG_dwload("toget:", d->bytes_toget, " geteverything ", d->geteverything); // send x-header keep-alive here if (o.content.trickle_delay > 0) { gettimeofday(&nowadays, NULL); if (d->doneinitialdelay ? nowadays.tv_sec - themdays.tv_sec > o.content.trickle_delay : nowadays.tv_sec - themdays.tv_sec > o.content.initial_trickle_delay) { themdays.tv_sec = nowadays.tv_sec; d->doneinitialdelay = true; if ((*headersent) < 1) { DEBUG_dwload("sending first line of header first"); if (!d->icap) { docheader->out(NULL, peersock, __E2HEADER_SENDFIRSTLINE); (*headersent) = 1; } } DEBUG_dwload("trickle delay - sending X-E2KeepAlive: on"); if (!d->icap) peersock->writeString("X-E2GKeepAlive: on\r\n"); } } int read_res; int rc; int bsize = blocksize; if((!d->geteverything) && (d->bytes_toget < bsize)) bsize = d->bytes_toget; DEBUG_dwload("bsize is ", bsize); rc = d->readInFromSocket(sock,bsize,wantall, read_res); if(read_res & DB_TOBIG) *toobig = true; if (rc <= 0) break; } if (!(*toobig) && !d->swappedtodisk) { // won't deflate stuff swapped to disk if (d->decompress.contains("deflate")) { DEBUG_dwload("zlib format"); d->zlibinflate(false); // incoming stream was zlib compressed } else if (d->decompress.contains("gzip")) { DEBUG_dwload("gzip format"); d->zlibinflate(true); // incoming stream was gzip compressed } } d->bytesalreadysent = 0; DEBUG_trace("Leaving default download manager plugin"); return 0; } e2guardian-5.5.8r/src/downloadmanagers/fancy.cpp000066400000000000000000000406351477372360500217020ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../DownloadManager.hpp" #include "../OptionContainer.hpp" #include "../HTMLTemplate.hpp" #include "../ConnectionHandler.hpp" #include "../Logger.hpp" #include #include #include #include #include #include #include // GLOBALS extern OptionContainer o; // DECLARATIONS class fancydm : public DMPlugin { public: fancydm(ConfigVar &definition) : DMPlugin(definition), upperlimit(0), toobig_unscanned(false), toobig_notdownloaded(false){}; int in(DataBuffer *d, Socket *sock, Socket *peersock, HTTPHeader *requestheader, HTTPHeader *docheader, bool wantall, int *headersent, bool *toobig); int init(void *args); bool sendLink(Socket &peersock, String &linkurl, String &prettyurl); private: // customisable fancy DM template HTMLTemplate progresspage; // upper (non-scanned) file download size limit // part of "multi-stage" downloads for when content length is unknown: // - start downloading & hope for the best // - if too large, warn the user, but keep downloading (won't be scanned) // - if larger than upper limit also, kill download. off_t upperlimit; // was file too large to be scanned? bool toobig_unscanned; // was file so large as to not even be downloaded? bool toobig_notdownloaded; // format an integer in seconds as hours:minutes:seconds std::string timestring(const int seconds); // format a number of bytes as a number of Gb/Mb/Kb String bytestring(const off_t bytes); }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin DMPlugin *fancydmcreate(ConfigVar &definition) { DEBUG_trace("Creating fancy DM"); return new fancydm(definition); } // end of Class factory // initialisation - load the template file int fancydm::init(void *args) { // call inherited init DMPlugin::init(args); StoryBoardOptions::SB_entry_map sen; sen.entry_function = cv["story_function"]; if (sen.entry_function.length() > 0) { sen.entry_id = ENT_STORYB_DM_FANCY; story_entry = sen.entry_id; o.story.dm_entry_dq.push_back(sen); } else { E2LOGGER_error("No story_function defined in fancy DM plugin config"); return -1; } // read in absolute max download limit upperlimit = cv["maxdownloadsize"].toOffset() * 1024; if (upperlimit <= o.content.max_content_filecache_scan_size) upperlimit = 0; DEBUG_dwload("Upper download limit: ", upperlimit); String fname(cv["template"]); if (fname.length() > 0) { // read the template file, and return OK on success, error on failure. fname = o.config.languagepath + fname; return progresspage.readTemplateFile(fname.toCharArray(), "-FILENAME-|-FILESIZE-|-SERVERIP-") ? 0 : -1; } else { // eek! there's no template option in our config. E2LOGGER_error("Template not specified for fancy download manager"); return -2; } } // call template's downloadlink JavaScript function bool fancydm::sendLink(Socket &peersock, String &linkurl, String &prettyurl) { String mess("\n"); peersock.writeString(mess.toCharArray()); // send text-only version for non-JS-enabled browsers // 1220 "Scan complete.

Click here to download: " // 1221 "Download complete; file not scanned.

Click here to download: " // 1222 "File too large to cache.

Click here to re-download, bypassing virus scanning: " mess = "

"; mess += o.language_list.getTranslation(toobig_notdownloaded ? 1222 : (toobig_unscanned ? 1221 : 1220)); mess += "" + prettyurl + "

"; peersock.writeString(mess.toCharArray()); peersock.writeString("\r\n"); if(!peersock.writeString("\n")) return false; if (toobig_notdownloaded) { // add URL to clean cache (for all groups) addToClean(prettyurl, o.filter.filter_groups + 1); } return true; } // download body for this request int fancydm::in(DataBuffer *d, Socket *sock, Socket *peersock, class HTTPHeader *requestheader, class HTTPHeader *docheader, bool wantall, int *headersent, bool *toobig) { //DataBuffer *d = where to stick the data back into //Socket *sock = where to read from //Socket *peersock = browser to send stuff to for keeping it alive //HTTPHeader *docheader = header used for sending first line of reply //HTTPHeader *requestheader = header client used to request //bool wantall = to determine if just content filter or a full scan //int *headersent = to use to send the first line of header if needed // or to mark the header has already been sent //bool *toobig = flag to modify to say if it could not all be downloaded DEBUG_trace("Inside fancy download manager plugin"); //int rc = 0; //off_t newsize; off_t expectedsize = docheader->contentLength(); d->bytes_toget = expectedsize; off_t bytessec = 0; off_t bytesgot = 0; int percentcomplete = 0; unsigned int eta = 0; int timeelapsed = 0; if (!d->icap) { DEBUG_dwload("tranencodeing is ", docheader->transferEncoding()); d->chunked = docheader->transferEncoding().contains("chunked"); } // if using non-persistent connections, some servers will not report // a content-length. in these situations, just download everything. d->geteverything = false; if ((d->bytes_toget < 0) || (d->chunked)) d->geteverything = true; if (expectedsize < 0) expectedsize = 0; bool initialsent = false; String message, jsmessage; d->swappedtodisk = false; d->doneinitialdelay = false; struct timeval starttime; struct timeval themdays; struct timeval nowadays; gettimeofday(&themdays, NULL); gettimeofday(&starttime, NULL); toobig_unscanned = false; toobig_notdownloaded = false; bool secondstage = false; // buffer size for streaming downloads off_t blocksize = 32768; // set to a sensible minimum if (!wantall && (blocksize > o.content.max_content_filter_size)) blocksize = o.content.max_content_filter_size; else if (wantall && (blocksize > o.content.max_content_ramcache_scan_size)) blocksize = o.content.max_content_ramcache_scan_size; DEBUG_dwload("blocksize: ", blocksize); // determine downloaded filename String filename(requestheader->disposition()); if (filename.length() == 0) { filename = requestheader->getUrl(); filename = requestheader->decode(filename); if (filename.contains("?")) filename = filename.before("?"); while (filename.contains("/")) filename = filename.after("/"); } while ((bytesgot < expectedsize) || d->geteverything) { // send text header to show status if (o.content.trickle_delay > 0) { gettimeofday(&nowadays, NULL); timeelapsed = nowadays.tv_sec - starttime.tv_sec; if ((!initialsent && timeelapsed > o.content.initial_trickle_delay) || (initialsent && nowadays.tv_sec - themdays.tv_sec > o.content.trickle_delay)) { initialsent = true; bytessec = bytesgot / timeelapsed; themdays.tv_sec = nowadays.tv_sec; if ((*headersent) < 1) { DEBUG_dwload("sending header for text status"); message = "HTTP/1.0 200 OK\nContent-Type: text/html\n\n"; // Output initial template std::deque::iterator i = progresspage.html.begin(); std::deque::iterator penultimate = progresspage.html.end() - 1; bool newline; while (i != progresspage.html.end()) { newline = false; message = *i; if (message == "-FILENAME-") { message = filename; } else if (message == "-FILESIZE-") { message = String(expectedsize); } else if (message == "-SERVERIP-") { message = peersock->getLocalIP(); } else if ((i == penultimate) || ((*(i + 1))[0] != '-')) { newline = true; } peersock->writeString(message.toCharArray()); // preserve line breaks from the original template file if (newline) peersock->writeString("\n"); i++; } // send please wait message for non-JS-enabled browsers // 1200 "Please wait - downloading to be scanned..." message = "\n"; peersock->writeString(message.toCharArray()); (*headersent) = 2; } DEBUG_dwload("trickle delay - sending progress..."); message = "Downloading status: "; // Output a call to template's JavaScript progressupdate function jsmessage = ""; peersock->writeString(jsmessage.toCharArray()); // send text only version for non-JS-enabled browsers. // checkme: translation? if (d->geteverything) { message = "\n"; } else { percentcomplete = bytesgot / (expectedsize / 100); eta = (expectedsize - bytesgot) / bytessec; message = "\n"; } peersock->writeString(message.toCharArray()); peersock->writeString("\r\n"); } } int read_res; int rc; int bsize = blocksize; if ((!d->geteverything) && (d->bytes_toget < bsize)) bsize = d->bytes_toget; DEBUG_dwload("bsize is ", bsize); rc = d->readInFromSocket(sock, bsize, wantall, read_res); if (read_res & DB_TOBIG) *toobig = true; if (read_res & DB_TOBIG_SCAN) { // multi-stage download enabled, and we don't know content length toobig_unscanned = true; if (d->geteverything && (upperlimit > 0)) { if ((!secondstage) && initialsent) { secondstage = true; // send download size warning message jsmessage = ""; peersock->writeString(jsmessage.toCharArray()); // text-only version message = "\n"; peersock->writeString(message.toCharArray()); peersock->writeString("\r\n"); // add URL to clean cache (for all groups) // TODO: aah - this will not work without clean cache! Needs a different method???? DEBUG_dwload("fancydm: file too big to be scanned, entering second stage of download"); } // too large to even download, let alone scan if (bytesgot > upperlimit) { DEBUG_dwload("fancydm: file too big to be downloaded, halting second stage of download"); toobig_unscanned = false; toobig_notdownloaded = true; break; } } else { // multi-stage download disabled, or we know content length // if swapped to disk and file too large for that too, then give up DEBUG_dwload("fancydm: file too big to be scanned, halting download"); toobig_unscanned = false; toobig_notdownloaded = true; break; } } if (read_res & DB_TOBIG_FILTER) { // may not need anything here } if (rc <= 0) break; } if (initialsent) { if (!d->swappedtodisk) { // if we sent textual content then we can't // stream the file to the user so we must save to disk for them // to download by clicking on the magic link // You can get to this point by having a large ram cache, or // slow internet connection with small initial trickle delay. // This should be rare. DEBUG_dwload("swapping to disk"); d->tempfilefd = d->getTempFileFD(); if (d->tempfilefd < 0) { E2LOGGER_error("error buffering complete to disk so skipping disk buffering"); } else { write(d->tempfilefd, d->data, d->buffer_length); d->swappedtodisk = true; d->tempfilesize = d->buffer_length; } } if (!(toobig_unscanned || toobig_notdownloaded)) { // Output a call to template's JavaScript nowscanning function peersock->writeString("\n"); // send text-only version // 1210 "Download Complete. Starting scan..." message = "\n"; peersock->writeString(message.toCharArray()); } // only keep full downloads if (!toobig_notdownloaded) (*d).preservetemp = true; (*d).dontsendbody = true; } if (!(*toobig) && !d->swappedtodisk) { // won't deflate stuff swapped to disk if (d->decompress.contains("deflate")) { DEBUG_dwload("zlib format"); d->zlibinflate(false); // incoming stream was zlib compressed } else if (d->decompress.contains("gzip")) { DEBUG_dwload("gzip format"); d->zlibinflate(true); // incoming stream was gzip compressed } } d->bytesalreadysent = 0; return 0; } // format an integer in seconds as hours:minutes:seconds std::string fancydm::timestring(const int seconds) { int hours = (int)floor((double)seconds / 3600); int minutes = (int)floor(((double)seconds / 60) - (hours * 3600)); int remainingseconds = seconds - (minutes * 60) - (hours * 3600); char result[9]; snprintf(result, 9, "%02i:%02i:%02i", hours, minutes, remainingseconds); return result; } // format a number of bytes as a number of Gb/Mb/Kb String fancydm::bytestring(const off_t bytes) { int b = (int)floor((double)bytes / (1024 * 1024 * 1024)); String num(b); if (b > 0) { return num += " Gb"; } b = (int)floor((double)bytes / (1024 * 1024)); num = b; if (b > 0) return num += " Mb"; b = (int)floor((double)bytes / 1024); num = b; if (b > 0) return num += " Kb"; b = (int)floor((double)bytes); num = b; return num += " bytes"; } e2guardian-5.5.8r/src/downloadmanagers/trickle.cpp000066400000000000000000000155031477372360500222330ustar00rootroot00000000000000// Trickle download manager - sends parts of a file being downloaded, a byte // at a time. // WARNING: Files which are/can be processed before they are complete - such // as certain image formats, shell scripts, and multimedia files - MAY have a // working, malicious portion sent to the browser before scanning has // completed! // For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "../DownloadManager.hpp" #include "../OptionContainer.hpp" #include "../Logger.hpp" #include #include #include #include // GLOBALS extern OptionContainer o; extern thread_local std::string thread_id; extern bool is_daemonised; // DECLARATIONS class trickledm : public DMPlugin { public: trickledm(ConfigVar &definition) : DMPlugin(definition){}; int in(DataBuffer *d, Socket *sock, Socket *peersock, HTTPHeader *requestheader, HTTPHeader *docheader, bool wantall, int *headersent, bool *toobig); int init(void *args); }; // IMPLEMENTATION // class factory code *MUST* be included in every plugin DMPlugin *trickledmcreate(ConfigVar &definition) { DEBUG_trace("Creating trickle DM"); return new trickledm(definition); } // end of Class factory // uncomment these if you wish to replace the default inherited functions // < 0 = error // = 0 = ok // > 0 = warning //int dminstance::init(void* args) { // return 0; //} //int dminstance::quit(void) { // return 0; //} int trickledm::init(void *args) { DMPlugin::init(args); StoryBoardOptions::SB_entry_map sen; sen.entry_function = cv["story_function"]; if (sen.entry_function.length() > 0) { sen.entry_id = ENT_STORYB_DM_TRICKLE; story_entry = sen.entry_id; o.story.dm_entry_dq.push_back(sen); return 0; } else { E2LOGGER_error("No story_function defined in trickle DM plugin config"); return -1; } } // download body for this request int trickledm::in(DataBuffer *d, Socket *sock, Socket *peersock, class HTTPHeader *requestheader, class HTTPHeader *docheader, bool wantall, int *headersent, bool *toobig) { //DataBuffer *d = where to stick the data back into //Socket *sock = where to read from //Socket *peersock = browser to send stuff to for keeping it alive //HTTPHeader *requestheader = header client used to request //HTTPHeader *docheader = header used for sending first line of reply //bool wantall = to determine if just content filter or a full scan //int *headersent = to use to send the first line of header if needed // or to mark the header has already been sent //bool *toobig = flag to modify to say if it could not all be downloaded DEBUG_trace("Inside trickle download manager plugin"); // To access settings for the plugin use the following example: // std::cout << "cvtest:" << cv["dummy"] << std::endl; //int rc = 0; d->bytesalreadysent = 0; d->bytes_toget = docheader->contentLength(); if (!d->icap) { DEBUG_dwload("tranencodeing is ", docheader->transferEncoding()); d->chunked = docheader->transferEncoding().contains("chunked"); } DEBUG_dwload("bytes remaining is ", d->bytes_toget); // if using non-persistent connections, some servers will not report // a content-length. in these situations, just download everything. d->geteverything = false; if ((d->bytes_toget < 0) || (d->chunked)) d->geteverything = true; d->swappedtodisk = false; d->doneinitialdelay = false; // struct timeval themdays; // struct timeval nowadays; // gettimeofday(&themdays, NULL); // buffer size for streaming downloads off_t blocksize = 32768; // set to a sensible minimum if (!wantall && (blocksize > o.content.max_content_filter_size)) blocksize = o.content.max_content_filter_size; else if (wantall && (blocksize > o.content.max_content_ramcache_scan_size)) blocksize = o.content.max_content_ramcache_scan_size; DEBUG_dwload("blocksize: ", blocksize); while ((d->bytes_toget > 0) || d->geteverything) { // send keep-alive bytes here if (o.content.trickle_delay > 0) { // themdays.tv_sec = nowadays.tv_sec; d->doneinitialdelay = true; if ((*headersent) < 1) { DEBUG_dwload("sending header first"); docheader->out(NULL, peersock, __E2HEADER_SENDALL); (*headersent) = 2; } if (!d->swappedtodisk) { // leave a kilobyte "barrier" so the whole file does not get sent before scanning if ((d->data_length > 1024) && (d->bytesalreadysent < (d->data_length - 1024))) { DEBUG_dwload("trickle delay - sending a byte from the memory buffer"); peersock->writeToSocket(d->data + (d->bytesalreadysent++), 1, 0, d->timeout); } else DEBUG_dwload("trickle delay - no unsent bytes remaining! (memory)"); } else { // check the file is at least one kilobyte ahead of our send pointer, so // the whole file does not get sent before scanning if (lseek(d->tempfilefd, d->bytesalreadysent + 1024, SEEK_SET) != (off_t) -1) { // ssize_t bytes_written; //new just remove GCC warning lseek(d->tempfilefd, d->bytesalreadysent, SEEK_SET); DEBUG_dwload("trickle delay - sending a byte from the file"); char byte; // bytes_written = read(d->tempfilefd, &byte, 1); peersock->writeToSocket(&byte, 1, 0, d->timeout); d->bytesalreadysent++; } else DEBUG_dwload("trickle delay - no unsent bytes remaining! (file)"); } } int read_res; int rc; int bsize = blocksize; if ((!d->geteverything) && (d->bytes_toget < bsize)) bsize = d->bytes_toget; DEBUG_dwload("bsize is ", bsize); rc = d->readInFromSocket(sock, bsize, wantall, read_res); if (read_res & DB_TOBIG) *toobig = true; if (rc <= 0) break; } if (!(*toobig) && !d->swappedtodisk) { // won't deflate stuff swapped to disk if (d->decompress.contains("deflate")) { DEBUG_dwload("zlib format"); d->zlibinflate(false); // incoming stream was zlib compressed } else if (d->decompress.contains("gzip")) { DEBUG_dwload("gzip format"); d->zlibinflate(true); // incoming stream was gzip compressed } } DEBUG_trace("Leaving trickle download manager plugin"); return 0; } e2guardian-5.5.8r/src/e2guardian.cpp000066400000000000000000000317331477372360500172750ustar00rootroot00000000000000// For all support, instructions and copyright go to: // http://e2guardian.org/ // Released under the GPL v2, with the OpenSSL exception described in the README file. // INCLUDES #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include "FatController.hpp" #include "SysV.hpp" #include "Queue.hpp" #include "Logger.hpp" #include "LoggerConfigurator.hpp" #include "UdpSocket.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __BENCHMARK #include #include "NaughtyFilter.hpp" #endif // GLOBALS OptionContainer o; thread_local std::string thread_id; std::atomic g_is_starting; LoggerConfigurator loggerConfig(&e2logger); bool is_daemonised; bool force_nodeamon = false; // regexp used during URL decoding by HTTPHeader // we want it compiled once, not every time it's used, so do so on startup RegExp urldecode_re; #ifdef HAVE_PCRE // regexes used for embedded URL extraction by NaughtyFilter RegExp absurl_re, relurl_re; #endif // DECLARATIONS int readCommandlineOptions(int &ret, int argc, char *argv[]); int startDaemon(); //int runBenchmarks(); bool check_enough_filedescriptors(); void prepareRegExp(); #define E2_LOAD 1 #define E2_EXIT 2 // IMPLEMENTATION int main(int argc, char *argv[]) { g_is_starting = true; thread_id = ""; int ret = 0; o.config.prog_name = PACKAGE; o.config.configfile = __CONFFILE; srand(time(NULL)); // Set current locale for proper character conversion setlocale(LC_ALL, ""); e2logger.setSyslogName(o.config.prog_name); // E2LOGGER_info("Start ", prog_name ); // No we are not starting here - we may be stopping, reloading etc #ifdef DEBUG_LOW e2logger.enable(LoggerSource::debug); // This is only for backward compatibilty with previous version // setting all debug features should be done in e2guardian.conf with the debuglevel setting // DEBUG_LOW mode is not suitable for production use so warn the user that is switched on E2LOGGER_warning("Running in debug_low mode..."); #endif #ifdef NOT_DEF // bespoke test code to be removed later - PIP UdpSocket test_udp; test_udp.connect("192.168.1.102", 33001); std::string line = "this is a test of upd logging"; if(test_udp.writeString(line)) { std::cerr << "upd write ok" << std::endl; } else { std::cerr << "upd write ok" << std::endl; } #endif DEBUG_trace("read CommandLineOptions"); int rc = readCommandlineOptions(ret, argc, argv); if (rc == E2_EXIT) return ret; DEBUG_trace("read Configfile: ", o.config.configfile); if (!o.read_config(o.config.configfile)) { E2LOGGER_error("Error parsing the e2guardian.conf file or other e2guardian configuration files"); exit(1); // OptionContainer class had an error reading the conf or other files so exit with error } if(force_nodeamon) o.proc.no_daemon = true; if (o.config.total_block_list) { if (o.readinStdin()) { DEBUG_debug("Total block lists read OK from stdin."); } else { E2LOGGER_error("Error on reading total_block_list"); } } DEBUG_trace("create Lists"); if (!o.createLists(0)) { E2LOGGER_error("Error reading filter group conf file(s)."); return 1; } prepareRegExp(); return startDaemon(); } int readCommandlineOptions(int &ret, int argc, char *argv[]) // returns E2_EXIT or E2_LOAD { bool needreset = false; std::string debugoptions; DEBUG_trace("parse Options"); for (int i = 1; i < argc; i++) { // first check for config file if (argv[i][0] == '-') { for (unsigned int j = 1; j < strlen(argv[i]); j++) { char option = argv[i][j]; bool dobreak = false; switch (option) { case 'c': if ((i + 1) < argc) { o.config.configfile = argv[i + 1]; i++; dobreak = true; } else { std::cerr << "No config file specified!" << std::endl; ret = 1; return E2_EXIT; } break; } if (dobreak) break; } } } for (int i = 1; i < argc; i++) { // then rest of args bool skip_next = false; if (argv[i][0] == '-') { for (unsigned int j = 1; j < strlen(argv[i]); j++) { char option = argv[i][j]; bool dobreak = false; switch (option) { case 'q': o.read_config(o.config.configfile, false); ret = sysv_kill(o.proc.pid_filename, true); return E2_EXIT; case 'Q': o.read_config(o.config.configfile, false); sysv_kill(o.proc.pid_filename, false); // give the old process time to die while (sysv_amirunning(o.proc.pid_filename)) sleep(1); unlink(o.proc.pid_filename.c_str()); // remember to reset config before continuing needreset = true; break; case 's': o.read_config(o.config.configfile, false); ret = sysv_showpid(o.proc.pid_filename); return E2_EXIT; case 'r': case 'g': o.read_config(o.config.configfile, false); ret = sysv_hup(o.proc.pid_filename); return E2_EXIT; case 't': o.read_config(o.config.configfile, false); ret = sysv_usr1(o.proc.pid_filename); return E2_EXIT; case 'v': std::cout << "e2guardian " << PACKAGE_VERSION << std::endl << std::endl << "Built with: " << E2_CONFIGURE_OPTIONS << std::endl; // temporary check code //{ //unsigned int ilen = 0; //ilen = ~ilen; //std::cout << " max int is " << ilen << std::endl; //} ret = 0; return E2_EXIT; case 'N': force_nodeamon = true; break; case 'c': // already processed this - so skip skip_next = true; break; case 'i': o.config.total_block_list = true; break; case 'd': if ((i + 1) < argc) { debugoptions = argv[i + 1]; i++; dobreak = true; }; break; case 'h': default: std::cout << "Usage: " << argv[0] << " [-c ConfigFileName|-v|-h|-N|-q|-Q|-s|-r|-g|-i] [-d debuglevel]" << std::endl; std::cout << " -v gives the version number and build options." << std::endl; std::cout << " -h gives this message." << std::endl; std::cout << " -c allows you to specify a different configuration file location." << std::endl; std::cout << " -N Do not go into the background." << std::endl; std::cout << " -q causes e2guardian to kill any running copy." << std::endl; std::cout << " -Q kill any running copy AND start a new one with current options." << std::endl; std::cout << " -s shows the parent process PID and exits." << std::endl; std::cout << " -r reloads lists and group config files by issuing a HUP," << std::endl; std::cout << " but this does not reset the httpworkers option (amongst others)." << std::endl; std::cout << " -g same as -r (Issues a HUP)" << std::endl; std::cout << " -t rotate logs (Issues a USR1)" << std::endl; std::cout << " -d allows you to specify a debuglevel" << std::endl; std::cout << " -i read lists from stdin" << std::endl; ret = 0; return E2_EXIT; } if (dobreak) break; // skip to the next argument } } if (skip_next) i++; } if (needreset) { DEBUG_trace("reset Options"); o.reset(); } if (!debugoptions.empty()) { loggerConfig.debuglevel(debugoptions); } return E2_LOAD; } int startDaemon() { DEBUG_trace("prepare Start"); if (sysv_amirunning(o.proc.pid_filename)) { E2LOGGER_error("I seem to be running already!"); return 1; // can't have two copies running!! } if (!check_enough_filedescriptors()) return 1; if (!o.proc.find_user_ids()) return 1; if (!o.proc.become_proxy_user()) return 1; DEBUG_trace("Starting Main loop"); while (true) { int rc = fc_controlit(); // its a little messy, but I wanted to split // all the ground work and non-daemon stuff // away from the daemon class // However the line is not so fine. // fc_controlit never returns 2 ??! KDG 2020-09-25 if (rc == 2) { // In order to re-read the conf files // we need to become root user again if (!o.proc.become_root_user()) return 1; DEBUG_trace("About to re-read conf file."); o.reset(); if (!o.read_config(o.config.configfile, true)) { // OptionContainer class had an error reading the conf or // other files so exit with errora E2LOGGER_error("Error re-parsing the e2guardian.conf file or other e2guardian configuration files"); return 1; } DEBUG_trace("conf file read."); while (waitpid(-1, NULL, WNOHANG) > 0) { } // mop up defunts // become low priv again if (!o.proc.become_proxy_user()) return 1; continue; } if (o.proc.is_daemonised) return 0; // exit without error if (rc > 0) { E2LOGGER_error("Exiting with error"); return rc; // exit returning the error number } return 0; } } void prepareRegExp() { urldecode_re.comp("%[0-9a-fA-F][0-9a-fA-F]"); // regexp for url decoding #ifdef HAVE_PCRE // todo: these only work with PCRE enabled (non-greedy matching). // change them, or make them a feature for which you need PCRE? absurl_re.comp("[\"'](http|ftp)://.*?[\"']"); // find absolute URLs in quotes relurl_re.comp("(href|src)\\s*=\\s*[\"'].*?[\"']"); // find relative URLs in quotes #endif } bool check_enough_filedescriptors() { // calc the number of listening processes int no_listen_fds; no_listen_fds = o.net.filter_ports.size() * o.net.filter_ip.size(); if (!o.net.TLS_filter_ports.empty()) { no_listen_fds += o.net.TLS_filter_ports.size() * o.net.filter_ip.size(); } if (o.net.transparenthttps_port > 0) no_listen_fds++; if (o.net.icap_port > 0) no_listen_fds++; struct rlimit rlim; if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) { E2LOGGER_error("getrlimit call returned error: ", errno); return false; } // enough fds needed for listening_fds + logger + ipcs + stdin/out/err // in addition to two for each worker thread int max_free_fds = rlim.rlim_cur - (no_listen_fds + 6); int fd_needed = (o.proc.http_workers * 2) + no_listen_fds + 6; if (((o.proc.http_workers * 2)) > max_free_fds) { E2LOGGER_error("httpworkers option in e2guardian.conf has a value too high for current file id limit (", rlim.rlim_cur, ")"); E2LOGGER_error("httpworkers ", o.proc.http_workers, " must not exceed 50% of ", max_free_fds); E2LOGGER_error("in this configuration."); E2LOGGER_error("Reduce httpworkers "); E2LOGGER_error("Or increase the filedescriptors available with ulimit -n to at least=", fd_needed); return false; // we can't have rampant proccesses can we? } return true; } e2guardian-5.5.8r/src/md5.cpp000066400000000000000000000325311477372360500157360ustar00rootroot00000000000000/* Functions to compute MD5 message digest of files or memory blocks. according to the definition of MD5 in RFC 1321 from April 1992. Copyright (C) 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Written by Ulrich Drepper , 1995. */ // Updated by Daniel Barron Tue 24th February 2004 to work with DansGuardian // on mulitple platforms. Then again on Sat 29th January 2005 to work with // autotools. #ifdef HAVE_CONFIG_H #include "e2config.h" #endif #include #if STDC_HEADERS || defined _LIBC #include #include #else #ifndef HAVE_MEMCPY #define memcpy(d, s, n) bcopy((s), (d), (n)) #endif #endif #include "md5.hpp" #ifndef WORDS_BIGENDIAN #ifdef __BYTE_ORDER #if __BYTE_ORDER == __BIG_ENDIAN #define WORDS_BIGENDIAN 1 #endif #else #if _BYTE_ORDER == _BIG_ENDIAN #define WORDS_BIGENDIAN 1 #endif #endif #endif #ifdef WORDS_BIGENDIAN #define SWAP(n) \ (((n) << 24) | (((n)&0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) #else #define SWAP(n) (n) #endif /* This array contains the bytes used to pad the buffer to the next 64-byte boundary. (RFC 1321, 3.1: Step 1) */ static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; /* Initialize structure containing state of computation. (RFC 1321, 3.3: Step 3) */ void md5_init_ctx(struct md5_ctx *ctx) { ctx->A = 0x67452301; ctx->B = 0xefcdab89; ctx->C = 0x98badcfe; ctx->D = 0x10325476; ctx->total[0] = ctx->total[1] = 0; ctx->buflen = 0; } /* Put result from CTX in first 16 bytes following RESBUF. The result must be in little endian byte order. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ void *md5_read_ctx(const struct md5_ctx *ctx, void *resbuf) { ((md5_uint32 *)resbuf)[0] = SWAP(ctx->A); ((md5_uint32 *)resbuf)[1] = SWAP(ctx->B); ((md5_uint32 *)resbuf)[2] = SWAP(ctx->C); ((md5_uint32 *)resbuf)[3] = SWAP(ctx->D); return resbuf; } /* Process the remaining bytes in the internal buffer and the usual prolog according to the standard and write the result to RESBUF. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ void *md5_finish_ctx(struct md5_ctx *ctx, void *resbuf) { /* Take yet unprocessed bytes into account. */ md5_uint32 bytes = ctx->buflen; size_t pad; /* Now count remaining bytes. */ ctx->total[0] += bytes; if (ctx->total[0] < bytes) ++ctx->total[1]; pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; memcpy(&ctx->buffer[bytes], fillbuf, pad); /* Put the 64-bit file length in *bits* at the end of the buffer. */ *(md5_uint32 *)&ctx->buffer[bytes + pad] = SWAP(ctx->total[0] << 3); *(md5_uint32 *)&ctx->buffer[bytes + pad + 4] = SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29)); /* Process last bytes. */ md5_process_block(ctx->buffer, bytes + pad + 8, ctx); return md5_read_ctx(ctx, resbuf); } /* Compute MD5 message digest for bytes read from STREAM. The resulting message digest number will be written into the 16 bytes beginning at RESBLOCK. */ int md5_stream(FILE *stream, void *resblock) { /* Important: BLOCKSIZE must be a multiple of 64. */ #define BLOCKSIZE 4096 struct md5_ctx ctx; char buffer[BLOCKSIZE + 72]; size_t sum; /* Initialize the computation context. */ md5_init_ctx(&ctx); /* Iterate over full file contents. */ while (1) { /* We read the file in blocks of BLOCKSIZE bytes. One call of the computation function processes the whole buffer so that with the next round of the loop another block can be read. */ size_t n; sum = 0; /* Read block. Take care for partial reads. */ do { n = fread(buffer + sum, 1, BLOCKSIZE - sum, stream); sum += n; } while (sum < BLOCKSIZE && n != 0); if (n == 0 && ferror(stream)) return 1; /* If end of file is reached, end the loop. */ if (n == 0) break; /* Process buffer with BLOCKSIZE bytes. Note that BLOCKSIZE % 64 == 0 */ md5_process_block(buffer, BLOCKSIZE, &ctx); } /* Add the last bytes if necessary. */ if (sum > 0) md5_process_bytes(buffer, sum, &ctx); /* Construct result in desired memory. */ md5_finish_ctx(&ctx, resblock); return 0; } /* Compute MD5 message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ void *md5_buffer(const char *buffer, size_t len, void *resblock) { struct md5_ctx ctx; /* Initialize the computation context. */ md5_init_ctx(&ctx); /* Process whole buffer but last len % 64 bytes. */ md5_process_bytes(buffer, len, &ctx); /* Put result in desired memory area. */ return md5_finish_ctx(&ctx, resblock); } void md5_process_bytes(const void *buffer, size_t len, struct md5_ctx *ctx) { /* When we already have some bits in our internal buffer concatenate both inputs first. */ if (ctx->buflen != 0) { size_t left_over = ctx->buflen; size_t add = 128 - left_over > len ? len : 128 - left_over; /* Only put full words in the buffer. */ add -= add % __alignof__(md5_uint32); memcpy(&ctx->buffer[left_over], buffer, add); ctx->buflen += add; if (ctx->buflen > 64) { md5_process_block(ctx->buffer, ctx->buflen & ~63, ctx); ctx->buflen &= 63; /* The regions in the following copy operation cannot overlap. */ memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63], ctx->buflen); } buffer = (const char *)buffer + add; len -= add; } /* Process available complete blocks. */ if (len > 64) { md5_process_block(buffer, len & ~63, ctx); buffer = (const char *)buffer + (len & ~63); len &= 63; } /* Move remaining bytes in internal buffer. */ if (len > 0) { size_t left_over = ctx->buflen; memcpy(&ctx->buffer[left_over], buffer, len); left_over += len; if (left_over >= 64) { md5_process_block(ctx->buffer, 64, ctx); left_over -= 64; memcpy(ctx->buffer, &ctx->buffer[64], left_over); } ctx->buflen = left_over; } } /* These are the four functions used in the four steps of the MD5 algorithm and defined in the RFC 1321. The first function is a little bit optimized (as found in Colin Plumbs public domain implementation). */ /* #define FF(b, c, d) ((b & c) | (~b & d)) */ #define FF(b, c, d) (d ^ (b & (c ^ d))) #define FG(b, c, d) FF(d, b, c) #define FH(b, c, d) (b ^ c ^ d) #define FI(b, c, d) (c ^ (b | ~d)) /* Process LEN bytes of BUFFER, accumulating context into CTX. It is assumed that LEN % 64 == 0. */ void md5_process_block(const void *buffer, size_t len, struct md5_ctx *ctx) { md5_uint32 correct_words[16]; const md5_uint32 *words = (md5_uint32 *)buffer; size_t nwords = len / sizeof(md5_uint32); const md5_uint32 *endp = words + nwords; md5_uint32 A = ctx->A; md5_uint32 B = ctx->B; md5_uint32 C = ctx->C; md5_uint32 D = ctx->D; /* First increment the byte count. RFC 1321 specifies the possible length of the file up to 2^64 bits. Here we only compute the number of bytes. Do a double word increment. */ ctx->total[0] += len; if (ctx->total[0] < len) ++ctx->total[1]; /* Process all bytes in the buffer with 64 bytes in each round of the loop. */ while (words < endp) { md5_uint32 *cwp = correct_words; md5_uint32 A_save = A; md5_uint32 B_save = B; md5_uint32 C_save = C; md5_uint32 D_save = D; /* First round: using the given function, the context and a constant the next context is computed. Because the algorithms processing unit is a 32-bit word and it is determined to work on words in little endian byte order we perhaps have to change the byte order before the computation. To reduce the work for the next steps we store the swapped words in the array CORRECT_WORDS. */ #define OP(a, b, c, d, s, T) \ do { \ a += FF(b, c, d) + (*cwp++ = SWAP(*words)) + T; \ ++words; \ CYCLIC(a, s); \ a += b; \ } while (0) /* It is unfortunate that C does not provide an operator for cyclic rotation. Hope the C compiler is smart enough. */ #define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) /* Before we start, one word to the strange constants. They are defined in RFC 1321 as T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 */ /* Round 1. */ OP(A, B, C, D, 7, 0xd76aa478); OP(D, A, B, C, 12, 0xe8c7b756); OP(C, D, A, B, 17, 0x242070db); OP(B, C, D, A, 22, 0xc1bdceee); OP(A, B, C, D, 7, 0xf57c0faf); OP(D, A, B, C, 12, 0x4787c62a); OP(C, D, A, B, 17, 0xa8304613); OP(B, C, D, A, 22, 0xfd469501); OP(A, B, C, D, 7, 0x698098d8); OP(D, A, B, C, 12, 0x8b44f7af); OP(C, D, A, B, 17, 0xffff5bb1); OP(B, C, D, A, 22, 0x895cd7be); OP(A, B, C, D, 7, 0x6b901122); OP(D, A, B, C, 12, 0xfd987193); OP(C, D, A, B, 17, 0xa679438e); OP(B, C, D, A, 22, 0x49b40821); /* For the second to fourth round we have the possibly swapped words in CORRECT_WORDS. Redefine the macro to take an additional first argument specifying the function to use. */ #undef OP #define OP(f, a, b, c, d, k, s, T) \ do { \ a += f(b, c, d) + correct_words[k] + T; \ CYCLIC(a, s); \ a += b; \ } while (0) /* Round 2. */ OP(FG, A, B, C, D, 1, 5, 0xf61e2562); OP(FG, D, A, B, C, 6, 9, 0xc040b340); OP(FG, C, D, A, B, 11, 14, 0x265e5a51); OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa); OP(FG, A, B, C, D, 5, 5, 0xd62f105d); OP(FG, D, A, B, C, 10, 9, 0x02441453); OP(FG, C, D, A, B, 15, 14, 0xd8a1e681); OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8); OP(FG, A, B, C, D, 9, 5, 0x21e1cde6); OP(FG, D, A, B, C, 14, 9, 0xc33707d6); OP(FG, C, D, A, B, 3, 14, 0xf4d50d87); OP(FG, B, C, D, A, 8, 20, 0x455a14ed); OP(FG, A, B, C, D, 13, 5, 0xa9e3e905); OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8); OP(FG, C, D, A, B, 7, 14, 0x676f02d9); OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a); /* Round 3. */ OP(FH, A, B, C, D, 5, 4, 0xfffa3942); OP(FH, D, A, B, C, 8, 11, 0x8771f681); OP(FH, C, D, A, B, 11, 16, 0x6d9d6122); OP(FH, B, C, D, A, 14, 23, 0xfde5380c); OP(FH, A, B, C, D, 1, 4, 0xa4beea44); OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9); OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60); OP(FH, B, C, D, A, 10, 23, 0xbebfbc70); OP(FH, A, B, C, D, 13, 4, 0x289b7ec6); OP(FH, D, A, B, C, 0, 11, 0xeaa127fa); OP(FH, C, D, A, B, 3, 16, 0xd4ef3085); OP(FH, B, C, D, A, 6, 23, 0x04881d05); OP(FH, A, B, C, D, 9, 4, 0xd9d4d039); OP(FH, D, A, B, C, 12, 11, 0xe6db99e5); OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8); OP(FH, B, C, D, A, 2, 23, 0xc4ac5665); /* Round 4. */ OP(FI, A, B, C, D, 0, 6, 0xf4292244); OP(FI, D, A, B, C, 7, 10, 0x432aff97); OP(FI, C, D, A, B, 14, 15, 0xab9423a7); OP(FI, B, C, D, A, 5, 21, 0xfc93a039); OP(FI, A, B, C, D, 12, 6, 0x655b59c3); OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92); OP(FI, C, D, A, B, 10, 15, 0xffeff47d); OP(FI, B, C, D, A, 1, 21, 0x85845dd1); OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f); OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0); OP(FI, C, D, A, B, 6, 15, 0xa3014314); OP(FI, B, C, D, A, 13, 21, 0x4e0811a1); OP(FI, A, B, C, D, 4, 6, 0xf7537e82); OP(FI, D, A, B, C, 11, 10, 0xbd3af235); OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb); OP(FI, B, C, D, A, 9, 21, 0xeb86d391); /* Add the starting values of the context. */ A += A_save; B += B_save; C += C_save; D += D_save; } /* Put checksum in context given as argument. */ ctx->A = A; ctx->B = B; ctx->C = C; ctx->D = D; } e2guardian-5.5.8r/src/md5.hpp000066400000000000000000000125001477372360500157350ustar00rootroot00000000000000/* Declaration of functions and data types used for MD5 sum computing library functions. Copyright (C) 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _MD5_H #define _MD5_H 1 #include #if defined HAVE_LIMITS_H || _LIBC #include #endif /* The following contortions are an attempt to use the C preprocessor to determine an unsigned integral type that is 32 bits wide. An alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but doing that would require that the configure script compile and *run* the resulting executable. Locally running cross-compiled executables is usually not possible. */ #ifdef _LIBC #include typedef u_int32_t md5_uint32; #else #if defined __STDC__ && __STDC__ #define UINT_MAX_32_BITS 4294967295U #else #define UINT_MAX_32_BITS 0xFFFFFFFF #endif /* If UINT_MAX isn't defined, assume it's a 32-bit type. This should be valid for all systems GNU cares about because that doesn't include 16-bit systems, and only modern systems (that certainly have ) have 64+-bit integral types. */ #ifndef UINT_MAX #define UINT_MAX UINT_MAX_32_BITS #endif #if UINT_MAX == UINT_MAX_32_BITS typedef unsigned int md5_uint32; #else #if USHRT_MAX == UINT_MAX_32_BITS typedef unsigned short md5_uint32; #else #if ULONG_MAX == UINT_MAX_32_BITS typedef unsigned long md5_uint32; #else /* The following line is intended to evoke an error. Using #error is not portable enough. */ "Cannot determine unsigned 32-bit data type." #endif #endif #endif #endif #undef __P #if defined(__STDC__) && __STDC__ #define __P(x) x #else #define __P(x) () #endif /* Structure to save state of computation between the single steps. */ struct md5_ctx { md5_uint32 A; md5_uint32 B; md5_uint32 C; md5_uint32 D; md5_uint32 total[2]; md5_uint32 buflen; #ifndef __attribute__ char buffer[128]; #else char buffer[128] __attribute__((__aligned__(__alignof__(md5_uint32)))); #endif }; #ifndef __attribute__ #define __alignof__(a)sizeof(a) #endif /* * The following three functions are build up the low level used in * the functions `md5_stream' and `md5_buffer'. */ /* Initialize structure containing state of computation. (RFC 1321, 3.3: Step 3) */ extern void __md5_init_ctx __P((struct md5_ctx * ctx)); /* Starting with the result of former calls of this function (or the initialization function update the context for the next LEN bytes starting at BUFFER. It is necessary that LEN is a multiple of 64!!! */ extern void __md5_process_block __P((const void *buffer, size_t len, struct md5_ctx *ctx)); /* Starting with the result of former calls of this function (or the initialization function update the context for the next LEN bytes starting at BUFFER. It is NOT required that LEN is a multiple of 64. */ extern void __md5_process_bytes __P((const void *buffer, size_t len, struct md5_ctx *ctx)); /* Process the remaining bytes in the buffer and put result from CTX in first 16 bytes following RESBUF. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ extern void *__md5_finish_ctx __P((struct md5_ctx * ctx, void *resbuf)); /* Put result from CTX in first 16 bytes following RESBUF. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ extern void *__md5_read_ctx __P((const struct md5_ctx *ctx, void *resbuf)); /* Compute MD5 message digest for bytes read from STREAM. The resulting message digest number will be written into the 16 bytes beginning at RESBLOCK. */ extern int __md5_stream __P((FILE * stream, void *resblock)); /* Compute MD5 message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ extern void *__md5_buffer __P((const char *buffer, size_t len, void *resblock)); #define md5_init_ctx __md5_init_ctx #define md5_process_block __md5_process_block #define md5_process_bytes __md5_process_bytes #define md5_finish_ctx __md5_finish_ctx #define md5_read_ctx __md5_read_ctx #define md5_stream __md5_stream #define md5_buffer __md5_buffer #endif /* md5.h */ e2guardian-5.5.8r/tarballup.sh000077500000000000000000000001331477372360500162740ustar00rootroot00000000000000#!/bin/sh make distclean ./autogen.sh ./configure make clean make make distcheck make dist