コード例 #1
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()
コード例 #2
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()
コード例 #3
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)))
コード例 #4
0
    def open_socket(self, host:str, port:int=None) -> bool:
        """
        Attemps to open a new socket with the current parameters.
        """
        self.error = None
        self.ssh_info = gkf.get_ssh_host_info(host)
        if not self.ssh_info: 
            self.error = 'unknown host'
            return False

        hostname = self.ssh_info['hostname'] 
        try:
            port = int(port)
        except:
            port = int(self.ssh_info.get('port', 22))

        self.sock = socket.socket(self.sock_domain, self.sock_type)
        try:
            self.sock.settimeout(self.tcp_timeout)
            self.sock.connect((hostname,port))

        except socket.timeout as e:
            self.error = 'timeout of {} seconds exceeded.'.format(self.tcp_timeout)

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

        else:
            self.remote_host = hostname
            self.remote_port = port
        
        return self.error is None
コード例 #5
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
コード例 #6
0
    def open_sftp(self, data:list=[]) -> bool:
        """
        Open an sftp connection to the remote host.
        """
        self.error = None
        try:
            self.sftp = paramiko.SFTPClient.from_transport(self.transport)

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

        finally:
            return self.error is None
コード例 #7
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
コード例 #8
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))
コード例 #9
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))
コード例 #10
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
コード例 #11
0
    def open_transport(self, data:str="") -> bool:
        """
        Creates a transport layer from an open/active ssh session.
        """
        self.error = None
        if not self.client:
            self.error = 'no open session for transport'
            return False

        try:
            self.transport = self.client.get_transport()

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

        finally:
            return self.error is None
コード例 #12
0
    def open_channel(self, channel_type:str="session") -> bool:
        """
        Acquire a channel of the desired type. "session" is the default.
        """
        self.error = None
        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():
            self.error = 'unknown channel type: {}'.format(data)
            return False

        try:
            self.channel = self.transport.open_channel(data)
        except Exception as e:
            self.error = gkf.type_and_text(e)
        finally:
            return self.error is not None
コード例 #13
0
    def preloop(self) -> None:
        """
        Get the config (if any). This function updates the self.cfg_file class member
            with the full file name of the one that we will use.
        """
        setproctitle.setproctitle('beachhead')

        default_ssh_config_file = '~/.ssh/config'
        f = fname.Fname(default_ssh_config_file)
        if not f:
            gkf.tombstone('You do not seem to have an ssh config file. This program')
            gkf.tombstone('may not be very useful.')

        try:
            for d in [ os.environ.get(_, "") for _ in [ 'PWD' ] ]:
                for this_dir, sub_dirs, files in os.walk(d):
                    for f in files:
                        if f == __default_config__: 
                            self.cfg_file = os.path.join(d, this_dir, f)
                            raise StopIteration

        except StopIteration as e:
            try:
                jp = jparse.JSONReader()
                self.cfg = jp.attach_IO(self.cfg_file, True).convert()
                gkf.tombstone('Using config info read from {}'.format(self.cfg_file))

            except Exception as e:
                gkf.tombstone(str(e))
                gkf.tombstone('{} failed to compile.'.format(self.cfg_file))

        except Exception as e:
            gkf.tombstone(gkf.type_and_text(e))
            gkf.tombstone('Something really bad happened.')
            raise

        else:
            gkf.tombstone('No config file found.')
コード例 #14
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)