commit f1f5a2ab1ba34a7377c92140434359fa5df8f0dc Author: Sven Slootweg Date: Mon Jul 2 17:31:28 2012 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..97c4b8d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +sample.cert +sample.key diff --git a/ircd.py b/ircd.py new file mode 100755 index 0000000..dfebe71 --- /dev/null +++ b/ircd.py @@ -0,0 +1,185 @@ +#!/usr/bin/python + +import math, socket, ssl, select, time, threading, random, string + +EOC = "\r\n" + +config_ownhost = "ircd.local" +config_password = "" +config_netname = "Cryto IRC" +config_version = "circd 0.1" + +def remove_from_list(ls, val): + return [value for value in ls if value is not val] + +def split_irc(message): + if ":" in message: + first, second = message.split(":", 2) + return first.rstrip().split(" ") + [second] + else: + return message.split(" ") + +class ircd: + channels = {} + users = {} + +class listener: + ssl = False + client_list = [] + client_map = {} + select_inputs = [] + select_outputs = [] + + def __init__(self): + pass + + def start(self, interface, port, cert_path, key_path): + bindsocket = socket.socket() + bindsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + bindsocket.bind((interface, port)) + bindsocket.listen(5) + + self.select_inputs = [ bindsocket ] + + while self.select_inputs: + readable, writable, error = select.select(self.select_inputs, self.select_outputs, self.select_inputs) + + if len(readable) > 0: + for sock in readable: + try: + if sock is bindsocket: + newsocket, fromaddr = bindsocket.accept() + remote_ip, remote_port = fromaddr + + if self.ssl == True: + connstream = ssl.wrap_socket(newsocket, server_side=True, certfile=cert_path, keyfile=key_path, ssl_version=ssl.PROTOCOL_TLSv1) + else: + connstream = newsocket + + new_client = client(connstream, remote_ip) + + self.select_inputs.append(connstream) + self.select_outputs.append(connstream) + self.client_map[connstream.fileno()] = new_client + self.client_list.append(new_client) + else: + data = sock.recv(1024) + cur_client = self.client_map[sock.fileno()] + + if data: + cur_client.process_data(data) + else: + cur_client.end() + self.select_inputs = remove_from_list(self.select_inputs, sock) + print "NOTICE: Client disconnected" + except ssl.SSLError, err: + if err.args[0] == ssl.SSL_ERROR_WANT_READ: + select.select([sock], [], []) + elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: + select.select([], [sock], []) + else: + raise + else: + time.sleep(0.010) + +class client: + buff = "" + stream = None + user = None + ip = "" + + def __init__(self, connstream, ip): + self.ip = ip + self.stream = connstream + self.user = user(self) + + def send_chunk(self, chunk): + self.stream.send(chunk + EOC) + + def send_global_notice(self, notice): + self.send_chunk(":%s NOTICE %s" % (config_ownhost, notice)) + + def send_numeric(self, numeric, notice): + self.send_chunk(":%s NOTICE %s :%s" % (config_ownhost, numeric, notice)) + + def process_data(self, data): + self.buff += data + stack = self.buff.split("\n") + self.buff = stack.pop() + + for chunk in stack: + print chunk + self.process_chunk(chunk.rstrip()) + + def process_chunk(self, chunkdata): + data = split_irc(chunkdata) + if data[0].upper() == "PING": + self.send_chunk("PONG %" % data[1]) + else: + self.user.process_data(data) + + def end(self): + pass + +class channel: + presences = {} + name = "" + registered = False + +class user: + client = None + registered = 0 + registered_nick = False + registered_user = False + nickname = "" + ident = "" + realname = "" + masked_host = "" + real_host = "" + ip = "" + + def __init__(self, client): + self.client = client + self.client.send_global_notice("AUTH :*** Looking up your hostname...") + hostname, aliaslist, ipaddrlist = socket.gethostbyaddr(self.client.ip) + self.real_host = hostname + self.masked_host = hostname + self.client.send_global_notice("AUTH :*** Found your hostname") + if config_password == "": + self.registered = 1 + + def process_data(self, data): + if data[0].upper() in ["USER", "NICK"] and self.registered == 1: + if data[0].upper() == "USER": + if len(data) >= 5: + self.ident = data[1] + self.realname = data[4] + self.registered_user = True + self.verify_registration() + else: + send_numeric("461", "%s USER :Not enough parameters." % self.nickname) + elif data[0].upper() == "NICK": + if len(data) >= 2: + self.nickname = data[1] + self.registered_nick = True + self.verify_registration() + else: + send_numeric("461", "%s NICK :Not enough parameters." % self.nickname) + else: + send_numeric("451", "%s %s :You have not registered." % self.nickname) + + def verify_registration(self): + if self.registered_nick == True and self.registered_user == True: + self.registered = 2 + self.client.send_numeric("001", "Welcome to %s, %s!%s@%s" % (config_netname, self.nickname, self.ident, self.real_host)) + self.client.send_numeric("002", "Your host is %s, running %s." % (config_ownhost, config_version)) + self.client.send_numeric("003", "This server has been running since unknown.") + self.client.send_numeric("004", "%s %s %s %s" % (config_ownhost, config_version, "", "")) + +class presence: + user = None + status = "none" + joined = 0 + +l = listener() +l.start("0.0.0.0", 6667, "sample.cert", "sample.key")