Пример #1
0
    def do_open(self, data:str="") -> None:
        """
        open { 
                socket { host port } |
                session |
                channel [ type ] |
                sftp
             }

        Usually, the order of opening is:

            1. get a *socket* connection.
            2. create an ssh *session*.
            3. open a *channel* in the established transport layer.
        """

        if not len(data): 
            self.do_help('open')
            return

        data = data.strip().split()
        f = getattr(self, '_do_'+data[0], None)

        if f: 
            f(data[1:]) 
            return
        else: 
            gkf.tombstone(red('no operation named {}'.format(data)))
Пример #2
0
    def attach_IO(self, f, strip_comments=False) -> JSONReader:
        """
        This function treats a string argument as a filename that needs to 
        be opened, and treats a file as something that needs to be read.

        The function does not /parse/ the contents, but it does return self
        for ease of chaining.

        Raises URException if no file exists, or if it cannot be opened for
        read access, or if the argument is an incompatible type.
        """

        try:
            x = open(f, 'r')
            self.s = x.read()
            x.close()
            if strip_comments: self.comment_stripper()

        except AttributeError as e:
            # This tests for a filename
            x = fname.Fname(f)
            if not x:
                raise Exception("No source named " + str(x) + " exists.")
            self.origin = str(x)
            self.attach_IO(self.origin, strip_comments)

        except IOError as e:
            gkf.tombstone(gkf.type_and_text(e))
            raise e from None

        return self
Пример #3
0
    def convert(self, source=None) -> object:
        """
        Parse the self.s string as JSON.
    
        Returns the parsed object.

        Raises URException on failure to parse. 

        NOTE: just because the parse is successful, the JSON message might still
        be meaningless. Thus, the name of the function is 'convert' not 'parse.'
        """
        if source is not None:
            self.s = source

        o = None
        try:
            o = simplejson.loads(self.s)
        except simplejson.JSONDecodeError as e:
            start = max(0, e.pos - 20)
            stop = min(e.pos + 1, len(self.s))

            t = ("Syntax error: " + e.msg + "\nin " + self.origin +
                 " at offset " + str(e.pos) +
                 ",\nnear the end of the phrase << " + self.s[start:stop] +
                 ">> of the original input, " + "Line: " + str(e.lineno) +
                 ", column:" + str(e.colno) + ". ")
            gkf.tombstone(t)
            raise ue.URException(self.origin + ' failed syntax check.')

        return o
Пример #4
0
    def do_send(self, data:str="") -> None:
        """
        send { file filename | string }

        Sends stuff over the channel.
        """
        if not self.hop.channel: 
            gkf.tombstone(red('channel not open.'))
            self.do_help('send')
            return

        if data.startswith('file'):
            try:
                _, filename = data.split()
                f = fname.Fname(filename)
                if f: data=f()
            except Exception as e:
                gkf.tombstone(red(gkf.type_and_text(e)))
            
        try:
            i = self.hop.channel.send(data)
            gkf.tombstone(blue('sent {} bytes.'.format(i)))

        except KeyboardInterrupt:
            gkf.tombstone(blue('aborting. Control-C pressed.'))

        except Exception as e:
            gkf.tombstone(red(gkf.type_and_text(e)))
            
        finally:
            self.hop.open_channel()
Пример #5
0
    def do_debug(self, data:str="") -> None:
        """
        debug [ value ]

            With no parameter, this function prints the current debug level (as if
            you cannot tell already). Otherwise, set the level.
        """
        if not len(data):
            gkf.tombstone(blue('debug level is {}'.format(self.hop.debug_level())))
            return

        logging_levels = {
            "CRITICAL":"50",
            "ERROR":"40",
            "WARNING":"30",
            "INFO":"20",
            "DEBUG":"10",
            "NOTSET":"0" 
            }

        data = data.strip().upper()
        if data not in logging_levels.keys() and data not in logging_levels.values():
            gkf.tombstone(red('not sure what this level means: {}'.format(data)))
            return
            
        try:
            level = int(data)
        except:
            level = int(logging_levels[data])
        finally:
            self.hop.debug_level(level)
Пример #6
0
    def do_error(self, data:str="") -> None:
        """
        error [reset]

        [re]displays the error of the connection, and optionally resets it
        """
        gkf.tombstone(blue(self.hop.error))
        if 'reset'.startswith(data.strip().lower()): self.hop.error = None
