+++ /dev/null
-#!/usr/bin/env python
-
-"""Back door shell server
-
-This exposes an shell terminal on a socket.
-
- --hostname : sets the remote host name to open an ssh connection to.
- --username : sets the user name to login with
- --password : (optional) sets the password to login with
- --port : set the local port for the server to listen on
- --watch : show the virtual screen after each client request
-"""
-
-# Having the password on the command line is not a good idea, but
-# then this entire project is probably not the most security concious thing
-# I've ever built. This should be considered an experimental tool -- at best.
-import pxssh, pexpect, ANSI
-import time, sys, os, getopt, getpass, traceback, threading, socket
-
-def exit_with_usage(exit_code=1):
-
- print globals()['__doc__']
- os._exit(exit_code)
-
-class roller (threading.Thread):
-
- """This runs a function in a loop in a thread."""
-
- def __init__(self, interval, function, args=[], kwargs={}):
-
- """The interval parameter defines time between each call to the function.
- """
-
- threading.Thread.__init__(self)
- self.interval = interval
- self.function = function
- self.args = args
- self.kwargs = kwargs
- self.finished = threading.Event()
-
- def cancel(self):
-
- """Stop the roller."""
-
- self.finished.set()
-
- def run(self):
-
- while not self.finished.isSet():
- # self.finished.wait(self.interval)
- self.function(*self.args, **self.kwargs)
-
-def endless_poll (child, prompt, screen, refresh_timeout=0.1):
-
- """This keeps the screen updated with the output of the child. This runs in
- a separate thread. See roller(). """
-
- #child.logfile_read = screen
- try:
- s = child.read_nonblocking(4000, 0.1)
- screen.write(s)
- except:
- pass
- #while True:
- # #child.prompt (timeout=refresh_timeout)
- # try:
- # #child.read_nonblocking(1,timeout=refresh_timeout)
- # child.read_nonblocking(4000, 0.1)
- # except:
- # pass
-
-def daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
-
- '''This forks the current process into a daemon. Almost none of this is
- necessary (or advisable) if your daemon is being started by inetd. In that
- case, stdin, stdout and stderr are all set up for you to refer to the
- network connection, and the fork()s and session manipulation should not be
- done (to avoid confusing inetd). Only the chdir() and umask() steps remain
- as useful.
-
- References:
- UNIX Programming FAQ
- 1.7 How do I get my program to act like a daemon?
- http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
-
- Advanced Programming in the Unix Environment
- W. Richard Stevens, 1992, Addison-Wesley, ISBN 0-201-56317-7.
-
- The stdin, stdout, and stderr arguments are file names that will be opened
- and be used to replace the standard file descriptors in sys.stdin,
- sys.stdout, and sys.stderr. These arguments are optional and default to
- /dev/null. Note that stderr is opened unbuffered, so if it shares a file
- with stdout then interleaved output may not appear in the order that you
- expect. '''
-
- # Do first fork.
- try:
- pid = os.fork()
- if pid > 0:
- sys.exit(0) # Exit first parent.
- except OSError, e:
- sys.stderr.write ("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror) )
- sys.exit(1)
-
- # Decouple from parent environment.
- os.chdir("/")
- os.umask(0)
- os.setsid()
-
- # Do second fork.
- try:
- pid = os.fork()
- if pid > 0:
- sys.exit(0) # Exit second parent.
- except OSError, e:
- sys.stderr.write ("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror) )
- sys.exit(1)
-
- # Now I am a daemon!
-
- # Redirect standard file descriptors.
- si = open(stdin, 'r')
- so = open(stdout, 'a+')
- se = open(stderr, 'a+', 0)
- os.dup2(si.fileno(), sys.stdin.fileno())
- os.dup2(so.fileno(), sys.stdout.fileno())
- os.dup2(se.fileno(), sys.stderr.fileno())
-
- # I now return as the daemon
- return 0
-
-def add_cursor_blink (response, row, col):
-
- i = (row-1) * 80 + col
- return response[:i]+'<img src="http://www.noah.org/cursor.gif">'+response[i:]
-
-def main ():
-
- try:
- optlist, args = getopt.getopt(sys.argv[1:], 'h?d', ['help','h','?', 'hostname=', 'username=', 'password=', 'port=', 'watch'])
- except Exception, e:
- print str(e)
- exit_with_usage()
-
- command_line_options = dict(optlist)
- options = dict(optlist)
- # There are a million ways to cry for help. These are but a few of them.
- if [elem for elem in command_line_options if elem in ['-h','--h','-?','--?','--help']]:
- exit_with_usage(0)
-
- hostname = "127.0.0.1"
- port = 1664
- username = os.getenv('USER')
- password = ""
- daemon_mode = False
- if '-d' in options:
- daemon_mode = True
- if '--watch' in options:
- watch_mode = True
- else:
- watch_mode = False
- if '--hostname' in options:
- hostname = options['--hostname']
- if '--port' in options:
- port = int(options['--port'])
- if '--username' in options:
- username = options['--username']
- print "Login for %s@%s:%s" % (username, hostname, port)
- if '--password' in options:
- password = options['--password']
- else:
- password = getpass.getpass('password: ')
-
- if daemon_mode:
- print "daemonizing server"
- daemonize()
- #daemonize('/dev/null','/tmp/daemon.log','/tmp/daemon.log')
-
- sys.stdout.write ('server started with pid %d\n' % os.getpid() )
-
- virtual_screen = ANSI.ANSI (24,80)
- child = pxssh.pxssh()
- child.login (hostname, username, password)
- print 'created shell. command line prompt is', child.PROMPT
- #child.sendline ('stty -echo')
- #child.setecho(False)
- virtual_screen.write (child.before)
- virtual_screen.write (child.after)
-
- if os.path.exists("/tmp/mysock"): os.remove("/tmp/mysock")
- s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- localhost = '127.0.0.1'
- s.bind('/tmp/mysock')
- os.chmod('/tmp/mysock',0777)
- print 'Listen'
- s.listen(1)
- print 'Accept'
- #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- #localhost = '127.0.0.1'
- #s.bind((localhost, port))
- #print 'Listen'
- #s.listen(1)
-
- r = roller (0.01, endless_poll, (child, child.PROMPT, virtual_screen))
- r.start()
- print "screen poll updater started in background thread"
- sys.stdout.flush()
-
- try:
- while True:
- conn, addr = s.accept()
- print 'Connected by', addr
- data = conn.recv(1024)
- if data[0]!=':':
- cmd = ':sendline'
- arg = data.strip()
- else:
- request = data.split(' ', 1)
- if len(request)>1:
- cmd = request[0].strip()
- arg = request[1].strip()
- else:
- cmd = request[0].strip()
- if cmd == ':exit':
- r.cancel()
- break
- elif cmd == ':sendline':
- child.sendline (arg)
- #child.prompt(timeout=2)
- time.sleep(0.2)
- shell_window = str(virtual_screen)
- elif cmd == ':send' or cmd==':xsend':
- if cmd==':xsend':
- arg = arg.decode("hex")
- child.send (arg)
- time.sleep(0.2)
- shell_window = str(virtual_screen)
- elif cmd == ':cursor':
- shell_window = '%x%x' % (virtual_screen.cur_r, virtual_screen.cur_c)
- elif cmd == ':refresh':
- shell_window = str(virtual_screen)
-
- response = []
- response.append (shell_window)
- #response = add_cursor_blink (response, row, col)
- sent = conn.send('\n'.join(response))
- if watch_mode: print '\n'.join(response)
- if sent < len (response):
- print "Sent is too short. Some data was cut off."
- conn.close()
- finally:
- r.cancel()
- print "cleaning up socket"
- s.close()
- if os.path.exists("/tmp/mysock"): os.remove("/tmp/mysock")
- print "done!"
-
-def pretty_box (rows, cols, s):
-
- """This puts an ASCII text box around the given string, s.
- """
-
- top_bot = '+' + '-'*cols + '+\n'
- return top_bot + '\n'.join(['|'+line+'|' for line in s.split('\n')]) + '\n' + top_bot
-
-def error_response (msg):
-
- response = []
- response.append ("""All commands start with :
-:{REQUEST} {ARGUMENT}
-{REQUEST} may be one of the following:
- :sendline: Run the ARGUMENT followed by a line feed.
- :send : send the characters in the ARGUMENT without a line feed.
- :refresh : Use to catch up the screen with the shell if state gets out of sync.
-Example:
- :sendline ls -l
-You may also leave off :command and it will be assumed.
-Example:
- ls -l
-is equivalent to:
- :sendline ls -l
-""")
- response.append (msg)
- return '\n'.join(response)
-
-def parse_host_connect_string (hcs):
-
- """This parses a host connection string in the form
- username:password@hostname:port. All fields are options expcet hostname. A
- dictionary is returned with all four keys. Keys that were not included are
- set to empty strings ''. Note that if your password has the '@' character
- then you must backslash escape it. """
-
- if '@' in hcs:
- p = re.compile (r'(?P<username>[^@:]*)(:?)(?P<password>.*)(?!\\)@(?P<hostname>[^:]*):?(?P<port>[0-9]*)')
- else:
- p = re.compile (r'(?P<username>)(?P<password>)(?P<hostname>[^:]*):?(?P<port>[0-9]*)')
- m = p.search (hcs)
- d = m.groupdict()
- d['password'] = d['password'].replace('\\@','@')
- return d
-
-if __name__ == "__main__":
-
- try:
- start_time = time.time()
- print time.asctime()
- main()
- print time.asctime()
- print "TOTAL TIME IN MINUTES:",
- print (time.time() - start_time) / 60.0
- except Exception, e:
- print str(e)
- tb_dump = traceback.format_exc()
- print str(tb_dump)
-