Example #1
0
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.'
                )
Example #2
0
    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()
Example #3
0
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'))
Example #4
0
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))
Example #5
0
    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)
Example #6
0
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))
Example #7
0
    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)
Example #8
0
    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)
Example #9
0
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
Example #10
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)
Example #11
0
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)
Example #12
0
    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()
Example #13
0
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)
Example #14
0
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)
Example #15
0
    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))
Example #16
0
    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)
Example #17
0
    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()
Example #18
0
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)