Пример #7
0
    def do_setpass(self, data:str="") -> None:
        """
        setpass [password]
        
            sets, displays, or clears ('none') the password to be used.
        """
        data = data.strip()

        if data.lower() == 'none': self.hop.password = None
        elif not data: gkf.tombstone(blue('password is set to {}'.format(self.hop.password)))
        else: self.hop.password = data        
Пример #8
0
    def do_save(self, data:str="") -> None:
        """
        save [ additional-file-name ]
        
            Saves the current configuration, including information about
            the current host. If you supply a filename, that file will contain
            a duplicate copy of this data.

        The configuration is written as a JSON file, indented 4 spaces per
            level, with the host names sorted alphabetically. The kex-es and
            ciphers are listed in the order the host perfers them. 
        """

        try:
            with open(data, 'w') as f:
                json.dump(self.cfg, f, sort_keys=True, indent=4)
                gkf.tombstone('Duplicate config file written to {}'.format(data))
        except:
            pass

        old_sec_info = self.cfg.get(self.hop.remote_host, {})
        new_sec_info = self.hop.security
        if new_sec_info == {}:
            gkf.tombstone('No active connection / no data to update.')
            return

        if old_sec_info == new_sec_info: 
            gkf.tombstone('Update not required for {}'.format(self.hop.remote_host))
            return

        self.cfg[self.hop.remote_host] = new_sec_info
        with open(self.cfg_file, 'w') as f:
            json.dump(self.cfg, f, sort_keys=True, indent=4)
            gkf.tombstone('Update successful. Written to {}'.format(self.cfg_file))
Пример #9
0
    def do_setsockdomain(self, data:str="") -> None:
        """
        setsockdomain [{ af_inet | af_unix }]

            af_inet -- internet sockets
            af_unix -- a socket on local host that most people call a 'pipe'
        """

        if not data: gkf.tombstone(blue('socket domain is {}'.format(self.hop.sock_domain))); return
        data = data.strip().lower()

        if data == 'af_inet': self.hop.sock_domain = socket.AF_INET
        elif data == 'af_unix': self.hop.sock_domain = socket.AF_UNIX
        else: gkf.tombstone(blue('unknown socket domain: {}'.format(data)))
Пример #10
0
    def do_do(self, data:str="") -> None:
        """
        do { something }

            attempt to exit a command by stuffing the text through the channel
        """
        if not self.hop.channel or not data: 
            self.do_help('do')
            return

        try:
            gkf.tombstone(blue('attempting remote command {}'.format(data)))
            in_, out_, err_ = self.hop.channel.exec_command(data)

        except KeyboardInterrupt as e:
            gkf.tombstone(blue('aborting. Control-C pressed.'))

        except Exception as e:
            gkf.tombstone(red(gkf.type_and_text(e)))

        else:
            out_.channel.recv_exit_status();
            gkf.tombstone(blue(out_.readlines()))

        finally:
            self.hop.open_channel()
Пример #11
0
    def do_settimeout(self, data:str="") -> None:
        """
        settimeout [ { tcp | auth | banner } {seconds} ]

        Without parameters, settimeout will show the current socket timeout values.
        Otherwise, set it and don't forget it.
        """
        if not data: 
            gkf.tombstone('timeouts (tcp, auth, banner): ({}, {}, {})'.format(
                self.hop.tcp_timeout, self.hop.auth_timeout, self.hop.banner_timeout))
            return

        data = data.strip().split()
        if len(data) < 2:
            gkf.tombstone(red('missing timeout value.'))
            self.do_help('settimeout')
            return

        try:
            setattr(self.hop, data[0]+'_timeout', float(data[1]))
        except AttributeError as e:
            gkf.tombstone(red('no timeout value for ' + data[0]))
        except ValueError as e:
            gkf.tombstone(red('bad value for timeout: {}' + data[1]))
        else:
            self.do_settimeout()
Пример #12
0
    def _do_transport(self, data:str="") -> None:
        """
        Creates a transport layer from an open/active ssh session.
        """
        if not self.hop.client:
            gkf.tombstone('You must create an ssh session before you can create a transport layer atop it.')
            return

        gkf.tombstone(blue('attempting to create a transport layer'))
        start_time = time.time()
        OK = self.hop.open_transport()
        stop_time = time.time()
        if OK: gkf.tombstone(blue('success'))
        else: gkf.tombstone(red('failed '+self.hop.error_msg()))
        gkf.tombstone(blue('elapsed time: {}'.format(elapsed_time(start_time, stop_time))))
