VERSION = "9987" import os import select import socket import sys import sys import tempfile import time import string import datetime import sqlite3 from datetime import datetime from funcs import * from lib.client import Client from lib.channel import Channel from lib.station import Station from lib.message import Message from lib.infosec import PACKET_SIZE import imp import pprint import logging class Server(object): def __init__(self, options): self.irc_ports = options.irc_ports self.udp_port = options.udp_port self.channel_name = options.channel_name self.password = options.password self.motdfile = options.motd self.verbose = options.verbose self.logdir = options.logdir self.chroot = options.chroot self.setuid = options.setuid self.statedir = options.statedir self.config_file_path = options.config_file_path self.pp = pprint.PrettyPrinter(indent=4) self.db_path = options.db_path self.address_table_path = options.address_table_path if options.listen: self.address = socket.gethostbyname(options.listen) else: self.address = "" server_name_limit = 63 # From the RFC. self.name = socket.getfqdn(self.address)[:server_name_limit] self.channels = {} # irc_lower(Channel name) --> Channel instance. self.client = None self.nicknames = {} # irc_lower(Nickname) --> Client instance. if self.logdir: create_directory(self.logdir) if self.statedir: create_directory(self.statedir) def daemonize(self): try: pid = os.fork() if pid > 0: sys.exit(0) except OSError: sys.exit(1) os.setsid() try: pid = os.fork() if pid > 0: logging.info("PID: %d" % pid) sys.exit(0) except OSError: sys.exit(1) os.chdir("/") os.umask(0) dev_null = open("/dev/null", "r+") os.dup2(dev_null.fileno(), sys.stdout.fileno()) os.dup2(dev_null.fileno(), sys.stderr.fileno()) os.dup2(dev_null.fileno(), sys.stdin.fileno()) def get_client(self, nickname): return self.nicknames.get(irc_lower(nickname)) def has_channel(self, name): return irc_lower(name) in self.channels def get_channel(self, channelname): if irc_lower(channelname) in self.channels: channel = self.channels[irc_lower(channelname)] else: channel = Channel(self, channelname) self.channels[irc_lower(channelname)] = channel return channel def get_motd_lines(self): if self.motdfile: try: return open(self.motdfile).readlines() except IOError: return ["Could not read MOTD file %r." % self.motdfile] else: return [] def client_changed_nickname(self, client, oldnickname): if oldnickname: del self.nicknames[irc_lower(oldnickname)] self.nicknames[irc_lower(client.nickname)] = client def remove_member_from_channel(self, client, channelname): if irc_lower(channelname) in self.channels: channel = self.channels[irc_lower(channelname)] channel.remove_client(client) def remove_client(self, client, quitmsg): client.message_related(":%s QUIT :%s" % (client.prefix, quitmsg)) for x in client.channels.values(): x.remove_client(client) if client.nickname \ and irc_lower(client.nickname) in self.nicknames: del self.nicknames[irc_lower(client.nickname)] self.client = None def remove_channel(self, channel): del self.channels[irc_lower(channel.name)] def start(self): # Setup UDP first self.udp_server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) self.udp_server_socket.bind((self.address, self.udp_port)) self.station = Station({ "socket": self.udp_server_socket, "db_path": self.db_path, "address_table_path": self.address_table_path }) self.station.start_embargo_queue_checking() self.station.start_rubbish() logging.info("Listening for Pest packets on udp port %d." % self.udp_port) serversockets = [] for port in self.irc_ports: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind((self.address, port)) except socket.error as e: logging.error("Could not bind port %s: %s." % (port, e)) sys.exit(1) s.listen(5) serversockets.append(s) del s logging.info("Listening for IRC connections on port %d." % port) if self.chroot: os.chdir(self.chroot) os.chroot(self.chroot) logging.info("Changed root directory to %s" % self.chroot) if self.setuid: os.setgid(self.setuid[1]) os.setuid(self.setuid[0]) logging.info("Setting uid:gid to %s:%s" % (self.setuid[0], self.setuid[1])) last_aliveness_check = time.time() while True: (inputready,outputready,exceptready) = select.select([self.udp_server_socket],[],[],0) (iwtd, owtd, ewtd) = select.select( serversockets + ([self.client.socket] if self.client else []), [self.client.socket] if self.client and self.client.write_queue_size() > 0 else [], [], .2) for x in inputready: if x == self.udp_server_socket: bytes_address_pair = self.udp_server_socket.recvfrom(PACKET_SIZE) self.station.embargo_queue_lock.acquire() try: self.station.handle_udp_data(bytes_address_pair) except sqlite3.ProgrammingError as ex: logging.error("sqlite3 concurrency problem") self.station.embargo_queue_lock.release() for x in iwtd: if self.client != None: self.client.socket_readable_notification() else: (conn, addr) = x.accept() self.client = Client(self, conn) self.station.client = self.client logging.info("Accepted connection from %s:%s." % ( addr[0], addr[1])) for x in owtd: if self.client and x == self.client.socket: # client may have been disconnected self.client.socket_writable_notification() now = time.time() if last_aliveness_check + 10 < now: if self.client: self.client.check_aliveness() last_aliveness_check = now def create_directory(path): if not os.path.isdir(path): os.makedirs(path)