/* 

                          Firewall Builder

                 Copyright (C) 2002 NetCitadel, LLC

  Author:  Vadim Kurland     vadim@vk.crocodile.org

  $Id: PolicyCompiler.h,v 1.2 2004/09/08 05:34:53 vkurland Exp $

  This program is free software which we release under the GNU General Public
  License. You may redistribute and/or modify this program under the terms
  of that 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.
 
  To get a copy of the GNU General Public License, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

#ifndef __POLICY_COMPILER_HH__
#define __POLICY_COMPILER_HH__

#include "fwcompiler/Compiler.h"
#include "fwbuilder/Rule.h"
#include "fwbuilder/RuleElement.h"

#include <string>

namespace fwcompiler {

    #define DECLARE_POLICY_RULE_PROCESSOR(_Name) \
        friend class _Name; \
	class _Name : public PolicyRuleProcessor { \
		public: \
		_Name(const std::string &n) :  \
			PolicyRuleProcessor(n) {}; \
                virtual ~_Name() {}; \
		virtual bool processNext(); \
	};


    class PolicyCompiler : public Compiler {

	protected:

	/**
	 *  this method scans combined_ruleset looking for atomic rule
	 *  which yields non-empty intersection with atomic rule r.
	 *  
	 *  it can start scan either from the beginning of combined_ruleset,
	 *  or from iterator 'start_here'
	 *
	 *  it returns iterator pointing at rule it has found (so we
	 *  can continue search later)
	 *
	 *  if parameter 'intersection' is not NULL, it is assumed to
	 *  be a pointer to a pointer at the object of class Rule. It
	 *  is used to return a pointer at intersection of rule 'rule'
	 *  and rule this function had found
	 */
	std::list<libfwbuilder::FWObject*>::iterator 
	find_more_specific_rule(libfwbuilder::PolicyRule *r,
                                bool check_interface,
                                const std::list<libfwbuilder::FWObject*>::iterator &start_here,
                                const std::list<libfwbuilder::FWObject*>::iterator &stop_here,
                                libfwbuilder::PolicyRule **intersection=NULL);


	public:

	PolicyCompiler(libfwbuilder::FWObjectDatabase *_db,
		       const std::string &fwname,
		       fwcompiler::OSConfigurator *_oscnf)   : Compiler(_db,fwname,_oscnf) {}

        /**
         * prints rule in some universal format (close to that visible
         * to user in the GUI). Used for debugging purposes
         */
        virtual std::string debugPrintRule(libfwbuilder::Rule *rule);

	
	/**
	 * this method substracts atomic rule r2 from atomic rule r1.
	 * Resulting rule may have multiple objects in src,dst or srv,
	 * so converting to atomic may be necessary. If rules can not
	 * be compared, then it throws an exception. If resultant rule
	 * is empty, method returns rule with empty src,dst,srv
	 *
	 * NOT IMPLEMENTED YET 
	 */
	libfwbuilder::Rule* getDifference(const libfwbuilder::PolicyRule &r1,
					  const libfwbuilder::PolicyRule &r2);

        /**
         * detects if rule r2  shades rule r1
         * returns:
         *
         *  true if r2 shades r1 
         */
        bool checkForShadowing(const libfwbuilder::PolicyRule &r1,
                             const libfwbuilder::PolicyRule &r2);

        /**
         * detects if rules r1 and r2 are identical (that is, have the
         * same effect, rather than use the same objects)
         *
         * returns:
         *
         *  true if r1 is identical to r2
         */
        bool cmpRules(const libfwbuilder::PolicyRule &r1,
                      const libfwbuilder::PolicyRule &r2);



	/**
	 * this class expands groups in src,dst,srv. It creates
	 * references to new objects "in place" (that is, it does not
	 * create new rules but rather uses rule elements of the old
	 * ones)
	 */
        DECLARE_POLICY_RULE_PROCESSOR(ExpandGroups);

        /**
         *  this inspector replaces references to hosts and firewalls
         *  in dst with references to their interfaces
         *
         * TODO: move to class Compiler because this might be useful
         * for both PolicyCompiler and NATCompiler
         */
        DECLARE_POLICY_RULE_PROCESSOR(ExpandMultipleAddressesInDST);

        /**
         *  this inspector replaces references to hosts and firewalls
         *  in src with references to their interfaces
         *
         * TODO: move to class Compiler because this might be useful
         * for both PolicyCompiler and NATCompiler
         */
        DECLARE_POLICY_RULE_PROCESSOR(ExpandMultipleAddressesInSRC);

	/**
	 *  this inspector replaces references to hosts and firewalls
	 *  in src or dst with references to their interfaces
	 *
	 * TODO: move to class Compiler because this might be useful
	 * for both PolicyCompiler and NATCompiler
	 */
        DECLARE_POLICY_RULE_PROCESSOR(ExpandMultipleAddresses);

	/**
	 * this processor splits rule element if src or dst contains
	 * address range
	 */
        DECLARE_POLICY_RULE_PROCESSOR(addressRanges);

	/**
	 * split rules with more than one service object, so that each
	 * rule has services with the same protocol
	 */
        DECLARE_POLICY_RULE_PROCESSOR(splitServices);

	/**
	 * separate TCP services with flags (can't use those in combination
	 * with others in groups of services)
	 */
        DECLARE_POLICY_RULE_PROCESSOR(separateTCPWithFlags);

	/**
	 * verify if custom services used in rules are configured for
	 * this platform
	 */
        DECLARE_POLICY_RULE_PROCESSOR(verifyCustomServices);

	/**
	 *  checks for unnumbered interface in rule elements (one can
	 *  not use unnumbered interfaces in rules). Call after
	 *  ExpandMultipleAddresses 
	 */
        class checkForUnnumbered : public PolicyRuleProcessor
        {
            public:
            checkForUnnumbered(const std::string &n) : PolicyRuleProcessor(n) {}
            virtual bool processNext();
        };

	/**
	 * this processor converts to atomic rules using all combinations
	 * of objects in Src,Dst. It ignores Srv.
	 */
        DECLARE_POLICY_RULE_PROCESSOR(ConvertToAtomicForAddresses);

	/**
	 * this processor splits rule so that each atomic rule has
	 * exactly one Interval rule element
	 */
        DECLARE_POLICY_RULE_PROCESSOR(ConvertToAtomicForIntervals);

	/**
	 * this processor converts to atomic rules
	 */
        DECLARE_POLICY_RULE_PROCESSOR(ConvertToAtomic);


        /**
         *  deals with recursive groups in Src. See description for
         *  Compiler::recursiveGroupsInRE
         */
        class recursiveGroupsInSrc : public Compiler::recursiveGroupsInRE
        {
            public:
            recursiveGroupsInSrc(const std::string &n) :
                recursiveGroupsInRE(n,libfwbuilder::RuleElementSrc::TYPENAME) {}
        };

        /**
         *  deals with recursive groups in Dst. See description for
         *  Compiler::recursiveGroupsInRE
         */
        class recursiveGroupsInDst : public Compiler::recursiveGroupsInRE
        {
            public:
            recursiveGroupsInDst(const std::string &n) :
                recursiveGroupsInRE(n,libfwbuilder::RuleElementDst::TYPENAME) {}
        };

        /**
         *  deals with recursive groups in Srv. See description for
         *  Compiler::recursiveGroupsInRE
         */
        class recursiveGroupsInSrv : public Compiler::recursiveGroupsInRE
        {
            public:
            recursiveGroupsInSrv(const std::string &n) :
                recursiveGroupsInRE(n,libfwbuilder::RuleElementSrv::TYPENAME) {}
        };




        /**
         *  deals with empty groups in Src. See description for
         *  Compiler::emptyGroupsInRE
         */
        class emptyGroupsInSrc : public Compiler::emptyGroupsInRE
        {
            public:
            emptyGroupsInSrc(const std::string &n) :
                emptyGroupsInRE(n,libfwbuilder::RuleElementSrc::TYPENAME) {}
        };

        /**
         *  deals with empty groups in Dst. See description for
         *  Compiler::emptyGroupsInRE
         */
        class emptyGroupsInDst : public Compiler::emptyGroupsInRE
        {
            public:
            emptyGroupsInDst(const std::string &n) :
                emptyGroupsInRE(n,libfwbuilder::RuleElementDst::TYPENAME) {}
        };

        /**
         *  deals with empty groups in Srv. See description for
         *  Compiler::emptyGroupsInRE
         */
        class emptyGroupsInSrv : public Compiler::emptyGroupsInRE
        {
            public:
            emptyGroupsInSrv(const std::string &n) :
                emptyGroupsInRE(n,libfwbuilder::RuleElementSrv::TYPENAME) {}
        };


	/**
	 * checks for host and network objects with address 0.0.0.0
	 */
        class checkForZeroAddr : public PolicyRuleProcessor 
        {
            libfwbuilder::Address *findHostWithNoInterfaces(libfwbuilder::RuleElement *re);
            libfwbuilder::Address *findZeroAddress(libfwbuilder::RuleElement *re);
            public:
            checkForZeroAddr(const std::string &n) : PolicyRuleProcessor(n) {}
            virtual bool processNext();
        };

        /**
         * this is a base class for the family of rule processors that
         * need to be able to find more general rule rule
         */
        class findMoreGeneralRule : public PolicyRuleProcessor 
        {
            protected:
            /**
             *  this method scans tmp_queue looking for atomic rule
             *  which is more general than atomic rule 'r'. 
             *
             *  it can starts scan at position defined by iterator
             *  start_here and ends it at the position given by
             *  iterator stop_here
             *
             *  it returns iterator pointing at the rule it has found (so we
             *  can continue search later)
             */
            std::deque<libfwbuilder::Rule*>::iterator 
            find_more_general_rule(libfwbuilder::PolicyRule *r,
                                   bool check_interface,
                                   const std::deque<libfwbuilder::Rule*>::iterator &start_here,
                                   const std::deque<libfwbuilder::Rule*>::iterator &stop_here);
            public:
            findMoreGeneralRule(const std::string &n) : PolicyRuleProcessor(n) {}
        };


	/**
	 * this inspector scans rules and detects those which "shade"
	 * other rules below them
	 */
        class DetectShadowing : public findMoreGeneralRule
        {
            std::deque<libfwbuilder::Rule*> rules_seen_so_far;
            public:
            DetectShadowing(const std::string &n) : findMoreGeneralRule(n) {}
            virtual bool processNext();
        };

	/**
	 *  splits rule if one of the objects in Src is firewall
	 *  itself or a broadcast address for one of its interfaces or
	 *  multicast (multicast support is not implemented yet). This
	 *  is needed to properly choose direction and chain
	 *  later. Src may have multiple objects when this processor
	 *  is called.  For firewall the iptables code compiler
	 *  generates should go into OUTPUT chain, while for all other
	 *  objects it should go into FORWARD chain. That is why we
	 *  need to split the rule and place firewall object in the
	 *  src of the rule of its own.
	 */
        class splitIfSrcMatchesFw : public Compiler::splitIfRuleElementMatchesFW
        {
            public:
            splitIfSrcMatchesFw (const std::string &n) :
                splitIfRuleElementMatchesFW(n,libfwbuilder::RuleElementSrc::TYPENAME) {}
        };


	/**
	 *  splits rule if one of the objects in Dst is firewall
	 *  itself. This is needed to properly choose direction and
	 *  chain later. See comment in splitIfInputChainObjectSrc.
	 */
        class splitIfDstMatchesFw : public Compiler::splitIfRuleElementMatchesFW
        {
            public:
            splitIfDstMatchesFw (const std::string &n) :
                splitIfRuleElementMatchesFW(n,libfwbuilder::RuleElementDst::TYPENAME) {}
        };


	/**
	 * many firewall platforms do not support filtering by MAC
	 * addresses. Issue warning if MAC address is used in the rule
	 * and remove it from the rule element. 
         *
         * Call this processor after ExpandMultipleAddresses.
	 */
        class MACFiltering : public PolicyRuleProcessor 
        {
            protected:
            std::string last_rule_lbl;
/* return value:
 * true  - ok
 * false - one or more MAC addresses have been removed, issue warning
 */
            bool  checkRuleElement(libfwbuilder::RuleElement *re);
            public:
            MACFiltering(const std::string &n) : PolicyRuleProcessor(n) {}
            virtual bool processNext();
        };



	virtual int prolog();

    };


};

#endif