Пример #13
0
    def open_session(self) -> bool:
        """
        Attempt to create an SSH session with the remote host using
        the socket, transport, and channel that we [may] have already
        openend.
        """
        global members

        self.error = None
        self.client = SSHClient()
        self.client.load_system_host_keys()
        self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy)

        try:
            username=self.ssh_info.get('user', getpass.getuser())
            if not self.password:
                self.client.connect(self.ssh_info['hostname'],
                    int(self.ssh_info['port']),
                    username=username,
                    key_filename=self.ssh_info['identityfile'],
                    sock=self.sock)        

            else:
                self.client.connect(self.ssh_info['hostname'], 
                    int(self.ssh_info['port']), 
                    username=username, 
                    password=self.password,
                    sock=self.sock)

        except paramiko.ssh_exception.BadAuthenticationType as e:
            self.error = str(e)

        except TypeError as e:
            gkf.tombstone(red('Socket not open.'))
            self.error = -1

        except Exception as e:
            self.error = gkf.type_and_text(e)

        else:
            self.open_transport()
            opts = self.transport.get_security_options()          
            self.security = { k:list(getattr(opts, k, None)) for k in members }
            self.security['host_key'] = self.transport.get_remote_server_key().get_base64()
            self.security['version'] = self.transport.remote_version

        finally:
            return self.error is None
Пример #14
0
    def do_setsocktype(self, data:str="") -> None:
        """
        setsocktype [{ stream | dgram | raw }]

            stream -- ordinary TCP socket
            dgram  -- ordinary UDP socket
            raw    -- bare metal 
        """
        sock_types = {'stream':socket.SOCK_STREAM, 'dgram':socket.SOCK_DGRAM, 'raw':socket.SOCK_RAW }
        if not data: gkf.tombstone('socket type is {}'.format(self.hop.sock_type)); return

        try:
            self.hop.sock_type = sock_types[data.strip().lower()]                

        except:
            gkf.tombstone(blue('unknown socket type: {}'.format(data)))
Пример #15
0
    def do_connect(self, info: str = "") -> None:
        """
        Usage: connect {IP|name} {port}
        """
        info = info.strip().split()
        self.connection = beachhead.SocketConnection()
        try:
            self.connection.open_socket(info[0], int(info[1]))
        except Exception as e:
            gkf.tombstone(gkf.type_and_text(e))
            return

        print('Connected to /something/ at {}:{}'.format(info[0], info[1]))
        print('Ready to talk. Sending TEST')
        self.connection.send('TEST')
        reply = self.read()
        print('Received {} as reply'.format(reply))
Пример #16
0
    def _do_sftp(self, data:list=[]) -> None:
        """
        Open an sftp connection to the remote host.
        """
        if not self.hop.transport:
            gkf.tombstone(red('Transport layer is not open. You must create it first.'))
            return

        gkf.tombstone(blue('creating sftp client.'))
        start_time = time.time()
        OK = self.hop.open_sftp()
        stop_time = time.time()

        if OK: gkf.tombstone(blue('success'))
        else: gkf.tombstone(red('failure '+self.hop.error_msg()))

        gkf.tombstone(blue('elapsed time: {}'.format(elapsed_time(start_time, stop_time))))
Пример #17
0
    def do_logging(self, data:str="") -> None:
        """
        Usage:
    
            logging { on | off }

        turns logging (to $PWD/beachhead.log) on or off. No error is
        created when logging is on and you ask to turn it on, etc.

        If you would like to specify a different logfile, there are 
        two solutions.

        [1] Symbolic links:
            rm -f $PWD/beachhead.log
            ln -s yourfile $PWD/beachhead.log

        [2] Use a different program.
        """
        states = {
            "on":True,
            "off":False
            }

        if not data:
            self.do_help('logging')
            return

        try:
            state = states.get(data.lower(), None)
            if state is None: raise StopIteration from None

        except StopIteration as e:
            self.do_help('logging')
            return

        except Exception as e:
            gkf.tombstone(gkf.type_and_text(e))
            return

        if state:
            logging.getLogger("paramiko").setLevel(logging.WARNING)
            paramiko.util.log_to_file("beachhead.log")
        else:
            logging.getLogger("paramiko").setLevel(logging.NOTSET)

        return
