def run(self): user = self.user blacklist = self.blacklist # logging.info('Looking up DNSBL query on {}: {}'.format(self.blacklist, user.ip)) try: result = socket.gethostbyname(RevIP(user.ip) + '.' + blacklist) reason = 'Your IP is blacklisted by {}'.format(blacklist) if user.ip not in user.server.dnsblCache: user.server.dnsblCache[user.ip] = {} user.server.dnsblCache[user.ip]['bl'] = blacklist user.server.dnsblCache[user.ip]['ctime'] = int(time.time()) msg = '*** DNSBL match for IP {}: {} [nick: {}]'.format( user.ip, blacklist, user.nickname) user.server.snotice('d', msg) if user in user.server.users: user.sendraw(RPL.TEXT, f"* :{reason}") user.sendbuffer = '' user.recvbuffer = '' user.quit(reason) except socket.gaierror: # socket.gaierror: [Errno -2] Name or service not known -> no match. pass except Exception as ex: logging.exception(ex)
def history_msg(self, localServer, channel, msg): try: if chmode not in channel.modes: return limit = channel.msg_backlog['limit'] # Max lines to remember. expire = channel.msg_backlog['expire'] * 60 while len(channel.msg_backlog['lines']) >= limit: channel.msg_backlog['lines'] = channel.msg_backlog['lines'][1:] utc_time = float(datetime.utcnow().strftime("%s.%f")) * 10 data = (self.fullmask(), utc_time, msg) if channel.msg_backlog['lines']: channel.msg_backlog['previous_last'] = channel.msg_backlog[ 'lines'][-1] channel.msg_backlog['lines'].append(data) if channel not in localServer.m_history: localServer.m_history[channel] = {} for user in channel.users: if user not in localServer.m_history[channel]: localServer.m_history[channel][user] = {} localServer.m_history[channel][user]['last'] = None localServer.m_history[channel][user]['replay_time'] = int( time.time()) localServer.m_history[channel][user]['last'] = data for user in [ user for user in list(localServer.m_history[channel]) if user not in channel.users ]: del localServer.m_history[channel][user] except Exception as ex: logging.exception(ex)
def chmode_H2(self, localServer, channel, modebuf, parambuf, action, modebar, param): try: if action == '+': limit = int(param.split(':')[0]) if limit > 25: limit = 25 expire = int(param.split(':')[1]) if expire > 10080: expire = 10080 param = '{}:{}'.format(limit, expire) if not hasattr(channel, 'msg_backlog'): channel.msg_backlog = {} elif 'lines' in channel.msg_backlog: if limit == channel.msg_backlog[ 'limit'] and expire == channel.msg_backlog['expire']: return channel.msg_backlog['limit'] = limit channel.msg_backlog['expire'] = expire channel.msg_backlog['lines'] = [] # modebuf.append(modebar) # parambuf.append(param) # channel.modes += modebar # Actually we should also add the chan_param here. BUT MEH F**K IT. # return 0 else: channel.msg_backlog = {} except Exception as ex: logging.exception(ex)
def remove_mode(self, user, channel, param=None): # Module hooks. process = self.pre_hook(user, channel, param, action='-') if not process and process is not None: return 0 # This should be handled by pre_* mode hooks for callable in [callable for callable in self.ircd.hooks if callable[0].lower() == 'modechar_del']: try: result = callable[2](self.ircd, self, channel, self.mode) if not result and result is not None: logging.debug('Mode remove denied by module: {}'.format(callable)) return 0 except Exception as ex: logging.exception(ex) if self.mode in self.ircd.chan_params[channel]: logging.debug(f'Forgetting param for {self.mode}: {self.ircd.chan_params[channel][self.mode]}') del self.ircd.chan_params[channel][self.mode] channel.modes = channel.modes.replace(self.mode, '') if ((param and param in self.parambuf) and self.mode in self.modebuf) or (not param and self.mode in self.modebuf): if param: logging.debug(f'Mode conflict: mode "{self.mode}" and param "{param}" are already stored in the buffer.') else: logging.debug(f'Mode conflict: mode "{self.mode}"is already stored in the buffer.') logging.debug(f'A module probably already handled it. Not adding again.') else: self.modebuf.append(self.mode) if param: self.parambuf.append(param) logging.debug('Channel mode "{}" removed from {} (param: {})'.format(self.mode, channel, param)) return 1
def resolve_ip(self): ip = self.ip if self.ip.replace('.', '').isdigit() else self.ip[7:] try: ip_resolved = socket.gethostbyaddr(ip)[0] except socket.herror: # Not a typo. ip_resolved = ip except Exception as ex: logging.exception(ex) 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 + '@' + ip_resolved): deny_except = True break if not deny_except: for entry in self.server.deny: if match(entry, self.ident + '@' + ip_resolved): self.server.deny_cache[ip] = {} self.server.deny_cache[ip]['ctime'] = int(time.time()) self.server.deny_cache[ip]['reason'] = self.server.deny[ entry] if self.server.deny[entry] else '' if self.server.deny_cache[ip]['reason']: self.server.notice( self, '* Connection denied: {}'.format( self.server.deny_cache[ip]['reason'])) return self.quit( 'Your host matches a deny block, and is therefore not allowed.' )
def savewhowas(self, ircd): try: if type(self).__name__ == 'Server' or not self.registered: return if not hasattr(ircd, 'whowas'): ircd.whowas = {} if self.nickname not in ircd.whowas: ircd.whowas[self.nickname] = [] whowasInfo = {self.nickname: {}} whowasInfo[self.nickname]['ident'] = self.ident whowasInfo[self.nickname]['cloakhost'] = self.cloakhost whowasInfo[self.nickname]['realname'] = self.realname whowasInfo[self.nickname]['hostname'] = self.hostname whowasInfo[self.nickname]['ip'] = self.ip whowasInfo[self.nickname]['server'] = self.server.hostname whowasInfo[self.nickname]['signoff'] = int(time.time()) ircd.whowas[self.nickname].append(whowasInfo[self.nickname]) if len(ircd.whowas[self.nickname]) > 12: del ircd.whowas[self.nickname][0] for nick in dict(ircd.whowas): info = list(ircd.whowas[nick]) for data in info: signoff = data['signoff'] if int(time.time() ) - signoff > 3600 * 24 * 30: # 1 month expire? ircd.whowas[nick].remove(data) except Exception as ex: logging.exception(ex)
def syncData(localServer, newServer, selfRequest=True, local_only=False): if localServer.users: syncUsers(localServer, newServer, local_only=local_only) if localServer.channels: syncChannels(localServer, newServer) try: for type in localServer.tkl: for entry in localServer.tkl[type]: if not localServer.tkl[type][entry]['global']: continue mask = '{} {}'.format(entry.split('@')[0], entry.split('@')[1]) setter = localServer.tkl[type][entry]['setter'] try: source = list( filter(lambda s: s.hostname == setter, localServer.servers)) if source: if source[0].hostname == newServer.hostname or source[ 0].introducedBy == newServer: continue except: pass expire = localServer.tkl[type][entry]['expire'] ctime = localServer.tkl[type][entry]['ctime'] reason = localServer.tkl[type][entry]['reason'] data = ':{} TKL + {} {} {} {} {} :{}'.format( localServer.sid, type, mask, setter, expire, ctime, reason) newServer._send(data) except Exception as ex: logging.exception(ex) logging.info('{}Server {} is done syncing to {}, sending EOS.{}'.format( Y, localServer.hostname, newServer.hostname, W)) newServer._send(':{} EOS'.format(localServer.sid)) if newServer not in localServer.syncDone: cloakhash = localServer.conf['settings']['cloak-key'] cloakhash = hashlib.md5(cloakhash.encode('utf-8')).hexdigest() data = ':{} NETINFO {} {} {} MD5:{} {} 0 0 :{}'.format( localServer.sid, localServer.maxgusers, int(time.time()), localServer.versionnumber.replace('.', ''), cloakhash, localServer.creationtime, localServer.name) newServer._send(data) localServer.syncDone.append(newServer) if not hasattr(newServer, 'outgoing') or not newServer.outgoing: newServer._send(':{} PONG {} {}'.format(localServer.sid, newServer.hostname, localServer.hostname)) else: newServer._send(':{} PING {} {}'.format(localServer.sid, localServer.hostname, newServer.hostname)) return
def HookToCore(self, callables, reload=False): try: # Tuple: callables, commands, user_modes, channel_modes, hooks, support, api, module hooks = [] commands = callables[0] user_modes = callables[1] channel_modes = callables[2] module_hooks = callables[3] support = callables[4] api = callables[5] module = callables[6] for c in commands + user_modes + channel_modes: # c = Command/Mode class m = c() m.module = module m.ircd = self m.register() if hasattr(module, 'init'): module.init(self) for callable in [ callable for callable in api if callable not in hooks ]: hooks.append(callable) for a in [a for a in callable.api if a not in self.api]: api_cmd = a[0] api_host = None if len(a) < 2 else a[1] api_password = None if len(a) < 3 else a[2] info = (api_cmd, callable, api_host, api_password, module) self.api.append( info ) # (cmd, callable, params, req_modes, req_flags, req_class, module) # logging.info('Hooked API "{}" (host: {}, password: {}) to function {}'.format(api_cmd, api_host, api_password, callable)) hooks = [] for callable in [ callable for callable in module_hooks if callable not in hooks ]: hooks.append(callable) for h in [h for h in callable.hooks if h]: # print(callable) info = (h[0], h[1], callable, module) self.hooks.append(info) # logging.info('Hooked {}'.format(info)) update_support(self) except Exception as ex: logging.exception(ex) return ex
def handle(self, cmd, data, kwargs=None): p = ' '.join([':' + self.sid, cmd.upper(), data]).split() try: c = next((x for x in self.localServer.command_class if cmd.upper() in list(x.command)), None) if c: if c.check(self, p): if kwargs: c.execute(self, p, **kwargs) else: c.execute(self, p) except Exception as ex: logging.exception(ex)
def run(self): try: exists = list( filter(lambda s: s.hostname == self.name, self.localServer.servers + [self.localServer])) if exists: logging.error( 'Server {} already exists on this network'.format( exists[0].hostname)) return serv = None if not self.host.replace('.', '').isdigit(): self.host = socket.gethostbyname(self.host) self.socket = socket.socket() if self.tls: self.socket = self.localServer.default_sslctx.wrap_socket( self.socket, server_side=False) logging.info('Wrapped outgoing socket {} in TLS'.format( self.socket)) from ircd import Server serv = Server(origin=self.localServer, serverLink=True, sock=self.socket, is_ssl=self.tls) serv.hostname = self.name serv.ip = self.host serv.port = self.port serv.outgoing = True if self.origin or self.autoLink: self.localServer.linkrequester[serv] = self.origin self.socket.settimeout(10) self.socket.connect((self.host, self.port)) selfIntroduction(self.localServer, serv, outgoing=True) if serv not in self.localServer.introducedTo: self.localServer.introducedTo.append(serv) except Exception as ex: logging.exception(ex) # Outgoing link timed out. if serv: serv.quit(str(ex))
def handle(self, command, data=None, params=None): recv = '{} {}'.format(command, data if data else '') parsed = self.parse_command(recv) command = command.split()[0].lower() localServer = self.server if self.socket else self.origin c = next((x for x in localServer.command_class if command.upper() in list(x.command)), None) if c: try: if c.check(self, parsed): if not params: c.execute(self, parsed) else: c.execute(self, parsed, **params) except Exception as ex: logging.exception(ex)
def syncUsers(localServer, newServer, local_only): try: totalServers = [localServer] if not local_only: totalServers.extend(localServer.servers) for server in [ server for server in totalServers if server != newServer and server.introducedBy != newServer and newServer.introducedBy != server and server not in newServer.syncDone and newServer.socket ]: newServer.syncDone.append(server) logging.info('{}Syncing info from {} to {}{}'.format( Y, server.hostname, newServer.hostname, W)) for u in [ u for u in localServer.users if u.server == server and u.registered ]: ip = IPtoBase64(u.ip) if u.ip.replace('.', '').isdigit() else u.ip if not ip: ip = '*' hopcount = str(u.server.hopcount + 1) data = ':{} UID {} {} {} {} {} {} 0 +{} {} {} {} :{}'.format( server.sid, u.nickname, hopcount, u.signon, u.ident, u.hostname, u.uid, u.modes, u.cloakhost, u.cloakhost, ip, u.realname) newServer._send(data) if u.fingerprint: data = 'MD client {} certfp :{}'.format( u.uid, u.fingerprint) newServer._send(':{} {}'.format(server.sid, data)) if u.operaccount: data = 'MD client {} operaccount :{}'.format( u.uid, u.operaccount) newServer._send(':{} {}'.format(server.sid, data)) if u.snomasks: newServer._send(':{} BV +{}'.format(u.uid, u.snomasks)) if 'o' in u.modes: for line in u.swhois: newServer._send(':{} SWHOIS {} :{}'.format( server.sid, u.uid, line)) if u.away: newServer._send(':{} AWAY :{}'.format(u.uid, u.away)) except Exception as ex: logging.exception(ex)
def snotice(self, sno, msg, sync=True, source=None, local=False): localServer = self.localServer try: if sno: if sno in localServer.snos: flags = localServer.snos[sno][1] # print('Flags for {}: {}'.format(sno, flags)) users = list( filter( lambda u: 'o' in u.modes and 's' in u.modes and sno in u. snomasks, localServer.users)) for user in users: try: if sno in localServer.conf['opers'][ user.operaccount]['ignore']['snomask']: for m in localServer.conf['opers'][ user.operaccount]['ignore']['snomask'][sno]: for word in msg.split(): if match( m, word ) and user in users and user.server == localServer: users.remove(user) break except Exception as ex: pass for user in [user for user in users if user.socket]: if source: displaySource = source.hostname else: displaySource = self.hostname user._send(':{} NOTICE {} :{}'.format(displaySource, user.nickname, msg)) localsno = ['d', 'j', 't', 'G'] # I removed 's' from localsno. See you soon. if sno not in localsno and sync and not local: if sno == 'c': sno = 'C' data = '@{} Ss {} :{}'.format(self.hostname, sno, msg) localServer.new_sync(localServer, self, data) except Exception as ex: logging.exception(ex)
def setinfo(self, info, t='', source=None): try: if not info or not type: return if not source: return logging.error('No source provided in setinfo()!') if type(source) == str or type(source).__name__ != 'Server': return logging.error( 'Wrong source type provided in setinfo(): {}'.format( source)) if t not in ['host', 'ident']: return logging.error( 'Incorrect type received in setinfo(): {}'.format(t)) valid = 'abcdefghijklmnopqrstuvwxyz0123456789.-' for c in str(info): if c.lower() not in valid: info = info.replace(c, '') if not info: return updated = [] if self.registered: for user in iter([ user for user in self.ircd.users if 'chghost' in user.caplist and user not in updated and user.socket ]): common_chan = list( filter(lambda c: user in c.users and self in c.users, self.ircd.channels)) if not common_chan: continue user._send(':{} CHGHOST {} {}'.format( self.fullmask(), info if t == 'ident' else self.ident, info if t == 'host' else self.cloakhost)) updated.append(user) data = ':{} {} {}'.format( self.uid, 'SETHOST' if t == 'host' else 'SETIDENT', info) self.ircd.new_sync(self.ircd, source, data) if t == 'host': self.cloakhost = info elif t == 'ident': self.ident = info except Exception as ex: logging.exception(ex)
def selfIntroduction(localServer, newServer, outgoing=False): try: if newServer not in localServer.introducedTo: if outgoing: destPass = localServer.conf['link'][newServer.hostname]['pass'] newServer._send(':{} PASS :{}'.format(localServer.sid, destPass)) info = [] for row in localServer.server_support: value = localServer.support[row] info.append('{}{}'.format( row, '={}'.format(value) if value else '')) newServer._send(':{} PROTOCTL EAUTH={} SID={} {}'.format( localServer.sid, localServer.hostname, localServer.sid, ' '.join(info))) newServer._send( ':{} PROTOCTL NOQUIT EAUTH SID NICKv2 CLK SJOIN SJOIN2 UMODE2 VL SJ3 TKLEXT TKLEXT2 NICKIP ESVID EXTSWHOIS' .format(localServer.sid)) version = 'P{}-{}'.format( localServer.versionnumber.replace('.', ''), localServer.sid) local_modules = [m.__name__ for m in localServer.modules] modlist = [] for entry in local_modules: totlen = len(' '.join(modlist)) if totlen >= 400: newServer._send('MODLIST :{}'.format(' '.join(modlist))) modlist = [] modlist.append(entry) if modlist: newServer._send('MODLIST :{}'.format(' '.join(modlist))) if outgoing: newServer._send( f':{localServer.sid} SID {localServer.hostname} 1 {localServer.sid} :{localServer.name}' ) else: newServer._send('SERVER {} 1 :{} {}'.format( localServer.hostname, version, localServer.name)) logging.info( '{}Introduced myself to {}. Expecting remote sync sequence...{}' .format(Y, newServer.hostname, W)) localServer.introducedTo.append(newServer) except Exception as ex: logging.exception(ex)
def set_mode(self, user, channel, param=None): process = self.pre_hook(user, channel, param, action='+') if not process and process is not None: # Assume the module handled everything correctly. logging.debug(f'Mode "{self.mode}" processing blocked by pre_hook(). We assume the module handled everything correctly.') return 0 for callable in [callable for callable in self.ircd.hooks if callable[0].lower() == 'modechar_add']: try: result = callable[2](self.ircd, self, channel, self.mode) if not result and result is not None: logging.debug('Mode set denied by module: {}'.format(callable)) return 0 except Exception as ex: logging.exception(ex) if self.type in [0, 1, 2] and param: if self.mode in self.ircd.core_chmodes or self.type == 2: # Type 2 modes update when being set. logging.debug('Storing param of {}: {}'.format(self.mode, param)) self.ircd.chan_params[channel][self.mode] = param elif self.mode not in self.ircd.chan_params[channel]: logging.debug('2 Storing param of {}: {}'.format(self.mode, param)) self.ircd.chan_params[channel][self.mode] = param if ((param and param in self.parambuf) and self.mode in self.modebuf) or (not param and self.mode in self.modebuf): if param: logging.debug(f'Mode conflict: mode "{self.mode}" and param "{param}" are already stored in the buffer.') else: logging.debug(f'Mode conflict: mode "{self.mode}"is already stored in the buffer.') logging.debug('A module probably already handled it. Not adding again.') else: self.modebuf.append(self.mode) if param: self.parambuf.append(param) if self.mode not in channel.modes: channel.modes += self.mode logging.debug('Channel mode "{}" set on {} (param: {})'.format(self.mode, channel, param)) else: logging.debug('Channel mode "{}" updated on {} (param: {})'.format(self.mode, channel, param)) return 1
def execute(self, client, recv): source = list(filter(lambda s: s.sid == recv[0][1:], self.ircd.servers)) if not source: logging.error('ERROR: could not find server for {}'.format( recv[0][1:])) return source = source[0] if source.eos: logging.error('ERROR: remote server sent EOS twice!') return self.ircd.new_sync(self.ircd, client, ' '.join(recv)) for server in [server for server in self.ircd.servers if server.eos]: data = ':{} PONG {} {}'.format(source.sid, source.hostname, server.hostname) server._send(data) data = ':{} PONG {} {}'.format(server.sid, server.hostname, source.hostname) source._send(data) logging.info('{}EOS received by: {}{}'.format(Y, source.hostname, W)) if source.socket: for callable in [ callable for callable in self.ircd.hooks if callable[0].lower() == 'server_link' ]: try: callable[2](client, self.ircd, source) except Exception as ex: logging.exception(ex) for s in [s for s in self.ircd.servers if s.introducedBy == source]: logging.info('Also setting EOS for {} to be true'.format(s)) s.eos = True if source.hostname.lower() in self.ircd.pendingLinks: self.ircd.pendingLinks.remove(source.hostname.lower()) source.eos = True if source in self.ircd.sync_queue: for e in self.ircd.sync_queue[source]: logging.info('Sending queued data to {}: {}'.format(source, e)) source._send(e)
def connectTo(self, ircd, name, autoLink=False): try: host, port = ircd.conf['link'][name]['outgoing']['host'], ircd.conf[ 'link'][name]['outgoing']['port'] # If the host is local, and you are listening for servers on the port, do not connect to yourself. if host in [ '127.0.0.1', '0.0.0.0', 'localhost' ] and (port in ircd.conf['listen'] and ircd.conf['listen'][str(port)]['options'] == 'servers'): return pswd = ircd.conf['link'][name]['pass'] is_ssl = False if 'options' in ircd.conf['link'][name] and ( 'tls' in ircd.conf['link'][name]['options'] or 'ssl' in ircd.conf['link'][name]['options']): is_ssl = True l = Link(self, ircd, name, host, port, pswd, is_ssl, autoLink) l.start() except Exception as ex: logging.exception(ex)
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 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 validate_server_info(self, client): try: ip, port = client.socket.getpeername() ip2, port2 = client.socket.getsockname() if client.hostname not in self.ircd.conf['link']: error = 'Error connecting to server {}[{}:{}]: no matching link configuration'.format( self.ircd.hostname, ip2, port2) client._send(':{} ERROR :{}'.format(self.ircd.sid, error)) client.quit('no matching link configuration') logging.info( f'Link denied for {client.hostname}: server not found in conf') return 0 client.cls = self.ircd.conf['link'][client.hostname]['class'] logging.info('{}Class: {}{}'.format(G, client.cls, W)) if not client.cls: error = 'Error connecting to server {}[{}:{}]: no matching link configuration'.format( self.ircd.hostname, ip2, port2) client._send(':{} ERROR :{}'.format(self.ircd.sid, error)) client.quit('no matching link configuration') logging.info( f'Link denied for {client.hostname}: unable to assign class to connection' ) return 0 totalClasses = list( filter(lambda s: s.cls == client.cls, self.ircd.servers)) if len(totalClasses) > int(self.ircd.conf['class'][client.cls]['max']): client.quit('Maximum server connections for this class reached') logging.info( f'Link denied for {client.hostname}: max connections for this class' ) return 0 if client.linkpass: if client.linkpass != self.ircd.conf['link'][ client.hostname]['pass']: error = 'Error connecting to server {}[{}:{}]: no matching link configuration'.format( self.ircd.hostname, ip2, port2) client._send(':{} ERROR :{}'.format(self.ircd.sid, error)) client.quit('no matching link configuration') logging.info( f'Link denied for {client.hostname}: incorrect password') return 0 if not match( self.ircd.conf['link'][client.hostname]['incoming']['host'], ip): error = 'Error connecting to server {}[{}:{}]: no matching link configuration'.format( self.ircd.hostname, ip2, port2) client._send(':{} ERROR :{}'.format(self.ircd.sid, error)) client.quit('no matching link configuration') logging.info( f'Link denied for {client.hostname}: incoming IP does not match conf' ) return 0 if client.hostname not in self.ircd.conf['settings']['ulines']: for cap in [cap.split('=')[0] for cap in self.ircd.server_support]: if cap in client.protoctl: logging.info( 'Cap {} is supported by both parties'.format(cap)) else: client._send( ':{} ERROR :Server {} is missing support for {}'. format(client.sid, client.hostname, cap)) client.quit('Server {} is missing support for {}'.format( client.hostname, cap)) logging.info( f'Link denied for {client.hostname}: no matching CAPs') return 0 if client.linkpass and client.linkpass != self.ircd.conf['link'][ client.hostname]['pass']: msg = 'Error connecting to server {}[{}:{}]: no matching link configuration'.format( client.hostname, ip, port) error = 'Error connecting to server {}[{}:{}]: no matching link configuration'.format( self.ircd.hostname, ip2, port2) if client not in self.ircd.linkrequester: client._send('ERROR :{}'.format(error)) elif self.ircd.linkrequester[client]['user']: self.ircd.linkrequester[client]['user'].send( 'NOTICE', '*** {}'.format(msg)) client.quit('no matching link configuration', silent=True) logging.info( f'Link denied for {client.hostname}: incorrect password') return 0 return 1 except Exception as ex: logging.exception(ex) return 0
def execute(self, client, recv): if not hasattr(client, 'protoctl'): client.protoctl = [] try: for p in [p for p in recv[2:] if p not in client.protoctl]: try: cap = p.split('=')[0] param = None client.protoctl.append(cap) if '=' in p: param = p.split('=')[1] if cap == 'EAUTH' and param: client.hostname = param.split(',')[0] logging.info('Hostname set from EAUTH: {}'.format( client.hostname)) if [ s for s in self.ircd.servers + [self.ircd] if s.hostname.lower() == client.hostname.lower() and s != client ]: ip, port = client.socket.getpeername() error = 'Error connecting to server {}[{}:{}]: server already exists on remote network'.format( self.ircd.hostname, ip, port) client._send(':{} ERROR :{}'.format( self.ircd.sid, error)) client.quit( 'server already exists on this network') return elif cap == 'SID' and param: for server in [ server for server in self.ircd.servers if server.sid == param and server != client ]: client._send( ':{} ERROR :SID {} is already in use on that network' .format(self.ircd.sid, param)) client.quit( 'SID {} is already in use on that network'. format(param)) return client.sid = param elif cap == 'CHANMODES': remote_modes = param.split(',') local_modes = self.ircd.chmodes_string.split(',') missing_modes = [] for n in self.ircd.channel_modes: for m in [ m for m in remote_modes[n] if m not in local_modes[n] ]: missing_modes.append(m) if missing_modes: # The error is outgoing and will be displayed on the REMOTE server. ip, port = client.socket.getpeername() error = 'Link denied for {}[{}:{}]: they are missing channel modes: {}'.format( client.hostname, ip, port, ', '.join(missing_modes)) client._send(':{} ERROR :{}'.format( self.ircd.sid, error)) client.quit( 'we are missing channel modes: {}'.format( ', '.join(missing_modes))) return elif cap == 'EXTBAN': remote_prefix = param[0] remote_ban_types = param.split(',')[1] local_prefix = None if 'EXTBAN' in self.ircd.support: local_prefix = self.ircd.support['EXTBAN'][0] if remote_prefix != local_prefix: ip, port = client.socket.getpeername() error = 'Link denied for {}[{}:{}]: extban prefixes are not the same. We have: {} but they have: {}'.format( client.hostname, ip, port, remote_prefix, local_prefix) client._send( ':{} ERROR :extban prefixes are not the same. We have: {} but they have: {}' .format(self.ircd.sid, remote_prefix, local_prefix)) client.quit( 'extban prefixes are not the same. We have: {} but they have: {}' .format(local_prefix, remote_prefix)) return local_ban_types = self.ircd.support['EXTBAN'][1:] logging.info( 'We have bantypes: {}'.format(local_ban_types)) missing_ext_types = [] for m in [ m for m in remote_ban_types if m not in local_ban_types ]: missing_ext_types.append(m) if missing_ext_types: error = 'Link denied for {}[{}:{}]: they are missing ext bans: {}'.format( client.hostname, ip, port, ', '.join(missing_ext_types)) client._send(':{} ERROR :{}'.format( self.ircd.sid, error)) client.quit('we are missing ext bans: {}'.format( ', '.join(missing_ext_types))) return except Exception as ex: logging.exception(ex) client.quit(str(ex)) logging.info( '{}Added PROTOCTL support for {} for server {}{}'.format( P, p, client, W)) except Exception as ex: logging.exception(ex)
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 handle_recv(self): try: while self.recvbuffer.find('\n') != -1: recv = self.recvbuffer[:self.recvbuffer.find('\n')] self.recvbuffer = self.recvbuffer[self.recvbuffer.find('\n') + 1:] recv = recv.rstrip(' \n\r') if not recv: continue ircd = self.server command = recv.split()[0].lower() self.ping = int(time.time()) if not hasattr(self, 'flood_safe') or not self.flood_safe: self.flood_penalty += 1000 + len(recv) check_flood(ircd, self) if not self.flood_penalty_time: self.flood_penalty_time = int(time.time()) dont_parse = ['topic', 'swhois', 'prop'] if command in dont_parse: parsed = recv.split(' ') else: parsed = self.parse_command(recv) pre_reg_cmds = [ 'nick', 'user', 'pass', 'pong', 'cap', 'starttls', 'webirc' ] if not self.registered and self.cls and not self.server_pass_accepted and 'password' in ircd.conf[ 'allow'][self.cls] and command not in ['pass', 'cap']: return self.quit('Password required') ignore = [ 'ping', 'pong', 'ison', 'watch', 'who', 'privmsg', 'notice', 'ns', 'cs', 'nickserv', 'chanserv', 'id', 'identify', 'login', 'auth' ] # ignore = [] if command not in ignore: pass # Looking for API calls. if not self.registered: for callable in [ callable for callable in self.server.api if callable[0].lower() == command ]: api_func = callable[1] api_host = callable[2] api_password = callable[3] if api_host and not match(api_host, self.ip): self.quit('API', api=True) break if api_password and recv[1] != api_password: self.quit('API', api=True) break api_func(self, ircd, parsed) self.quit('API', api=True) return # print('ik ga zo slaaaaaapen maar jij bent ernie?') if type( self ).__name__ == 'User' and command not in pre_reg_cmds and not self.registered: return self.sendraw(ERR.NOTREGISTERED, 'You have not registered') if command == 'pong': if self in self.server.pings: ping = recv.split()[1] if ping.startswith(':'): ping = ping[1:] if self.server.pings[self] == ping: del self.server.pings[self] self.validping = True if self.ident != '' and self.nickname != '*' and ( self.cap_end or not self.sends_cap): self.welcome() else: return self.quit('Unauthorized connection') try: cmd = importlib.import_module('cmds.cmd_' + command.lower()) getattr(cmd, 'cmd_' + command.upper())(self, ircd, parsed) continue except ImportError: try: alias = ircd.conf['aliases'] if alias[command.lower()]['type'] == 'services': service = list( filter( lambda u: u.nickname == alias[ command.lower()]['target'] and 'services' in ircd.conf['settings'] and u. server.hostname == ircd.conf['settings'][ 'services'], ircd.users)) if not service: return self.sendraw( ERR.SERVICESDOWN, ':Services are currently down. Please try again later.' ) data = '{} :{}'.format( alias[command.lower()]['target'], ' '.join(recv.split()[1:])) self.handle('PRIVMSG', data) continue except KeyError: pass # pre_command hook. allow = 1 for callable in [ callable for callable in self.server.hooks if callable[0].lower() == 'pre_command' and callable[1].lower() == command.lower() ]: try: allow = callable[2](self, ircd, parsed) except Exception as ex: logging.exception(ex) if not allow and allow is not None: continue false_cmd = True c = next((x for x in ircd.command_class if command.upper() in list(x.command)), None) if c: false_cmd = False if c.check(self, parsed): c.execute( self, parsed ) # <--- instant reply from /stats u (where psutil.Process() is being called) # threading.Thread(target=c.execute, args=([self, parsed])).start() # ~1 second delay in /stats u if false_cmd: self.sendraw(ERR.UNKNOWNCOMMAND, '{} :Unknown command'.format(command.upper())) except Exception as ex: logging.exception(ex)