raw
9989-show-wot-nicks     1 VERSION = "9989"
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
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 # From the RFC.
genesis 61 self.name = socket.getfqdn(self.address)[:server_name_limit]
genesis 62
genesis 63 self.channels = {} # irc_lower(Channel name) --> Channel instance.
genesis 64 self.clients = {} # Socket --> Client instance..peers = ""
genesis 65 self.nicknames = {} # irc_lower(Nickname) --> Client instance.
genesis 66 self.recent = Ringbuffer(100)
genesis 67 if self.logdir:
genesis 68 create_directory(self.logdir)
genesis 69 if self.statedir:
genesis 70 create_directory(self.statedir)
genesis 71
genesis 72 def daemonize(self):
genesis 73 try:
genesis 74 pid = os.fork()
genesis 75 if pid > 0:
genesis 76 sys.exit(0)
genesis 77 except OSError:
genesis 78 sys.exit(1)
genesis 79 os.setsid()
genesis 80 try:
genesis 81 pid = os.fork()
genesis 82 if pid > 0:
genesis 83 self.print_info("PID: %d" % pid)
genesis 84 sys.exit(0)
genesis 85 except OSError:
genesis 86 sys.exit(1)
genesis 87 os.chdir("/")
genesis 88 os.umask(0)
genesis 89 dev_null = open("/dev/null", "r+")
genesis 90 os.dup2(dev_null.fileno(), sys.stdout.fileno())
genesis 91 os.dup2(dev_null.fileno(), sys.stderr.fileno())
genesis 92 os.dup2(dev_null.fileno(), sys.stdin.fileno())
genesis 93
genesis 94 def get_client(self, nickname):
genesis 95 return self.nicknames.get(irc_lower(nickname))
genesis 96
genesis 97 def has_channel(self, name):
genesis 98 return irc_lower(name) in self.channels
genesis 99
genesis 100 def get_channel(self, channelname):
genesis 101 if irc_lower(channelname) in self.channels:
genesis 102 channel = self.channels[irc_lower(channelname)]
genesis 103 else:
genesis 104 channel = Channel(self, channelname)
genesis 105 self.channels[irc_lower(channelname)] = channel
genesis 106 return channel
genesis 107
genesis 108 def get_motd_lines(self):
genesis 109 if self.motdfile:
genesis 110 try:
genesis 111 return open(self.motdfile).readlines()
genesis 112 except IOError:
genesis 113 return ["Could not read MOTD file %r." % self.motdfile]
genesis 114 else:
genesis 115 return []
genesis 116
genesis 117 def print_info(self, msg):
genesis 118 if self.verbose:
genesis 119 print(msg)
genesis 120 sys.stdout.flush()
genesis 121
genesis 122 def print_debug(self, msg):
genesis 123 if self.debug:
9991-improved-log... 124 print("%s %s" % (datetime.now(), msg))
genesis 125 sys.stdout.flush()
genesis 126
genesis 127 def print_error(self, msg):
genesis 128 sys.stderr.write("%s\n" % msg)
genesis 129
genesis 130 def client_changed_nickname(self, client, oldnickname):
genesis 131 if oldnickname:
genesis 132 del self.nicknames[irc_lower(oldnickname)]
genesis 133 self.nicknames[irc_lower(client.nickname)] = client
genesis 134
genesis 135 def remove_member_from_channel(self, client, channelname):
genesis 136 if irc_lower(channelname) in self.channels:
genesis 137 channel = self.channels[irc_lower(channelname)]
genesis 138 channel.remove_client(client)
genesis 139
genesis 140 def remove_client(self, client, quitmsg):
genesis 141 client.message_related(":%s QUIT :%s" % (client.prefix, quitmsg))
genesis 142 for x in client.channels.values():
genesis 143 client.channel_log(x, "quit (%s)" % quitmsg, meta=True)
genesis 144 x.remove_client(client)
genesis 145 if client.nickname \
genesis 146 and irc_lower(client.nickname) in self.nicknames:
genesis 147 del self.nicknames[irc_lower(client.nickname)]
genesis 148 del self.clients[client.socket]
genesis 149
genesis 150 def remove_channel(self, channel):
genesis 151 del self.channels[irc_lower(channel.name)]
genesis 152
genesis 153 def handle_udp_data(self, bytes_address_pair):
9992-handle-edge-... 154 data = bytes_address_pair[0]
9992-handle-edge-... 155 address = bytes_address_pair[1]
9991-improved-log... 156 packet_info = (address[0],
9991-improved-log... 157 address[1],
9991-improved-log... 158 binascii.hexlify(data)[0:16])
9991-improved-log... 159 self.print_debug("[%s:%d] -> %s" % packet_info)
genesis 160 for peer in self.state.get_peers():
9992-handle-edge-... 161 if peer.get_key() != None:
9992-handle-edge-... 162 message = self.infosec.unpack(peer, data)
9990-keep-ephemer... 163 error_code = message.error_code
9991-improved-log... 164 if(error_code == None):
9990-keep-ephemer... 165 self.print_debug("[%s] -> %s" % (peer.handles[0], message.body))
9992-handle-edge-... 166
9989-show-wot-nicks 167 self.conditionally_update_address_table(peer, message, address)
9992-handle-edge-... 168 # send the message to all clients
9992-handle-edge-... 169 for c in self.clients:
9990-keep-ephemer... 170 if (self.clients[c].is_addressed_to_me(message.body)):
9990-keep-ephemer... 171 self.clients[c].message(message.body)
9992-handle-edge-... 172 # send the message to all other peers if it should be propagated
9990-keep-ephemer... 173 if(message.command == BROADCAST) and message.bounces < MAX_BOUNCES:
9992-handle-edge-... 174 self.rebroadcast(peer, message)
9992-handle-edge-... 175 return
9991-improved-log... 176 elif error_code == STALE_PACKET:
9991-improved-log... 177 self.print_debug("[%s:%d] -> stale packet: %s" % packet_info)
9991-improved-log... 178 return
9991-improved-log... 179 elif error_code == DUPLICATE_PACKET:
9991-improved-log... 180 self.print_debug("[%s:%d] -> duplicate packet: %s" % packet_info)
9991-improved-log... 181 return
9991-improved-log... 182 elif error_code == MALFORMED_PACKET:
9991-improved-log... 183 self.print_debug("[%s:%d] -> malformed packet: %s" % packet_info)
9991-improved-log... 184 return
9990-keep-ephemer... 185 elif error_code == IGNORED:
9989-show-wot-nicks 186 self.conditionally_update_address_table(peer, message, address)
9990-keep-ephemer... 187 self.print_debug("[%s:%d] -> ignoring packet: %s" % packet_info)
9990-keep-ephemer... 188 return
9991-improved-log... 189 elif error_code == INVALID_SIGNATURE:
9991-improved-log... 190 pass
9991-improved-log... 191 self.print_debug("[%s:%d] -> martian packet: %s" % packet_info)
genesis 192
9989-show-wot-nicks 193 # we only update the address table if the speaker is same as peer
9989-show-wot-nicks 194
9989-show-wot-nicks 195 def conditionally_update_address_table(self, peer, message, address):
9989-show-wot-nicks 196 try:
9989-show-wot-nicks 197 idx = peer.handles.index(message.speaker)
9989-show-wot-nicks 198 except:
9989-show-wot-nicks 199 idx = None
9989-show-wot-nicks 200
9989-show-wot-nicks 201 if idx != None:
9989-show-wot-nicks 202 self.state.update_address_table({"handle": message.speaker,
9989-show-wot-nicks 203 "address": address[0],
9989-show-wot-nicks 204 "port": address[1]
9989-show-wot-nicks 205 })
genesis 206 def peer_message(self, message):
9990-keep-ephemer... 207 message.original = True
9990-keep-ephemer... 208 if message.command == DIRECT:
9990-keep-ephemer... 209 peer = self.state.get_peer_by_handle(message.handle)
9992-handle-edge-... 210 if peer and (peer.get_key() != None):
genesis 211 peer.send(message)
genesis 212 else:
9990-keep-ephemer... 213 self.print_debug("Discarding message to unknown handle or handle with no key: %s" % message.handle)
genesis 214 else:
genesis 215 for peer in self.state.get_peers():
9992-handle-edge-... 216 if peer.get_key() != None:
9992-handle-edge-... 217 peer.send(message)
9992-handle-edge-... 218 else:
9990-keep-ephemer... 219 self.print_debug("Discarding message to handle with no key: %s" % message.handle)
genesis 220
genesis 221 def rebroadcast(self, source_peer, message):
9990-keep-ephemer... 222 message.original = False
genesis 223 for peer in self.state.get_peers():
genesis 224 if(peer.peer_id != source_peer.peer_id):
9990-keep-ephemer... 225 message.command = BROADCAST
9990-keep-ephemer... 226 message.bounces = message.bounces + 1
genesis 227 peer.send(message)
genesis 228
genesis 229
9990-keep-ephemer... 230 def sendrubbish(self):
9990-keep-ephemer... 231 for peer in self.state.get_peers():
9990-keep-ephemer... 232 for socket in self.clients:
9990-keep-ephemer... 233 self.peer_message(Message({
9990-keep-ephemer... 234 "speaker": self.clients[socket].nickname,
9990-keep-ephemer... 235 "command": IGNORE,
9990-keep-ephemer... 236 "bounces": 0,
9990-keep-ephemer... 237 "body": self.infosec.gen_rubbish_body()
9990-keep-ephemer... 238 }, self))
9990-keep-ephemer... 239
genesis 240 def start(self):
genesis 241 # Setup UDP first
genesis 242 self.udp_server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
genesis 243 self.udp_server_socket.bind((self.address, self.udp_port))
9991-improved-log... 244 self.print_info("Listening for Pest packets on udp port %d." % self.udp_port)
genesis 245
genesis 246 serversockets = []
9991-improved-log... 247 for port in self.irc_ports:
genesis 248 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
genesis 249 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
genesis 250 try:
genesis 251 s.bind((self.address, port))
genesis 252 except socket.error as e:
genesis 253 self.print_error("Could not bind port %s: %s." % (port, e))
genesis 254 sys.exit(1)
genesis 255 s.listen(5)
genesis 256 serversockets.append(s)
genesis 257 del s
9991-improved-log... 258 self.print_info("Listening for IRC connections on port %d." % port)
genesis 259 if self.chroot:
genesis 260 os.chdir(self.chroot)
genesis 261 os.chroot(self.chroot)
genesis 262 self.print_info("Changed root directory to %s" % self.chroot)
genesis 263 if self.setuid:
genesis 264 os.setgid(self.setuid[1])
genesis 265 os.setuid(self.setuid[0])
genesis 266 self.print_info("Setting uid:gid to %s:%s"
genesis 267 % (self.setuid[0], self.setuid[1]))
genesis 268 last_aliveness_check = time.time()
genesis 269 while True:
genesis 270 (inputready,outputready,exceptready) = select.select([self.udp_server_socket],[],[],0)
genesis 271 (iwtd, owtd, ewtd) = select.select(
genesis 272 serversockets + [x.socket for x in self.clients.values()],
genesis 273 [x.socket for x in self.clients.values()
genesis 274 if x.write_queue_size() > 0],
genesis 275 [],
9990-keep-ephemer... 276 .2)
genesis 277 for x in inputready:
genesis 278 if x == self.udp_server_socket:
genesis 279 bytes_address_pair = self.udp_server_socket.recvfrom(PACKET_SIZE)
genesis 280 self.handle_udp_data(bytes_address_pair)
genesis 281 for x in iwtd:
genesis 282 if x in self.clients:
genesis 283 self.clients[x].socket_readable_notification()
genesis 284 else:
genesis 285 (conn, addr) = x.accept()
genesis 286 self.clients[conn] = Client(self, conn)
genesis 287 self.print_info("Accepted connection from %s:%s." % (
genesis 288 addr[0], addr[1]))
genesis 289 for x in owtd:
genesis 290 if x in self.clients: # client may have been disconnected
genesis 291 self.clients[x].socket_writable_notification()
genesis 292 now = time.time()
genesis 293 if last_aliveness_check + 10 < now:
genesis 294 for client in self.clients.values():
genesis 295 client.check_aliveness()
genesis 296 last_aliveness_check = now
9990-keep-ephemer... 297 self.sendrubbish() # Kludge to keep ephemeral port open when NATed
genesis 298
genesis 299 def create_directory(path):
genesis 300 if not os.path.isdir(path):
genesis 301 os.makedirs(path)
genesis 302