diff --git a/client/shell/yadync.sh b/client/shell/yadync.sh index 8fdae78..665fa47 100755 --- a/client/shell/yadync.sh +++ b/client/shell/yadync.sh @@ -3,7 +3,7 @@ SHAREDSECRET="test123" DYNID="testhost" DATE=`date +%s` -CHECKSUM=`echo "$DYNID $SHAREDSECRET $DATE" | md5sum | awk '{print $1}'` +CHECKSUM=`echo -n "$DYNID $SHAREDSECRET $DATE" | md5sum | awk '{print $1}'` OUT="$DYNID $DATE $CHECKSUM" -echo $OUT | nc -q 0 -u 88.198.170.2 9090 +echo -n $OUT | nc -q 0 -u 127.0.0.1 9090 diff --git a/server/yadyn b/server/yadyn old mode 100644 new mode 100755 index 1bc4a83..dccea0e --- a/server/yadyn +++ b/server/yadyn @@ -1,111 +1,130 @@ #!/usr/bin/python -import select import socket +import Queue +import threading +import md5 import time -import random + +class Entry(object): + def __init__(self, dynid, sharedSecret, name): + self.dynid = dynid + self.sharedSecret = sharedSecret + self.name = name + self.lastEventTime = 0 + self.address = '' + +class IllegalEventException(Exception): + def __init__(self, msg): + self.msg = msg -ALPHANUM = (0,1,2,3,4,5,6,7,8,9, - 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z') - -class DoneException(Exception): pass - -class TimeoutException(Exception): pass - -class Client(object): - def __init__(self, sock, address): - self.sock = sock +class Event(object): + def __init__(self, address, data, receiveTime): self.address = address - self.timestamp = int(time.time()) - self.timeout = 5 - self.state = 0 + self.data = data + self.receiveTime = receiveTime + + def prepare(self): + self.port = self.address[1] + self.address = self.address[0] + (self.dynid, self.msgTime, self.checksum) = self.data.split(' ') + self.msgTime = int(self.msgTime) + self.prepared = True def process(self): - self.timestamp = int(time.time()) - data = self.sock.recv(8192) - if data == None or data == '': - raise DoneException - if data[-1] in ('\r','\n'): - data = data[:-1] - if data[-1] in ('\r','\n'): - data = data[:-1] - print "<%s> %d" % (data, len(data)) + if not self.prepared: + self.prepare() + + if not ENTRIES.has_key(self.dynid): + raise IllegalEventException("unknown dynid in event %s" % str(self)) + entry = ENTRIES[self.dynid] + + if self.msgTime + MSG_TIME_CORRIDOR < self.receiveTime: + raise IllegalEventException("event too old %s" % str(self)) + if self.msgTime - MSG_TIME_CORRIDOR > self.receiveTime: + raise IllegalEventException("event too young %s" % str(self)) - if self.state == 0: - #print "State 0" - if data == "hello": - self.challenge = ''.join(random.sample(ALPHANUM, 5)) - print self.challenge - self.sock.send(self.challenge) - self.state = 1 - return - else: - raise DoneException - elif self.state == 1: - print "State 1" - if data == self.challenge: - self.sock.send("OK") - raise DoneException + if entry.lastEventTime >= self.msgTime: + raise IllegalEventException("timing sequence failure in event, possibly replay %s" % str(self)) - print "After state machine" + di = "%s %s %d" % (self.dynid, entry.sharedSecret, self.msgTime) + d = md5.new(di).hexdigest() + print "%s, received: %s, calculated: %s" % (di, self.checksum, d) + if d != self.checksum: + raise IllegalEventException("wrong checksum for event %s" % str(self)) + + entry.lastEventTime = self.msgTime + + if entry.address == self.address: + print "Same address, nothing to do." + else: + entry.address = self.address + print "Set in DNS: %s -> %s" % (entry.name, entry.address) + def __str__(self): + if not self.prepared: + self.prepare() + return "%s from %s:%d" % (self.data, self.address, self.port) +class Handler(threading.Thread): + def __init__(self, queue): + threading.Thread.__init__(self) + self.q = queue + self.setDaemon(True) - def checkTimeout(self): - if self.timestamp + self.timeout < int(time.time()): - raise TimeoutException() + def run(self): + while True: + event = self.q.get() + try: + event.prepare() + print "Processing event %s" % str(event) + event.process() + except IllegalEventException, e: + print "Some failure: %s" % e.msg - def close(self): - self.sock.close() +class Expirer(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + self.setDaemon(True) + def run(self): + while True: + print "Expiring ..." + currentTime = int(time.time()) + for entry in ENTRIES.values(): + if entry.lastEventTime != 0 and entry.lastEventTime + EVENT_LIFE_TIME < currentTime: + print "Entry %s expired" % entry.dynid + entry.lastEventTime = 0 + entry.address = NULL_ADDRESS + print "Set in DNS: %s -> %s" % (entry.name, entry.address) + time.sleep(10) + +ENTRIES = { + 'testhost': Entry('testhost,', 'test123', 'test.test.de'), + } +MSG_TIME_CORRIDOR = 5 +EVENT_LIFE_TIME = 10 +NULL_ADDRESS = '0.0.0.0' +q = Queue.Queue() -sSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -sSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) -sSock.bind(("", 9090)) -sSock.listen(5) -print "sSock: ", sSock.fileno() +handler = Handler(q) +handler.start() -p = select.poll() -p.register(sSock.fileno(), select.POLLIN) +expirer = Expirer() +expirer.start() -clients = {} +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +s.bind(("", 9090)) while True: - events = p.poll(1000) - - for (fd, event) in events: - # print "fd: %d, event: %d" % (fd, event) - - if fd == sSock.fileno() and (event & select.POLLIN): - # print "received connect" - cSock, cAddress = sSock.accept() - clients[cSock.fileno()] = Client(cSock, cAddress) - # print "cSock: %d, address: %s" % (cSock.fileno(), str(cAddress)) - p.register(cSock.fileno(), select.POLLIN) - else: - # print "received data for ", fd - if event & select.POLLIN: - client = clients[fd] - try: - client.process() - except Exception, e: - print "Closing %d, %s" % (fd, str(e)) - client.close() - del clients[fd] - p.unregister(fd) - - # print "clients: " + str(clients) - for fd in clients.keys(): - try: - clients[fd].checkTimeout() - except TimeoutException: - # print "Timeout %d" % fd - clients[fd].close() - del clients[fd] - p.unregister(fd) + data, address = s.recvfrom(256) + try: + q.put_nowait(Event(address, data, int(time.time()))) + except Queue.Full: + print "Event %s from %s dropped" % (data, str(address))