diff options
-rw-r--r-- | server.py | 137 | ||||
-rw-r--r-- | vt100.py | 80 |
2 files changed, 217 insertions, 0 deletions
diff --git a/server.py b/server.py new file mode 100644 index 0000000..f09e2b0 --- /dev/null +++ b/server.py @@ -0,0 +1,137 @@ +import paramiko +import re +import socket +import time +import threading +import vt100 + +class Connection(object): + def __init__(self): + self.buf = b'' + self.vt = vt100.vt100() + + def process(self, data): + self.buf += data + self.vt.process(data) + + def get_term(self): + term = '' + for i in range(0, 24): + for j in range(0, 80): + term += self.vt.cell(i, j).contents.contents() + term += "\n" + + return term[:-1] + +class SSHServer(paramiko.ServerInterface): + def check_channel_request(self, kind, chanid): + return paramiko.OPEN_SUCCEEDED + + def check_channel_pty_request(self, channel, term, width, height, pixelwidth, pixelheight, modes): + return True + + def check_channel_shell_request(self, channel): + return True + + def check_auth_password(self, username, password): + if password == "blah": + return paramiko.AUTH_SUCCESSFUL + return paramiko.AUTH_FAILED + + def get_allowed_auths(self, username): + return "password" + +class TermcastServer(object): + def __init__(self): + self.sessions = {} + + def listen(self): + ssh_sock = self._open_socket(2200) + tc_sock = self._open_socket(2201) + + threading.Thread( + target=lambda: self.wait_for_ssh_connection(ssh_sock) + ).start() + threading.Thread( + target=lambda: self.wait_for_tc_connection(tc_sock) + ).start() + + def wait_for_ssh_connection(self, sock): + self._wait_for_connection( + sock, + lambda client: self.handle_ssh_connection(client) + ) + + def wait_for_tc_connection(self, sock): + self._wait_for_connection( + sock, + lambda client: self.handle_tc_connection(client) + ) + + def handle_ssh_connection(self, client): + t = paramiko.Transport(client) + t.add_server_key(paramiko.RSAKey(filename='test_rsa.key')) + t.start_server(server=SSHServer()) + chan = t.accept(None) + + if b'doy' in self.sessions: + chan.send(self.sessions[b'doy'].get_term()) + else: + chan.send("no data for doy\r\n") + + time.sleep(5) + chan.close() + + def handle_tc_connection(self, client): + buf = b'' + while len(buf) < 1024 and b"\n" not in buf: + buf += client.recv(1024) + + pos = buf.find(b"\n") + if pos == -1: + print("no authentication found") + return + + auth = buf[:pos] + buf = buf[pos+1:] + + auth_re = re.compile(b'^hello ([^ ]+) ([^ ]+)$') + m = auth_re.match(auth) + if m is None: + print("no authentication found (%s)" % auth) + return + + print(b"got auth: " + auth) + conn = Connection() + self.sessions[m.group(1)] = conn + client.send(b"hello, " + m.group(1) + b"\n") + + conn.process(buf) + while True: + buf = client.recv(1024) + if len(buf) > 0: + conn.process(buf) + else: + return + + def _wait_for_connection(self, sock, cb): + while True: + try: + sock.listen(100) + client, addr = sock.accept() + except Exception as e: + print('*** Listen/accept failed: ' + str(e)) + traceback.print_exc() + continue + + threading.Thread(target=cb, args=(client,)).start() + + def _open_socket(self, port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(('', port)) + return sock + +if __name__ == '__main__': + server = TermcastServer() + server.listen() diff --git a/vt100.py b/vt100.py new file mode 100644 index 0000000..db3509c --- /dev/null +++ b/vt100.py @@ -0,0 +1,80 @@ +from ctypes import * + +libvt100 = CDLL("libvt100.so") + +class vt100_rgb_color(Union): + _fields_ = [ + ("r", c_ubyte), + ("g", c_ubyte), + ("b", c_ubyte), + ] + +class vt100_color(Structure): + _anonymous_ = ("rgb",) + _fields_ = [ + ("rgb", vt100_rgb_color), + ("type", c_ubyte), + ] + +class vt100_named_attrs(Structure): + _fields_ = [ + ("bold", c_ubyte, 1), + ("italic", c_ubyte, 1), + ("underline", c_ubyte, 1), + ("inverse", c_ubyte, 1), + ] + +class vt100_attrs(Union): + _anonymous_ = ("named",) + _fields_ = [ + ("named", vt100_named_attrs), + ("attrs", c_ubyte), + ] + +class vt100_cell_attrs(Structure): + _anonymous_ = ("cell_attrs",) + _fields_ = [ + ("fgcolor", vt100_color), + ("bgcolor", vt100_color), + ("cell_attrs", vt100_attrs), + ] + +class vt100_cell(Structure): + _fields_ = [ + ("_contents", c_char * 8), + ("len", c_size_t), + ("attrs", vt100_cell_attrs), + ("is_wide", c_ubyte, 1), + ] + + def contents(self): + return self._contents[:self.len].decode('utf-8') + +new_prototype = CFUNCTYPE(c_void_p) +vt100_new = new_prototype(("vt100_screen_new", libvt100)) + +set_window_size_prototype = CFUNCTYPE(None, c_void_p) +vt100_set_window_size = set_window_size_prototype(("vt100_screen_set_window_size", libvt100)) + +process_string_prototype = CFUNCTYPE(c_int, c_void_p, c_char_p, c_size_t) +vt100_process_string = process_string_prototype(("vt100_screen_process_string", libvt100)) + +cell_at_prototype = CFUNCTYPE(POINTER(vt100_cell), c_void_p, c_int, c_int) +vt100_cell_at = cell_at_prototype(("vt100_screen_cell_at", libvt100)) + +delete_prototype = CFUNCTYPE(None, c_void_p) +vt100_delete = delete_prototype(("vt100_screen_delete", libvt100)) + +class vt100(object): + def __init__(self): + self.vt = vt100_new() + vt100_set_window_size(self.vt) + + def __del__(self): + vt100_delete(self.vt) + + def process(self, string): + return vt100_process_string(self.vt, string, len(string)) + + def cell(self, x, y): + return vt100_cell_at(self.vt, x, y) |