9990-keep-ephemer... 1 VERSION = "9990"
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
9991-improved-log... 12 import datetime
genesis 13 from datetime import datetime
genesis 14 from lib.client import Client
genesis 15 from lib.state import State
genesis 16 from lib.channel import Channel
genesis 17 from lib.infosec import PACKET_SIZE
genesis 18 from lib.infosec import MAX_BOUNCES
9991-improved-log... 19 from lib.infosec import STALE_PACKET
9991-improved-log... 20 from lib.infosec import DUPLICATE_PACKET
9991-improved-log... 21 from lib.infosec import MALFORMED_PACKET
9991-improved-log... 22 from lib.infosec import INVALID_SIGNATURE
9990-keep-ephemer... 23 from lib.infosec import IGNORED
genesis 24 from lib.infosec import Infosec
genesis 25 from lib.peer import Peer
9990-keep-ephemer... 26 from lib.message import Message
genesis 27 from lib.ringbuffer import Ringbuffer
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
genesis 39 self.password = options.password
genesis 40 self.motdfile = options.motd
genesis 41 self.verbose = options.verbose
genesis 42 self.debug = options.debug
genesis 43 self.logdir = options.logdir
genesis 44 self.chroot = options.chroot
genesis 45 self.setuid = options.setuid
genesis 46 self.statedir = options.statedir
genesis 47 self.infosec = Infosec(self)
genesis 48 self.config_file_path = options.config_file_path
genesis 49 self.state = State(self, options.db_path)
genesis 50 self.pp = pprint.PrettyPrinter(indent=4)
genesis 51
genesis 52 if options.address_table_path != None:
genesis 53 self.state.import_at_and_wot(options.address_table_path)
genesis 54
genesis 55 if options.listen:
genesis 56 self.address = socket.gethostbyname(options.listen)
genesis 57 else:
genesis 58 self.address = ""
genesis 59 server_name_limit = 63
genesis 60 self.name = socket.getfqdn(self.address)[:server_name_limit]
genesis 61
genesis 62 self.channels = {}
genesis 63 self.clients = {}
genesis 64 self.nicknames = {}
genesis 65 self.recent = Ringbuffer(100)
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
9992-handle-edge-... 166
9992-handle-edge-... 167
9992-handle-edge-... 168 try:
9990-keep-ephemer... 169 idx = peer.handles.index(message.speaker)
9992-handle-edge-... 170 except:
9992-handle-edge-... 171 idx = None
9992-handle-edge-... 172
9992-handle-edge-... 173 if idx != None:
9990-keep-ephemer... 174 self.state.update_address_table({"handle": message.speaker,
9991-improved-log... 175 "address": address[0],
9991-improved-log... 176 "port": address[1]
9992-handle-edge-... 177 })
9992-handle-edge-... 178
9992-handle-edge-... 179 for c in self.clients:
9990-keep-ephemer... 180 if (self.clients[c].is_addressed_to_me(message.body)):
9990-keep-ephemer... 181 self.clients[c].message(message.body)
9992-handle-edge-... 182
9990-keep-ephemer... 183 if(message.command == BROADCAST) and message.bounces < MAX_BOUNCES:
9992-handle-edge-... 184 self.rebroadcast(peer, message)
9992-handle-edge-... 185 return
9991-improved-log... 186 elif error_code == STALE_PACKET:
9991-improved-log... 187 self.print_debug("[%s:%d] -> stale packet: %s" % packet_info)
9991-improved-log... 188 return
9991-improved-log... 189 elif error_code == DUPLICATE_PACKET:
9991-improved-log... 190 self.print_debug("[%s:%d] -> duplicate packet: %s" % packet_info)
9991-improved-log... 191 return
9991-improved-log... 192 elif error_code == MALFORMED_PACKET:
9991-improved-log... 193 self.print_debug("[%s:%d] -> malformed packet: %s" % packet_info)
9991-improved-log... 194 return
9990-keep-ephemer... 195 elif error_code == IGNORED:
9990-keep-ephemer... 196 self.print_debug("[%s:%d] -> ignoring packet: %s" % packet_info)
9990-keep-ephemer... 197 return
9991-improved-log... 198 elif error_code == INVALID_SIGNATURE:
9991-improved-log... 199 pass
9991-improved-log... 200 self.print_debug("[%s:%d] -> martian packet: %s" % packet_info)
genesis 201
genesis 202 def peer_message(self, message):
9990-keep-ephemer... 203 message.original = True
9990-keep-ephemer... 204 if message.command == DIRECT:
9990-keep-ephemer... 205 peer = self.state.get_peer_by_handle(message.handle)
9992-handle-edge-... 206 if peer and (peer.get_key() != None):
genesis 207 peer.send(message)
genesis 208 else:
9990-keep-ephemer... 209 self.print_debug("Discarding message to unknown handle or handle with no key: %s" % message.handle)
genesis 210 else:
genesis 211 for peer in self.state.get_peers():
9992-handle-edge-... 212 if peer.get_key() != None:
9992-handle-edge-... 213 peer.send(message)
9992-handle-edge-... 214 else:
9990-keep-ephemer... 215 self.print_debug("Discarding message to handle with no key: %s" % message.handle)
genesis 216
genesis 217 def rebroadcast(self, source_peer, message):
9990-keep-ephemer... 218 message.original = False
genesis 219 for peer in self.state.get_peers():
genesis 220 if(peer.peer_id != source_peer.peer_id):
9990-keep-ephemer... 221 message.command = BROADCAST
9990-keep-ephemer... 222 message.bounces = message.bounces + 1
genesis 223 peer.send(message)
genesis 224
genesis 225
9990-keep-ephemer... 226 def sendrubbish(self):
9990-keep-ephemer... 227 for peer in self.state.get_peers():
9990-keep-ephemer... 228 for socket in self.clients:
9990-keep-ephemer... 229 self.peer_message(Message({
9990-keep-ephemer... 230 "speaker": self.clients[socket].nickname,
9990-keep-ephemer... 231 "command": IGNORE,
9990-keep-ephemer... 232 "bounces": 0,
9990-keep-ephemer... 233 "body": self.infosec.gen_rubbish_body()
9990-keep-ephemer... 234 }, self))
9990-keep-ephemer... 235
genesis 236 def start(self):
genesis 237
genesis 238 self.udp_server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
genesis 239 self.udp_server_socket.bind((self.address, self.udp_port))
9991-improved-log... 240 self.print_info("Listening for Pest packets on udp port %d." % self.udp_port)
genesis 241
genesis 242 serversockets = []
9991-improved-log... 243 for port in self.irc_ports:
genesis 244 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
genesis 245 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
genesis 246 try:
genesis 247 s.bind((self.address, port))
genesis 248 except socket.error as e:
genesis 249 self.print_error("Could not bind port %s: %s." % (port, e))
genesis 250 sys.exit(1)
genesis 251 s.listen(5)
genesis 252 serversockets.append(s)
genesis 253 del s
9991-improved-log... 254 self.print_info("Listening for IRC connections on port %d." % port)
genesis 255 if self.chroot:
genesis 256 os.chdir(self.chroot)
genesis 257 os.chroot(self.chroot)
genesis 258 self.print_info("Changed root directory to %s" % self.chroot)
genesis 259 if self.setuid:
genesis 260 os.setgid(self.setuid[1])
genesis 261 os.setuid(self.setuid[0])
genesis 262 self.print_info("Setting uid:gid to %s:%s"
genesis 263 % (self.setuid[0], self.setuid[1]))
genesis 264 last_aliveness_check = time.time()
genesis 265 while True:
genesis 266 (inputready,outputready,exceptready) = select.select([self.udp_server_socket],[],[],0)
genesis 267 (iwtd, owtd, ewtd) = select.select(
genesis 268 serversockets + [x.socket for x in self.clients.values()],
genesis 269 [x.socket for x in self.clients.values()
genesis 270 if x.write_queue_size() > 0],
genesis 271 [],
9990-keep-ephemer... 272 .2)
genesis 273 for x in inputready:
genesis 274 if x == self.udp_server_socket:
genesis 275 bytes_address_pair = self.udp_server_socket.recvfrom(PACKET_SIZE)
genesis 276 self.handle_udp_data(bytes_address_pair)
genesis 277 for x in iwtd:
genesis 278 if x in self.clients:
genesis 279 self.clients[x].socket_readable_notification()
genesis 280 else:
genesis 281 (conn, addr) = x.accept()
genesis 282 self.clients[conn] = Client(self, conn)
genesis 283 self.print_info("Accepted connection from %s:%s." % (
genesis 284 addr[0], addr[1]))
genesis 285 for x in owtd:
genesis 286 if x in self.clients:
genesis 287 self.clients[x].socket_writable_notification()
genesis 288 now = time.time()
genesis 289 if last_aliveness_check + 10 < now:
genesis 290 for client in self.clients.values():
genesis 291 client.check_aliveness()
genesis 292 last_aliveness_check = now
9990-keep-ephemer... 293 self.sendrubbish()
genesis 294
genesis 295 def create_directory(path):
genesis 296 if not os.path.isdir(path):
genesis 297 os.makedirs(path)
genesis 298