Пример #18
0
    def comment_stripper(self) -> JSONReader:
        """
        Remove (illegal) bash type comments from the source code.
        Build a list of lines that are really JSON, and join them
        back into a string. 
        """
        if __name__ == "__main__": gkf.tombstone("Strippin' the comments...")
        if self.s is None: return self

        comment_free_lines = []
        for line in self.s.split("\n"):
            if len(line.strip()) == 0:
                continue

            elif line.strip()[0] == '#':
                gkf.tombstone(line)
                continue

            else:
                comment_free_lines.append(line)

        self.s = "\n".join(comment_free_lines)
        return self
Пример #19
0
    def _do_channel(self, data:list=[]) -> None:
        """
        channel [ session | forward | direct | x11 ]

            Acquire a channel of the desired type. "session" is the default.
        """
        data = 'session' if not data else data[0].lower()
        channel_types = {"session":"session", "forward":"forwarded-tcpip", 
            "direct":"direct-tcpip", "x":"x11"}

        if data not in channel_types.keys() and data not in channel_types.values():
            gkf.tombstone(blue('unknown channel type: {}'.format(data)))
            return

        gkf.tombstone(blue('attempting to create a channel of type {}'.format(data)))

        start_time = time.time()
        OK = self.hop.transport.open_channel(data)
        stop_time = time.time()

        if OK: gkf.tombstone(blue('success'))
        else: gkf.tombstone(red('failed ' + self.hop.error_msg()))

        gkf.tombstone(blue('elapsed time: {}'.format(elapsed_time(start_time, stop_time))))
Пример #20
0
    def do_get(self, data:str="") -> None:
        """
        get a file from the remote host.

        Syntax: get filename
        """
        if not self.hop.sftp:
            gkf.tombstone(red('sftp channel is not open.'))
            return

        if not data:
            self.do_help('get')
            return

        start_time = time.time()
        OK = self.hop.sftp.get(data, Fname(data).fname)
        stop_time = time.time()
        if OK: gkf.tombstone('success')
        else: gkf.tombstone('failure {}'.format(self.hop.error_msg()))
        gkf.tombstone('elapsed time: {}'.format(elapsed_time(stop_time, start_time)))
Пример #21
0
    def _do_socket(self, data:list=[]) -> None:
        """
        Attemps to open a new socket with the current parameters.
        """

        if len(data) < 1: 
            gkf.tombstone('nothing to do.')
            return

        elif len(data) == 1:
            data.append(None)

        start_time = time.time()
        OK = self.hop.open_socket(data[0], data[1])
        stop_time = time.time()
        if OK: 
            gkf.tombstone(blue('connected.'))
        else: 
            gkf.tombstone(self.hop.error_msg())     
            return     
        
        gkf.tombstone(blue('elapsed time: {}'.format(elapsed_time(start_time, stop_time))))
Пример #22
0
    def do_probe(self, data:str="") -> None:
        """
        Syntax:
            probe {host} [ host, [host] .. ]

        The 'probe' is nothing more than a convenience. It connects to
        a host with logging on and set to the debug level. The transaction
        is appended to the logfile for later inspection.

        Each probe is given a 9-digit random ID. In the logfile, you will
        find a BEGIN TRANSACTION and an END TRANSACTION containing the information
        that is gleaned from the probe.
        """

        self.do_logging('on')
        self.do_debug('10')
        
        hostnames = data.strip().split()
        if not hostnames: 
            self.do_help('probe')
            return

        if 'all' in hostnames:
            hostnames = sorted(list(gkf.get_ssh_host_info('all')))
            hostnames.remove('*')
        
        transaction_log = open('beachhead.log', 'a')
        transaction_id = "{:0>9}".format(random.randrange(1000000000))
        try:
            transaction_log.write('BEGIN TRANSACTION {}\n'.format(transaction_id))
            transaction_log.flush()
            for _ in hostnames:
                gkf.tombstone('probing {}'.format(_))
                transaction_log.write('probing {}\n'.format(_))
                transaction_log.flush()
                try:
                    self.do_open('socket {}'.format(_))
                    if not self.hop: continue
                    self.do_open('session')
                    self.do_close()
                except Exception as e:
                    gkf.tombstone(gkf.type_and_text(e))
        finally:
            transaction_log.write('END TRANSACTION {}\n'.format(transaction_id))
            transaction_log.flush()
            transaction_log.close()
            gkf.tombstone("Written to logfile as transaction ID {}".format(transaction_id))
