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 execute(self, client, recv): if type(client).__name__ == 'Server': # Command USER is not being used by any server. Assume normal user. return client.quit('This port is for servers only') if client.ident: return client.sendraw(462, ':You may not reregister') if 'nmap' in ''.join(recv).lower(): return client.quit('Connection reset by peer') ident = str(recv[1][:12]).strip() realname = recv[4][:48] valid = "abcdefghijklmnopqrstuvwxyz0123456789-_" for c in ident: if c.lower() not in valid: ident = ident.replace(c, '') block = 0 for cls in iter([ cls for cls in self.ircd.conf['allow'] if cls in self.ircd.conf['class'] ]): t = self.ircd.conf['allow'][cls] isMatch = False if 'ip' in t: clientmask = '{}@{}'.format(client.ident, client.ip) isMatch = match(t['ip'], clientmask) if 'hostname' in t and not isMatch: # Try with hostname. IP has higher priority. clientmask = '{}@{}'.format(client.ident, client.hostname) isMatch = match(t['hostname'], clientmask) if isMatch: if 'options' in t: if 'ssl' in t['options'] and not client.ssl: continue client.cls = cls if 'block' in t: for entry in t['block']: clientmask_ip = '{}@{}'.format(client.ident, client.ip) clientmask_host = '{}@{}'.format( client.ident, client.hostname) block = match(entry, clientmask_ip) or match( entry, clientmask_host) if block: logging.info('Client {} blocked by {}: {}'.format( client, cls, entry)) break break client.ident = ident client.realname = realname if client.nickname != '*' and client.validping and ( client.cap_end or not client.sends_cap): client.welcome()
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 join(self, ircd, channel): mod = next((m for m in ircd.channel_mode_class if m.mode == chmode), None) if not mod: logging.error(f"Module for channele mode '{chmode}' not found.") return if not hasattr(channel, mod.list_name): setattr(channel, mod.list_name, {}) last_level = 0 total_modes, total_params = '+', '' for entry in channel.whitelist: level = int(entry.split(':')[0]) if level > last_level: # Found higher level. last_level = level mask = entry.split(':')[1] modes = '' if match(mask, self.fullmask()): if level >= 9999: modes += 'oq' elif level >= 10: modes += 'oa' elif level >= 5: modes += 'o' elif level >= 4: modes += 'h' elif level >= 1: modes += 'v' if modes: nicks = '{} '.format(self.nickname) * len(modes) total_modes += modes total_params += nicks if total_modes and total_params: ircd.handle('MODE', '{} {} {}'.format(channel.name, total_modes, total_params))
def execute(self, client, recv): if type(client).__name__ == 'Server': target = list(filter(lambda u: u.nickname.lower() == recv[2].lower() or u.uid.lower() == recv[2].lower(), self.ircd.users)) if not target: return S = recv[0][1:] source = [s for s in self.ircd.servers + [self.ircd] if s.sid == S or s.hostname == S] + [u for u in self.ircd.users if u.uid == S or u.nickname == S] if not source: return source = source[0] if type(source).__name__ == 'User': sourceID = source.uid path = source.nickname else: sourceID = source.sid path = source.hostname reason = quitmsg = ' '.join(recv[3:])[1:] quitmsg = '[{}] Global kill by {} ({})'.format(client.hostname, path, reason) if target[0].socket: target[0].sendraw(self.RPL.TEXT, '{}'.format(':[{}] {}'.format(path, reason))) data = ':{} KILL {} :{}'.format(sourceID, target[0].uid, reason) self.ircd.new_sync(self.ircd, client, data) target[0].quit(quitmsg, kill=True) return target = list(filter(lambda c: c.nickname.lower() == recv[1].lower() or c.uid.lower() == recv[1].lower(), self.ircd.users)) if not target: return client.sendraw(self.ERR.NOSUCHNICK, '{} :No such nick'.format(recv[1])) if target[0].server != self.ircd and not client.ocheck('o', 'globalkill'): return client.sendraw(self.ERR.NOPRIVILEGES, ':Permission denied - You do not have the correct IRC Operator privileges') if 'except' in self.ircd.conf and 'kill' in self.ircd.conf['except'] and type(client).__name__ != 'Server': check_host = '{}@{}'.format(target[0].ident, target[0].hostname) for e in self.ircd.conf['except']['kill']: if match(e, check_host): self.ircd.notice(client, '*** User {} matches a kill-except ({}) and cannot be killed'.format(target[0].nickname, e)) return reason = ' '.join(recv[2:]) if reason.startswith(':'): reason = reason[1:] path = client.nickname self.ircd.notice(target[0], '*** You are being disconnected from this server: [{}] ({})'.format(path, reason)) if target[0].socket: target[0].sendraw(self.RPL.TEXT, '{}'.format(':[{}] {}'.format(path, reason))) msg = '*** Received kill msg for {} ({}@{}) Path {} ({})'.format(target[0].nickname, target[0].ident, target[0].hostname, path, reason) self.ircd.snotice('k', msg) quitmsg = '[{}] {} kill by {} ({})'.format(client.server.hostname, 'Local' if target[0].server == self.ircd else 'Global', client.nickname, reason) # data = ':{} KILL {} :{}'.format(client.uid, target[0].uid, quitmsg) data = ':{} KILL {} :{}'.format(client.uid, target[0].uid, reason) self.ircd.new_sync(self.ircd, client.server, data) target[0].quit(quitmsg, kill=True)
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 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 __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 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 'o' in client.modes: return if 'opers' not in self.ircd.conf: client.flood_penalty += 350000 return client.sendraw(self.ERR.NOOPERHOST, ':No O:lines for your host') if recv[1] not in self.ircd.conf['opers']: client.flood_penalty += 350000 client.sendraw(self.ERR.NOOPERHOST, ':No O:lines for your host') msg = '*** Failed oper attempt by {} [{}] ({}@{}): username not found'.format( client.nickname, recv[1], client.ident, client.hostname) return self.ircd.snotice('o', msg) if self.ircd.conf['opers'][recv[1]]['password'].startswith( '$2b$') and len( self.ircd.conf['opers'][recv[1]]['password']) > 58: logging.debug('Detected bcrypt for /oper') password = recv[2].encode('utf-8') ### Bytes password, plain. hashed = self.ircd.conf['opers'][recv[1]]['password'].encode( 'utf-8') ### Bytes password, hashed. if not bcrypt.checkpw(password, hashed): client.flood_penalty += 350000 client.sendraw(self.ERR.NOOPERHOST, ':No O:lines for your host') msg = '*** Failed oper attempt by {} [{}] ({}@{}): incorrect password'.format( client.nickname, recv[1], client.ident, client.hostname) return self.ircd.snotice('o', msg) elif recv[2] != self.ircd.conf['opers'][recv[1]]['password']: client.flood_penalty += 350000 client.sendraw(self.ERR.NOOPERHOST, ':No O:lines for your host') msg = '*** Failed oper attempt by {} [{}] ({}@{}): incorrect password'.format( client.nickname, recv[1], client.ident, client.hostname) return self.ircd.snotice('o', msg) if 'requiremodes' in self.ircd.conf['opers'][recv[1]]: for m in str(self.ircd.conf['opers'][recv[1]]['requiremodes']): if m not in client.modes and m not in '+-': client.flood_penalty += 350000 client.sendraw(self.ERR.NOOPERHOST, ':No O:lines for your host') msg = '*** Failed oper attempt by {} [{}] ({}@{}): mode requirement not met'.format( client.nickname, recv[1], client.ident, client.hostname) return self.ircd.snotice('o', msg) selfhost = client.fullrealhost().split('!')[1] operhost = self.ircd.conf['opers'][recv[1]]['host'] hostMatch = False for host in self.ircd.conf['opers'][recv[1]]['host']: if match(host, selfhost): hostMatch = True break if not hostMatch: client.flood_penalty += 350000 client.sendraw(self.ERR.NOOPERHOST, ':No O:lines for your host') msg = '*** Failed oper attempt by {} [{}] ({}@{}): host does not match'.format( client.nickname, recv[1], client.ident, client.hostname) return self.ircd.snotice('o', msg) operClass = self.ircd.conf['opers'][recv[1]]['class'] totalClasses = list( filter(lambda u: u.server == client.server and u.cls == operClass, client.server.users)) if len(totalClasses) >= int( client.server.conf['class'][operClass]['max']): client.flood_penalty += 350000 client.sendraw(self.ERR.NOOPERHOST, ':No O:lines for your host') msg = '*** Failed oper attempt by {} [{}] ({}@{}): limit reached for their oper class'.format( client.nickname, recv[1], client.ident, client.hostname) return self.ircd.snotice('o', msg) else: client.cls = operClass operclass = self.ircd.conf['opers'][recv[1]]['operclass'] parent = None if 'parent' not in self.ircd.conf['operclass'][ operclass] else self.ircd.conf['operclass'][operclass]['parent'] client.operflags = [] all_flags = [ flag for flag in self.ircd.conf['operclass'][operclass]['flags'] if '|' not in flag ] if parent: all_flags += [ flag for flag in self.ircd.conf['operclass'][parent]['flags'] if '|' not in flag ] for flag in [ flag for flag in all_flags if flag.lower() not in client.operflags ]: client.operflags.append(flag.lower()) # Do not automatically set following modes: gqrzH modes = 'o' + re.sub('[ogqrzH]', '', self.ircd.conf['opers'][recv[1]]['modes']) client.opermodes = '' for m in [m for m in modes if m in self.ircd.user_modes]: client.opermodes += m client.operaccount = recv[1] client.operclass = operclass if 'swhois' in self.ircd.conf['opers'][recv[1]]: client.swhois = [] client.operswhois = self.ircd.conf['opers'][recv[1]]['swhois'] if client.operswhois not in client.swhois: client.swhois.append(client.operswhois[:128]) if 's' in modes: snomasks = '' client.snomasks = '' for snomask in self.ircd.conf['opers'][recv[1]]['snomasks']: if snomask in self.ircd.snomasks and snomask not in client.snomasks: snomasks += snomask if 'operhost' in self.ircd.conf['opers'][ recv[1]] and '@' not in self.ircd.conf['opers'][recv[1]][ 'operhost'] and '!' not in self.ircd.conf['opers'][ recv[1]]['operhost']: client.setinfo(self.ircd.conf['opers'][recv[1]]['operhost'], t='host', source=self.ircd) p = {'override': True} client.handle('MODE', '{} +{} {}'.format( client.nickname, client.opermodes, '+' + snomasks if snomasks else ''), params=p) client.sendraw(self.RPL.YOUREOPER, ':You are now an IRC Operator.') client.flood_penalty = 0 msg = '*** {} ({}@{}) [{}] is now an IRC Operator (+{})'.format( client.nickname, client.ident, client.hostname, client.operaccount, client.opermodes) self.ircd.snotice('o', msg) data = ':{} MD client {} operaccount :{}'.format( self.ircd.sid, client.uid, client.operaccount) self.ircd.new_sync(self.ircd, client.server, data) for line in client.swhois: data = ':{} SWHOIS {} :{}'.format(self.ircd.sid, client.uid, line) self.ircd.new_sync(self.ircd, client.server, data)
def checkExtMatch(type, action, channel, msg): try: if type == 'b': replaceDone, did_replace = False, False tempMsg = msg regex = re.compile( '\x1d|\x1f|\x02|\x12|\x0f|\x16|\x03(?:\d{1,2}(?:,\d{1,2})?)?', re.UNICODE) for ban in [ ban for ban in channel.bans if ban[:2] == '~T' and ban.split(':')[1] == action ]: m = ban.split(':', 2)[2] m = regex.sub('', m) rep_char_block = None try: int(ban.split(':')[3]) > 0 rep_char_block = ban.split(':')[3] except: pass if action == 'block': try: p = re.compile(m) if p.findall(msg): return 1 except: pass char = m.split(':')[0] if rep_char_block and char_repeat(msg, char, rep_char_block): return True block = match( m.lower(), msg.lower()) or m.lower() in msg.lower().split() if not rep_char_block and block: return True if action == 'replace': # This just works, so don't mess it up. m = ban.split(':', 2)[2] if m.startswith(':'): search = ':' + m.split(':')[1] replaceWith = m.split(':', 2)[2] else: search = m.split(':')[0] if m.split(':')[1] != '': replaceWith = m.split(':')[1] else: replaceWith = ':' + m.split(':', 2)[2] did_replace = 0 try: regex_replace = re.sub(search, replaceWith, msg) if regex_replace and regex_replace != msg: did_replace = 1 replaced_msg = regex_replace except: pass if did_replace: replaceDone = True tempMsg = replaced_msg else: for word in msg.split(): replaceWith = replaceWith.replace('_', ' ') word = regex.sub('', word) tempWord = word.lower() if match(search.lower(), tempWord) or search.lower() == tempWord: temp = search.replace('*', '') if word.isupper(): temp = temp.upper() did_replace = True replaceWith = replaceWith.upper() elif not word.islower(): temp = re.search( temp, word, flags=re.IGNORECASE).group() did_replace = True # tempMsg = tempMsg.replace(temp, replaceWith) tempMsg = tempMsg.replace(word, replaceWith) if did_replace: replaceDone = True if replaceDone: return tempMsg except Exception as ex: logging.exception(ex)
def execute(self, client, recv, override=False, sanick=False): if type(client).__name__ == 'Server': sourceServer = client override = True _client = client # If the first param is not a UID, it means a new client is trying to connect. # Closing connection. client = [u for u in self.ircd.users if u.uid == recv[0][1:]] if not client: _client.quit('This port is for servers only') return client = client[0] recv = recv[1:] hook = 'remote_nickchange' else: sourceServer = self.ircd hook = 'local_nickchange' if len(recv) < 2: return client.sendraw(431, ':No nickname given') nick = str(recv[1]).strip() if not override: nick = str(recv[1][:int(self.ircd.nicklen)]).strip() if nick.strip() == '': return client.sendraw(431, ':No nickname given') if nick[0].isdigit(): return client.sendraw( 432, '{} :Erroneous nickname (Invalid: {})'.format(nick, nick[0])) valid = 'abcdefghijklmnopqrstuvwxyz0123456789`^-_[]{}|\\' for c in nick: if c.lower() not in valid and not override: return client.sendraw( 432, '{} :Erroneous nickname (Invalid: {})'.format(nick, c)) if sanick: override = True if client in self.ircd.nickflood and len( self.ircd.nickflood[client]) >= int( self.ircd.conf['settings']['nickflood'].split( ':')[0]) and 'o' not in client.modes and not override: client.flood_penalty += 150000 return client.sendraw( 438, '{} :Nick change too fast. Please wait a while before attempting again.' .format(nick)) inUse = list( filter(lambda u: u.nickname.lower() == nick.lower(), self.ircd.users)) if inUse and nick == client.nickname: # Exact nick. return if inUse and nick.lower() != client.nickname.lower(): return client.sendraw( 433, '{} :Nickname is already in use'.format(nick)) if 'Q' in self.ircd.tkl and not override: for entry in [ entry for entry in self.ircd.tkl['Q'] if entry != '*' ]: if match(entry.split('@')[1].lower(), nick.lower()): client.sendraw( 432, '{} :Erroneous nickname ({})'.format( nick, self.ircd.tkl['Q'][entry]['reason'])) msg = '*** Q:Line Rejection -- Forbidden nick {} from client {} {}'.format( nick, client.ip, '[Current nick: {}]'.format( client.nickname) if client.nickname != '*' else '') return self.ircd.snotice('Q', msg) users = [client] for channel in client.channels: if 'N' in channel.modes and client.chlevel( channel) < 5 and not client.ocheck( 'o', 'override') and not override: return client.sendraw( 447, ':{} Nick changes are not allowed on this channel'.format( channel.name)) for u in channel.users: if u not in users and u != client: users.append(u) if sourceServer == self.ircd: # pre_local_nickchanage success = 1 for callable in [ callable for callable in self.ircd.hooks if callable[0].lower() == 'pre_' + hook ]: try: success = callable[2](client, self.ircd) if not success and success is not None: # None will default to True. break except Exception as ex: logging.exception(ex) if not success: return if client.registered: if client not in self.ircd.nickflood: self.ircd.nickflood[client] = {} self.ircd.nickflood[client][time.time()] = True if client.server == self.ircd and not sanick: msg = '*** {} ({}@{}) has changed their nickname to {}'.format( client.nickname, client.ident, client.hostname, nick) client.server.snotice('N', msg) if sanick and type(sanick).__name__ == 'User': snomsg = '*** {} ({}@{}) used SANICK to change nickname {} to {}'.format( sanick.nickname, sanick.ident, sanick.hostname, client.nickname, nick) self.ircd.snotice('S', snomsg) msg = '*** Your nick has been forcefully changed by {}.'.format( sanick.nickname) self.ircd.handle('NOTICE', '{} :{}'.format(client.nickname, msg)) # Check module hooks for visible_in_channel() all_broadcast = [client] for channel in client.channels: for u in channel.users: if u not in all_broadcast and u != client: all_broadcast.append(u) for u in [u for u in all_broadcast if u != client]: visible = 0 for channel in client.channels: for callable in [ callable for callable in self.ircd.hooks if callable[0].lower() == 'visible_in_channel' ]: try: visible = callable[2](u, self.ircd, client, channel) # logging.debug('Is {} visible for {} on {}? :: {}'.format(client.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: logging.debug( 'User {} is not allowed to see {} on any channel, not sending nickchange.' .format(u.nickname, client.nickname)) all_broadcast.remove(u) client.broadcast(all_broadcast, 'NICK :{}'.format(nick)) self.ircd.new_sync( self.ircd, sourceServer, ':{} NICK {} {}'.format(client.uid, nick, int(time.time()))) watch_notify_offline = [ u for u in self.ircd.users if client.nickname.lower() in [x.lower() for x in u.watchlist] ] watch_notify_online = [ u for u in self.ircd.users if nick.lower() in [x.lower() for x in u.watchlist] ] for watch_user in watch_notify_offline: watch_user.sendraw( 601, '{} {} {} {} :logged offline'.format( client.nickname, client.ident, client.cloakhost, client.signon)) for watch_user in watch_notify_online: watch_user.sendraw( 600, '{} {} {} {} :logged online'.format( nick, client.ident, client.cloakhost, client.signon)) for callable in [ callable for callable in self.ircd.hooks if callable[0].lower() == hook ]: try: callable[2](client, self.ircd) except Exception as ex: logging.exception(ex) old = client.nickname client.nickname = nick if old == '*' and client.ident != '' and client.validping and ( client.cap_end or not client.sends_cap): client.welcome()
def trace_join(self, localServer, channel, **kwargs): try: if 'override' in kwargs: logging.debug('Skipping extban checks: override') return (1, []) overrides = [] invite_override = 0 if self in channel.invites: invite_override = channel.invites[self]['override'] if invite_override: overrides.append('b') overrides.append('i') if hasattr(localServer, 'geodata') and self.ip in localServer.geodata: # Exceptions. if 'b' not in overrides: for e in [e for e in channel.excepts if e.startswith('~C')]: # Country except. country = e.split(':')[1] if match(country.lower(), localServer.geodata[self.ip] ['country'].lower()) or match( country.lower(), localServer.geodata[self.ip] ['countryCode'].lower()): overrides.append('b') for e in [e for e in channel.excepts if e.startswith('~i')]: # ISP except. exceptIsp = e.split(':')[1] if localServer.geodata[ self.ip]['isp'].lower() == exceptIsp.lower( ) or invite_override and 'b' not in overrides: overrides.append('b') for b in [b for b in channel.bans if b.startswith('~i')]: # ISP ban. isp = b.split(':')[1] if match(isp.lower(), localServer.geodata[self.ip] ['isp'].lower()) and not checkMatch( self, localServer, 'e', channel) and 'b' not in overrides: self.sendraw( 474, '{} :Cannot join channel (+b)'.format(channel.name)) return 0, overrides for b in [b for b in channel.bans if b.startswith('~C')]: # Country ban. country = b.split(':')[1] if (match(country.lower(), localServer.geodata[self.ip]['country'].lower()) or match( country.lower(), localServer.geodata[self.ip] ['countryCode'].lower())) and not checkMatch( self, localServer, 'e', channel) and 'b' not in overrides: self.sendraw( 474, '{} :Cannot join channel (+b)'.format(channel.name)) return 0, overrides for b in [b for b in channel.bans if b.startswith('~i')]: # ISP ban. isp = b.split(':')[1] if match(isp.lower(), localServer.geodata[self.ip] ['isp'].lower()) and not checkMatch( self, localServer, 'e', channel) and 'b' not in overrides: self.sendraw( 474, '{} :Cannot join channel (+b)'.format(channel.name)) return 0, overrides for i in channel.invex: if i.startswith('~C'): country = i.split(':')[1].lower() if 'i' in channel.modes and match( country, localServer.geodata[self.ip] ['country'].lower()) or match( country, localServer.geodata[self.ip] ['countryCode'].lower()): overrides.append('i') if i.startswith('~i'): isp = i.split(':')[1].lower() if 'i' in channel.modes and match( isp, localServer.geodata[self.ip] ['isp'].lower()) and 'i' not in overrides: overrides.append('i') return 1, overrides except Exception as ex: logging.exception(ex)
def chgumode(client, ircd, recv, override, sourceServer=None, sourceUser=None): try: modebuf = [] action = '' target = list( filter(lambda u: u.nickname.lower() == recv[1].lower(), ircd.users)) if not target: return target = target[0] if type(client).__name__ == 'Server': override = True displaySource = client.hostname if client != ircd: displaySource = sourceUser.nickname else: if client.server != ircd: override = True displaySource = client.nickname client = sourceUser warn = [] unknown = [] showsno = False for m in str(recv[2]): if 'modelock' in ircd.conf['settings'] and m in ircd.conf[ 'settings']['modelock'] and not client.ocheck( 'o', 'override') and not override: if 'lock' not in warn: warn.append('lock') ircd.broadcast([ client ], 'NOTICE {} :The following modes cannot be changed: \'{}\'' .format(client.nickname, ircd.conf['settings']['modelock'])) warn = [] continue if m == 'r' and type(client).__name__ != 'Server': if client.server.hostname not in ircd.conf['settings'][ 'ulines']: continue if m in '+-' and action != m: action = m try: if modebuf[-1] in '+-': del modebuf[-1] except: pass modebuf.append(action) else: for umode in [ umode for umode in ircd.user_mode_class if umode.mode == m ]: # logging.debug('/MODE: Found a UserMode class: {}'.format(umode)) umode.modebuf = modebuf if (action == '+' and umode.give_mode(client)) or ( action == '-' and umode.take_mode(client)): # modebuf.append(m) continue if m not in '+-' and m not in ircd.user_modes and type( client).__name__ != 'Server': if m not in unknown and not override: unknown.append(m) continue if m in 'z' and not override: if m not in warn: client.sendraw( ircd.ERR.UMODEUNKNOWNFLAG, 'Mode +{} may only be set by servers'.format(m)) warn.append(m) continue if m in 'ohsqHW' and ( not client.operaccount or m not in ircd.conf['opers'][ client.operaccount]['modes']) and not override: continue if action == '+': if m == 'x': cloaked = cloak(client) client.setinfo(cloaked, t='host', source=sourceServer) client.cloakhost = cloaked elif m == 'S' and client.server.hostname not in ircd.conf[ 'settings']['ulines']: client.sendraw( ircd.ERR.UMODEUNKNOWNFLAG, 'Mode +{} may only be set by servers'.format(m)) continue elif m == 's': if len(recv) > 3: for s in recv[3]: if s in '+-': saction = s continue if saction == '-' and s in target.snomasks: showsno = True target.snomasks = target.snomasks.replace( s, '') continue if saction == '+' and s in ircd.snomasks and ( client.operaccount and s in ircd.conf['opers'][ client.operaccount]['snomasks'] ) and s not in target.snomasks: showsno = True target.snomasks += s continue elif m == 'o': updated = [] for user in [ user for user in ircd.users if 'operwatch' in user.caplist and user not in updated and user.socket ]: # common_chan = list(filter(lambda c: user in c.users and client in c.users, ircd.channels)) if not [ c for c in ircd.channels if user in c.users and client in c.users ]: continue user._send(':{} UMODE {}{}'.format( client.fullmask(), action, m)) updated.append(user) # Handle core modes. These aren't handled by UserMode class. if m not in target.modes: if m in 'sqHSW' and (not hasattr(target, 'opermodes') or m not in target.opermodes): if not hasattr(target, 'opermodes'): target.opermodes = '' target.opermodes += m target.modes += m modebuf.append(m) if action == '-' and m in target.modes: if m == 'x': client.setinfo(client.hostname, t='host', source=sourceServer) elif m == 'S' and client.server.hostname not in ircd.conf[ 'settings']['ulines']: client.sendraw( ircd.ERR.UMODEUNKNOWNFLAG, 'Mode +{} may only be set by servers'.format(m)) continue elif m == 'r': target.svid = '*' elif m == 's': target.snomasks = '' elif m == 'o': target.operflags = [] # Assign a class. for cls in ircd.conf['allow']: clientmaskhost = '{}@{}'.format( target.ident, target.ip) if 'ip' in ircd.conf['allow'][cls]: clientmask = '{}@{}'.format( target.ident, target.ip) isMatch = match(ircd.conf['allow'][cls]['ip'], clientmask) if 'hostname' in ircd.conf['allow'][cls]: clientmask = '{}@{}'.format( target.ident, target.hostname) isMatch = match( ircd.conf['allow'][cls]['hostname'], clientmask) if isMatch: if 'options' in ircd.conf['allow'][cls]: if 'ssl' in ircd.conf['allow'][cls][ 'options'] and not target.ssl: continue target.cls = cls if hasattr(target, 'opermodes'): for mode in [ m for m in target.opermodes if m in target.modes ]: modebuf.append(mode) if mode == 's': target.snomasks = '' if target.swhois: operSwhois = '' if 'swhois' in ircd.conf['opers'][ target.operaccount]: operSwhois = ircd.conf['opers'][ target.operaccount]['swhois'] if operSwhois in target.swhois: target.swhois.remove(operSwhois) if target.operswhois in target.swhois: target.swhois.remove(target.operswhois) data = ':{} SWHOIS {} :'.format( ircd.sid, target.uid) ircd.new_sync(ircd, sourceServer, data) for line in target.swhois: data = ':{} SWHOIS {} :{}'.format( ircd.sid, target.uid, line) ircd.new_sync(ircd, sourceServer, data) target.opermodes = '' client.operaccount = None updated = [] for user in [ user for user in ircd.users if 'operwatch' in user.caplist and user not in updated and user.socket ]: common_chan = list( filter( lambda c: user in c.users and client in c. users, ircd.channels)) if not common_chan: continue user._send(':{} UMODE {}{}'.format( client.fullmask(), action, m)) updated.append(user) # Handle core modes. These aren't handled by UserMode class. if m not in modebuf: modebuf.append(m) # Removing modes from user class. for mode in modebuf: target.modes = target.modes.replace(mode, '') if 'o' in target.modes: target.modes = 'o' + target.modes.replace('o', '') if len(modebuf) > 1 and ' '.join(modebuf)[-1] in '+-': del modebuf[-1] modes = ''.join(modebuf) if len(modes) > 1: target._send(':{} MODE {} :{}'.format(displaySource, target.nickname, modes)) if client != target: client.sendraw(ircd.ERR.UMODEUNKNOWNFLAG, 'UMODE {} :{}'.format(target.nickname, modes)) if target.server != ircd: ircd.new_sync( ircd, sourceServer, ':{} MODE {} {}'.format(displaySource, target.nickname, modes)) else: ircd.new_sync(ircd, sourceServer, ':{} UMODE2 {}'.format(target.uid, modes)) if 's' in modes or showsno: ircd.new_sync(ircd, sourceServer, ':{} BV +{}'.format(target.uid, target.snomasks)) target.sendraw(8, 'Server notice mask (+{})'.format(target.snomasks)) if unknown: client.sendraw( ircd.ERR.UMODEUNKNOWNFLAG, 'Mode{} \'{}\' not found'.format( 's' if len(unknown) > 1 else '', ''.join(unknown))) except Exception as ex: logging.exception(ex)
def execute(self, client, recv): if int(time.time()) - client.signon < 10: client.flood_safe = True who = [] if len(recv) == 1 or recv[1] == '*': mask = '*' # Match all. else: mask = recv[1] # This parameter contains a comma-separated list of query filters, # such as nicknames, channel names or wild-card masks which are matched against all clients currently on-line. flags = '' if len(recv) > 2: flags = recv[2] params = '' if len(recv) < 3 else recv[3:] # logging.debug('WHO mask: {}'.format(mask)) for user in iter(self.ircd.users): chan = '*' if not iter([c for c in self.ircd.channels if client in c.users and user in c.users]) and 'i' in client.modes and not 'o' in client.modes and user != client: continue # logging.debug('Checking mask: {}'.format(m)) if (mask[0] not in self.ircd.chantypes + '*' or mask.lower() not in iter([c.name.lower() for c in self.ircd.channels])) and mask not in [user.nickname, '*']: continue modes = '' if mask[0] in self.ircd.chantypes: # Mask is a channel. if mask.lower() not in iter([c.name.lower() for c in user.channels]) and mask != '*': continue else: channel = next((c for c in self.ircd.channels if c.name.lower() == mask.lower()), None) modes = ''.join([{'q': '~', 'a': '&', 'o': '@', 'h': '%', 'v': ''}[x] for x in channel.usermodes[user]]) chan = channel.name paramcount = 0 pos_match = [] neg_match = [] user_match = [] action = '' for f in iter([f for f in flags if f in WHO_FLAGS or f in '+-']): if f in '+-': action = f continue if action == '+': pos_match.append(f) else: neg_match.append(f) if f in 'Ahrsu': # ['WHO', '#Home', '%cuhsnfdar'] if not params: # logging.debug('Found arg flag but no params found') continue param = params[paramcount] logging.debug(f'Param for WHO flag {f} set: {param}') paramcount += 1 else: param = None if f == 'A': if (action == '+' and user.svid == param) or (action == '-' and not user.svid == param): user_match.append(f) elif f == 'a': if (action == '+' and user.away) or (action == '-' and not user.away): user_match.append(f) elif f == 'h': if (action == '+' and (match(param, user.fullmask()))) or (('o' in client.modes or user == client) and match(param, '{}!{}@{}'.format(user.nickname, user.ident, user.hostname))): user_match.append(f) if action == '-': if not match(param, user.fullmask()) and (('o' in client.modes or user == client) and not match(param, '{}!{}@{}'.format(user.nickname, user.ident, user.hostname))): user_match.append(f) elif ('o' not in client.modes and user != client) and not match(param, user.fullmask()): user_match.append(f) elif f == 'o': if (action == '+' and 'o' in user.modes) or (action == '-' and not 'o' in user.modes): user_match.append(f) elif f == 'r': gcos_match = 0 for word in user.realname.split(): if match(param.lower(), word.lower()): gcos_match = 1 break if (action == '+' and gcos_match) or (action == '-' and not gcos_match): user_match.append(f) elif f == 's': if action == '+' and match(param, user.server.hostname) or (action == '-' and not match(param, user.server.hostname)): user_match.append(f) elif f == 'u': if (action == '+' and match(param, user.ident)) or (action == '-' and not match(param, user.ident)): user_match.append(f) # logging.debug('User {} must match these flags: {}'.format(user.nickname, pos_match)) # logging.debug('User {} must NOT match these flags: {}'.format(user.nickname, neg_match)) # logging.debug('User {} has these matches: {}'.format(user.nickname, user_match)) diff = set(pos_match + neg_match).difference(set(user_match)) if diff or user in who: continue who.append(user) if user.channels and chan == '*': # We received a /who * or /who <nickname> -- Assign a channel. channel = None for c in user.channels: if ('s' in c.modes or 'p' in c.modes) and (client not in c.users and 'o' not in client.modes): continue else: channel = c visible = 1 for callable in [callable for callable in self.ircd.hooks if callable[0].lower() == 'visible_in_channel']: try: visible = callable[2](client, self.ircd, user, channel) except Exception as ex: logging.exception(ex) if not visible: break if not visible and user != client: channel = None continue if not channel: continue chan = channel.name modes = ''.join([{'q': '~', 'a': '&', 'o': '@', 'h': '%', 'v': ''}[x] for x in channel.usermodes[user]]) if 'x' in user.modes: modes += 'x' if user.away: away = 'G' else: away = 'H' if 'r' in user.modes: modes += 'r' if 'o' in user.modes and 'H' not in user.modes: modes += '*' if 'H' in user.modes: modes += '?' if 'z' in user.modes: modes += 'z' if not user.server: continue hopcount = 0 if user.server == self.ircd else user.server.hopcount client.sendraw(self.RPL.WHOREPLY, '{} {} {} {} {} {}{} :{} {}'.format(chan, user.ident, user.cloakhost, self.ircd.hostname, user.nickname, away, modes, hopcount, user.realname)) client.sendraw(self.RPL.ENDOFWHO, '{} :End of /WHO list.'.format(mask))
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)
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 join(self, localServer, channel, **kwargs): try: if 'override' in kwargs: logging.debug('Skipping extban checks: override') return 1, [] overrides = [] invite_override = 0 if self in channel.invites: invite_override = channel.invites[self]['override'] for c in self.channels: for b in [b for b in channel.bans if b[:2] == '~c']: banChan = b.split(':')[1] ison_banchan = [ chan for chan in localServer.channels if chan.name.lower() == banChan.lower() and self in chan.users ] if (banChan.lower() == channel.name.lower() or ison_banchan ) and not invite_override and not checkMatch( self, localServer, 'e', channel): self.sendraw( 474, '{} :Cannot join channel (+b)'.format(channel.name)) return 0, overrides for b in [b for b in c.bans if b[:2] == '~c']: banChan = b.split(':')[1] ison_banchan = [ chan for chan in localServer.channels if chan.name.lower() == banChan.lower() and self in chan.users ] if (banChan.lower() == channel.name.lower() or ison_banchan ) and not invite_override and not checkMatch( self, localServer, 'e', channel): self.sendraw( 474, '{} :Cannot join channel (+b)'.format(channel.name)) return 0, overrides for b in [ b for b in channel.bans if b[:2] == '~L' and not invite_override and not checkMatch(self, localServer, 'e', channel) ]: # ~L:host:#chan redirect_host = b.split(':')[1] redirect_chan = b.split(':')[2] if not next((c for c in localServer.channels if c.name.lower() == redirect_chan.lower()), None): # Redirect channel does not exist pass redir = 0 if match( redirect_host, '{}!{}@{}'.format(self.nickname, self.ident, self.hostname)): redir = 1 if match(redirect_host, '{}!{}@{}'.format(self.nickname, self.ident, self.ip)): redir = 1 if match( redirect_host, '{}!{}@{}'.format(self.nickname, self.ident, self.cloakhost)): redir = 1 if redir: self.handle('JOIN', redirect_chan) self.sendraw( 471, '{} :Channel is full so you are redirected to {}'.format( channel.name, redirect_chan)) return 0, overrides for b in [ b for b in channel.bans if b[:2] == '~t' and not invite_override and not checkMatch(self, localServer, 'e', channel) ]: mask = b.split(':')[2] ban = 0 if match( mask, '{}!{}@{}'.format(self.nickname, self.ident, self.hostname)): ban = 1 if match(mask, '{}!{}@{}'.format(self.nickname, self.ident, self.ip)): ban = 1 if match( mask, '{}!{}@{}'.format(self.nickname, self.ident, self.cloakhost)): ban = 1 if ban: self.sendraw( 474, '{} :Cannot join channel (+b)'.format(channel.name)) return 0, overrides for i in channel.invex: if i.startswith('~O'): oper_class = i.split(':')[1] if 'i' in channel.modes and ( 'o' in self.modes and (hasattr(self, 'operclass') and match( oper_class, self.operclass))) and 'i' not in overrides: overrides.append('i') if i.startswith('~a'): account = i.split(':')[1] if 'i' in channel.modes and ( 'r' in self.modes and (hasattr(self, 'svid') and match(account, self.svid))) and 'b' not in overrides: overrides.append('i') if i.startswith('~c'): chan_ban = i.split(':')[1] prefix = chan_ban[0] if chan_ban[0] in '+%@&~' else '' chan_ban = re.sub('[:*!~&@%+]', '', chan_ban) chan_ban_class = [ c for c in localServer.channels if c.name.lower() == chan_ban.lower() ] if chan_ban_class and 'i' not in overrides: chan_ban_class = chan_ban_class[0] if prefix == '+' and self.chlevel(chan_ban_class) >= 1: overrides.append('i') elif prefix == '%' and self.chlevel(chan_ban_class) >= 2: overrides.append('i') elif prefix == '@' and self.chlevel(chan_ban_class) >= 3: overrides.append('i') elif prefix == '&' and self.chlevel(chan_ban_class) >= 4: overrides.append('i') elif prefix == '~' and self.chlevel(chan_ban_class) >= 5: overrides.append('i') for e in channel.excepts: if e.startswith('~a'): account = e.split(':')[1] if ('r' in self.modes and (hasattr(self, 'svid') and match(account, self.svid))) and 'b' not in overrides: overrides.append('b') if e.startswith('~c'): chan_ban = e.split(':')[1] prefix = chan_ban[0] if chan_ban[0] in '+%@&~' else '' chan_ban = re.sub('[:*!~&@%+]', '', chan_ban) chan_ban_class = [ c for c in localServer.channels if c.name.lower() == chan_ban.lower() ] if chan_ban_class and 'b' not in overrides: chan_ban_class = chan_ban_class[0] if prefix == '+' and self.chlevel(chan_ban_class) >= 1: overrides.append('b') elif prefix == '%' and self.chlevel(chan_ban_class) >= 2: overrides.append('b') elif prefix == '@' and self.chlevel(chan_ban_class) >= 3: overrides.append('b') elif prefix == '&' and self.chlevel(chan_ban_class) >= 4: overrides.append('b') elif prefix == '~' and self.chlevel(chan_ban_class) >= 5: overrides.append('b') for b in channel.bans: if b.startswith('~a'): account = b.split(':')[1] if ('r' in self.modes and (hasattr(self, 'svid') and match(account, self.svid))) and 'b' not in overrides: self.sendraw( 474, '{} :Cannot join channel (+b)'.format(channel.name)) return False, overrides if b.startswith('~b'): exemp = b.split(':')[1] host = b.split(':')[2] if match(host, self.fullmask()) and (exemp == 'R' and 'r' not in self.modes and 'b' not in overrides): self.sendraw( 474, '{} :Cannot join channel (+b)'.format(channel.name)) return False, overrides return True, overrides except Exception as ex: logging.exception(ex)