raw
9985-single-thread      1 VERSION = "9985"
genesis 2
genesis 3 import os
genesis 4 import select
genesis 5 import socket
genesis 6 import sys
genesis 7 import tempfile
genesis 8 import time
genesis 9 import string
9991-improved-log... 10 import datetime
9987-embargoing 11 import sqlite3
genesis 12 from datetime import datetime
9987-embargoing 13 from funcs import *
9985-single-thread 14 from client import Client
9985-single-thread 15 from channel import Channel
9985-single-thread 16 from station import Station
9985-single-thread 17 from station import EMBARGO_INTERVAL
9985-single-thread 18 from station import RUBBISH_INTERVAL
9985-single-thread 19 from message import Message
9985-single-thread 20 from infosec import PACKET_SIZE
genesis 21 import imp
genesis 22 import pprint
9987-embargoing 23 import logging
genesis 24
genesis 25 class Server(object):
genesis 26 def __init__(self, options):
9991-improved-log... 27 self.irc_ports = options.irc_ports
genesis 28 self.udp_port = options.udp_port
9989-show-wot-nicks 29 self.channel_name = options.channel_name
genesis 30 self.password = options.password
genesis 31 self.motdfile = options.motd
genesis 32 self.logdir = options.logdir
genesis 33 self.chroot = options.chroot
genesis 34 self.setuid = options.setuid
genesis 35 self.statedir = options.statedir
genesis 36 self.config_file_path = options.config_file_path
genesis 37 self.pp = pprint.PrettyPrinter(indent=4)
9987-embargoing 38 self.db_path = options.db_path
9987-embargoing 39 self.address_table_path = options.address_table_path
9985-single-thread 40 self.irc_server_address = "127.0.0.1"
genesis 41
genesis 42 if options.listen:
9985-single-thread 43 self.udp_address = socket.gethostbyname(options.listen)
genesis 44 else:
9985-single-thread 45 self.udp_address = ""
genesis 46 server_name_limit = 63 # From the RFC.
9985-single-thread 47 self.name = socket.getfqdn(self.udp_address)[:server_name_limit]
genesis 48
genesis 49 self.channels = {} # irc_lower(Channel name) --> Channel instance.
9987-embargoing 50 self.client = None
genesis 51 self.nicknames = {} # irc_lower(Nickname) --> Client instance.
9987-embargoing 52
genesis 53 if self.logdir:
genesis 54 create_directory(self.logdir)
genesis 55 if self.statedir:
genesis 56 create_directory(self.statedir)
genesis 57
genesis 58 def daemonize(self):
genesis 59 try:
genesis 60 pid = os.fork()
genesis 61 if pid > 0:
genesis 62 sys.exit(0)
genesis 63 except OSError:
genesis 64 sys.exit(1)
genesis 65 os.setsid()
genesis 66 try:
genesis 67 pid = os.fork()
genesis 68 if pid > 0:
9987-embargoing 69 logging.info("PID: %d" % pid)
genesis 70 sys.exit(0)
genesis 71 except OSError:
genesis 72 sys.exit(1)
genesis 73 os.chdir("/")
genesis 74 os.umask(0)
genesis 75 dev_null = open("/dev/null", "r+")
genesis 76 os.dup2(dev_null.fileno(), sys.stdout.fileno())
genesis 77 os.dup2(dev_null.fileno(), sys.stderr.fileno())
genesis 78 os.dup2(dev_null.fileno(), sys.stdin.fileno())
genesis 79
genesis 80 def get_client(self, nickname):
genesis 81 return self.nicknames.get(irc_lower(nickname))
genesis 82
genesis 83 def has_channel(self, name):
genesis 84 return irc_lower(name) in self.channels
genesis 85
genesis 86 def get_channel(self, channelname):
genesis 87 if irc_lower(channelname) in self.channels:
genesis 88 channel = self.channels[irc_lower(channelname)]
genesis 89 else:
genesis 90 channel = Channel(self, channelname)
genesis 91 self.channels[irc_lower(channelname)] = channel
genesis 92 return channel
genesis 93
genesis 94 def get_motd_lines(self):
genesis 95 if self.motdfile:
genesis 96 try:
genesis 97 return open(self.motdfile).readlines()
genesis 98 except IOError:
genesis 99 return ["Could not read MOTD file %r." % self.motdfile]
genesis 100 else:
genesis 101 return []
genesis 102
genesis 103 def client_changed_nickname(self, client, oldnickname):
genesis 104 if oldnickname:
genesis 105 del self.nicknames[irc_lower(oldnickname)]
genesis 106 self.nicknames[irc_lower(client.nickname)] = client
genesis 107
genesis 108 def remove_member_from_channel(self, client, channelname):
genesis 109 if irc_lower(channelname) in self.channels:
genesis 110 channel = self.channels[irc_lower(channelname)]
genesis 111 channel.remove_client(client)
genesis 112
genesis 113 def remove_client(self, client, quitmsg):
genesis 114 client.message_related(":%s QUIT :%s" % (client.prefix, quitmsg))
genesis 115 for x in client.channels.values():
genesis 116 x.remove_client(client)
genesis 117 if client.nickname \
genesis 118 and irc_lower(client.nickname) in self.nicknames:
genesis 119 del self.nicknames[irc_lower(client.nickname)]
9987-embargoing 120 self.client = None
genesis 121
genesis 122 def remove_channel(self, channel):
genesis 123 del self.channels[irc_lower(channel.name)]
genesis 124
genesis 125 def start(self):
genesis 126 # Setup UDP first
genesis 127 self.udp_server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
9985-single-thread 128 self.udp_server_socket.bind((self.udp_address, self.udp_port))
9987-embargoing 129 self.station = Station({ "socket": self.udp_server_socket,
9987-embargoing 130 "db_path": self.db_path,
9987-embargoing 131 "address_table_path": self.address_table_path
9987-embargoing 132 })
9987-embargoing 133 logging.info("Listening for Pest packets on udp port %d." % self.udp_port)
genesis 134
genesis 135 serversockets = []
9991-improved-log... 136 for port in self.irc_ports:
genesis 137 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
genesis 138 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
genesis 139 try:
9985-single-thread 140 s.bind((self.irc_server_address, port))
genesis 141 except socket.error as e:
9987-embargoing 142 logging.error("Could not bind port %s: %s." % (port, e))
genesis 143 sys.exit(1)
genesis 144 s.listen(5)
genesis 145 serversockets.append(s)
genesis 146 del s
9987-embargoing 147 logging.info("Listening for IRC connections on port %d." % port)
genesis 148 if self.chroot:
genesis 149 os.chdir(self.chroot)
genesis 150 os.chroot(self.chroot)
9987-embargoing 151 logging.info("Changed root directory to %s" % self.chroot)
genesis 152 if self.setuid:
genesis 153 os.setgid(self.setuid[1])
genesis 154 os.setuid(self.setuid[0])
9987-embargoing 155 logging.info("Setting uid:gid to %s:%s"
genesis 156 % (self.setuid[0], self.setuid[1]))
9985-single-thread 157
9985-single-thread 158 # event loop setup
genesis 159 last_aliveness_check = time.time()
9985-single-thread 160 last_embargo_queue_check = time.time()
9985-single-thread 161 last_rubbish_dispatch = time.time()
genesis 162 while True:
9985-single-thread 163 # we don't want to be listening for client connections if there's already a client connected
9985-single-thread 164 if self.client == None:
9985-single-thread 165 input_sockets = serversockets
9985-single-thread 166 else:
9985-single-thread 167 input_sockets = [self.client.socket]
9985-single-thread 168 output_sockets = ([self.client.socket]
9985-single-thread 169 if self.client and self.client.write_queue_size() > 0 else [])
9985-single-thread 170
9985-single-thread 171 # handle tcp socket events
9985-single-thread 172 (iwtd, owtd, ewtd) = select.select(input_sockets, output_sockets, [], .2)
genesis 173 for x in iwtd:
9987-embargoing 174 if self.client != None:
9987-embargoing 175 self.client.socket_readable_notification()
genesis 176 else:
9985-single-thread 177 try:
9985-single-thread 178 (conn, addr) = x.accept()
9985-single-thread 179 self.client = Client(self, conn)
9985-single-thread 180 self.station.client = self.client
9985-single-thread 181 logging.info("Accepted connection from %s:%s." % (
9985-single-thread 182 addr[0], addr[1]))
9985-single-thread 183 except socket.error as e:
9985-single-thread 184 logging.error("Failed to accept new client connection: %s" % e)
genesis 185 for x in owtd:
9987-embargoing 186 if self.client and x == self.client.socket: # client may have been disconnected
9987-embargoing 187 self.client.socket_writable_notification()
9985-single-thread 188
9985-single-thread 189 # handle udp socket events
9985-single-thread 190 (inputready,outputready,exceptready) = select.select([self.udp_server_socket],[],[],0)
9985-single-thread 191 for x in inputready:
9985-single-thread 192 if x == self.udp_server_socket:
9985-single-thread 193 bytes_address_pair = self.udp_server_socket.recvfrom(PACKET_SIZE)
9985-single-thread 194 self.station.handle_udp_data(bytes_address_pair)
9985-single-thread 195
9985-single-thread 196 # ping pong
genesis 197 now = time.time()
genesis 198 if last_aliveness_check + 10 < now:
9985-single-thread 199 if self.client:
9985-single-thread 200 self.client.check_aliveness()
9987-embargoing 201 last_aliveness_check = now
genesis 202
9985-single-thread 203 # clear embargo queue if enough time has elapsed
9985-single-thread 204 if last_embargo_queue_check + EMBARGO_INTERVAL < now:
9985-single-thread 205 self.station.check_embargo_queue()
9985-single-thread 206 last_embargo_queue_check = now
9985-single-thread 207
9985-single-thread 208 # spray rubbish
9985-single-thread 209 if last_rubbish_dispatch + RUBBISH_INTERVAL < now:
9985-single-thread 210 self.station.send_rubbish()
9985-single-thread 211 last_rubbish_dispatch = now
9985-single-thread 212
genesis 213 def create_directory(path):
genesis 214 if not os.path.isdir(path):
genesis 215 os.makedirs(path)
genesis 216