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