diff -uNr a/blatta/lib/client.py b/blatta/lib/client.py --- a/blatta/lib/client.py 39920c295fdaf1bb2b6a76564da9b8dc1770ba9fed4c145b72854432f302d919c9ea611485e0de01eb544eba1052471133c509ab8c4cc61c12e6c7ec83ea2b8d +++ b/blatta/lib/client.py 1fea4f4a6a2c05324ba98ddf9615bed578e108a6e299a79a10ee5ad3748341f3df42a4d68799cad74655be7ac8820669fc2ba15d72cc6ea6b325f345ad54393b @@ -5,6 +5,8 @@ import string import os import base64 +import traceback +from lib.message import Message from lib.server import VERSION from funcs import * from lib.commands import BROADCAST @@ -150,7 +152,7 @@ self.disconnect("Client quit") return if self.nickname and self.user: - self.reply("001 %s :Hi, welcome to IRC" % self.nickname) + self.reply("001 %s :Hi, welcome to Pest" % self.nickname) self.reply("002 %s :Your host is %s, running version blatta-%s" % (self.nickname, server.name, VERSION)) self.reply("003 %s :This server was created sometime" @@ -341,13 +343,13 @@ self.channel_log(channel, message) else: formatted_message = ":%s %s %s :%s" % (self.prefix, command, targetname, message) - self.server.peer_message({ + self.server.peer_message(Message({ "speaker": self.nickname, "handle": targetname, "body": formatted_message, "bounces": 0, "command": DIRECT - }) + }, self.server)) if(client): client.message(formatted_message) @@ -537,13 +539,18 @@ handle = arguments[0] at = self.server.state.get_at(handle) elif len(arguments) == 2: - handle, address = arguments - address_ip, port = string.split(address, ":") - self.server.state.update_address_table({"handle": handle, - "address": address_ip, - "port": port}, - False) - self.pest_reply("updated address table: %s %s" % (handle, address)) + try: + handle, address = arguments + address_ip, port = string.split(address, ":") + self.server.state.update_address_table({"handle": handle, + "address": address_ip, + "port": port}, + False) + self.pest_reply("updated address table: %s %s" % (handle, address)) + except Exception as ex: + self.pest_reply("Error attempting to update address table") + stack = traceback.format_exc() + print(stack) return elif len(arguments) > 2: self.pest_reply("Usage: AT [] [
]") @@ -588,7 +595,9 @@ try: handler_table[command]() except KeyError: - self.reply("421 %s %s :Unknown command" % (self.nickname, command)) + self.reply("421 %s %s :Unknown command" % (self.nickname, command)) + stack = traceback.format_exc() + print(stack) def socket_readable_notification(self): try: @@ -647,12 +656,14 @@ if client != self or include_self: client.message(line) # send the channel message to peers as well - self.server.peer_message({ + self.server.peer_message( + Message( + { "speaker": self.nickname, "command": BROADCAST, "bounces": 0, "body": line - }) + }, self.server)) def channel_log(self, channel, message, meta=False): if not self.server.logdir: diff -uNr a/blatta/lib/infosec.py b/blatta/lib/infosec.py --- a/blatta/lib/infosec.py 0646687de5d54fd0278b8265786d9e5bb65492dc81f323249b57c129d5293cf605f384b13354379d970aaf04c1757866529320f2b0d8226f64a0f269398de6cb +++ b/blatta/lib/infosec.py e983984d664daef6d42fd55c86f784bfead4057b78673c18a8f8e85346db539f3f3429de5185a86805a1e698c2907441b5c2017e224bb1606358b930615ce84c @@ -5,6 +5,8 @@ from lib.serpent import serpent_cbc_decrypt from commands import BROADCAST from commands import DIRECT +from commands import IGNORE +from lib.message import Message import base64 import binascii import time @@ -12,6 +14,7 @@ import sys import hmac import random +import os import pprint pp = pprint.PrettyPrinter(indent=4) @@ -28,6 +31,7 @@ DUPLICATE_PACKET = 1 MALFORMED_PACKET = 2 INVALID_SIGNATURE = 3 +IGNORED = 4 class Infosec(object): def __init__(self, server=None): @@ -35,11 +39,11 @@ def pack(self, peer, message): try: - timestamp = message["timestamp"] + timestamp = message.timestamp except: timestamp = None - command = message["command"] - speaker = self._pad(message["speaker"], MAX_SPEAKER_SIZE) + command = message.command + speaker = self._pad(message.speaker, MAX_SPEAKER_SIZE) # if we are rebroadcasting we need to use the original timestamp @@ -56,32 +60,34 @@ # there this is the first message - if message["original"]: + if message.original: if command == DIRECT: - self_chain = self.server.state.get_last_message_hash(message["speaker"], peer.peer_id) + self_chain = self.server.state.get_last_message_hash(message.speaker, peer.peer_id) elif command == BROADCAST: - self_chain = self.server.state.get_last_message_hash(message["speaker"]) + self_chain = self.server.state.get_last_message_hash(message.speaker) + elif command == IGNORE: + self_chain = "\x00" * 32 net_chain = "\x00" * 32 else: - self_chain = message["self_chain"] - net_chain = message["net_chain"] + self_chain = message.self_chain + net_chain = message.net_chain # pack message bytes - message_bytes = struct.pack(MESSAGE_PACKET_FORMAT, int_ts, self_chain, net_chain, speaker, message["body"].encode("ascii")) + message_bytes = struct.pack(MESSAGE_PACKET_FORMAT, int_ts, self_chain, net_chain, speaker, message.body) # log messages - if message["original"]: + if message.original: if command == DIRECT: - self.server.state.log(message["speaker"], message_bytes, peer.peer_id) + self.server.state.log(message.speaker, message_bytes, peer.peer_id) elif command == BROADCAST: - self.server.state.log(message["speaker"], message_bytes) + self.server.state.log(message.speaker, message_bytes) # pack packet bytes nonce = self._generate_nonce(16) - bounces = message["bounces"] + bounces = message.bounces version = 0xfe red_packet_bytes = struct.pack(RED_PACKET_FORMAT, nonce, bounces, version, command, self._pad(message_bytes, MAX_MESSAGE_LENGTH)) @@ -114,14 +120,14 @@ black_packet_bytes, signature_bytes = struct.unpack(BLACK_PACKET_FORMAT, black_packet) except: self.server.print_error("Discarding malformed black packet from %s" % peer.get_key()) - return { "error_code": MALFORMED_PACKET } + return Message({ "error_code": MALFORMED_PACKET }, self.server) # check signature signature_check_bytes = hmac.new(signing_key, black_packet_bytes, hashlib.sha384).digest() if(signature_check_bytes != signature_bytes): - return { "error_code": INVALID_SIGNATURE } + return Message({ "error_code": INVALID_SIGNATURE }, self.server) # try to decrypt black packet @@ -132,6 +138,11 @@ nonce, bounces, version, command, message_bytes = struct.unpack(RED_PACKET_FORMAT, red_packet_bytes) + # nothing to be done for an IGNORE command + + if command == IGNORE: + return Message({"error_code": IGNORED}) + # unpack message int_ts, self_chain, net_chain, speaker, message = struct.unpack(MESSAGE_PACKET_FORMAT, message_bytes) @@ -140,10 +151,10 @@ # check timestamp if(int_ts not in self._ts_range()): - return { "error_code": STALE_PACKET } + return Message({ "error_code": STALE_PACKET }, self.server) if(self.server.recent.has(int_ts)): - return { "error_code": DUPLICATE_PACKET } + return Message({ "error_code": DUPLICATE_PACKET }, self.server) else: self.server.recent.insert(int_ts) @@ -167,17 +178,19 @@ unpadded_message = message[0:index] break - # return the message, timestamp, and command (command replaces propagate) - - return { "body": unpadded_message.rstrip(), - "timestamp": int_ts, - "command": command, - "speaker": speaker, - "bounces": bounces, - "self_chain": self_chain, - "net_chain": net_chain, - "self_chain_valid": self_chain_valid, - "error_code": None} + return Message({ + "peer": peer, + "body": unpadded_message.rstrip(), + "timestamp": int_ts, + "command": command, + "speaker": speaker, + "bounces": bounces, + "self_chain": self_chain, + "net_chain": net_chain, + "self_chain_valid": self_chain_valid, + "error_code": None + }, + self.server) def _pad(self, text, size): return text.ljust(size) @@ -189,3 +202,6 @@ def _generate_nonce(self, length=8): """Generate pseudorandom number.""" return ''.join([str(random.randint(0, 9)) for i in range(length)]) + + def gen_rubbish_body(self): + return os.urandom(MAX_MESSAGE_LENGTH) diff -uNr a/blatta/lib/message.py b/blatta/lib/message.py --- a/blatta/lib/message.py false +++ b/blatta/lib/message.py 0096d80e9d0c52787f1ad8c43d6b392c5e5434dfd04f62f41361365d858ea8f92b92f901ce4165e2b4fb4079b7aa9e857cf4296dee3eb48759cf63120f3975c5 @@ -0,0 +1,17 @@ +class Message(object): + def __init__(self, message, server=None): + self.original = True + self.server = server + self.handle = message.get("handle") + self.peer = message.get("peer") + self.body = message.get("body") + self.timestamp = message.get("timestamp") + self.command = message.get("command") + self.speaker = message.get("speaker") + self.bounces = message.get("bounces") + self.self_chain = message.get("self_chain") + self.net_chain = message.get("net_chain") + self.self_chain_valid = message.get("self_chain_valid") + self.error_code = message.get("error_code") + if server: + self.state = server.state diff -uNr a/blatta/lib/peer.py b/blatta/lib/peer.py --- a/blatta/lib/peer.py 351a587cedcdc65ab2989a2f27a2c57d3cbc196376b9c3de1e41a6a407cc94727b24c73bd13b768a722e229c80a416668a372209ff273df0a8f41bf1f79e8064 +++ b/blatta/lib/peer.py 42717c9441e8b3e62b53ca92879cbb9929ee1625046c622acdfe7063f52941cf9dc40b7448726691a5946acf6250e54e79e3156a509cc0507ee374a9c214ef27 @@ -1,7 +1,9 @@ import socket from infosec import Infosec +from commands import IGNORE import sys import binascii +import traceback class Peer(object): def __init__(self, server, peer_entry): @@ -22,14 +24,14 @@ def send(self, msg): try: - full_message = str.encode(msg["body"]) - signed_packet_bytes = self.infosec.pack(self, msg) - self.server.print_debug("packing message: %s" % full_message) + if msg.command != IGNORE: + self.server.print_debug("packing message: %s" % msg.body) self.socket.sendto(signed_packet_bytes, (self.address, self.port)) self.server.print_debug("[%s:%d] <- %s" % (self.address, self.port, binascii.hexlify(signed_packet_bytes)[0:16])) except Exception as ex: - print("Exception while attempting to encode message: %s" % ex) + stack = traceback.format_exc() + print(stack) diff -uNr a/blatta/lib/server.py b/blatta/lib/server.py --- a/blatta/lib/server.py 44b1b3516f89ad8b7e7b9769a5c6089e22e9d459d012c1b72232926e0abbdf42fbf74d2cd38f1344fe29143386c21432a61dc1df454bf1cba19abdd8910c05cf +++ b/blatta/lib/server.py 7a172ddfd99e3c419bdca866441ad222790353ca0eddcb452b6d607e1998041ac0d61e8841d23e82d7c13e850cf7ff01bbfcba5d1924169795a100c4674bbcb5 @@ -1,4 +1,4 @@ -VERSION = "9991" +VERSION = "9990" import os import select @@ -20,12 +20,15 @@ from lib.infosec import DUPLICATE_PACKET from lib.infosec import MALFORMED_PACKET from lib.infosec import INVALID_SIGNATURE +from lib.infosec import IGNORED from lib.infosec import Infosec from lib.peer import Peer +from lib.message import Message from lib.ringbuffer import Ringbuffer from funcs import * from commands import BROADCAST from commands import DIRECT +from commands import IGNORE import imp import pprint @@ -156,29 +159,28 @@ for peer in self.state.get_peers(): if peer.get_key() != None: message = self.infosec.unpack(peer, data) - error_code = message["error_code"] + error_code = message.error_code if(error_code == None): - self.print_debug("[%s] -> %s" % (peer.handles[0], message["body"])) + self.print_debug("[%s] -> %s" % (peer.handles[0], message.body)) # we only update the address table if the speaker is same as peer try: - idx = peer.handles.index(message["speaker"]) + idx = peer.handles.index(message.speaker) except: idx = None if idx != None: - self.state.update_address_table({"handle": message["speaker"], + self.state.update_address_table({"handle": message.speaker, "address": address[0], "port": address[1] }) # send the message to all clients for c in self.clients: - # self.clients[c].udp_socket_readable_notification(message) - if (self.clients[c].is_addressed_to_me(message["body"])): - self.clients[c].message(message["body"]) + if (self.clients[c].is_addressed_to_me(message.body)): + self.clients[c].message(message.body) # send the message to all other peers if it should be propagated - if(message["command"] == BROADCAST) and message["bounces"] < MAX_BOUNCES: + if(message.command == BROADCAST) and message.bounces < MAX_BOUNCES: self.rebroadcast(peer, message) return elif error_code == STALE_PACKET: @@ -190,34 +192,47 @@ elif error_code == MALFORMED_PACKET: self.print_debug("[%s:%d] -> malformed packet: %s" % packet_info) return + elif error_code == IGNORED: + self.print_debug("[%s:%d] -> ignoring packet: %s" % packet_info) + return elif error_code == INVALID_SIGNATURE: pass self.print_debug("[%s:%d] -> martian packet: %s" % packet_info) def peer_message(self, message): - message["original"] = True - if message["command"] == DIRECT: - peer = self.state.get_peer_by_handle(message["handle"]) + message.original = True + if message.command == DIRECT: + peer = self.state.get_peer_by_handle(message.handle) if peer and (peer.get_key() != None): peer.send(message) else: - self.print_debug("Discarding message to unknown handle or handle with no key: %s" % message["handle"]) + self.print_debug("Discarding message to unknown handle or handle with no key: %s" % message.handle) else: for peer in self.state.get_peers(): if peer.get_key() != None: peer.send(message) else: - self.print_debug("Discarding message to handle with no key: %s" % message["handle"]) + self.print_debug("Discarding message to handle with no key: %s" % message.handle) def rebroadcast(self, source_peer, message): - message["original"] = False + message.original = False for peer in self.state.get_peers(): if(peer.peer_id != source_peer.peer_id): - message["command"] = BROADCAST - message["bounces"] = message["bounces"] + 1 + message.command = BROADCAST + message.bounces = message.bounces + 1 peer.send(message) + def sendrubbish(self): + for peer in self.state.get_peers(): + for socket in self.clients: + self.peer_message(Message({ + "speaker": self.clients[socket].nickname, + "command": IGNORE, + "bounces": 0, + "body": self.infosec.gen_rubbish_body() + }, self)) + def start(self): # Setup UDP first self.udp_server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) @@ -254,7 +269,7 @@ [x.socket for x in self.clients.values() if x.write_queue_size() > 0], [], - 1) + .2) for x in inputready: if x == self.udp_server_socket: bytes_address_pair = self.udp_server_socket.recvfrom(PACKET_SIZE) @@ -275,7 +290,7 @@ for client in self.clients.values(): client.check_aliveness() last_aliveness_check = now - + self.sendrubbish() # Kludge to keep ephemeral port open when NATed def create_directory(path): if not os.path.isdir(path):