9988-hash-dedup 1 VERSION = "9988"
genesis 2
genesis 3 import os
genesis 4 import select
genesis 5 import socket
genesis 6 import sys
genesis 7 import sys
genesis 8 import tempfile
genesis 9 import time
genesis 10 import string
9991-improved-log... 11 import binascii
9988-hash-dedup 12 import hashlib
9991-improved-log... 13 import datetime
genesis 14 from datetime import datetime
genesis 15 from lib.client import Client
genesis 16 from lib.state import State
genesis 17 from lib.channel import Channel
genesis 18 from lib.infosec import PACKET_SIZE
genesis 19 from lib.infosec import MAX_BOUNCES
9991-improved-log... 20 from lib.infosec import STALE_PACKET
9991-improved-log... 21 from lib.infosec import DUPLICATE_PACKET
9991-improved-log... 22 from lib.infosec import MALFORMED_PACKET
9991-improved-log... 23 from lib.infosec import INVALID_SIGNATURE
9990-keep-ephemer... 24 from lib.infosec import IGNORED
genesis 25 from lib.infosec import Infosec
genesis 26 from lib.peer import Peer
9990-keep-ephemer... 27 from lib.message import Message
genesis 28 from funcs import *
genesis 29 from commands import BROADCAST
genesis 30 from commands import DIRECT
9990-keep-ephemer... 31 from commands import IGNORE
genesis 32 import imp
genesis 33 import pprint
genesis 34
genesis 35 class Server(object):
genesis 36 def __init__(self, options):
9991-improved-log... 37 self.irc_ports = options.irc_ports
genesis 38 self.udp_port = options.udp_port
9989-show-wot-nicks 39 self.channel_name = options.channel_name
genesis 40 self.password = options.password
genesis 41 self.motdfile = options.motd
genesis 42 self.verbose = options.verbose
genesis 43 self.debug = options.debug
genesis 44 self.logdir = options.logdir
genesis 45 self.chroot = options.chroot
genesis 46 self.setuid = options.setuid
genesis 47 self.statedir = options.statedir
genesis 48 self.infosec = Infosec(self)
genesis 49 self.config_file_path = options.config_file_path
genesis 50 self.state = State(self, options.db_path)
genesis 51 self.pp = pprint.PrettyPrinter(indent=4)
genesis 52
genesis 53 if options.address_table_path != None:
genesis 54 self.state.import_at_and_wot(options.address_table_path)
genesis 55
genesis 56 if options.listen:
genesis 57 self.address = socket.gethostbyname(options.listen)
genesis 58 else:
genesis 59 self.address = ""
genesis 60 server_name_limit = 63
genesis 61 self.name = socket.getfqdn(self.address)[:server_name_limit]
genesis 62
genesis 63 self.channels = {}
genesis 64 self.clients = {}
genesis 65 self.nicknames = {}
genesis 66 if self.logdir:
genesis 67 create_directory(self.logdir)
genesis 68 if self.statedir:
genesis 69 create_directory(self.statedir)
genesis 70
genesis 71 def daemonize(self):
genesis 72 try:
genesis 73 pid = os.fork()
genesis 74 if pid > 0:
genesis 75 sys.exit(0)
genesis 76 except OSError:
genesis 77 sys.exit(1)
genesis 78 os.setsid()
genesis 79 try:
genesis 80 pid = os.fork()
genesis 81 if pid > 0:
genesis 82 self.print_info("PID: %d" % pid)
genesis 83 sys.exit(0)
genesis 84 except OSError:
genesis 85 sys.exit(1)
genesis 86 os.chdir("/")
genesis 87 os.umask(0)
genesis 88 dev_null = open("/dev/null", "r+")
genesis 89 os.dup2(dev_null.fileno(), sys.stdout.fileno())
genesis 90 os.dup2(dev_null.fileno(), sys.stderr.fileno())
genesis 91 os.dup2(dev_null.fileno(), sys.stdin.fileno())
genesis 92
genesis 93 def get_client(self, nickname):
genesis 94 return self.nicknames.get(irc_lower(nickname))
genesis 95
genesis 96 def has_channel(self, name):
genesis 97 return irc_lower(name) in self.channels
genesis 98
genesis 99 def get_channel(self, channelname):
genesis 100 if irc_lower(channelname) in self.channels:
genesis 101 channel = self.channels[irc_lower(channelname)]
genesis 102 else:
genesis 103 channel = Channel(self, channelname)
genesis 104 self.channels[irc_lower(channelname)] = channel
genesis 105 return channel
genesis 106
genesis 107 def get_motd_lines(self):
genesis 108 if self.motdfile:
genesis 109 try:
genesis 110 return open(self.motdfile).readlines()
genesis 111 except IOError:
genesis 112 return ["Could not read MOTD file %r." % self.motdfile]
genesis 113 else:
genesis 114 return []
genesis 115
genesis 116 def print_info(self, msg):
genesis 117 if self.verbose:
genesis 118 print(msg)
genesis 119 sys.stdout.flush()
genesis 120
genesis 121 def print_debug(self, msg):
genesis 122 if self.debug:
9991-improved-log... 123 print("%s %s" % (datetime.now(), msg))
genesis 124 sys.stdout.flush()
genesis 125
genesis 126 def print_error(self, msg):
genesis 127 sys.stderr.write("%s\n" % msg)
genesis 128
genesis 129 def client_changed_nickname(self, client, oldnickname):
genesis 130 if oldnickname:
genesis 131 del self.nicknames[irc_lower(oldnickname)]
genesis 132 self.nicknames[irc_lower(client.nickname)] = client
genesis 133
genesis 134 def remove_member_from_channel(self, client, channelname):
genesis 135 if irc_lower(channelname) in self.channels:
genesis 136 channel = self.channels[irc_lower(channelname)]
genesis 137 channel.remove_client(client)
genesis 138
genesis 139 def remove_client(self, client, quitmsg):
genesis 140 client.message_related(":%s QUIT :%s" % (client.prefix, quitmsg))
genesis 141 for x in client.channels.values():
genesis 142 client.channel_log(x, "quit (%s)" % quitmsg, meta=True)
genesis 143 x.remove_client(client)
genesis 144 if client.nickname \
genesis 145 and irc_lower(client.nickname) in self.nicknames:
genesis 146 del self.nicknames[irc_lower(client.nickname)]
genesis 147 del self.clients[client.socket]
genesis 148
genesis 149 def remove_channel(self, channel):
genesis 150 del self.channels[irc_lower(channel.name)]
genesis 151
genesis 152 def handle_udp_data(self, bytes_address_pair):
9992-handle-edge-... 153 data = bytes_address_pair[0]
9992-handle-edge-... 154 address = bytes_address_pair[1]
9991-improved-log... 155 packet_info = (address[0],
9991-improved-log... 156 address[1],
9991-improved-log... 157 binascii.hexlify(data)[0:16])
9991-improved-log... 158 self.print_debug("[%s:%d] -> %s" % packet_info)
genesis 159 for peer in self.state.get_peers():
9992-handle-edge-... 160 if peer.get_key() != None:
9992-handle-edge-... 161 message = self.infosec.unpack(peer, data)
9990-keep-ephemer... 162 error_code = message.error_code
9991-improved-log... 163 if(error_code == None):
9990-keep-ephemer... 164 self.print_debug("[%s] -> %s" % (peer.handles[0], message.body))
9992-handle-edge-... 165
9989-show-wot-nicks 166 self.conditionally_update_address_table(peer, message, address)
9992-handle-edge-... 167
9992-handle-edge-... 168 for c in self.clients:
9990-keep-ephemer... 169 if (self.clients[c].is_addressed_to_me(message.body)):
9990-keep-ephemer... 170 self.clients[c].message(message.body)
9992-handle-edge-... 171
9990-keep-ephemer... 172 if(message.command == BROADCAST) and message.bounces < MAX_BOUNCES:
9992-handle-edge-... 173 self.rebroadcast(peer, message)
9992-handle-edge-... 174 return
9991-improved-log... 175 elif error_code == STALE_PACKET:
9991-improved-log... 176 self.print_debug("[%s:%d] -> stale packet: %s" % packet_info)
9991-improved-log... 177 return
9991-improved-log... 178 elif error_code == DUPLICATE_PACKET:
9991-improved-log... 179 self.print_debug("[%s:%d] -> duplicate packet: %s" % packet_info)
9991-improved-log... 180 return
9991-improved-log... 181 elif error_code == MALFORMED_PACKET:
9991-improved-log... 182 self.print_debug("[%s:%d] -> malformed packet: %s" % packet_info)
9991-improved-log... 183 return
9990-keep-ephemer... 184 elif error_code == IGNORED:
9989-show-wot-nicks 185 self.conditionally_update_address_table(peer, message, address)
9990-keep-ephemer... 186 self.print_debug("[%s:%d] -> ignoring packet: %s" % packet_info)
9990-keep-ephemer... 187 return
9991-improved-log... 188 elif error_code == INVALID_SIGNATURE:
9991-improved-log... 189 pass
9991-improved-log... 190 self.print_debug("[%s:%d] -> martian packet: %s" % packet_info)
genesis 191
9989-show-wot-nicks 192
9989-show-wot-nicks 193
9989-show-wot-nicks 194 def conditionally_update_address_table(self, peer, message, address):
9989-show-wot-nicks 195 try:
9989-show-wot-nicks 196 idx = peer.handles.index(message.speaker)
9989-show-wot-nicks 197 except:
9989-show-wot-nicks 198 idx = None
9989-show-wot-nicks 199
9989-show-wot-nicks 200 if idx != None:
9989-show-wot-nicks 201 self.state.update_address_table({"handle": message.speaker,
9989-show-wot-nicks 202 "address": address[0],
9989-show-wot-nicks 203 "port": address[1]
9989-show-wot-nicks 204 })
genesis 205 def peer_message(self, message):
9990-keep-ephemer... 206 message.original = True
9990-keep-ephemer... 207 if message.command == DIRECT:
9990-keep-ephemer... 208 peer = self.state.get_peer_by_handle(message.handle)
9988-hash-dedup 209 message_bytes = self.infosec.get_message_bytes(message, peer)
9988-hash-dedup 210 message_hash = binascii.hexlify(hashlib.sha256(message_bytes).digest())
9988-hash-dedup 211 self.state.add_to_dedup_queue(message_hash)
9988-hash-dedup 212
9988-hash-dedup 213 self.state.log(message.speaker, message_bytes, peer.peer_id)
9992-handle-edge-... 214 if peer and (peer.get_key() != None):
genesis 215 peer.send(message)
genesis 216 else:
9990-keep-ephemer... 217 self.print_debug("Discarding message to unknown handle or handle with no key: %s" % message.handle)
genesis 218 else:
9988-hash-dedup 219 message.timestamp = int(time.time())
9988-hash-dedup 220 message_bytes = self.infosec.get_message_bytes(message)
9988-hash-dedup 221 if message.command != IGNORE:
9988-hash-dedup 222 self.state.log(message.speaker, message_bytes)
9988-hash-dedup 223 message_hash = binascii.hexlify(hashlib.sha256(message_bytes).digest())
9988-hash-dedup 224 self.state.add_to_dedup_queue(message_hash)
genesis 225 for peer in self.state.get_peers():
9992-handle-edge-... 226 if peer.get_key() != None:
9992-handle-edge-... 227 peer.send(message)
9992-handle-edge-... 228 else:
9990-keep-ephemer... 229 self.print_debug("Discarding message to handle with no key: %s" % message.handle)
genesis 230
genesis 231 def rebroadcast(self, source_peer, message):
9990-keep-ephemer... 232 message.original = False
genesis 233 for peer in self.state.get_peers():
genesis 234 if(peer.peer_id != source_peer.peer_id):
9990-keep-ephemer... 235 message.command = BROADCAST
9990-keep-ephemer... 236 message.bounces = message.bounces + 1
genesis 237 peer.send(message)
genesis 238
genesis 239
9990-keep-ephemer... 240 def sendrubbish(self):
9988-hash-dedup 241 for socket in self.clients:
9988-hash-dedup 242 self.peer_message(Message({
9988-hash-dedup 243 "speaker": self.clients[socket].nickname,
9988-hash-dedup 244 "command": IGNORE,
9988-hash-dedup 245 "bounces": 0,
9988-hash-dedup 246 "body": self.infosec.gen_rubbish_body()
9988-hash-dedup 247 }, self))
9990-keep-ephemer... 248
genesis 249 def start(self):
genesis 250
genesis 251 self.udp_server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
genesis 252 self.udp_server_socket.bind((self.address, self.udp_port))
9991-improved-log... 253 self.print_info("Listening for Pest packets on udp port %d." % self.udp_port)
genesis 254
genesis 255 serversockets = []
9991-improved-log... 256 for port in self.irc_ports:
genesis 257 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
genesis 258 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
genesis 259 try:
genesis 260 s.bind((self.address, port))
genesis 261 except socket.error as e:
genesis 262 self.print_error("Could not bind port %s: %s." % (port, e))
genesis 263 sys.exit(1)
genesis 264 s.listen(5)
genesis 265 serversockets.append(s)
genesis 266 del s
9991-improved-log... 267 self.print_info("Listening for IRC connections on port %d." % port)
genesis 268 if self.chroot:
genesis 269 os.chdir(self.chroot)
genesis 270 os.chroot(self.chroot)
genesis 271 self.print_info("Changed root directory to %s" % self.chroot)
genesis 272 if self.setuid:
genesis 273 os.setgid(self.setuid[1])
genesis 274 os.setuid(self.setuid[0])
genesis 275 self.print_info("Setting uid:gid to %s:%s"
genesis 276 % (self.setuid[0], self.setuid[1]))
genesis 277 last_aliveness_check = time.time()
genesis 278 while True:
genesis 279 (inputready,outputready,exceptready) = select.select([self.udp_server_socket],[],[],0)
genesis 280 (iwtd, owtd, ewtd) = select.select(
genesis 281 serversockets + [x.socket for x in self.clients.values()],
genesis 282 [x.socket for x in self.clients.values()
genesis 283 if x.write_queue_size() > 0],
genesis 284 [],
9990-keep-ephemer... 285 .2)
genesis 286 for x in inputready:
genesis 287 if x == self.udp_server_socket:
genesis 288 bytes_address_pair = self.udp_server_socket.recvfrom(PACKET_SIZE)
genesis 289 self.handle_udp_data(bytes_address_pair)
genesis 290 for x in iwtd:
genesis 291 if x in self.clients:
genesis 292 self.clients[x].socket_readable_notification()
genesis 293 else:
genesis 294 (conn, addr) = x.accept()
genesis 295 self.clients[conn] = Client(self, conn)
genesis 296 self.print_info("Accepted connection from %s:%s." % (
genesis 297 addr[0], addr[1]))
genesis 298 for x in owtd:
genesis 299 if x in self.clients:
genesis 300 self.clients[x].socket_writable_notification()
genesis 301 now = time.time()
genesis 302 if last_aliveness_check + 10 < now:
genesis 303 for client in self.clients.values():
genesis 304 client.check_aliveness()
genesis 305 last_aliveness_check = now
9990-keep-ephemer... 306 self.sendrubbish()
genesis 307
genesis 308 def create_directory(path):
genesis 309 if not os.path.isdir(path):
genesis 310 os.makedirs(path)
genesis 311