Пример #23
0
    def _do_session(self, data:list=[]) -> None:
        """
        session

            Attempt to create an SSH session with the remote host using
            the socket, transport, and channel that we [may] have already
            openend.
        """
        self.hop.client = SSHClient()
        self.hop.client.load_system_host_keys()
        self.hop.client.set_missing_host_key_policy(paramiko.AutoAddPolicy)
    
        start_time = time.time()
        OK = self.hop.open_session()
        stop_time = time.time()

        if OK: gkf.tombstone(blue('ssh session established.'))
        else: gkf.tombstone(red('failed '+self.hop.error_msg()))

        gkf.tombstone(blue('elapsed time: {}'.format(elapsed_time(start_time, stop_time))))
Пример #24
0
 def default(self, data: str = "") -> None:
     gkf.tombstone(beachhead.red('unknown command {}'.format(data)))
     self.do_help(data)
Пример #25
0
        print('Connected to /something/ at {}:{}'.format(info[0], info[1]))
        print('Ready to talk. Sending TEST')
        self.connection.send('TEST')
        reply = self.read()
        print('Received {} as reply'.format(reply))

    def do_exit(self, info: str = "") -> None:
        """
        Usage: exit
        """
        if self.connection: self.connection.close()
        sys.exit(os.EX_OK)


if __name__ == "__main__":

    # subprocess.call('clear',shell=True)
    while True:
        try:
            Davis().cmdloop()

        except KeyboardInterrupt:
            gkf.tombstone("Exiting via control-C.")
            sys.exit(os.EX_OK)

        except Exception as e:
            gkf.tombstone(gkf.type_and_text(e))
            gkf.tombstone(gkf.formatted_stack_trace(True))
            sys.exit(1)
Пример #26
0
 def _do_version(self) -> None:
     gkf.tombstone("This is the only version you will ever need.")
     gkf.tombstone("What difference does it make?")
     pass
Пример #27
0
    def do_status(self, data:str="") -> None:
        """
        status

            displays the current state of the connection.
        """
        global members

        gkf.tombstone(blue("debug level: {}".format(self.hop.debug_level())))
        if not self.hop.sock: gkf.tombstone('not connected.'); return

        gkf.tombstone(blue("local end:     {}".format(self.hop.sock.getsockname())))
        gkf.tombstone(blue("remote end:    {}".format(self.hop.sock.getpeername())))
        gkf.tombstone(blue("type/domain:   {} / {}".format(self.hop.sock_type, self.hop.sock_domain)))
        gkf.tombstone(blue("ssh session:   {}".format(self.hop.client)))
        gkf.tombstone(blue("transport:     {}".format(self.hop.transport)))
        gkf.tombstone(blue("sftp layer:    {}".format(self.hop.sftp)))
        gkf.tombstone(blue("channel:       {}".format(self.hop.channel)))
        try:
            banner=self.hop.transport.get_banner().decode('utf-8')
        except:
            banner="no banner found"
        gkf.tombstone(blue("banner:        {}".format(banner)))
        gkf.tombstone(blue("*** security info: ***"))
        for _ in self.hop.security.keys():
            gkf.tombstone(blue("{} : {}").format(_,self.hop.security.get(_, None)))
Пример #28
0
 def do_close(self, data="") -> None:
     """
     Close the open socket connection (if any)
     """
     if self.hop: self.hop.close()
     else: gkf.tombstone(blue('nothing to do'))
Пример #29
0
 def do_hosts(self, data:str="") -> None:
     """
     hosts:
         print a list of the available (known) hosts
     """
     gkf.tombstone("\n"+blue("\n".join(sorted(list(gkf.get_ssh_host_info('all'))))))
Пример #30
0
    def do_put(self, data:str="") -> None:
        """
        put a file onto the remote host.

        Syntax: put filename

            NOTE: filename can be a wildcard spec.
        """
        if not self.hop.sftp:
            gkf.tombstone(red('sftp channel is not open.'))
            return

        if not data:
            gkf.tombstone(red('you have to send something ...'))
            self.do_help('put')
            return

        files = glob.glob(data)
        if not files:
            gkf.tombstone(red('no file[s] named {}'.format(data)))
            return

        start_time = time.time()
        OK = None
        for f in files:
            try:
                OK = self.hop.sftp.put(f.fqn, f.fname)
            except Exception as e:
                gkf.tombstone(red(gkf.type_and_text(e)))

            stop_time = time.time()
            if OK: gkf.tombstone('success')
            else: gkf.tombstone('failure {}'.format(self.hop.error_msg()))

        gkf.tombstone('elapsed time: '.format(elapsed_time(stop_time, start_time)))