From 7cce3da2979468fcbfbe96d0949b013a2ac9e787 Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Sat, 2 Jun 2012 23:14:24 +0200 Subject: [PATCH] SSLClient abstraction and basic PingHandler --- daemon/dist/__init__.pyc | Bin 142 -> 0 bytes daemon/dist/core.py | 82 +---------------- daemon/dist/core.pyc | Bin 2876 -> 0 bytes daemon/node/__init__.pyc | Bin 142 -> 0 bytes daemon/node/core.py | 37 ++++++-- daemon/node/core.pyc | Bin 722 -> 0 bytes daemon/shared/__init__.pyc | Bin 144 -> 0 bytes daemon/shared/core.py | 179 ++++++++++++++++++++++++++++++++++++- daemon/shared/core.pyc | Bin 2336 -> 0 bytes 9 files changed, 210 insertions(+), 88 deletions(-) delete mode 100644 daemon/dist/__init__.pyc delete mode 100644 daemon/dist/core.pyc delete mode 100644 daemon/node/__init__.pyc delete mode 100644 daemon/node/core.pyc delete mode 100644 daemon/shared/__init__.pyc delete mode 100644 daemon/shared/core.pyc diff --git a/daemon/dist/__init__.pyc b/daemon/dist/__init__.pyc deleted file mode 100644 index c71f3b171963db54d0ee1f3e9446a7bf0d990c35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142 zcmZSn%*&;6bFY6g0~9aQuFi+ zit@8klS_*A^YT+tlk@XRit=;xQxa2i^YipmGK)*}a`RJ4 Nb5iX 2: - channel_identifier = chunk[:2] - data = chunk[2:] - - channel_numeric = to_numeric(channel_identifier) - - if channel_numeric in self.channel_map: - self.channel_map[channel_numeric].process_chunk(data) - else: - print "WARNING: Received data on non-existent channel %d" % channel_numeric - - - -bindsocket = socket.socket() -bindsocket.bind(('0.0.0.0', 9151)) -bindsocket.listen(5) - -select_inputs = [ bindsocket ] - -while select_inputs: - readable, writable, error = select.select(select_inputs, select_outputs, select_inputs) - - for sock in readable: - try: - if sock is bindsocket: - newsocket, fromaddr = bindsocket.accept() - connstream = ssl.wrap_socket(newsocket, - server_side=True, - certfile=cert_path, - keyfile=key_path, - ssl_version=ssl.PROTOCOL_TLSv1) - - new_client = Client(connstream) - - select_inputs.append(connstream) - client_map[connstream.fileno()] = new_client - client_list.append(new_client) - else: - data = sock.recv(1024) - - if data: - cur_client = client_map[sock.fileno()] - cur_client.process_data(data) - else: - select_inputs = remove_from_list(select_inputs, sock) - print "NOTICE: Client disconnected" - except ssl.SSLError, err: - if err.args[0] == ssl.SSL_ERROR_WANT_READ: - select.select([sock], [], []) - elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: - select.select([], [sock], []) - else: - raise - - print "Server socket closed, exiting..." diff --git a/daemon/dist/core.pyc b/daemon/dist/core.pyc deleted file mode 100644 index 6514ea59885c0680e4dcd6503a46e00a7ea3b2dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2876 zcmb_d?`|7K5T89ePVCxs>ZEDYLSy;|tO{Z(K2)kIL`kDIQstm?AjN8Xy54S*v(IeyM0`p?AI#?sKM#DC*as25rnTiD1q7!&pCA~-w9_}9gN@aHV z0~*yRzv!vKRe*JshSx}+qg7tIP9u-K6RaCF*z1bIvS*bPuwz>DnWccCAgik;$E1d3%16$48+v@n87$ zItmJo$;}99MFUsov$U&Y>$*{voAt>{w+Oo& z(?=%Q%d>HiV6n*?@|mPu1#}~cP3j_^;2(SUC5G*SAg8#ZKs_l9RFRT;krLvB5_C=x+Q5YU5Cmudoj@Q2gUC;Czd(s-C|lpfQT-0v^-zfbNH%0It;vU|_(ZS@}9Y5OKJ@sZJOcDtRW?KDe2F~=w))J%JpZS5PnNL0S?1PdIR%Yt+| zHhCQREcz{w4hJZR``m^c&z)v$tYH}M#U}T;J4uPnnyPtq zucVjp*K3$i8{L3|>SW=!7*P8suTUGLU$lu!m&b-#wHs}mEtob9U1)!PDnqq2VrjXI`?y*TT0R7q+?4nFWOom zqvuJH7v|_plc}?gFKsrHO&w1O&SLmHPIVDj(Lf-I1N@mlHgYbY>+0}15M|*-9y<~E z0!*G~x!Ca*Vi!h{nK+3!H7{m6vmt+cLamD){UQ%1!5hc!c2jYOv}1+kUWnueTHUoA>U!<+tZ|{jHtN;?A-7j(w4#xm%Ri=aLKjp+MTtT}M?` zOHu+Tf~6~U)ldzu>Q&Vh^vaT|qjKw9Ao$l&zYVph7QLodDc<-9wbY7Q<>56;e*;43 BV6*@L diff --git a/daemon/node/__init__.pyc b/daemon/node/__init__.pyc deleted file mode 100644 index a04d05a4c1770f95fc5bd767f66c54ec467a64df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142 zcmZSn%*&;6bFY6g0~9aQuFi+ zit@8klS_*A^YT+tlk@XRit=;xQxa2i^Yg$$`tk9Zd6^~g@p=W7B^*G#Ho5sJr8%i~ KARCH-m;nHJuOGSq diff --git a/daemon/node/core.py b/daemon/node/core.py index afee0ea..802e615 100755 --- a/daemon/node/core.py +++ b/daemon/node/core.py @@ -11,12 +11,33 @@ allowed_certs = '/home/sven/ssl/allowed' import socket, ssl, time, math from shared.core import * -s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -ssl_sock = ssl.wrap_socket(s, cert_reqs=ssl.CERT_REQUIRED, ca_certs=allowed_certs) -ssl_sock.connect(('localhost', 9151)) +select_inputs = [] +select_outputs = [] +client_map = {} + +client = SSLClient("localhost", 9151, allowed_certs) +select_inputs.append(client.ssl_sock) +select_outputs.append(client.ssl_sock) +client_map[client.ssl_sock.fileno()] = client.client + +while True: + readable, writable, error = select.select(select_inputs, select_outputs, select_inputs) + + for sock in readable: + try: + data = sock.recv(1024) + + if data: + cur_client = client_map[sock.fileno()] + cur_client.process_data(data) + else: + select_inputs = remove_from_list(select_inputs, sock) + print "NOTICE: Client disconnected" + except ssl.SSLError, err: + if err.args[0] == ssl.SSL_ERROR_WANT_READ: + select.select([sock], [], []) + elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: + select.select([], [sock], []) + else: + raise -ssl_sock.write(to_identifier(0) + 'test data' + EOC + to_identifier(4190) + 'SAMPLEDATA' + EOC) - -print ssl_sock.read()[2:] - -ssl_sock.close() diff --git a/daemon/node/core.pyc b/daemon/node/core.pyc deleted file mode 100644 index 178fe4a7c48040662bd4a5d4c30c3ac2535837df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 722 zcmYjO&2G~`5T3P@G;!*-aG;z3iHn6qjl>lptkggSZNM=XAFx*5sp4RJgLfd}oaaJ3 z0Z+hV@BlEoO$3(q^Y_iq&bPb%?=ZOj^?hE!?i=y{1uyfHK>-wu0+a`hfP%>l_n`3h z+=s&7^8pkC$9-sqkfVT=0E;7s2e9SNF`x&l3E&vi!0`ao5D;K1ykZJCh1DUbkR=E! zLck+XW2@B!PVtx*2Lc;7RnwcuzW4;TZ21uGzj|$ZZ&@kW{~|iF$GXE1FLjHt`HEd^ zOcTqd(Hm6tAO5Z)!3AXD5lt8??5XVWv!St7va|u?QjM-;v($#_9}fX)PJ^*6goYj{ zNwWTU!Ybg1mbko;_&MoP(R@hN}UGt?_i|Kskob@P`E3O)CFyajS1M`&S ze{Znv^@=w)aiQuFi+ zit@8klS_*A^YT+tlk@XRit=;xQxa2i^YiqJGZKqZQ}pBGGxIV_;^XxSDoZ$kx@~gv PQ%ZAE?Lc-E12F>t#seUY diff --git a/daemon/shared/core.py b/daemon/shared/core.py index ea83340..7baf9fb 100644 --- a/daemon/shared/core.py +++ b/daemon/shared/core.py @@ -1,4 +1,5 @@ -import math +import math, socket, ssl, select, time, threading, random, string +from datetime import datetime EOC = "\0" @@ -7,6 +8,57 @@ def to_numeric(identifier): def to_identifier(numeric): return chr(int(math.floor(numeric / 255) + 1)) + chr((numeric % 255) + 1) + +def remove_from_list(ls, val): + return [value for value in ls if value is not val] + +class PingThread(threading.Thread): + def __init__(self, channel): + self.channel = channel + threading.Thread.__init__(self) + self.daemon = True + + def run(self): + while True: + self.pingkey = ''.join(random.choice(string.letters + string.digits) for i in xrange(5)) + self.pingtime = datetime.time(datetime.now()) + self.channel.send("PING %s" % self.pingkey) + time.sleep(10) + +class Client: + buff = "" + channel_map = {} + handshake_status = 0 + + version = 1.0 + + def __init__(self, connstream): + self.stream = connstream + self.channel_map[0] = Channel(self, ControlHandler(self)) + + def start_handshake(self): + self.handshake_status = 1 + self.channel_map[0].send("OHAI") + + def process_data(self, data): + self.buff += data + stack = self.buff.split(EOC) + self.buff = stack.pop() + + for chunk in stack: + self.process_chunk(chunk) + + def process_chunk(self, chunk): + if len(chunk) > 2: + channel_identifier = chunk[:2] + data = chunk[2:] + + channel_numeric = to_numeric(channel_identifier) + + if channel_numeric in self.channel_map: + self.channel_map[channel_numeric].process_chunk(data) + else: + print "WARNING: Received data on non-existent channel %d" % channel_numeric class Channel: numeric = 0 @@ -26,9 +78,134 @@ class Channel: self.client.stream.send(to_identifier(self.numeric) + data + EOC) class Handler: + client = None + channel = None + + def __init__(self, client): + self.client = client + def process(self, chunk): pass +class ControlHandler(Handler): + pingthread = None + + def process(self, chunk): + target = self.client.channel_map[0] + + if chunk == "OHAI": + self.client.handshake_status = 1 + target.send("HITHAR") + elif chunk == "HITHAR": + self.client.handshake_status = 2 + target.send("VERSION %d" % self.client.version) + elif chunk[:7] == "VERSION": + if self.client.handshake_status == 1: + # Version received, return own version + self.client.handshake_status = 3 + target.send("VERSION %d" % self.client.version) + elif self.client.handshake_status == 2: + # Version received and already sent, version exchange complete + self.client.handshake_status = 4 + target.send("KGO") + print "Handshake complete!" + self.pingthread = PingThread(target) + self.pingthread.start() + else: + raise Exception("VERSION received outside version exchange.") + elif chunk == "KGO": + if self.client.handshake_status == 3: + # Handshake complete + self.client.handshake_status = 4 + print "Handshake complete!" + self.pingthread = PingThread(target) + self.pingthread.start() + else: + raise Exception("KGO received before handshake finalization.") + elif chunk[:4] == "PING": + command, pingkey = chunk.split(' ') + target.send("PONG %s" % pingkey) + elif chunk[:4] == "PONG": + command, pingkey = chunk.split(' ') + microseconds = (self.pingthread.pingtime.microsecond * 1.0) / 1000000 + seconds_start = self.pingthread.pingtime.second + microseconds + current_time = datetime.time(datetime.now()) + microseconds = (current_time.microsecond * 1.0) / 1000000 + seconds_end = current_time.second + microseconds + + print "Latency: %f seconds" % (seconds_end - seconds_start) + class EchoHandler(Handler): def process(self, chunk): print "Received %s" % chunk + +class SSLClient: + hostname = "" + port = 0 + client = None + controlchannel = None + controlhandler = None + ssl_sock = None + + def __init__(self, hostname, port, allowed_certs): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.ssl_sock = ssl.wrap_socket(sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=allowed_certs) + self.ssl_sock.connect((hostname, port)) + + self.client = Client(self.ssl_sock) + + self.client.start_handshake() + + +class SSLDaemon: + client_list = [] + client_map = {} + select_inputs = [] + select_outputs = [] + + def __init__(self): + pass + + def start(self, interface, port, cert_path, key_path): + bindsocket = socket.socket() + bindsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + bindsocket.bind((interface, port)) + bindsocket.listen(5) + + self.select_inputs = [ bindsocket ] + + while self.select_inputs: + readable, writable, error = select.select(self.select_inputs, self.select_outputs, self.select_inputs) + + for sock in readable: + try: + if sock is bindsocket: + newsocket, fromaddr = bindsocket.accept() + connstream = ssl.wrap_socket(newsocket, + server_side=True, + certfile=cert_path, + keyfile=key_path, + ssl_version=ssl.PROTOCOL_TLSv1) + + new_client = Client(connstream) + + self.select_inputs.append(connstream) + self.select_outputs.append(connstream) + self.client_map[connstream.fileno()] = new_client + self.client_list.append(new_client) + else: + data = sock.recv(1024) + + if data: + cur_client = self.client_map[sock.fileno()] + cur_client.process_data(data) + else: + self.select_inputs = remove_from_list(self.select_inputs, sock) + print "NOTICE: Client disconnected" + except ssl.SSLError, err: + if err.args[0] == ssl.SSL_ERROR_WANT_READ: + select.select([sock], [], []) + elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: + select.select([], [sock], []) + else: + raise diff --git a/daemon/shared/core.pyc b/daemon/shared/core.pyc deleted file mode 100644 index a5299873db9075099209b39bbe11ec8a9a25d22f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2336 zcmcgtO>fgc5FOiT+VlfN3vEGs6$vRPazNY=LR3IcspOu>#WLQl;^5eocU!4aZ}e~R zOZXZ50C;cgCOwmYlby_r?VWiuZ#L1dt@W=ze;jAhUJ-uZV>ttXWdbQgMo>Z-1!5w( zh8c?Ks1k`;5wjttOY^(Pmr+N|s+dhNYcm%f>4{yDQ*5w4^LDxUNq-B0^53|IK7cA2 zC07UQ5X7KzMubw=u`7_R<&u2b>1_!^!aIS zYi=$o+#6_vaW%CAcVWxHSzS$R=G~yIjLoXj*Htkvshw8kz>U+|nn708_SM;?=a#-o z%JZqM^K1o{3n3(!fuT>qyoQ3jfz^jHiCR<-2q2U_{NOvowcGk;xClwX^;sRom5pot z%}ej;oTh&48Sb>Gs=9%^3bRdaDR|j4;GI`-4UQJFXRyO@T9&rRpz6hHe86(=AS@UJ zxXL3N2^rIo5$FK9D)6Ac*MW@ISdwcz*1O@|y2K}aV#~24BU~=-uEl)@+%tL}6J{e)%EQNOf2ars35WtgwMj#U)R^9{(kgp7t3e`Bx8>9_{(rEUI z8|==kbA3J?-+@rhWaIPl+fsnBR&9(#cin}*74xKGj*czw z2JI)@TF^$>aUhC{)7I}NdQySV!E%AKT;Ffs<;vJgmqgVjT zHeP?qEy!UBljs`h{FsfaMGD5ewP0*H4n6RE_w$iq z<#=o8^sKR&%`dEZ{!;%4|5+tpAmV?iB&&FE6RaEDY7zfAVx4Lp7I8$OqfFMu>_E(y gZ@mF)0S4)yzHLVH9og%6u5X6juov~Bo$Z~@A5-ANF#rGn