summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornoah <noah@656d521f-e311-0410-88e0-e7920216d269>2006-08-24 20:52:46 +0000
committernoah <noah@656d521f-e311-0410-88e0-e7920216d269>2006-08-24 20:52:46 +0000
commit310f342ac89810990643740f94c713c74c20ee73 (patch)
tree387293750f5c55ac2a299e9338ff4d0e7f7a111f
parenta0410c953f9423063640050ad8fd542cf111214d (diff)
downloadpexpect-310f342ac89810990643740f94c713c74c20ee73.tar.gz
Merged client and server into a single script.
git-svn-id: http://pexpect.svn.sourceforge.net/svnroot/pexpect/trunk@419 656d521f-e311-0410-88e0-e7920216d269
-rwxr-xr-xpexpect/examples/bd_client.cgi407
1 files changed, 340 insertions, 67 deletions
diff --git a/pexpect/examples/bd_client.cgi b/pexpect/examples/bd_client.cgi
index 66341b1..73e7955 100755
--- a/pexpect/examples/bd_client.cgi
+++ b/pexpect/examples/bd_client.cgi
@@ -1,9 +1,339 @@
#!/usr/bin/env python
-# #sys.path.insert (0,"/var/www/cgi-bin")
-#sys.path.insert (0,"/usr/local/apache/cgi-bin")
+"""Back door shell server
-import sys, os, socket, random, string, traceback, cgi
+This exposes a 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
+
+This project is probably not the most security concious thing I've ever built.
+This should be considered an experimental tool -- at best.
+"""
+import sys,os
+sys.path.insert (0,os.getcwd())
+import socket, random, string, traceback, cgi, time, getopt, getpass, threading
+import pxssh, pexpect, ANSI
+import pxssh, pexpect, ANSI
+
+def exit_with_usage(exit_code=1):
+ print globals()['__doc__']
+ os._exit(exit_code)
+
+def client (command, host='localhost', port=-1):
+ """This sends a request to the server and returns the response.
+ If port is <= 0 then host is assumed to be the filename of a Unix domain socket.
+ If port is greater 0 then host is an inet hostname.
+ """
+ if port <= 0:
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ s.connect(host)
+ else:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((host, port))
+ s.send(command)
+ data = s.recv (2500)
+ s.close()
+ return data
+
+def server (hostname, username, password, socket_filename='/tmp/mysock', verbose=False):
+ """This starts and services requests from the client.
+ """
+ if verbose: 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)
+ if verbose: print 'created shell. command line prompt is', child.PROMPT
+ virtual_screen.write (child.before)
+ virtual_screen.write (child.after)
+
+ if os.path.exists(socket_filename): os.remove(socket_filename)
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ localhost = '127.0.0.1'
+ s.bind(socket_filename)
+ os.chmod(socket_filename,0777)
+ if verbose: print 'Listen'
+ s.listen(1)
+ #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()
+ if verbose: print "screen poll updater started in background thread"
+ sys.stdout.flush()
+ try:
+ while True:
+ conn, addr = s.accept()
+ if verbose: 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)
+ if verbose: print '\n'.join(response)
+ sent = conn.send('\n'.join(response))
+ if sent < len (response):
+ if verbose: print "Sent is too short. Some data was cut off."
+ conn.close()
+ finally:
+ r.cancel()
+ if verbose: print "cleaning up socket"
+ s.close()
+ if os.path.exists(socket_filename): os.remove(socket_filename)
+ if verbose: print "server done!"
+
+class roller (threading.Thread):
+ """This class continuously loops a function 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 class.
+ """
+ #child.logfile_read = screen
+ try:
+ s = child.read_nonblocking(4000, 0.1)
+ screen.write(s)
+ 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 random_sid ():
+ a=random.randint(0,65535)
+ b=random.randint(0,65535)
+ return '%04x%04x.sid' % (a,b)
+
+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
+
+def pretty_box (s, rows=24, cols=80):
+ """This puts an ASCII text box around the given string.
+ """
+ top_bot = '+' + '-'*cols + '+\n'
+ return top_bot + '\n'.join(['|'+line+'|' for line in s.split('\n')]) + '\n' + top_bot
+
+def client_cgi ():
+ """This handles the request if this script was called as a cgi.
+ """
+ print "Content-type: text/html;charset=utf-8\r\n"
+ sys.stderr = sys.stdout
+ ajax_mode = False
+ TITLE="Shell"
+ SHELL_OUTPUT=""
+ SID="NOT"
+ try:
+ form = cgi.FieldStorage()
+ if not form.has_key('sid'):
+ SID=random_sid()
+ else:
+ SID=form['sid'].value
+ if form.has_key('ajax'):
+ ajax_mode = True
+ ajax_cmd = form['ajax'].value
+ if ajax_cmd == 'send':
+ command = ':xsend'
+ arg = form['arg'].value.encode('hex')
+ result = client (command + ' ' + arg, '/tmp/mysock')
+ print result
+ elif ajax_cmd == 'refresh':
+ command = ':refresh'
+ result = client (command, '/tmp/mysock')
+ print result
+ elif ajax_cmd == 'cursor':
+ command = ':cursor'
+ result = client (command, '/tmp/mysock')
+ print result
+ elif form.has_key('command'):
+ command = form["command"].value
+ SHELL_OUTPUT = client (command, '/tmp/mysock')
+ print CGISH_HTML % locals()
+ else:
+ print CGISH_HTML % locals()
+ except:
+ tb_dump = traceback.format_exc()
+ if ajax_mode:
+ print str(tb_dump)
+ else:
+ SHELL_OUTPUT=str(tb_dump)
+ print CGISH_HTML % locals()
+
+def server_cli():
+ """This is the command line interface to starting the server.
+ This handles things if the script was not called as a CGI
+ (if you run it from the command line).
+ """
+ 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']
+ 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')
+
+ server (hostname, username, password)
+
+def main ():
+ if os.getenv('REQUEST_METHOD') is None:
+ server_cli()
+ else:
+ client_cgi()
+
+# It's mostly HTML and Javascript from here on out.
CGISH_HTML="""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
@@ -112,7 +442,6 @@ function query_cursor()
{
loadurl('bd_client.cgi?ajax=cursor');
}
-
function type_key (chars)
{
var ch = '?';
@@ -243,7 +572,7 @@ function KeyCheck(e)
<body onload="init()">
<form id="form" name="form" action="/cgi-bin/bd_client.cgi" method="POST">
<input name="sid" value="%(SID)s" type="hidden">
-<textarea name="screen_text" cols="80" rows="24">%(SHELL_OUTPUT)s</textarea>
+<textarea name="screen_text" cols="81" rows="25">%(SHELL_OUTPUT)s</textarea>
<hr noshade="1">
&nbsp;<input name="command" id="command" type="text" size="80"><br>
<p align="left">
@@ -251,7 +580,6 @@ function KeyCheck(e)
<tr>
<td width="86%%" align="center">
<input name="submit" type="submit" value="Enter">
- <input name="esc" type="submit" value="ESC">
<input name="refresh" type="button" value="REFRESH" onclick="refresh_screen()">
<input name="refresh" type="button" value="CURSOR" onclick="query_cursor()">
<br>
@@ -326,66 +654,11 @@ function KeyCheck(e)
</html>
"""
-def random_sid ():
- a=random.randint(0,65535)
- b=random.randint(0,65535)
- return 'pxssh%04x%04x' % (a,b)
-
-def bd_client (command, host='localhost', port=-1):
- """This sends a request to the server and returns the response.
- If port is less than 0 then host is assumed
- to be the filename of a Unix domain socket.
- """
- if port < 0:
- s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- s.connect(host)
- else:
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((host, port))
- s.send(command)
- data = s.recv (2500)
- s.close()
- return data
-
-print "Content-type: text/html;charset=utf-8\r\n"
-sys.stderr = sys.stdout
-ajax_mode = False
-TITLE="Shell"
-SHELL_OUTPUT=""
-SID="NOT"
-try:
- form = cgi.FieldStorage()
- if not form.has_key('sid'):
- SID=random_sid()
- else:
- SID=form['sid'].value
- if form.has_key('ajax'):
- ajax_mode = True
- ajax_cmd = form['ajax'].value
- if ajax_cmd == 'send':
- command = ':xsend'
- arg = form['arg'].value.encode('hex')
- result = bd_client (command + ' ' + arg, "/tmp/mysock")
- print result
- elif ajax_cmd == 'refresh':
- command = ':refresh'
- result = bd_client (command, "/tmp/mysock")
- print result
- elif ajax_cmd == 'cursor':
- command = ':cursor'
- result = bd_client (command, "/tmp/mysock")
- print result
- elif form.has_key('command'):
- command = form["command"].value
- SHELL_OUTPUT = bd_client (command, "/tmp/mysock")
- print CGISH_HTML % locals()
- else:
- print CGISH_HTML % locals()
-except:
- tb_dump = traceback.format_exc()
- if ajax_mode:
+if __name__ == "__main__":
+ try:
+ main()
+ except Exception, e:
+ print str(e)
+ tb_dump = traceback.format_exc()
print str(tb_dump)
- else:
- SHELL_OUTPUT=str(tb_dump)
- print CGISH_HTML % locals()