
import string
import uuid
import random
from util import iDict
import exception


# Nothing is validating the Redir section in the xml.  I may get nonexistent
# params.
#

__all__ = ['RedirectionManager', 'LocalRedirection', 'RemoteRedirection']


def str2bool(x):
    if x is None:
        return False
    if x.lower() in ('true', 't', '1'):
        return True
    else:
        return False

def strip_xpath(x):
    if not x:
        return x
    if x.startswith('/'):
        return ""
    else:
        return x

def is_identifier(x):
    if not x:
        return False
    else:
        if x[0] in string.digits:
            return False
        else:
            return True

def rand_port():
    RAND_PORT_MIN = 1025
    RAND_PORT_MAX = 65535
    return str(random.randint(RAND_PORT_MIN, RAND_PORT_MAX))

class LocalRedirection(object):
    """
    listenaddr: Name of parameter to give the IP addr of listening tunnel on local LP
    listenport: Name of parameter to give the port of the listening tunnel on local LP
    destaddr:   Name of parameter that contains the IP of the target computer
    destport:   Name of parameter that contains the port on the target computer
    """
    def __init__(self, protocol, listenaddr, listenport, destaddr, destport,
                    closeoncompletion="false", name="", srcport=None, srcportlist=None,
                    srcportrange=None, *trashargs, **trashkargs):
        self.protocol   = protocol
        # XXX - The redirection section really shouldn't have XPath in it.
        self.listenaddr = strip_xpath(listenaddr)
        self.listenport = strip_xpath(listenp
ort)
        self.destaddr   = strip_xpath(destaddr)
        self.destport   = strip_xpath(destport)
        self.closeoncompletion = str2bool(closeoncompletion)
        self.name = name

        self.srcport = strip_xpath(srcport)
        self.srcportlist = srcportlis
t
        self.srcportrange = srcportrange

    def __repr__(self):
        return str(self.__dict__)


class RemoteRedirection(object):
    def __init__(self, protocol, listenaddr, destport, closeoncompletion="false",
                    name="", listenport=None, listenportlist=None,
                    listenportrange=None, listenportcount=None, destaddr="0.0.0.0",
                    *trashargs, **trashkargs):
        self.protocol   = protocol
        self.listenaddr = strip_xpath(listenaddr)
        self.listenport = strip_xpath(listenport)
        self.destaddr   = strip_xpath(destaddr)
        self.destport   = strip_xpath(destport)
        self.closeoncompletion = str2bool(closeoncompletion)
        self.name = name

        # Need conversion?
        self.listenportlist = listenportlist
        self.listenportrange = listenportrange
        self.listenportcount = listenportcount

    def __repr__(self):
        return str(self.__dict__)


class RedirectionManager:
    """This is something of a misnomer.  This is really a redirection manager rather than
    a redirection object.  This is responsible for taking the defined tunnels in the 
    plug-in's XML and 'swapping out' the parameters as they pertain to redirection.

    A sample redirection section appears as follows:
    <redirection>
        <!-- (1) The "throwing" tunnel -->
        <local name="Launch"
               protocol="TCP"
               listenaddr="TargetIp"        # IP of redirector
               listenport="TargetPort"      # Port of redirector 
               destaddr="TargetIp"          # IP of target computer
               destport="TargetPort"        # Port of target computer
               closeoncompletion="true"/>
        <!-- (2) The "Callin" tunnel -->
        <local name="Callin"
               protocol="TCP"
               listenaddr="TargetIp"        # IP on redirector
               listenport="CallinPort"      # Port on redirector
               destaddr="TargetIp"          # IP of target callin
               destport="ListenPort"        # Port of target callin
               closeoncompletion="false"/>
        <!-- (3) The "callback" tunnel -->
        <remote name="Callback"
                protocol="TCP"
                listenaddr="CallbackIp"         # IP opened by egg (last redirector)
                listenport="CallbackPort"       # Port opened by egg (last redirector)
                destport="CallbackLocalPort"    # Port for throwing side to listen
                closeoncompletion="false"/>
    </redirection>

    For the "throwing" (launch) tunnel, we:
        1: Ask for/retrieve the "Destination IP" and "Destination Port", which default to
           the "TargetIp" and "TargetPort" parameters
        2: Ask for the "Listen IP" (listenaddr) and "Listen Port" (listenport) 
           and then swap them in "TargetIp" and "TargetPort"
        3: After execution, restore the proper session parameters
        * (listenaddr, listenport) = l(destaddr, destport)

    For the "callin" tunnel, we:
        1: Ask for/retrieve the "Destination IP" and Destination Port", which default to
           the "TargetIp" and the "ListenPort" parameters
        2: Ask for the "Listen IP" (listenaddr) and "Listen Port" (listenport) and
           then swap them into "TargetIp" and "CallinPort" parameters
        3: After execution, restore the proper session parameters
        * (listenaddr, listenport) = l(destaddr, destport)

    For the "callback" tunnel, we:
        1: Ask for the Listen IP and Listen Port for which the payload will callback.
           This is most likely the last hop redirector IP and a port on it
        2: Ask for the Destination IP and Destination Port, which will likely be the
           operator workstation.  Store the Destination port as "CallbackLocalPort", 
           basically ignoring the DestinationIp
        3: After execution, restore the proper session parameters
        * (destaddr, destport) = l(listenaddr, listenport)

    """
    def __init__(self, io):
        self.io = io
        self.active = False
        # A place to store parameters for the session.  We push the info, run the plug-in
        # (with redirection), and then pop the info to restore it
        self.session_cache = {}       

    def on(self):
        self.active = True

    def off(self):
        self.active = False

    def is_active(self):
        return self.active

    def get_status(self):
        if self.active:
            return "ON"
        else:
            return "OFF"

    def get_session(self, id):
        return self.session_cache.get(id)
        
    def pre_exec(self, plugin):
        if not plugin.canRedirect():
            return 0
        
        if self.is_active():
            self.io.print_msg("Redirection ON")
            return self.config_redirect(plugin, True)
        else:
            self.io.print_msg("Redirection OFF")
            return self.config_redirect(plugin, False)

    def post_exec(self, plugin, id):
        if id == 0:
            return
        # if plugin doesn't do redir, return

        try:
            stored_session_data = self.session_cache.pop(id)
        except KeyError:
            return

        # Restore the old information to the session
        for key,val in stored_session_data['params'].items():
            plugin.set(key, val)

    def print_session(self, id):
        try:
            session = self.session_cache[id]
        except KeyError:
            return
        self.io.print_global_redir(session)

    """
    Pre plugin execution

    """
    def conv_param(self, val, params, session_data={}):
        """Resolve a value from one of session, params, or the hard value"""
        try:
            # First try to find the session parameter
            if val in session_data:
                return session_data[val]
            # Then try to find the context-specific parameter
            if is_identifier(val):
                return params[val]
        except:
            return None
        # If it is neither a session or context parameter, return the value as is
        return val

    def prompt_redir_fake(self, msg, default):
        done = None
        while not done:
            try:
                line 