aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server.py137
-rw-r--r--vt100.py80
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)