def unload(self): global can_see for channel in [ channel for channel in self.channels if hasattr(channel, 'delayjoins') and channel in can_see ]: if not hasattr(channel, 'delayjoins') or not channel.delayjoins: channel.delayjoins = [] # Show user joins to whoever needs them. for user in channel.users: for user2 in [ user2 for user2 in channel.users if user2 not in can_see[channel][user] ]: logging.debug( 'Module unload: Showing join from {} to {}'.format( user2, user)) data = ':{}!{}@{} JOIN {}{}'.format( user2.nickname, user2.ident, user2.cloakhost, channel.name, ' {} :{}'.format(user2.svid, user2.realname) if 'extended-join' in user.caplist else '') user._send(data) can_see[channel][user].append(user2) can_see = {}
def register(self): if not self.registered: self.validate() if hasattr(self, 'level'): pass mode_class_list = None if issubclass(self.__class__, UserMode): mode_class_list = self.ircd.user_mode_class elif issubclass(self.__class__, ChannelMode): mode_class_list = self.ircd.channel_mode_class if self not in mode_class_list: mode_class_list.append(self) if issubclass(self.__class__, UserMode): self.ircd.user_modes[self.mode] = (self.req_flag, self.desc) logging.debug('Usermode registered: {}'.format(self)) # logging.debug('Permission flag: {}'.format(self.req_flag)) # logging.debug('Description: {}'.format(self.desc)) elif issubclass(self.__class__, ChannelMode): if self.type != 3 and self.param_help: t = (self.req_flag, self.desc, self.param_help) else: t = (self.req_flag, self.desc) self.ircd.channel_modes[self.type][self.mode] = t logging.debug('Channelmode registered: {}'.format(self)) # logging.debug('Permission flag: {}'.format(self.req_flag)) self.registered = 1
def unload(self): if self in self.ircd.command_class: self.ircd.command_class.remove(self) if self.support and self.support[0] in self.ircd.support: logging.debug('Removed support data') del self.ircd.support[self.support[0]] logging.debug('{} successfully unhooked'.format(self))
def take_mode(self, user): if self.mode not in user.modes: logging.debug('Failed attempt at removing non-active usermode "{}" from {}'.format(self.mode, user.nickname)) return 0 user.modes = user.modes.replace(self.mode, '') self.modebuf.append(self.mode) logging.debug('Usermode "{}" removed from {} usermodes. Now: {}'.format(self.mode, user.nickname, user.modes)) return 1
def _send(self, data): if not hasattr(self, 'socket'): # logging.debug('Socket {} got sent flashed out.'.format(self)) return if self.socket: self.sendbuffer += data + '\r\n' if self.server.use_poll: logging.debug( 'Flag for {} set to READ_WRITE (_send())'.format(self)) self.server.pollerObject.modify(self.socket, READ_WRITE)
def init(self, reload=False): global can_see if can_see or reload: return can_see = {} for chan in self.channels: if chan not in can_see: can_see[chan] = {} logging.debug('INIT: Channel {} added to can_see dict'.format( chan.name)) for user in [user for user in chan.users if user not in can_see[chan]]: can_see[chan][user] = []
def hidequit(self, localServer): global can_see for channel in [chan for chan in self.channels if chmode in chan.modes]: del can_see[channel][self] for user in [ user for user in channel.users if user in can_see[channel] and self in can_see[channel][user] and user.chlevel(channel) < 2 ]: logging.debug('/quit: User {} can not see {} anymore.'.format( user.nickname, self.nickname)) can_see[channel][user].remove(self)
def register(self, **kwargs): if not self.registered: if type(self.command) == str: self.command = [self.command.upper()] else: self.command = [x.upper() for x in self.command] self.help = self.__doc__ self.validate() self.ircd.command_class.append(self) logging.debug(f'Command registered: {self}') self.registered = 1
def give_mode(self, user): if self.mode in user.modes: logging.error(f'Usermode "{self.mode}" is already active on user {user.nickname}') return 0 if self.req_flag == 1 and 'o' not in user.modes: logging.error(f'User {user} is not allowed to set this mode: {self.req_flag}') return 0 user.modes += self.mode self.modebuf.append(self.mode) logging.debug('Usermode of {} is now: {} (+{})'.format(user.nickname, user.modes, self.mode)) return 1
def webstats_ratelimit(localServer): global allowed_ips for ip in dict(localServer.webstats_ip_requests): rate_ip = [i for i in allowed_ips if match(i, ip)] if not rate_ip: logging.debug( 'Something went wrong. Could not find a rate_limit for IP "{}", removing from dict.' .format(ip)) del localServer.webstats_ip_requests[ip] break rate_limit = allowed_ips[rate_ip[0]]["rate_limit"] if int(time.time( )) - localServer.webstats_ip_requests[ip]['ctime'] > rate_limit[1]: del localServer.webstats_ip_requests[ip] logging.debug("API rate limit for {} reset.".format(ip))
def unload(self): mode_class_list = None if issubclass(self.__class__, UserMode): mode_class_list = self.ircd.user_mode_class del self.ircd.user_modes[self.mode] elif issubclass(self.__class__, ChannelMode): mode_class_list = self.ircd.channel_mode_class if self.mode in self.ircd.channel_modes[self.type]: del self.ircd.channel_modes[self.type][self.mode] if self in mode_class_list: mode_class_list.remove(self) logging.debug('{} successfully unhooked'.format(self))
def hide_kick(self, ircd, user, channel, reason): if chmode in channel.modes: global can_see if channel not in can_see: logging.error( '/KICK: CRITICAL ERROR: channel {} is not found in the can_see dict!' .format(channel.name)) return can_see[channel][user] = [] for u in [ u for u in channel.users if u in can_see[channel] and user in can_see[channel][u] ]: logging.debug('/kick: User {} can not see {} anymore.'.format( u.nickname, user.nickname)) can_see[channel][u].remove(user)
def showjoin(self, ircd, channel, msg): if chmode in channel.modes: global can_see for user in [ user for user in channel.users if self not in can_see[channel][user] and user != self ]: logging.debug( '/privmsg: Allowing visibility state for {} to {}'.format( self.nickname, user.nickname)) data = ':{}!{}@{} JOIN {}{}'.format( self.nickname, self.ident, self.cloakhost, channel.name, ' {} :{}'.format(self.svid, self.realname) if 'extended-join' in user.caplist else '') user._send(data) can_see[channel][user].append(self) return msg
def visible_in_chan(self, localServer, user, channel): global can_see if chmode not in channel.modes or ( chmode in channel.modes and self.chlevel(channel) > 2 or user == self ): # or ('o' in self.modes and user.chlevel(channel) <= 2)): if chmode in channel.modes and user not in can_see[channel][self]: can_see[channel][self].append(user) return 1 if not can_see: return 1 # if self in can_see[channel]: # logging.debug('User {} can see the following users on {}: {}'.format(self.nickname, channel.name, can_see[channel][self])) if self in can_see[channel] and user in can_see[channel][self]: logging.debug('visible_in_chan() dict, returning 1') return 1 return 0
def hidepart(self, localServer, channel): if chmode in channel.modes: global can_see if channel not in can_see: logging.error( '/PART: CRITICAL ERROR: channel {} is not found in the can_see dict!' .format(channel.name)) return can_see[channel][self] = [] for user in [ user for user in channel.users if user in can_see[channel] and self in can_see[channel][user] and user.chlevel(channel) < 2 ]: logging.debug('/part: User {} can not see {} anymore.'.format( user.nickname, self.nickname)) can_see[channel][user].remove(self)
def check(self, client, recv): cmd = recv[0].upper() if type(client ).__name__ != self.req_class and self.req_class == 'Server': client.sendraw(ERR.SERVERONLY, f':{cmd} is a server only command') return 0 received_params = len(recv) - 1 if received_params < self.params: client.sendraw( ERR.NEEDMOREPARAMS, f':{cmd} Not enough parameters. Required: {self.params}') return 0 if self.req_modes and type(client).__name__ != 'Server': req_modes = ' '.join(self.req_modes) if 'o' in req_modes and 'o' not in client.modes: client.sendraw( ERR.NOPRIVILEGES, ':Permission denied - You are not an IRC Operator') return 0 forbid = set(req_modes).difference(set(client.modes)) if forbid: client.sendraw(ERR.NOPRIVILEGES, ':Permission denied - Required mode not set') return 0 if self.req_flags: forbid = 1 if '|' in self.req_flags: if list( filter(lambda f: f in client.operflags, self.req_flags.split('|'))): forbid = False logging.debug( 'You have one of the required flags. Allowing command.' ) else: forbid = {self.req_flags}.difference(set(client.operflags)) if forbid: client.sendraw( ERR.NOPRIVILEGES, ':Permission denied - You do not have the correct IRC Operator privileges' ) return 0 return 1
def set_D(localServer, self, channel, mode): if mode == chmode: if 'u' in channel.modes: localServer.notice(self, 'Mode +D cannot be set: channel has +u') return 0 global can_see if channel not in can_see: can_see[channel] = {} for user1 in [u for u in channel.users if u not in can_see[channel]]: can_see[channel][user1] = [] for user2 in [ user2 for user2 in channel.users if user2 not in can_see[channel][user1] ]: can_see[channel][user1].append(user2) logging.debug('Mode set, so user {} is visible to {}'.format( user2.nickname, user1.nickname)) return 1
def process_webstats(self, localServer, recv): global allowed_ips ip = [i for i in allowed_ips if match(i, recv[1])] if not ip or ip[0] not in allowed_ips: return self._send( 'WEBSTATS 403 You are not allowed to make that API call.') if "rate_limit" in allowed_ips[ip[0]]: rate_limit = allowed_ips[ip[0]]["rate_limit"] else: rate_limit = (9999, 1) # 2 Unlimited. ip = recv[1] if ip not in localServer.webstats_ip_requests: localServer.webstats_ip_requests[ip] = {} localServer.webstats_ip_requests[ip]['calls'] = {} localServer.webstats_ip_requests[ip]['ctime'] = int( time.time()) # First API call for this IP. logging.debug('Rate limit: {}'.format(rate_limit)) ago = int(time.time()) - localServer.webstats_ip_requests[ip]['ctime'] if len(localServer.webstats_ip_requests[ip]['calls']) >= rate_limit[0]: logging.debug("Max. calls exceeded for IP: {}".format(ip)) return self._send('WEBSTATS 403 Rate limited.') response = {} response['users'] = [u.nickname for u in localServer.users if u.registered] response['channels'] = [ c.name for c in localServer.channels if 's' not in c.modes and 'p' not in c.modes ] self._send('WEBSTATS 200 {}'.format(response)) localServer.webstats_ip_requests[ip]['calls'][int(round(time.time() * 1000))] = 1 calls = len(localServer.webstats_ip_requests[ip]['calls']) logging.debug( "This IP made {} call{} this session, first call was {} second{} ago.". format(calls, '' if calls == 1 else 's', ago, '' if ago == 1 else 's'))
def new_sync(self, ircd, skip, data, direct=None): try: if type(skip) != list: skip = [skip] for t in [t for t in skip if type(t).__name__ != 'Server']: logging.error( '{}HALT: wrong source type in new_sync(): {} with data: {}{}' .format(R2, t, data, W)) return if data.split()[1] in ['UID', 'SID']: data = data.split() data = '{} {} {}'.format(' '.join(data[:3]), str(int(data[3]) + 1), ' '.join(data[4:])) if direct: # Private messages and notices. direct represents the target.server dest = direct if direct.socket else direct.uplink # if direct.socket: # logging.debug('Directly linked to us, no more hops needed.') if not direct.socket: logging.debug( 'Server has hopcount of {d.hopcount}, sending to {d.uplink} first.' .format(d=direct)) dest._send(data) return for server in [ server for server in ircd.servers if server and server.socket and server not in skip ]: if not server.eos: if server not in ircd.sync_queue: ircd.sync_queue[server] = [] ircd.sync_queue[server].append(data) logging.debug( '{}Added to {} sync queue because they are not done syncing: {}{}' .format(R2, server, data, W)) continue server._send(data) except Exception as ex: logging.exception(ex)
def _send(self, data): try: if self.socket: self.sendbuffer += data + '\r\n' if self.localServer.use_poll: logging.debug( 'Flag for {} set to READ_WRITE (_send())'.format(self)) self.localServer.pollerObject.modify( self.socket, READ_WRITE) ignore = ['PRIVMSG', 'NOTICE', 'PING', 'PONG'] try: if data.split()[0] not in ['PING', 'PONG']: if len(data) > 1 and data.split()[1] not in ignore: # pass logging.info('{}{} <<<-- {}{}'.format( B, self.hostname if self.hostname != '' else self, data, W)) except: pass except Exception as ex: logging.exception(ex)
def read_socket(ircd, sock): if not hasattr(sock, 'socket'): # Client probably repidly disconnected. Possible causes can be ZNC that have not yet accepted new cert. # sock.quit('No socket') return try: if sock.cls: buffer_len = int(ircd.conf['class'][sock.cls]['sendq']) * 2 else: buffer_len = 8192 if type(sock).__name__ == 'User' else 65536 try: recv = sock.socket.recv(buffer_len).decode('utf-8') except UnicodeDecodeError as ex: # Do nothing, skip read. logging.debug(f'Unable to read socket {sock}: {ex}') ##### sock.quit('') # Drunk shit, REMOVE THIS!!!!!!!!! ##### ##### return except Exception as ex: # logging.exception(ex) sock.quit('Read error: {}'.format(ex)) return if not recv: # logging.error('No data received from {}'.format(sock)) sock.quit('Read error') return sock.recvbuffer += recv check_flood(ircd, sock) sock.handle_recv() return recv except Exception as ex: logging.exception(ex)
def execute(self, client, recv): nick = recv[2] params = [] allow = 1 for p in recv: params.append(p) for user in [ user for user in self.ircd.users if user.nickname.lower() == nick.lower() ]: logging.error('Found double user in UID: {}'.format(user)) if not user.server: logging.error( 'Quitting {} because their server could not be found (UID)' .format(user)) user.quit('Unknown or corrupted connection with the same nick') continue logging.error( '{}ERROR: user {} already found on the network{}'.format( R, user, W)) localTS = int(user.signon) remoteTS = int(recv[4]) if remoteTS <= localTS: logging.info( '{}Local user {} disconnected from local server.{}'.format( R, user, W)) user.quit('Local Nick Collision', silent=True) continue else: allow = 0 logging.debug('Disallowing remote user {}'.format(user)) return if allow: u = ircd.User(client, server_class=self.ircd, params=params) cmd = ' '.join(recv) self.ircd.new_sync(self.ircd, client, cmd)
def pre_hook(self, user, channel, param, action=''): if type(user).__name__ == 'User': # Servers can set modes too. hook = 'local_chanmode' if user.server != self.ircd else 'remote_chanmode' else: hook = 'local_chanmode' if user == self.ircd else 'remote_chanmode' # logging.debug('Looking for pre_* hooks for {}'.format(self)) if self.mode not in self.ircd.core_chmodes and self.type == 0: if not hasattr(channel, self.list_name): setattr(channel, self.list_name, {}) for callable in [callable for callable in self.ircd.hooks if callable[0].lower() == 'pre_' + hook and self.mode in callable[1]]: try: # logging.debug('Calling {} with action {}'.format(callable, action)) # We pass the modebar to the module hook because we need to know which mode to work on. ok = callable[2](user, self.ircd, channel, self.modebuf, self.parambuf, action, self.mode, param) if not ok and ok is not None: logging.debug('Further processing halted for {}{}{}'.format(action, self.mode, ' ' + param if param else '')) logging.debug('Blocked by: {}'.format(callable)) return 0 except Exception as ex: logging.exception(ex) return 1
def LoadModule(self, name, path, reload=False, module=None): package = name.replace('/', '.') try: if reload: module = importlib.reload(module) logging.debug('Requesting reload from importlib') else: module = importlib.import_module(package) importlib.reload(module) if not module.__doc__: logging.info('Invalid module.') return 'Invalid module' callables = FindCallables(module) hook_fail = HookToCore( self, callables, reload=reload) # If None is returned, assume success. if hook_fail: logging.debug('Hook failed: {}'.format(hook_fail)) UnloadModule(self, name) return hook_fail self.modules[module] = callables name = module.__name__ update_support(self) logging.info('Loaded: {}'.format(name)) except FileNotFoundError as ex: return ex except Exception as ex: logging.exception(ex) UnloadModule(self, name) # if not reload: if not self.running: print( 'Server could not be started due to an error in {}: {}'.format( name, ex)) sys.exit() raise
def hidejoin(self, ircd, channel, **kwargs): try: if chmode in channel.modes: global can_see if channel not in can_see: can_see[channel] = {} logging.debug('/JOIN: Channel {} added to can_see dict'.format( channel.name)) if self not in can_see[channel]: can_see[channel][self] = [] # <self> just joined <channel>. They can see everyone currently on the channel. can_see[channel][self] = list( channel.users ) # /!\ Do NOT RE-ASSIGN the list. Make a copy! /!\ for user in [user for user in channel.users if user != self]: if visible_in_chan( user, ircd, self, channel) and self not in can_see[channel][user]: can_see[channel][user].append(self) logging.debug('/join: User {} can see {}'.format( user.nickname, self.nickname)) return 1, 0 except Exception as ex: logging.exception(ex)
def execute(self, client, recv): if type(client).__name__ == 'Server': if len(recv) == 1: with open(self.ircd.confdir + 'ircd.motd') as f: b_motd = bytes(f.read(), 'utf-8') logging.debug('Sending to remote: {}'.format(b_motd)) client._send('MOTD {}'.format(b_motd)) return else: logging.debug('Received remote motd response.') logging.debug('Sending reply to: {}'.format(self.ircd.remote_motd_request[client])) b_motd = ' '.join(recv[1:]) logging.debug('Bytes: {}'.format(b_motd)) self.ircd.remote_motd_request[client].sendraw(self.RPL.ENDOFMOTD, '{} Message of the Day'.format(client.hostname)) for line in eval(b_motd).decode('utf-8').split('\n'): self.ircd.remote_motd_request[client].sendraw(self.RPL.MOTD, ':- {}'.format(line)) self.ircd.remote_motd_request[client].sendraw(self.RPL.ENDOFMOTD, ':End of Message of the Day.') else: if len(recv) == 1: client.sendraw(self.RPL.MOTDSTART, '{} Message of the Day'.format(self.ircd.hostname)) with open(self.ircd.confdir + 'ircd.motd') as f: for line in f.read().split('\n'): client.sendraw(self.RPL.MOTD, ':- {}'.format(line)) client.sendraw(self.RPL.ENDOFMOTD, ':End of Message of the Day.') else: remoteserver = recv[1].lower() if hasattr(self.ircd, 'remote_motd_request') and remoteserver.lower() != self.ircd.hostname.lower(): server_exists = [server for server in self.ircd.servers if server.hostname.lower() == remoteserver] if not server_exists and remoteserver != self.ircd.hostname: return client.sendraw(402, '{} :No such server'.format(remoteserver)) if not server_exists[0].socket: ### Will fix hops later. return self.ircd.notice(client, '* You can only request remote MOTDs from directly linked servers.') if 'o' not in client.modes: client.flood_penalty += 50000 server = server_exists[0] if server_exists[0].socket else server_exists[0].introducedBy self.ircd.remote_motd_request[server] = client server._send('MOTD')
def quit(self, reason, error=True, banmsg=None, kill=False, silent=False, api=False, squit=False): # Why source? try: if not hasattr(self, 'socket'): self.socket = None self.recvbuffer = '' ircd = self.ircd if not self.socket else self.server sourceServer = self.server if (self.server.socket or self.server == ircd) else self.server.uplink if self.registered: logging.debug('User {} quit. Uplink source: {}'.format( self.nickname, sourceServer)) for callable in [ callable for callable in ircd.hooks if callable[0].lower() == 'pre_local_quit' ]: try: callable[2](self, ircd) except Exception as ex: logging.exception(ex) if banmsg: ircd.notice( self, '*** You are banned from this server: {}'.format(banmsg)) if int( time.time() ) - self.signon < 60 and self.registered and not error and self.socket: reason = str(ircd.conf['settings']['quitprefix']).strip() if reason.endswith(':'): reason = reason[:-1] reason += ': ' + self.nickname if self.socket and reason and not api: self._send('ERROR :Closing link: [{}] ({})'.format( self.hostname, reason)) while self.sendbuffer: # logging.info('User {} has sendbuffer remaining: {}'.format(self, self.sendbuffer.rstrip())) try: sent = self.socket.send( bytes(self.sendbuffer + '\n', 'utf-8')) self.sendbuffer = self.sendbuffer[sent:] except: break if self in ircd.pings: del ircd.pings[self] # logging.debug('Removed {} from server PING check'.format(self)) if self.registered and (self.server == ircd or self.server.eos): if reason and not kill: skip = [sourceServer] if squit: for server in [ server for server in ircd.servers if hasattr(server, 'protoctl') and 'NOQUIT' in server.protoctl ]: # and not server.eos]: skip.append(server) ircd.new_sync(ircd, skip, ':{} QUIT :{}'.format(self.uid, reason)) if self.socket and reason and not silent: ircd.snotice( 'c', '*** Client exiting: {} ({}@{}) ({})'.format( self.nickname, self.ident, self.hostname, reason)) self.registered = False for channel in iter( [channel for channel in self.channels if 'j' in channel.modes]): self.handle('PART', '{}'.format(channel.name)) continue # Check module hooks for visible_in_channel() # FIX: [05:57:26] * vknzvmwlcrcdzz ([email protected]) Quit (Write error: [Errno 110] Connection timed out) # IN STATUS WINDOW? all_broadcast = [self] for channel in self.channels: for user in channel.users: if user not in all_broadcast and user != self: all_broadcast.append(user) inv_checked = 0 for u in iter([u for u in all_broadcast if u != self]): visible = 0 for channel in iter( [chan for chan in self.channels if not visible]): for callable in [ callable for callable in ircd.hooks if callable[0].lower() == 'visible_in_channel' ]: try: visible = callable[2](u, ircd, self, channel) inv_checked = 1 # logging.debug('Is {} visible for {} on {}? :: {}'.format(self.nickname, u.nickname, channel.name, visible)) except Exception as ex: logging.exception(ex) if visible: # Break out of the channels loop. No further checks are required. break if not visible and inv_checked: logging.debug( 'User {} is not allowed to see {} on any channel, not sending quit.' .format(u.nickname, self.nickname)) all_broadcast.remove(u) if self.nickname != '*' and self.ident != '' and reason: self.broadcast(all_broadcast, 'QUIT :{}'.format(reason)) for channel in list(self.channels): channel.users.remove(self) del channel.usermodes[self] self.channels.remove(channel) if len(channel.users) == 0 and 'P' not in channel.modes: ircd.channels.remove(channel) del ircd.chan_params[channel] for callable in [ callable for callable in ircd.hooks if callable[0].lower() == 'channel_destroy' ]: try: callable[2](self, ircd, channel) except Exception as ex: logging.exception(ex) watch_notify_offline = iter([ user for user in ircd.users if self.nickname.lower() in [x.lower() for x in user.watchlist] ]) for user in watch_notify_offline: user.sendraw( RPL.LOGOFF, '{} {} {} {} :logged offline'.format( self.nickname, self.ident, self.cloakhost, self.signon)) if self in ircd.users: ircd.users.remove(self) if self.socket: if ircd.use_poll: ircd.pollerObject.unregister(self.socket) try: self.socket.shutdown(socket.SHUT_WR) except: pass self.socket.close() hook = 'local_quit' if self.server == ircd else 'remote_quit' for callable in [ callable for callable in ircd.hooks if callable[0].lower() == hook ]: try: callable[2](self, ircd) except Exception as ex: logging.exception(ex) gc.collect() del gc.garbage[:] if not ircd.forked: try: logging.debug('Growth after self.quit() (if any):') objgraph.show_growth(limit=10) except: # Prevent weird spam shit. pass del self except Exception as ex: logging.exception(ex)
def welcome(self): if not self.registered: for callable in [ callable for callable in self.server.hooks if callable[0].lower() == 'pre_local_connect' ]: try: success = callable[2](self, self.server) if not success and success is not None: # Modules need to explicitly return False or 0, not the default None. logging.debug( f"Connection process denied for user {self} by module: {callable}" ) return except Exception as ex: logging.exception(ex) deny, reason = 0, '' if self.ip in self.server.deny_cache: deny = 1 if 'reason' in self.server.deny_cache[self.ip]: reason = self.server.deny_cache[self.ip]['reason'] deny_except = False if 'except' in self.server.conf and 'deny' in self.server.conf[ 'except']: for e in self.server.conf['except']['deny']: if match(e, self.ident + '@' + self.ip) or match( e, self.ident + '@' + self.hostname): deny_except = True break if not deny_except and not deny: for entry in self.server.deny: if match(entry, self.ident + '@' + self.ip) or match( entry, self.ident + '@' + self.hostname): self.server.deny_cache[self.ip] = {} self.server.deny_cache[self.ip]['ctime'] = int( time.time()) reason = self.server.deny[entry] if self.server.deny[ entry] else '' self.server.deny_cache[self.ip]['reason'] = reason logging.info( 'Denied client {} with match: {} [{}]'.format( self, entry, reason)) deny = 1 break if deny: if reason: self.server.notice( self, '* Connection denied: {}'.format(reason)) return self.quit( 'Your host matches a deny block, and is therefore not allowed.' ) block = 0 for cls in iter([ cls for cls in self.server.conf['allow'] if cls in self.server.conf['class'] ]): t = self.server.conf['allow'][cls] isMatch = False if 'ip' in t: clientmask = '{}@{}'.format(self.ident, self.ip) isMatch = match(t['ip'], clientmask) if 'hostname' in t and not isMatch: # Try with hostname. IP has higher priority. clientmask = '{}@{}'.format(self.ident, self.hostname) isMatch = match(t['hostname'], clientmask) if isMatch: if 'options' in t: if 'ssl' in t['options'] and not self.ssl: continue self.cls = cls if 'block' in t: for entry in t['block']: clientmask_ip = '{}@{}'.format(self.ident, self.ip) clientmask_host = '{}@{}'.format( self.ident, self.hostname) block = match(entry, clientmask_ip) or match( entry, clientmask_host) if block: logging.info( 'Client {} blocked by {}: {}'.format( self, cls, entry)) break break if not self.cls or block: return self.quit( 'You are not authorized to connect to this server') totalClasses = list( filter(lambda u: u.server == self.server and u.cls == self.cls, self.server.users)) if len(totalClasses) > int( self.server.conf['class'][self.cls]['max']): return self.quit('Maximum connections for this class reached') clones = [ u for u in self.server.users if u.socket and u.ip == self.ip ] if len(clones) >= int( self.server.conf['allow'][self.cls]['maxperip']): return self.quit('Maximum connections from your IP') # Resolve IP in the background to test against deny-block matches, if host resolution is disabled. if 'dontresolve' in self.server.conf['settings']: threading.Thread(target=resolve_ip, args=([self])).start() if not hasattr(self, 'socket'): return self.sendraw( RPL.WELCOME, ':Welcome to the {} IRC Network {}!{}@{}'.format( self.server.name, self.nickname, self.ident, self.hostname)) self.sendraw( RPL.YOURHOST, ':Your host is {}, running version {}'.format( self.server.hostname, self.server.version)) d = datetime.datetime.fromtimestamp( self.server.creationtime).strftime('%a %b %d %Y') t = datetime.datetime.fromtimestamp( self.server.creationtime).strftime('%H:%M:%S %Z') self.sendraw(RPL.CREATED, ':This server was created {} at {}'.format(d, t)) umodes, chmodes = '', '' for m in iter([ m for m in self.server.user_modes if m.isalpha() and m not in umodes ]): umodes += m for t in self.server.channel_modes: for m in iter([ m for m in self.server.channel_modes[t] if m.isalpha() and m not in chmodes ]): chmodes += m umodes = ''.join(sorted(set(umodes))) chmodes = ''.join(sorted(set(chmodes))) self.sendraw( RPL.MYINFO, '{} {} {} {}'.format(self.server.hostname, self.server.version, umodes, chmodes)) show_support(self, self.server) cipher = None if self.ssl and hasattr(self.socket, 'cipher') and self.socket.cipher: if self.socket.cipher(): cipher = self.socket.cipher()[0] self.send( 'NOTICE', ':*** You are connected to {} with {}-{}'.format( self.server.hostname, self.socket.version(), cipher)) msg = '*** Client connecting: {u.nickname} ({u.ident}@{u.hostname}) {{{u.cls}}} [{0}{1}]'.format( 'secure' if self.ssl else 'plain', '' if not cipher else ' ' + cipher, u=self) self.server.snotice('c', msg) binip = IPtoBase64(self.ip) if self.ip.replace( '.', '').isdigit() else self.ip data = '{s.nickname} {s.server.hopcount} {s.signon} {s.ident} {s.hostname} {s.uid} 0 +{s.modes} {s.cloakhost} {s.cloakhost} {0} :{s.realname}'.format( binip, s=self) self.server.new_sync(self.server, self.server, ':{} UID {}'.format(self.server.sid, data)) self.registered = True current_lusers = len([ user for user in self.server.users if user.server == self.server and user.registered ]) if current_lusers > self.server.maxusers: self.server.maxusers = current_lusers if len(self.server.users) > self.server.maxgusers: self.server.maxgusers = len(self.server.users) if self.server.maxgusers % 10 == 0: self.server.snotice( 's', '*** New global user record: {}'.format( self.server.maxgusers)) self.handle('lusers') self.handle('motd') for callable in [ callable for callable in self.server.hooks if callable[0].lower() == 'welcome' ]: try: callable[2](self, self.server) except Exception as ex: logging.exception(ex) if self.fingerprint: self.send( 'NOTICE', ':*** Your TLS fingerprint is {}'.format(self.fingerprint)) data = 'MD client {} certfp :{}'.format( self.uid, self.fingerprint) self.server.new_sync(self.server, self.server, ':{} {}'.format(self.server.sid, data)) modes = [] for mode in self.server.conf['settings']['modesonconnect']: if mode in self.server.user_modes and mode not in 'oqrzS': modes.append(mode) if self.ssl and hasattr(self.socket, 'cipher'): modes.append('z') if len(modes) > 0: p = {'override': True} self.handle('mode', '{} +{}'.format(self.nickname, ''.join(modes)), params=p) watch_notify = iter([ user for user in self.server.users if self.nickname.lower() in [x.lower() for x in user.watchlist] ]) for user in watch_notify: user.sendraw( RPL.LOGON, '{} {} {} {} :logged online'.format( self.nickname, self.ident, self.cloakhost, self.signon)) for callable in [ callable for callable in self.server.hooks if callable[0].lower() == 'local_connect' ]: try: callable[2](self, self.server) except Exception as ex: logging.exception(ex) gc.collect()
def __init__(self, server, sock=None, address=None, is_ssl=None, server_class=None, params=None): try: self.socket = sock self.server = None self.cloakhost = '*' self.connected = True self.nickname = '*' self.ident = '' self.hostname = '' self.realname = '' self.svid = '*' self.channels = [] self.modes = '' self.operflags = [] self.snomasks = '' self.swhois = [] self.watchlist = [] self.caplist = [] self.sends_cap = False self.cap_end = False self.watchC = False self.watchS = False self.ssl = is_ssl self.operaccount = '' self.away = False self.sendbuffer = '' self.operswhois = '' self.fingerprint = None self.flood_penalty = 0 self.flood_penalty_time = 0 if self.socket: self.server = server self.ircd = server self.addr = address self.ip, self.hostname = self.addr[0], self.addr[0] if self.ip.startswith('::ffff:') and self.ip[7:].replace( '.', '').isdigit(): # logging.debug('Invalid IPv6, using {}'.format(self.ip[7:])) self.ip = self.ip[7:] if self.hostname.startswith( '::ffff:') and self.hostname[7:].replace('.', '').isdigit(): # logging.debug('Invalid IPv6, using {}'.format(self.ip[7:])) self.hostname = self.hostname[7:] self.cls = None self.signon = int(time.time()) self.registered = False self.ping = int(time.time()) self.recvbuffer = '' self.validping = False self.server_pass_accepted = False self.uid = '{}{}'.format( self.server.sid, ''.join( random.choice(string.ascii_uppercase + string.digits) for _ in range(6))) while [ u for u in self.server.users if hasattr(u, 'uid') and u != self and u.uid == self.uid ]: # while list(filter(lambda u: u.uid == self.uid, self.server.users)): self.uid = '{}{}'.format( self.server.sid, ''.join( random.choice(string.ascii_uppercase + string.digits) for _ in range(6))) self.lastPingSent = time.time() * 1000 self.lag_measure = self.lastPingSent self.server.users.append(self) for callable in [ callable for callable in server.hooks if callable[0].lower() == 'new_connection' ]: try: callable[2](self, server) except Exception as ex: logging.exception(ex) if 'dnsbl' in self.server.conf and self.ip.replace( '.', '').isdigit() and not ipaddress.ip_address( self.ip).is_private: # self.sendraw('020', ':Please wait while we process your connection.') dnsbl_except = False if 'except' in self.server.conf and 'dnsbl' in self.server.conf[ 'except']: for e in self.server.conf['except']['dnsbl']: if match(e, self.ip): dnsbl_except = True break if not dnsbl_except: DNSBLCheck(self) TKL.check(self, self.server, self, 'z') TKL.check(self, self.server, self, 'Z') throttleTreshhold = int( self.server.conf['settings']['throttle'].split(':')[0]) throttleTime = int( self.server.conf['settings']['throttle'].split(':')[1]) total_conns = [ u for u in self.server.throttle if u.ip == self.ip and int(time.time()) - self.server.throttle[u]['ctime'] <= throttleTime ] throttle_except = False if 'except' in self.server.conf and 'throttle' in self.server.conf[ 'except']: for e in self.server.conf['except']['throttle']: if match(e, self.ip): throttle_except = True break if len(total_conns ) >= throttleTreshhold and not throttle_except: self.quit('Throttling - You are (re)connecting too fast') unknown_conn = [ user for user in self.server.users if user.ip == self.ip and not user.registered ] if len(unknown_conn) > 20: self.quit('Too many unknown connections from your IP') self.server.throttle[self] = {} self.server.throttle[self]['ip'] = self.ip self.server.throttle[self]['ctime'] = int(time.time()) self.server.totalcons += 1 if self.ssl and self.socket: try: fp = self.socket.getpeercert(binary_form=True) if fp: self.fingerprint = hashlib.sha256( repr(fp).encode('utf-8')).hexdigest() except Exception as ex: logging.exception(ex) self.idle = int(time.time()) if self.ip in self.server.hostcache: self.hostname = self.server.hostcache[self.ip]['host'] self._send( ':{u.server.hostname} NOTICE AUTH :*** Found your hostname ({u.hostname}) [cached]' .format(u=self)) elif 'dontresolve' not in self.server.conf['settings'] or ( 'dontresolve' in self.server.conf['settings'] and not self.server.conf['settings']['dontresolve']): try: self.hostname = socket.gethostbyaddr(self.ip)[0] if not self.hostname.split('.')[1]: raise self.server.hostcache[self.ip] = {} self.server.hostcache[self.ip]['host'] = self.hostname self.server.hostcache[self.ip]['ctime'] = int( time.time()) self._send( ':{u.server.hostname} NOTICE AUTH :*** Found your hostname ({u.hostname})' .format(u=self)) except Exception as ex: self.hostname = self.ip # self._send(':{} NOTICE AUTH :*** Couldn\'t resolve your hostname; using IP address instead ({})'.format(self.server.hostname, self.hostname)) self._send( ':{u.server.hostname} NOTICE AUTH :*** Couldn\'t resolve your hostname; using IP address instead ({u.hostname})' .format(u=self)) else: self._send( ':{u.server.hostname} NOTICE AUTH :*** Host resolution is disabled, using IP ({u.ip})' .format(u=self)) TKL.check(self, self.server, self, 'g') TKL.check(self, self.server, self, 'G') self.cloakhost = cloak(self) else: try: self.origin = server_class self.ircd = server_class self.origin.users.append(self) self.cls = 0 self.nickname = params[2] self.idle = int(params[4]) self.signon = int(params[4]) self.ident = params[5] self.hostname = params[6] self.uid = params[7] server = list( filter(lambda s: s.sid == params[0][1:], self.ircd.servers)) if not server: logging.debug( f'Quitting {self.nickname} because their server does not exist' ) self.quit('Unknown connection') return self.server = server[0] self.modes = params[9].strip('+') if params[11] == '*': self.cloakhost = params[6] else: self.cloakhost = params[11] if params[12] != '*' and not params[12].replace( '.', '').isdigit() and params[12] is not None: self.ip = Base64toIP(params[12]) else: self.ip = params[12] self.realname = ' '.join(params[13:])[1:] self.registered = True TKL.check(self, self.origin, self, 'Z') TKL.check(self, self.origin, self, 'G') if len(self.origin.users) > self.origin.maxgusers: self.origin.maxgusers = len(self.origin.users) watch_notify = iter([ user for user in self.origin.users if self.nickname.lower() in [x.lower() for x in user.watchlist] ]) for user in watch_notify: user.sendraw( RPL.LOGON, '{} {} {} {} :logged online'.format( self.nickname, self.ident, self.cloakhost, self.signon)) # msg = '*** Remote client connecting: {} ({}@{}) {{{}}} [{}{}]'.format(self.nickname, self.ident, self.hostname, str(self.cls), 'secure' if 'z' in self.modes else 'plain', ' '+self.socket.cipher()[0] if self.ssl else '') # self.server.snotice('C', msg) except Exception as ex: logging.exception(ex) # logging.info('New user class {} successfully created'.format(self)) gc.collect() except Exception as ex: logging.exception(ex)
def __del__(self): # pass logging.debug('User {} closed'.format(self))