Esempio n. 1
0
    def execute(self, client, recv):
        # :asdf UMODE2 +ot
        target = next((u for u in self.ircd.users
                       if u.uid == recv[0][1:] or u.nickname == recv[0][1:]),
                      None)
        if not target:
            logging.info(
                f'Could not set umode for {recv[0][1:]}: maybe it got SVSKILLed?'
            )
            return
        modeset = None
        for m in recv[2]:
            if m in '+-':
                modeset = m
                continue
            if modeset == '+':
                if m not in target.modes:
                    target.modes += m

            elif modeset == '-':
                target.modes = target.modes.replace(m, '')
                if m == 'o':
                    target.operflags = []
                    target.swhois = []
                    target.opermodes = ''
                elif m == 's':
                    target.snomasks = ''

        self.ircd.new_sync(self.ircd, client, ' '.join(recv))
Esempio n. 2
0
    def execute(self, client, recv):
        source = recv[0][1:]
        if type(client).__name__ == 'User':
            if client.registered:
                return client.sendraw(462, ':You may not reregister')
            # Check for server password.
            if client.cls and 'password' in self.ircd.conf['allow'][
                    client.cls]:
                if recv[1] == self.ircd.conf['allow'][client.cls]['password']:
                    client.server_pass_accepted = 1
                    logging.info(
                        'Server password accepted for {}'.format(client))
                    return
                else:
                    return client.quit('Invalid password')

        if type(client).__name__ == 'Server' and 'link' not in self.ircd.conf:
            return client.quit('Target has no links configured')

        if len(recv) < 3:
            return
        client.linkpass = recv[2][1:]
        logging.info('Password for {} set: {}'.format(client, client.linkpass))
        ip, port = client.socket.getpeername()
        ip2, port2 = client.socket.getsockname()

        if client.hostname:
            validate_server_info(self, client)
Esempio n. 3
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()
Esempio n. 4
0
def syncData(localServer, newServer, selfRequest=True, local_only=False):
    if localServer.users:
        syncUsers(localServer, newServer, local_only=local_only)
    if localServer.channels:
        syncChannels(localServer, newServer)
    try:
        for type in localServer.tkl:
            for entry in localServer.tkl[type]:
                if not localServer.tkl[type][entry]['global']:
                    continue
                mask = '{} {}'.format(entry.split('@')[0], entry.split('@')[1])
                setter = localServer.tkl[type][entry]['setter']
                try:
                    source = list(
                        filter(lambda s: s.hostname == setter,
                               localServer.servers))
                    if source:
                        if source[0].hostname == newServer.hostname or source[
                                0].introducedBy == newServer:
                            continue
                except:
                    pass
                expire = localServer.tkl[type][entry]['expire']
                ctime = localServer.tkl[type][entry]['ctime']
                reason = localServer.tkl[type][entry]['reason']
                data = ':{} TKL + {} {} {} {} {} :{}'.format(
                    localServer.sid, type, mask, setter, expire, ctime, reason)
                newServer._send(data)
    except Exception as ex:
        logging.exception(ex)
    logging.info('{}Server {} is done syncing to {}, sending EOS.{}'.format(
        Y, localServer.hostname, newServer.hostname, W))
    newServer._send(':{} EOS'.format(localServer.sid))

    if newServer not in localServer.syncDone:
        cloakhash = localServer.conf['settings']['cloak-key']
        cloakhash = hashlib.md5(cloakhash.encode('utf-8')).hexdigest()
        data = ':{} NETINFO {} {} {} MD5:{} {} 0 0 :{}'.format(
            localServer.sid, localServer.maxgusers, int(time.time()),
            localServer.versionnumber.replace('.', ''), cloakhash,
            localServer.creationtime, localServer.name)
        newServer._send(data)
        localServer.syncDone.append(newServer)

    if not hasattr(newServer, 'outgoing') or not newServer.outgoing:
        newServer._send(':{} PONG {} {}'.format(localServer.sid,
                                                newServer.hostname,
                                                localServer.hostname))
    else:
        newServer._send(':{} PING {} {}'.format(localServer.sid,
                                                localServer.hostname,
                                                newServer.hostname))
    return
Esempio n. 5
0
    def run(self):
        try:
            exists = list(
                filter(lambda s: s.hostname == self.name,
                       self.localServer.servers + [self.localServer]))
            if exists:
                logging.error(
                    'Server {} already exists on this network'.format(
                        exists[0].hostname))
                return

            serv = None
            if not self.host.replace('.', '').isdigit():
                self.host = socket.gethostbyname(self.host)
            self.socket = socket.socket()
            if self.tls:
                self.socket = self.localServer.default_sslctx.wrap_socket(
                    self.socket, server_side=False)
                logging.info('Wrapped outgoing socket {} in TLS'.format(
                    self.socket))

            from ircd import Server
            serv = Server(origin=self.localServer,
                          serverLink=True,
                          sock=self.socket,
                          is_ssl=self.tls)
            serv.hostname = self.name
            serv.ip = self.host
            serv.port = self.port
            serv.outgoing = True
            if self.origin or self.autoLink:
                self.localServer.linkrequester[serv] = self.origin

            self.socket.settimeout(10)
            self.socket.connect((self.host, self.port))

            selfIntroduction(self.localServer, serv, outgoing=True)

            if serv not in self.localServer.introducedTo:
                self.localServer.introducedTo.append(serv)

        except Exception as ex:
            logging.exception(ex)
            # Outgoing link timed out.
            if serv:
                serv.quit(str(ex))
Esempio n. 6
0
def syncUsers(localServer, newServer, local_only):
    try:
        totalServers = [localServer]
        if not local_only:
            totalServers.extend(localServer.servers)
        for server in [
                server for server in totalServers
                if server != newServer and server.introducedBy != newServer
                and newServer.introducedBy != server
                and server not in newServer.syncDone and newServer.socket
        ]:
            newServer.syncDone.append(server)
            logging.info('{}Syncing info from {} to {}{}'.format(
                Y, server.hostname, newServer.hostname, W))
            for u in [
                    u for u in localServer.users
                    if u.server == server and u.registered
            ]:
                ip = IPtoBase64(u.ip) if u.ip.replace('.',
                                                      '').isdigit() else u.ip
                if not ip:
                    ip = '*'
                hopcount = str(u.server.hopcount + 1)
                data = ':{} UID {} {} {} {} {} {} 0 +{} {} {} {} :{}'.format(
                    server.sid, u.nickname, hopcount, u.signon, u.ident,
                    u.hostname, u.uid, u.modes, u.cloakhost, u.cloakhost, ip,
                    u.realname)
                newServer._send(data)
                if u.fingerprint:
                    data = 'MD client {} certfp :{}'.format(
                        u.uid, u.fingerprint)
                    newServer._send(':{} {}'.format(server.sid, data))
                if u.operaccount:
                    data = 'MD client {} operaccount :{}'.format(
                        u.uid, u.operaccount)
                    newServer._send(':{} {}'.format(server.sid, data))
                if u.snomasks:
                    newServer._send(':{} BV +{}'.format(u.uid, u.snomasks))
                if 'o' in u.modes:
                    for line in u.swhois:
                        newServer._send(':{} SWHOIS {} :{}'.format(
                            server.sid, u.uid, line))
                if u.away:
                    newServer._send(':{} AWAY :{}'.format(u.uid, u.away))
    except Exception as ex:
        logging.exception(ex)
Esempio n. 7
0
def selfIntroduction(localServer, newServer, outgoing=False):
    try:
        if newServer not in localServer.introducedTo:
            if outgoing:
                destPass = localServer.conf['link'][newServer.hostname]['pass']
                newServer._send(':{} PASS :{}'.format(localServer.sid,
                                                      destPass))
            info = []
            for row in localServer.server_support:
                value = localServer.support[row]
                info.append('{}{}'.format(
                    row, '={}'.format(value) if value else ''))
            newServer._send(':{} PROTOCTL EAUTH={} SID={} {}'.format(
                localServer.sid, localServer.hostname, localServer.sid,
                ' '.join(info)))
            newServer._send(
                ':{} PROTOCTL NOQUIT EAUTH SID NICKv2 CLK SJOIN SJOIN2 UMODE2 VL SJ3 TKLEXT TKLEXT2 NICKIP ESVID EXTSWHOIS'
                .format(localServer.sid))
            version = 'P{}-{}'.format(
                localServer.versionnumber.replace('.', ''), localServer.sid)
            local_modules = [m.__name__ for m in localServer.modules]
            modlist = []
            for entry in local_modules:
                totlen = len(' '.join(modlist))
                if totlen >= 400:
                    newServer._send('MODLIST :{}'.format(' '.join(modlist)))
                    modlist = []
                modlist.append(entry)
            if modlist:
                newServer._send('MODLIST :{}'.format(' '.join(modlist)))
            if outgoing:
                newServer._send(
                    f':{localServer.sid} SID {localServer.hostname} 1 {localServer.sid} :{localServer.name}'
                )
            else:
                newServer._send('SERVER {} 1 :{} {}'.format(
                    localServer.hostname, version, localServer.name))
            logging.info(
                '{}Introduced myself to {}. Expecting remote sync sequence...{}'
                .format(Y, newServer.hostname, W))
        localServer.introducedTo.append(newServer)

    except Exception as ex:
        logging.exception(ex)
Esempio n. 8
0
 def _send(self, data):
     try:
         if self.socket:
             self.sendbuffer += data + '\r\n'
             if self.localServer.use_poll:
                 logging.debug(
                     'Flag for {} set to READ_WRITE (_send())'.format(self))
                 self.localServer.pollerObject.modify(
                     self.socket, READ_WRITE)
             ignore = ['PRIVMSG', 'NOTICE', 'PING', 'PONG']
             try:
                 if data.split()[0] not in ['PING', 'PONG']:
                     if len(data) > 1 and data.split()[1] not in ignore:
                         # pass
                         logging.info('{}{} <<<-- {}{}'.format(
                             B,
                             self.hostname if self.hostname != '' else self,
                             data, W))
             except:
                 pass
     except Exception as ex:
         logging.exception(ex)
Esempio n. 9
0
    def execute(self, client, recv):
        nick = recv[2]
        params = []
        allow = 1
        for p in recv:
            params.append(p)
        for user in [
                user for user in self.ircd.users
                if user.nickname.lower() == nick.lower()
        ]:
            logging.error('Found double user in UID: {}'.format(user))
            if not user.server:
                logging.error(
                    'Quitting {} because their server could not be found (UID)'
                    .format(user))
                user.quit('Unknown or corrupted connection with the same nick')
                continue

            logging.error(
                '{}ERROR: user {} already found on the network{}'.format(
                    R, user, W))
            localTS = int(user.signon)
            remoteTS = int(recv[4])
            if remoteTS <= localTS:
                logging.info(
                    '{}Local user {} disconnected from local server.{}'.format(
                        R, user, W))
                user.quit('Local Nick Collision', silent=True)
                continue
            else:
                allow = 0
                logging.debug('Disallowing remote user {}'.format(user))
                return

        if allow:
            u = ircd.User(client, server_class=self.ircd, params=params)
            cmd = ' '.join(recv)
            self.ircd.new_sync(self.ircd, client, cmd)
Esempio n. 10
0
    def execute(self, client, recv):
        if type(client).__name__ == 'Server':
            dest = list(filter(lambda s: s.sid == recv[3] or s.hostname == recv[3], self.ircd.servers + [self.ircd]))
            if not dest:
                logging.error('Server {} requested a PING to unknown server {}'.format(client, recv[3]))
                return
            source = list(filter(lambda s: s.sid == recv[2] or s.hostname == recv[2], self.ircd.servers + [self.ircd]))[0]

            if source not in self.ircd.syncDone:
                local_only = False
                if source in self.ircd.sync_queue:
                    local_only = True
                    logging.info('Syncing only local users to {}'.format(source))
                    del self.ircd.sync_queue[source]
                syncData(self.ircd, source, local_only=local_only)
                return

            ### Old: data = ':{} PONG {} {}'.format(dest[0].sid, dest[0].hostname, recv[2])
            if client.eos and (dest[0].eos or dest[0] == self.ircd):
                data = ':{} PONG {} {}'.format(dest[0].sid, dest[0].hostname, recv[2])
                client._send(data)
        else:
            client._send(':{} PONG {} :{}'.format(self.ircd.hostname, self.ircd.hostname, recv[1]))
Esempio n. 11
0
def LoadModule(self, name, path, reload=False, module=None):
    package = name.replace('/', '.')
    try:
        if reload:
            module = importlib.reload(module)
            logging.debug('Requesting reload from importlib')
        else:
            module = importlib.import_module(package)
            importlib.reload(module)
        if not module.__doc__:
            logging.info('Invalid module.')
            return 'Invalid module'
        callables = FindCallables(module)
        hook_fail = HookToCore(
            self, callables,
            reload=reload)  # If None is returned, assume success.
        if hook_fail:
            logging.debug('Hook failed: {}'.format(hook_fail))
            UnloadModule(self, name)

            return hook_fail
        self.modules[module] = callables
        name = module.__name__
        update_support(self)
        logging.info('Loaded: {}'.format(name))
    except FileNotFoundError as ex:
        return ex
    except Exception as ex:
        logging.exception(ex)
        UnloadModule(self, name)
        # if not reload:
        if not self.running:
            print(
                'Server could not be started due to an error in {}: {}'.format(
                    name, ex))
            sys.exit()
        raise
Esempio n. 12
0
    def execute(self, client, recv):
        source = list(filter(lambda s: s.sid == recv[0][1:],
                             self.ircd.servers))
        if not source:
            logging.error('ERROR: could not find server for {}'.format(
                recv[0][1:]))
            return
        source = source[0]
        if source.eos:
            logging.error('ERROR: remote server sent EOS twice!')
            return
        self.ircd.new_sync(self.ircd, client, ' '.join(recv))
        for server in [server for server in self.ircd.servers if server.eos]:
            data = ':{} PONG {} {}'.format(source.sid, source.hostname,
                                           server.hostname)
            server._send(data)
            data = ':{} PONG {} {}'.format(server.sid, server.hostname,
                                           source.hostname)
            source._send(data)
        logging.info('{}EOS received by: {}{}'.format(Y, source.hostname, W))

        if source.socket:
            for callable in [
                    callable for callable in self.ircd.hooks
                    if callable[0].lower() == 'server_link'
            ]:
                try:
                    callable[2](client, self.ircd, source)
                except Exception as ex:
                    logging.exception(ex)

        for s in [s for s in self.ircd.servers if s.introducedBy == source]:
            logging.info('Also setting EOS for {} to be true'.format(s))
            s.eos = True
        if source.hostname.lower() in self.ircd.pendingLinks:
            self.ircd.pendingLinks.remove(source.hostname.lower())
        source.eos = True
        if source in self.ircd.sync_queue:
            for e in self.ircd.sync_queue[source]:
                logging.info('Sending queued data to {}: {}'.format(source, e))
                source._send(e)
Esempio n. 13
0
def sock_accept(ircd, s):
    if ircd.listen_socks[s] == 'clients':
        try:
            conn, addr = s.accept()
            if ircd.use_poll:
                ircd.pollerObject.register(conn, READ_ONLY)
            port = conn.getsockname()[1]
            tls = is_sslport(ircd, port)
            conn_backlog = [
                user for user in ircd.users
                if user.socket and not user.registered
            ]
            logging.info('Accepting client on {} -- fd: {}, with IP {}'.format(
                s, conn.fileno(), addr[0]))
            if len(conn_backlog) > 100:
                logging.warning(
                    'Current connection backlog is >{}, so not allowing any more connections for now. Bye.'
                    .format(len(conn_backlog)))
                conn.close()
                return
            # conn.settimeout(15)
            if tls and not ircd.pre_wrap:
                conn = ircd.sslctx[str(port)].wrap_socket(conn,
                                                          server_side=True)
                tls = 1
                logging.info(
                    'Wrapped incoming user socket {} in TLS'.format(conn))
                try:
                    fp = conn.getpeercert(True)
                    if fp:
                        tls_fingerprint = hashlib.sha256(
                            repr(fp).encode('utf-8')).hexdigest()
                        logging.info('Fingerprint: {}'.format(tls_fingerprint))
                except Exception as ex:
                    logging.exception(ex)
            u = User(ircd, conn, addr, tls)
            if ircd.use_poll:
                ircd.fd_to_socket[u.fileno()] = (u.socket, u)
            '''
            try:
                u.socket.setblocking(1) ### Uncomment this. Why? I don't remember.
            except OSError as ex:
                logging.debug(R+'Client {} probably refused the connection due to self-signed cert (ZNC?). This can cause memory leaks.'.format(u)+W)
                logging.debug(R+'Gently disconnecting user. IP: {}'.format(u.ip)+W)
                #logging.exception(ex)
                u.quit(ex)
                return
            '''

            gc.collect()
            if u.fileno() == -1:
                logging.error('{}Invalid fd for {} -- quit() on user{}'.format(
                    R, u, W))
                u.quit('Invalid fd')
                return
            try:
                random_ping = ''.join(
                    random.choice(string.ascii_uppercase + string.digits)
                    for _ in range(8))
                ircd.pings[u] = random_ping
                u._send('PING :{}'.format(random_ping))

            except Exception as ex:
                # ircd.snotice('t', '[{}](1) {}'.format(addr[0], ex))
                logging.exception(ex)
                u.quit(ex)
                return

        except Exception as ex:
            logging.exception(ex)
            conn.close()
            return

    elif ircd.listen_socks[s] == 'servers':
        try:
            conn, addr = s.accept()
            if ircd.use_poll:
                ircd.pollerObject.register(conn, READ_ONLY)
            port = conn.getsockname()[1]
            tls = is_sslport(ircd, port)
            # conn.settimeout(15)
            if tls and not ircd.pre_wrap:
                tls = 0
                version = '{}{}'.format(sys.version_info[0],
                                        sys.version_info[1])
                conn = ircd.sslctx[str(port)].wrap_socket(conn,
                                                          server_side=True)
                tls = 1
                logging.info(
                    'Wrapped incoming server socket {} in TLS'.format(conn))

            s = Server(origin=ircd, serverLink=True, sock=conn, is_ssl=tls)

        except ssl.SSLError as ex:
            logging.exception(ex)
            return
        except Exception as ex:
            logging.exception(ex)
            return
Esempio n. 14
0
def checkConf(ircd, user, confdir, conffile, rehash=False):
    global errors
    global confOpers
    global confLinks
    confOpers = []
    confLinks = []
    errors = []
    main_conf = conffile
    if rehash:
        user.sendraw(382, '{} :Rehashing'.format(confdir + conffile))
    try:
        with open(confdir + conffile) as j:
            j = j.read().split('\n')
            j = json_preprocess(j)
            tempconf = json.loads(j, object_pairs_hook=collections.OrderedDict)
        if 'me' not in tempconf:
            conferr('\'me\' block not found!')

        else:
            if 'server' not in tempconf['me']:
                conferr('\'me::server\' not found!')

            if 'name' not in tempconf['me']:
                conferr('\'me::name\' not found!')
            else:
                ircd.name = tempconf['me']['name']

            if 'sid' not in tempconf['me']:
                conferr('\'me::sid\' not found!')

            if tempconf['me']['sid'] == '000':
                conferr(
                    '\'me::sid\' must be a unique number. Please change this to a random number between 001-999'
                )

            if not tempconf['me']['sid'].isdigit() or len(
                    tempconf['me']['sid']) != 3:
                conferr('\'me::sid\' must be a 3 length number')
            if isinstance(tempconf['me']['sid'], int):
                tempconf['me']['sid'] = str(tempconf['me']['sid'])

        ircd.hostname = tempconf['me']['server']
        ircd.name = tempconf['me']['name']
        ircd.sid = tempconf['me']['sid']

        if not os.path.isfile("conf/ircd.motd"):
            conferr("conf/ircd.motd not found")

        if 'admin' not in tempconf:
            conferr('\'admin\' block not found!')

        if 'listen' not in tempconf:
            conferr('\'listen\' block not found')
        else:
            for port in tempconf['listen']:
                if not str(port).isdigit():
                    conferr(
                        'invalid port in \'listen\' block: {} -- ports must be numeric.'
                        .format(port))
                    continue
                if int(port) <= 1024:
                    conferr(
                        'invalid port in \'listen\' block: {} -- port cannot be lower than 1024.'
                        .format(port))
                    continue
                if 'options' not in tempconf['listen'][port]:
                    conferr(
                        '\'options\' missing in \'listen\' block for port {}.'.
                        format(port))
                elif 'clients' not in tempconf['listen'][port][
                        'options'] and 'servers' not in tempconf['listen'][
                            port]['options']:
                    conferr(
                        "Port {} does not have a 'servers' or 'clients' option in their listen-block."
                        .format(port))
                elif 'clients' in tempconf['listen'][port][
                        'options'] and 'servers' in tempconf['listen'][port][
                            'options']:
                    conferr(
                        "'options' can not contain both 'servers' and 'clients' in 'listen' block for port {}."
                        .format(port))

            default_cert, default_key = ircd.rootdir + '/ssl/server.cert.pem', ircd.rootdir + '/ssl/server.key.pem'

            if not os.path.isfile(default_cert) or not os.path.isfile(
                    default_key):
                conferr(
                    "You have one or more SSL ports listening but there are files missing in {}/ssl/ folder. Make sure you have 'server.cert.pem' and 'server.key.pem' present!"
                    .format(ircd.rootdir),
                    noConf=True)
                conferr(
                    "You can create self-signed certs (not recommended) by issuing the following command in your terminal: openssl req -x509 -nodes -newkey rsa:4096 -keyout server.key.pem -out server.cert.pem",
                    noConf=True)
                conferr(
                    "or you can get a free CA cert from Let's Encrypt: https://letsencrypt.org",
                    noConf=True)

            ircd.default_cert, ircd.default_key = default_cert, default_key
            ircd.default_ca_file = 'ssl/curl-ca-bundle.crt'

            for port in tempconf['listen']:
                ircd.tls_files[port] = {}
                ircd.tls_files[port]['keypass'] = None
                if 'ssl' in tempconf['listen'][port]['options']:
                    ircd.tls_files[port]['cert'] = default_cert
                    ircd.tls_files[port]['key'] = default_key
                    if 'ssl-options' not in tempconf['listen'][port]:
                        if not os.path.isfile(
                                default_cert) or not os.path.isfile(
                                    default_key):
                            conferr(
                                "You have one or more SSL ports listening but there are files missing in {}/ssl/ folder. Make sure you have 'server.cert.pem' and 'server.key.pem' present!"
                                .format(ircd.rootdir),
                                noConf=True)
                            conferr(
                                "You can create self-signed certs (not recommended) by issuing the following command in your terminal: openssl req -x509 -newkey rsa:4096 -keyout server.key.pem -out server.cert.pem",
                                noConf=True)
                            conferr(
                                "or you can get a free CA cert from Let's Encrypt: https://letsencrypt.org",
                                noConf=True)
                            break

                    if 'ssl-options' in tempconf['listen'][port]:
                        t = tempconf['listen'][port]['ssl-options']
                        if 'certificate' not in t:
                            logging.warning(
                                f"Certificate path is missing in 'ssl-options' for TLS port {port}"
                            )
                            logging.warning("Falling back to default.")

                        elif not os.path.realpath(t['certificate']):
                            logging.warning(
                                f"Certificate for port {port} could not be found: {t['certificate']}."
                            )
                            logging.warning(
                                "Make sure the file exists and is accessible by the current user. Falling back to default."
                            )

                        else:
                            ircd.tls_files[port]['cert'] = os.path.realpath(
                                t['certificate'])

                        if 'key' not in t:
                            logging.warning(
                                f"Key path is missing in 'ssl-options' for TLS port {port}"
                            )
                            logging.warning("Falling back to default.")
                        elif not os.path.realpath(t['key']):
                            logging.warning(
                                f"Key could for port {port} not be found: {t['key']}."
                            )
                            logging.warning(
                                "Make sure the file exists and is accessible by the current user. Falling back to default."
                            )

                        else:
                            ircd.tls_files[port]['key'] = os.path.realpath(
                                t['key'])

                            if 'keypass' in t and t['keypass']:
                                if len(t['keypass']) < 6:
                                    logging.warning(
                                        f"Insecure TLS key password for file: '{ircd.tls_files[port]['key']}'"
                                    )
                                ircd.tls_files[port]['keypass'] = t['keypass']

                        if 'verify-certs' in t:
                            ircd.tls_files[port]['verify-certs'] = t[
                                'verify-certs']

        if 'class' not in tempconf:
            conferr('\'class\' block not found')
        else:
            for cls in tempconf['class']:
                if 'sendq' not in tempconf['class'][cls]:
                    conferr('\'sendq\' missing for class \'{}\''.format(cls))
                if 'recvq' not in tempconf['class'][cls]:
                    conferr('\'recvq\' missing for class \'{}\''.format(cls))
                if 'max' not in tempconf['class'][cls]:
                    conferr('\'max\' missing for class \'{}\''.format(cls))

        if 'allow' not in tempconf:
            conferr('\'allow\' block not found')
        else:
            for cls in tempconf['allow']:
                if cls not in tempconf['class']:
                    conferr(
                        'Class \'{}\' found in allow-block, but it does not exist'
                        .format(cls))
                else:
                    if 'ip' not in tempconf['allow'][
                            cls] and 'hostname' not in tempconf['allow'][cls]:
                        conferr(
                            '\'ip\' or \'hostname\' missing for allow-class \'{}\''
                            .format(cls))
                    if 'maxperip' not in tempconf['allow'][cls]:
                        conferr('\'maxperip\' missing for allow-class \'{}\''.
                                format(cls))

        if 'settings' not in tempconf:
            conferr('\'settings\' block not found!')

        else:
            reqvalues = [
                'cloak-key', 'throttle', 'nickflood', 'regtimeout',
                'restartpass', 'diepass'
            ]
            for v in reqvalues:
                if v not in tempconf['settings']:
                    conferr('\'{}\' missing in settings-block'.format(v))

            if 'throttle' in tempconf['settings']:
                try:
                    tempconf['settings']['throttle'].split(':')[1]
                except:
                    conferr(
                        'invalid \'throttle\' in settings-block: must be connections:time in integer format'
                    )
                if not tempconf['settings']['throttle'].split(':')[0].isdigit(
                ) or not tempconf['settings']['throttle'].split(
                        ':')[1].isdigit():
                    conferr(
                        'invalid \'throttle\' in settings-block: must be connections:time in integer format'
                    )

            if 'nickflood' in tempconf['settings']:
                try:
                    tempconf['settings']['nickflood'].split(':')[1]
                except:
                    conferr(
                        'invalid \'nickflood\' in settings-block: must be nickchanges:time in integer format'
                    )
                if not tempconf['settings']['nickflood'].split(':')[0].isdigit(
                ) or not tempconf['settings']['nickflood'].split(
                        ':')[1].isdigit():
                    conferr(
                        'invalid \'nickflood\' in settings-block: must be nickchanges:time in integer format'
                    )

            if 'regtimeout' in tempconf['settings']:
                if not str(tempconf['settings']['regtimeout']).isdigit(
                ) or not isinstance(tempconf['settings']['regtimeout'], int):
                    conferr(
                        'invalid \'regtimeout\' in settings-block: must be an integer'
                    )

            if 'cloak-prefix' in tempconf['settings'] and tempconf['settings'][
                    'cloak-prefix']:
                prefix = tempconf['settings']['cloak-prefix']
                if len(prefix) > 5:
                    conferr(
                        '\'cloak-prefix\' is set, but exceeds the maximum length, which is 5'
                    )
                else:
                    p = re.compile("(^[a-zA-Z0-9]{1,5}$)")
                    match = p.search(prefix)
                    if not match:
                        conferr(
                            'invalid \'cloak-prefix\': only AZ-az0-9 characters are allowed, up to 5'
                        )

        if 'ulines' in tempconf['settings'] and not isinstance(
                tempconf['settings']['ulines'], list):
            conferr('invalid \'ulines\' in settings-block: must be a list')

        if 'services' in tempconf['settings'] and not isinstance(
                tempconf['settings']['services'], str):
            conferr('invalid \'ulines\' in settings-block: must be a string')

        if 'restartpass' in tempconf['settings'] and not isinstance(
                tempconf['settings']['restartpass'], str):
            conferr(
                'invalid \'restartpass\' in settings-block: must be a string')
        else:
            if 'restartpass' in tempconf['settings'] and len(
                    tempconf['settings']['restartpass']) < 8:
                conferr(
                    'Restart password is too short in settings-block: minimum 8 characters'
                )

        if 'diepass' in tempconf['settings'] and not isinstance(
                tempconf['settings']['diepass'], str):
            conferr('invalid \'diepass\' in settings-block: must be a string')
        else:
            if 'diepass' in tempconf['settings'] and len(
                    tempconf['settings']['diepass']) < 8:
                conferr(
                    'Die password is too short in settings-block: minimum 8 characters'
                )

        if 'dnsbl' in tempconf:
            if 'list' not in tempconf['dnsbl']:
                conferr('\'list\' containing DNSBL\'s missing')
            if 'iplist' in tempconf['dnsbl']:
                try:
                    with open(confdir + tempconf['dnsbl']['iplist'], 'r') as f:
                        ircd.bannedList = f.read().splitlines()
                        logging.debug(
                            'Added {} entries to bannedList: {}'.format(
                                len(ircd.bannedList), ircd.bannedList))
                except Exception as ex:
                    pass

        if 'opers' in tempconf:
            oper_conf = main_conf

        if 'link' in tempconf:
            link_conf = main_conf

        if 'include' in tempconf:
            for include in tempconf['include']:
                noUpdate = False
                conffile = include
                try:
                    with open(confdir + conffile) as j:
                        t = j.read().split('\n')
                        t = json_preprocess(t)
                        t = json.loads(t)
                        for entry in dict(t):
                            if entry in tempconf:
                                tempconf[entry].update(t[entry])
                                noUpdate = True
                                continue
                        if not noUpdate:
                            tempconf.update(t)
                        if 'opers' in t:
                            oper_conf = include
                        if 'link' in t:
                            link_conf = include
                        if 'dnsbl' in t:
                            if 'list' not in t['dnsbl']:
                                conferr('\'list\' containing DNSBL\'s missing')

                except Exception as ex:
                    logging.exception(ex)
                    conferr(ex, err_conf=include)
        if 'opers' in tempconf:
            check_opers(tempconf, err_conf=oper_conf)
        if 'link' in tempconf:
            check_links(tempconf, err_conf=link_conf)
        ircd.excepts = {}
        if 'except' in tempconf:
            for type in tempconf['except']:
                if type not in ircd.excepts:
                    ircd.excepts[type] = []
                for entry in tempconf['except'][type]:
                    ircd.excepts[type].append(entry)

        ircd.deny = {}
        if 'deny' in tempconf:
            for entry in tempconf['deny']:
                ircd.deny[entry] = tempconf['deny'][entry]
                logging.debug('Added deny for {}: {}'.format(entry, ircd.deny))

        # Checking optional modules.
        if 'modules' not in tempconf:
            conferr(
                'You don\'t seem to have any modules loaded. ProvisionIRCd will not function without them.'
            )

        if 'modules' in tempconf:
            local_modules = [m for m in ircd.modules]
            for m in dict(ircd.modules):
                # logging.info('Unloading module {}'.format(m.__name__))
                Modules.UnloadModule(ircd, m.__name__)
            modules = Modules.ListModules(ircd)
            for m in [
                    m for m in local_modules if m not in tempconf['modules']
            ]:
                tempconf['modules'].append(m.__name__)
            for m in [
                    m for m in tempconf['modules']
                    if not m.startswith('modules.')
            ]:
                m = 'modules.' + m.replace('/', '.')
                try:
                    reload = 1 if m in [mod.__name__
                                        for mod in local_modules] else 0
                    module = [
                        mod for mod in local_modules if mod.__name__ == m
                    ]
                    if module:
                        module = module[0]
                    try:
                        result = Modules.LoadModule(ircd,
                                                    m,
                                                    modules[m],
                                                    reload=reload,
                                                    module=module)
                        if result:
                            raise Exception(result)
                    except Exception as ex:
                        err = 'Unable to load module \'{}\': {}'.format(m, ex)
                        logging.error(err)
                        if rehash:
                            ircd.broadcast(
                                [user], 'NOTICE {} :*** [info] -- {}'.format(
                                    user.nickname, err))
                        else:
                            conferr(err)
                        continue
                except Exception as ex:
                    logging.exception(ex)

            ircd.default_sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
            tls_cert, tls_key = ircd.default_cert, ircd.default_key
            ircd.default_sslctx.load_cert_chain(certfile=tls_cert,
                                                keyfile=tls_key)
            ircd.default_sslctx.load_default_certs(
                purpose=ssl.Purpose.CLIENT_AUTH)
            ircd.default_sslctx.load_verify_locations(
                cafile=ircd.default_ca_file)
            ircd.default_sslctx.verify_mode = ssl.CERT_NONE

            ircd.sslctx = {}
            for port in [
                    p for p in tempconf['listen']
                    if 'ssl' in tempconf['listen'][p]['options']
            ]:
                try:
                    ircd.sslctx[port] = ssl.SSLContext(ssl.PROTOCOL_TLS)
                    tls_cert = ircd.tls_files[port]['cert']
                    tls_key = ircd.tls_files[port]['key']
                    tls_key_pass = ircd.tls_files[port]['keypass']
                    ircd.sslctx[port].load_cert_chain(certfile=tls_cert,
                                                      keyfile=tls_key,
                                                      password=tls_key_pass)
                    logging.info(
                        f'Using on port {port}: cert {tls_cert} and key {tls_key}'
                    )
                    logging.info(
                        f"Password protected key: {'yes' if tls_key_pass else 'no'}"
                    )

                    ircd.sslctx[port].load_default_certs(
                        purpose=ssl.Purpose.CLIENT_AUTH)
                    ircd.sslctx[port].load_verify_locations(
                        cafile=ircd.default_ca_file)
                    ircd.sslctx[port].verify_mode = ssl.CERT_NONE

                    if 'verify-certs' in ircd.tls_files[
                            port] and ircd.tls_files[port]['verify-certs']:
                        ircd.sslctx[port].verify_mode = ssl.CERT_OPTIONAL
                        logging.warning(
                            f"TLS port {port} will only accept validatable certificates."
                        )

                    # localServer.sslctx = temp_sslctx

                except PermissionError as ex:
                    err = f'Reloading TLS certificates for port {port} failed with PermissionError. Make sure the files can be read by the current user.'
                    logging.exception(ex)

                    if rehash:
                        ircd.notice(user, '*** {}'.format(err))
                    else:
                        conferr(err)

                except FileNotFoundError as ex:
                    err = "One or more required SSL files could not be found."
                    err += "\nCheck to see if the following files are present and valid:"
                    err += "\n" + tls_key
                    err += "\n" + tls_cert

                    conferr(err)
                    logging.exception(ex)

                except Exception as ex:
                    logging.exception(ex)
                    if rehash:
                        ircd.notice(user, '*** [ssl] -- Error: {}'.format(ex))

    except KeyError as ex:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
        e = 'EXCEPTION: {} in file {} line {}: {}'.format(
            exc_type.__name__, fname, exc_tb.tb_lineno, exc_obj)
        conferr('Missing conf block: {}'.format(ex), err_conf=main_conf)

    except json.JSONDecodeError as ex:
        s = 'Invalid conf format. Make sure the JSON format is correct: {}'.format(
            ex)
        conferr(s)
        logging.exception(ex)

    except Exception as ex:
        # pass
        exc_type, exc_obj, exc_tb = sys.exc_info()
        fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
        e = 'EXCEPTION: {} in file {} line {}: {}'.format(
            exc_type.__name__, fname, exc_tb.tb_lineno, exc_obj)
        conferr(e)

    if errors:
        for e in errors:
            s = 'ERROR: ' + e
            if ircd.running:
                logging.error(e)
            else:
                print(s)
            if rehash:
                ircd.broadcast([user], 'NOTICE {} :*** [error] -- {}'.format(
                    user.nickname, e))
        if rehash:
            ircd.broadcast(
                [user],
                'NOTICE {} :*** Configuration failed to reload.'.format(
                    user.nickname))
        return 0

    else:
        # Open new ports?
        currently_listening = []
        new_ports = []
        for sock in ircd.listen_socks:
            try:
                ip, port = sock.getsockname()
                currently_listening.append(str(port))
            except Exception as ex:
                logging.exception(ex)
        for port in tempconf['listen']:
            new_ports.append(port)

        ircd.conf = tempconf
        for p in [
                p for p in ircd.conf['listen']
                if str(p) not in currently_listening
        ]:
            if 'clients' in set(ircd.conf['listen'][p]['options']):
                try:
                    ircd.listen_socks[ircd.listenToPort(int(p),
                                                        'clients')] = 'clients'
                except Exception as ex:
                    logging.warning('Unable to listen on port {}: {}'.format(
                        p, ex))

            elif 'servers' in set(ircd.conf['listen'][p]['options']):
                try:
                    ircd.listen_socks[ircd.listenToPort(int(p),
                                                        'servers')] = 'servers'
                except Exception as ex:
                    logging.warning('Unable to listen on port {}: {}'.format(
                        p, ex))
        # Now close ports.
        for p in [
                p for p in ircd.listen_socks
                if str(p.getsockname()[1]) not in new_ports
        ]:
            try:
                logging.info('Closing port {}'.format(p))
                del ircd.listen_socks[p]
                p.close()
            except Exception as ex:
                print(ex)

        if rehash:
            ircd.broadcast(
                [user],
                'NOTICE {} :*** Configuration reloaded without any problems.'.
                format(user.nickname))

        del j, t, tempconf

        # Load +P channels from db/chans.db with their modes/settings.
        if os.path.exists(ircd.rootdir + '/db/chans.db'):
            perm_data = {}
            try:
                with open(ircd.rootdir + '/db/chans.db') as f:
                    perm_data = f.read().split('\n')[0]
                    perm_data = json.loads(perm_data)
                    # Restoring permanent channels.
                    for chan in [
                            chan for chan in perm_data if chan.lower() not in
                        [c.name.lower() for c in ircd.channels]
                    ]:
                        c = Channel(chan)
                        ircd.channels.append(c)
                        ircd.chan_params[c] = {}
                        for callable in [
                                callable for callable in ircd.hooks
                                if callable[0].lower() == 'channel_create'
                        ]:
                            try:
                                callable[2](ircd, ircd, c)
                            except Exception as ex:
                                logging.exception(ex)

                        if 'creation' in perm_data[chan]:
                            c.creation = perm_data[chan]['creation']
                        params = []
                        for m in [
                                m for m in perm_data[chan]['modes']
                                if m in perm_data[chan]['modeparams']
                        ]:
                            params.append(perm_data[chan]['modeparams'][m])
                        # params = ' '.join([perm_data[chan]['modeparams'][key] for key in perm_data[chan]['modeparams']])
                        modestring = '+{} {}'.format(perm_data[chan]['modes'],
                                                     ' '.join(params))
                        logging.debug('Sending: {}'.format(modestring))
                        ircd.handle('MODE', c.name + ' ' + modestring)
                        logging.debug('Sent: {}'.format(modestring))
                        c.bans = perm_data[chan]['bans']
                        c.excepts = perm_data[chan]['excepts']
                        c.invex = perm_data[chan]['invex']
                        if perm_data[chan]['topic']:
                            c.topic = perm_data[chan]['topic'][0]
                            c.topic_author = perm_data[chan]['topic'][1]
                            c.topic_time = perm_data[chan]['topic'][2]
                        logging.debug('Restored: {}'.format(c))
                        logging.debug('Modes: {}'.format(c.modes))
                        logging.debug('Params: {}'.format(ircd.chan_params[c]))
            except Exception as ex:
                logging.exception(ex)

        # Restore TKL.
        if os.path.exists(ircd.rootdir + '/db/tkl.db') and not ircd.tkl:
            try:
                with open(ircd.rootdir + '/db/tkl.db') as f:
                    tkl_data = f.read().split('\n')[0]
                    tkl_data = json.loads(tkl_data)
                    ircd.tkl = tkl_data
                    num = 0
                    for key in ircd.tkl:
                        for item in ircd.tkl[key]:
                            num += 1

                    logging.debug(
                        f'Restored {(num)} TKL entr{"y" if num == 1 else "ies"}.'
                    )
            except Exception as ex:
                logging.exception(ex)

        return 1
    gc.collect()
    return 1
Esempio n. 15
0
    def execute(self, client, recv):
        exists = [
            s for s in self.ircd.servers + [self.ircd]
            if s.hostname.lower() == recv[2].lower()
        ]
        if exists and client != exists[0]:
            logging.error('Server {} already exists on this network2'.format(
                recv[2]))
            client.quit('Server already exists on this network')
            return
        if not client.sid:
            logging.error(
                'Direct link with {} denied because their SID is unknown to me'
                .format(recv[2]))
            client.quit('No SID received')
            return

        # SERVER irc.example.com 1 :versionstring Server name goes here.
        if not client.linkAccept and not client.eos:
            # Information is gathered backwards from recv.
            client.linkAccept = True
            tempName = ' '.join(recv).split(':')[-2]
            client.hostname = tempName.split()[-2].strip()
            client.hopcount = int(tempName.split()[-1])
            client.name = ' '.join(
                recv[1:]).split(':')[1]  # ' '.join(recv[4:])
            # Snowflake fix.
            if client.hostname not in self.ircd.conf['settings']['ulines'] + [
                    self.ircd.conf['settings']['services']
            ]:
                client.name = ' '.join(client.name.split()[1:])
            if client.name.startswith(':'):
                client.name = client.name[1:]

            logging.info('{}Hostname for {} set: {}{}'.format(
                G, client, client.hostname, W))
            if [
                    s for s in self.ircd.servers + [self.ircd]
                    if s.hostname.lower() == client.hostname.lower()
                    and s != client
            ]:
                logging.error(
                    'Server {} already exists on this network'.format(
                        client.hostname))
                error = 'Error connecting to server {}[{}:{}]: server {} already exists on remote network'.format(
                    client.hostname, ip, port, client.hostname)
                client._send(':{} ERROR :{}'.format(self.ircd.sid, error))
                client.quit('server {} already exists on this network'.format(
                    client.hostname))
                return

            logging.info('{}Server name for {} set: {}{}'.format(
                G, client, client.name, W))
            logging.info('{}Hopcount for {} set: {}{}'.format(
                G, client, client.hopcount, W))
            logging.info('{}SID for {} set: {}{}'.format(
                G, client, client.sid, W))

            if validate_server_info(self, client):
                selfIntroduction(self.ircd, client)
                data = ':{} SID {} 1 {} :{}'.format(self.ircd.sid,
                                                    client.hostname,
                                                    client.sid, client.name)
                self.ircd.new_sync(self.ircd, client, data)
                for server in [
                        server for server in self.ircd.servers
                        if server.sid and server != client and server.eos
                ]:
                    logging.info('Introducing {} to {}'.format(
                        server.hostname, client.hostname))
                    sid = self.ircd.sid if server.socket else server.uplink.sid
                    data = ':{} SID {} {} {} :{}'.format(
                        sid, server.hostname,
                        int(server.hopcount) + 1, server.sid, server.name)
                    client._send(data)

                if hasattr(client, 'outgoing') and client.outgoing:
                    syncData(self.ircd, client)
Esempio n. 16
0
    def execute(self, client, recv):
        # :420 SID link1.provisionweb.org 1 420 :ProvisionDev
        uplink = [s for s in self.ircd.servers if s.sid == recv[0][1:]]
        if not uplink:
            client._send(':{} ERROR :Could not find uplink for {}'.format(
                self.ircd.sid, recv[0][1:]))
            client.quit()
            return
        uplink = uplink[0]
        sid = recv[4]
        hostname = recv[2]
        name = ' '.join(recv[5:])[1:]
        hopcount = int(recv[3])
        if client.hostname == hostname:
            logging.debug(f'New incoming link: {hostname}')
            if validate_server_info(self, client):
                client.name = name
                selfIntroduction(self.ircd, client)
                data = ':{} SID {} 1 {} :{}'.format(self.ircd.sid,
                                                    client.hostname,
                                                    client.sid, client.name)
                self.ircd.new_sync(self.ircd, client, data)
                for server in [
                        server for server in self.ircd.servers
                        if server.sid and server != client and server.eos
                ]:
                    logging.info('Introducing {} to {}'.format(
                        server.hostname, client.hostname))
                    sid = self.ircd.sid if server.socket else server.uplink.sid
                    data = ':{} SID {} {} {} :{}'.format(
                        sid, server.hostname,
                        int(server.hopcount) + 1, server.sid, server.name)
                    client._send(data)

                    if hasattr(client, 'outgoing') and client.outgoing:
                        syncData(self.ircd, client)
            return

        for server in [
                server for server in self.ircd.servers
                if server.sid == sid and server != client
        ]:
            client._send(
                ':{} ERROR :SID {} is already in use on that network'.format(
                    self.ircd.sid, sid))
            client.quit('SID {} is already in use on that network'.format(sid))
            return
        for server in [
                server for server in self.ircd.servers
                if server.hostname.lower() == hostname.lower()
                and server != client
        ]:
            client._send(
                ':{} ERROR :Hostname {} is already in use on that network'.
                format(self.ircd.sid, hostname))
            client.quit(
                'Server {} is already in use on that network'.format(hostname))
            return

        newServer = ircd.Server(origin=self.ircd, serverLink=True)
        newServer.hostname = hostname
        newServer.hopcount = hopcount
        newServer.name = ' '.join(recv[5:])[1:]
        newServer.introducedBy = client
        newServer.uplink = uplink
        newServer.sid = sid
        logging.info(
            f'{G}New server added to the network: {newServer.hostname} ({newServer.name}{W})'
        )
        logging.info('{}SID: {}{}'.format(G, newServer.sid, W))
        logging.info('{}Introduced by: {} ({}) {}'.format(
            G, newServer.introducedBy.hostname, newServer.introducedBy.sid, W))
        logging.info('{}Uplinked to: {} ({}) {}'.format(
            G, newServer.uplink.hostname, newServer.uplink.sid, W))
        logging.info('{}Hopcount: {}{}'.format(G, newServer.hopcount, W))
        self.ircd.new_sync(self.ircd, client, ' '.join(recv))
Esempio n. 17
0
def extbans(self, localServer, channel, modebuf, parambuf, action, modebar,
            param):
    try:
        if modebar not in 'beI' or action != '+':
            return
        if not param:
            logging.error('ERROR: invalid param received for {}{}: {}'.format(
                action, modebar, param))
            return
        if not re.findall("(^{}[{}]):(.*)".format(prefix, ''.join(ext_bans)),
                          param):
            # logging.info('Param {} is invalid for {}{}'.format(param, action, modebar))
            return

        logging.info('Param for {}{} set: {}'.format(action, modebar, param))

        try:
            setter = self.fullmask()
        except:
            setter = self.hostname

        if modebar == 'b':
            if param[:2] not in ['~L', '~T', '~c', '~t', '~b']:
                return

            if param[:2] == '~L':
                # Channel redirect. ~L:host:#chan
                if len(param.split(':')) < 3:
                    return
                redirect_mask = make_mask(localServer, param.split(':')[1])
                redirect_chan = param.split(':')[2]
                param = param[:2] + ':' + redirect_mask + ':' + redirect_chan
                if redirect_chan[0] not in localServer.chantypes:
                    logging.info('Channel {} is invalid for {}{} {}'.format(
                        redirect_chan, action, modebar, param))
                    return
                redirect_chan = next(
                    (c for c in localServer.channels
                     if c.name.lower() == redirect_chan.lower()), None)
                if redirect_chan and 'L' in redirect_chan.modes:
                    logging.info(
                        'Channel {} is invalid for {}{} {} (target has +L set)'
                        .format(redirect_chan.name, action, modebar, param))
                    self.sendraw(690, ':Destination channel already has +L.')
                    return

            elif param[:2] == '~T':
                # Text block.
                if param.split(':')[1] not in ['block', 'replace'
                                               ] or len(param.split(':')) < 3:
                    return
                bAction = param.split(':')[1]
                if not param.split(':')[2:][0]:
                    return
                if bAction == 'replace':
                    # Replace requires an additional parameter: ~T:replace:match:replacement
                    if len(param.split(':')) < 4:
                        return
                    if not param.split(':')[3]:
                        return

            elif param[:2] == '~c':
                ### Channel block.
                if len(param.split(':')) < 2:
                    return
                chanBan = param.split(':')[1]
                if chanBan[0] not in localServer.chantypes or chanBan[
                        0] not in '+%@&~':
                    logging.info('Channel {} is invalid for {}{} {}'.format(
                        chanBan, action, modebar, param))
                    return
                tempchan = list(
                    filter(lambda c: c.name.lower() == chanBan.lower(),
                           localServer.channels))
                if tempchan and len(channel.users) > 2:
                    tempchan = tempchan[0]
                    # tempchan users are forbidden on channel.
                    for user in [
                            user for user in channel.users
                            if tempchan in user.channels
                            and user.chlevel(channel) < 2
                            and not user.ocheck('o', 'override')
                            and not checkMatch(user, localServer, 'e', channel)
                    ]:
                        cmd = (
                            'KICK',
                            '{} {} :Users from {} are not welcome here'.format(
                                channel.name, user.nickname, tempchan.name))
                        commandQueue.append(cmd)

            elif param[:2] == '~t':
                # Timed bans.
                if len(param.split(':')) < 3:
                    return
                bTime = param.split(':')[1]
                if not bTime.isdigit():
                    return
                banmask = make_mask(localServer, param.split(':')[2])
                param = '{}:{}'.format(':'.join(param.split(':')[:2]), banmask)

            elif param[:2] == '~b':
                # Extension on normal bans.
                if len(param.split(':')) < 3:
                    return
                bParam = param.split(':')[1]
                if bParam not in ['R']:
                    return
                banmask = make_mask(localServer, param.split(':')[2])
                param = '{}:{}'.format(':'.join(param.split(':')[:2]), banmask)

        elif modebar == 'I':
            if param[:2] == '~O':
                if len(param.split(':')) < 2:
                    return

        if modebar == 'b':
            c = channel.bans
        elif modebar == 'I':
            c = channel.invex
        elif modebar == 'e':
            c = channel.excepts
        if param not in c:
            modebuf.append(modebar)
            parambuf.append(param)
            c[param] = {}
            c[param]['setter'] = setter
            c[param]['ctime'] = int(time.time())

    except Exception as ex:
        logging.exception(ex)
Esempio n. 18
0
    def __init__(self,
                 conffile=None,
                 forked=False,
                 origin=None,
                 serverLink=False,
                 sock=None,
                 is_ssl=False):
        self.ctime = int(time.time())
        self.syncDone = []
        self.eos = False
        self.sendbuffer = ''
        self.hopcount = 0
        if not serverLink:
            try:
                self.forked = forked
                self.hostname = '*'
                from handle.functions import initlogging
                initlogging(self)
                self.listen_socks = {}
                self.rootdir = dir_path
                self.confdir = dir_path + '/conf/'
                self.modules_dir = dir_path + '/modules/'
                self.tls_files = {}
                self.conffile = conffile
                self.ERR = ERR
                self.RPL = RPL

                self.modules = {}
                self.commands = []
                self.user_modes = {}
                self.channel_modes = {}
                self.hooks = []
                self.api = []
                self.support = {}

                self.command_class = []
                self.user_mode_class = []
                self.channel_mode_class = []

                self.localServer = self
                self.linkRequests = {}
                self.sync_queue = {}

                self.creationtime = int(time.time())
                self.versionnumber = '2.0'
                self.version = 'ProvisionIRCd-{}-beta'.format(
                    self.versionnumber)
                self.hostinfo = 'Python {}'.format(
                    sys.version.split('\n')[0].strip())

                # Polling does not work.
                self.use_poll = 0  # Polling does not work.
                self.pre_wrap = 0  # Polling does not work. Also pre-wrapping may cause memleak? Not sure, needs checking. It will prevent you from reloading certs.
                if self.use_poll:
                    self.pollerObject = select.poll()
                    self.fd_to_socket = {}
                # Polling does not work.

                self.socket = None
                self.introducedBy = None
                self.uplink = None

                self.users = []
                self.channels = []

                self.dnsblCache = {}
                self.hostcache = {}
                self.deny_cache = {}
                self.throttle = {}
                self.tkl = {}
                self.bannedList = []

                self.user_modes = {
                    "i": (0, "User does not show up in outside /who"),
                    "o": (2, "IRC Operator"),
                    "x": (0, "Hides real host with cloaked host"),
                    "q": (1, "Protected on all channels"),
                    "r": (2, "Identifies the nick as being logged in"),
                    "s": (1, "Can receive server notices"),
                    "z": (2, "User is using a secure connection"),
                    "B": (0, "Marks the client as a bot"),
                    "H": (1, "Hide IRCop status"),
                    "S": (2, "Marks the client as a network service"),
                }

                self.channel_modes = {
                    # +v = 1
                    # +h = 2
                    # +o = 3
                    # +a = 4
                    # +q = 5
                    # oper = 6
                    # server = 7
                    0: {
                        "b": (2, "Bans the given hostmask from the channel",
                              "<nick!ident@host>"),
                        "e":
                        (2,
                         "Users matching an except can go through channel bans",
                         "<nick!ident@host>"),
                        "I":
                        (2, "Matching users can go through channel mode +i",
                         "<nick!ident@host>"),
                    },
                    1: {
                        "k":
                        (2,
                         "User must give a key in order to join the channel",
                         "<key>"),
                        "L":
                        (5,
                         "When the channel is full, redirect users to another channel (requires +l)",
                         "<chan>"),
                    },
                    2: {
                        "l": (2, "Set a channel user limit", "[number]"),
                    },
                    3: {
                        "m":
                        (2, "Moderated channel, need +v or higher to talk"),
                        "n": (2, "No outside messages allowed"),
                        "j": (3, "Quits appear as parts"),
                        "p": (3, "Private channel"),
                        "r": (7, "Channel is registered"),
                        "s": (3, "Channel is secret"),
                        "t": (3, "Only +h or higher can change topic"),
                        "z": (3, "Requires SSL to join the channel"),
                        "C": (2, "CTCPs are not allowed in the channel"),
                        "N": (4, "Nickchanges are not allowed in the channel"),
                        "O": (6, "Only IRCops can join"),
                        "P": (6, "Permanent channel"),
                        "Q": (4, "No kicks allowed"),
                        "R": (3, "You must be registered to join the channel"),
                        "T": (2, "Notices are not allowed in the channel"),
                        "V": (3, "Invite is not permitted on the channel"),
                    },
                }

                self.core_chmodes = 'vhoaq'
                chmodes_string = ''
                for t in self.channel_modes:
                    for m in self.channel_modes[t]:
                        chmodes_string += m
                        if t > 0:
                            self.core_chmodes += m
                    chmodes_string += ','
                logging.info('Core modes set: {}'.format(self.core_chmodes))
                self.chmodes_string = chmodes_string[:-1]
                # self.snomasks = 'cdfjkostzCFGNQS'

                # TODO: { "c": (desc, flags) },
                self.snos = {
                    # 1 = local
                    "d": ("Can read local connect/disconnect notices", (1, 3)),
                    "o": ("See oper-up notices", (0, 2)),
                }

                self.snomasks = {
                    "c": "Can read local connect/disconnect notices",
                    "d": "Can see DNSNL hits",
                    "f": "See flood alerts",
                    "k": "View kill notices",
                    "o": "See oper-up notices",
                    "s": "General server notices",
                    "t": "Trash notices (unimportant stuff)",
                    "C": "Can read global connect/disconnect notices",
                    "F": "View spamfilter matches",
                    "G": "View TKL usages",
                    "N": "Can see nick changes",
                    "Q": "View Q:line rejections",
                    "S": "Can see /sanick, /sajoin, and /sapart usage",
                }

                self.chstatus = 'yqaohv'
                self.chprefix = OrderedDict([('y', '!'), ('q', '~'),
                                             ('a', '&'), ('o', '@'),
                                             ('h', '%'), ('v', '+')])
                self.chprefix = OrderedDict(self.chprefix)
                first = '('
                second = ''
                for key in self.chprefix:
                    first += key
                    second += self.chprefix[key]
                first += ')'
                self.chprefix_string = '{}{}'.format(first, second)
                self.parammodes = self.chstatus
                for x in range(0, 4):
                    for m in [
                            m for m in self.channel_modes[x]
                            if str(x) in '012' and m not in self.parammodes
                    ]:
                        self.parammodes += m
                self.chan_params = {}
                self.maxlist = {'b': 500, 'e': 500, 'I': 500}
                self.maxlist_string = "b:{s[b]},e:{s[e]},I:{s[I]}".format(
                    s=self.maxlist)
                self.servers = []
                self.running = 0
                self.caps = [
                    'account-notify', 'away-notify', 'server-time', 'chghost',
                    'echo-message', 'userhost-in-names', 'extended-join',
                    'operwatch'
                ]

                validconf = handle.handleConf.checkConf(
                    self, None, self.confdir, self.conffile)

                if not validconf:
                    exit()
                    return

            except Exception as ex:
                logging.exception(ex)
                exit()
                return

            self.totalcons = 0
            self.gusers = []

            self.linkrequester = {}
            self.pendingLinks = []
            self.introducedTo = []

            self.maxusers = 0
            self.maxgusers = 0
            self.pings = {}

            update_support(self)
            return

        if serverLink:
            self.localServer = origin
            self.socket = sock
            if self.localServer.use_poll:
                fd = self.fileno()
                self.localServer.pollerObject.register(self.socket, READ_WRITE)
                self.localServer.fd_to_socket[fd] = (self.socket, self)
                logging.debug('Added {} to fd dict with fd {}'.format(
                    self, fd))
            self.creationtime = int(time.time())
            self.introducedBy = None
            self.uplink = None
            self.introducedTo = []
            self.sid = None
            self.netinfo = False
            self.linkAccept = False
            self.linkpass = None
            self.cls = None
            self.is_ssl = is_ssl
            self.recvbuffer = ''
            self.name = ''
            self.hostname = ''
            self.ping = int(time.time())
            self.lastPingSent = time.time() * 1000
            self.lag = int((time.time() * 1000) - self.lastPingSent)
            self.origin = origin
            self.localServer.servers.append(self)
Esempio n. 19
0
    def run(self):
        ircd = self.ircd
        while ircd.running:
            try:

                if ircd.use_poll:
                    fdVsEvent = ircd.pollerObject.poll(1000)
                    for fd, Event in fdVsEvent:
                        try:
                            s = ircd.fd_to_socket[fd][0]
                            c = ircd.fd_to_socket[fd][1]
                            t = type(c).__name__

                            if Event & (select.POLLIN | select.POLLPRI):
                                logging.debug('POLLIN for {}'.format(c))
                                if s in self.listen_socks:
                                    logging.debug(
                                        'Incoming connection on {}'.format(s))
                                    threading.Thread(target=sock_accept,
                                                     args=([ircd, s])).start()

                                elif t in ['User', 'Server']:
                                    logging.debug(
                                        'Reading data from {}'.format(c))
                                    read_socket(ircd, c)
                                try:
                                    ircd.pollerObject.modify(s, READ_WRITE)
                                    logging.debug(
                                        'Flag for {} set to READ_WRITE'.format(
                                            c))
                                except FileNotFoundError:  # Already closed.
                                    pass
                                continue
                            elif Event & select.POLLOUT:
                                logging.debug('POLLOUT for {} ({})'.format(
                                    s, c))
                                if c.sendbuffer:
                                    logging.debug(
                                        'Sending data to {}'.format(c))
                                    check_flood(ircd, c)
                                    try:
                                        sent = s.send(
                                            bytes(c.sendbuffer, 'utf-8'))
                                        c.sendbuffer = c.sendbuffer[sent:]
                                    except Exception as ex:
                                        logging.exception(ex)
                                        c.quit('Write error')
                                        time.sleep(1000)
                                logging.debug(
                                    'Flag for {} set to READ_ONLY'.format(c))
                                ircd.pollerObject.modify(s, READ_ONLY)
                                continue
                            elif Event & select.POLLHUP:
                                # ircd.pollerObject.unregister(s)
                                c.quit('Hung up poll')

                            elif Event & select.POLLERR:
                                # ircd.pollerObject.unregister(s)
                                c.quit('Polling error')
                        except Exception as ex:
                            logging.exception(ex)
                            time.sleep(1000)

                        check_loops(ircd)
                        continue

                else:
                    read_clients = itertools.chain(listen_socks(ircd),
                                                   users(ircd), servers(ircd))
                    write_clients = itertools.chain(users(ircd, 'w'),
                                                    servers(ircd, 'w'))

                    # print(f"Size of read_clients: {sys.getsizeof(read_clients)}")
                    try:
                        read, write, error = select.select(
                            read_clients, write_clients, read_clients, 1.0)
                        # read and error need the same iteratable.
                        # read, write, error = select.select(list(self.listen_socks) + read_users + read_servers, write_users + write_servers, read_users + #read_servers + write_users + write_servers + list(self.listen_socks), 1.0)
                    except ValueError as ex:
                        for fd in iter([
                                fd for fd in iter(ircd.users)
                                if fd.socket and not fd.registered
                        ]):
                            fd.quit('Limit reached')
                        logging.info('Cleanup done')
                        logging.exception(ex)
                        continue

                    for s in error:
                        logging.error('Error occurred in {}'.format(s))

                    for s in read:
                        if s in write:  # Write first.
                            continue
                        if type(s).__name__ in ['User', 'Server']:
                            read_socket(ircd, s)
                            continue
                        if self.listen_socks[s] in ['clients', 'servers']:
                            threading.Thread(target=sock_accept,
                                             args=([ircd, s])).start()
                            continue

                    for s in write:
                        check_flood(ircd, s)
                        if type(s).__name__ == 'User' or type(
                                s).__name__ == 'Server':
                            try:
                                sent = s.socket.send(
                                    bytes(s.sendbuffer, 'utf-8'))
                                s.sendbuffer = s.sendbuffer[sent:]
                                if type(s).__name__ == 'User' and (hasattr(
                                        s, 'flood_safe') and s.flood_safe):
                                    s.flood_safe = False
                                    logging.debug(
                                        'Flood_safe for {} unset.'.format(s))
                            except Exception as ex:
                                s.quit('Write error: {}'.format(str(ex)))
                                continue

                    check_loops(ircd)
            except KeyboardInterrupt as ex:
                # cleanup(ircd)
                os._exit(0)
                return

            except Exception as ex:
                logging.exception(ex)
                break
        os._exit(0)
        logging.warning(
            'data_handler loop broke! This should only happen after /restart.')
Esempio n. 20
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()
Esempio n. 21
0
def UnloadModule(self, name):
    try:
        for module in [
                module for module in list(self.modules)
                if module.__name__ == name
        ]:
            # Tuple: commands, user_modes, channel_modes, hooks, support, api, module
            m = module.__name__
            if m == name:
                core_classes = self.user_mode_class + self.channel_mode_class + self.command_class
                for m in [m for m in core_classes if m.module == module]:
                    m.unload()

                if hasattr(module, 'unload'):
                    try:
                        module.unload(self)
                    except Exception as ex:
                        logging.exception(ex)

                for function in [
                        function for function in self.modules[module][3]
                        if hasattr(function, 'hooks')
                ]:
                    for h in list(function.hooks):
                        info = (h[0], h[1], function, module)
                        function.hooks.remove(h)
                        if info in self.hooks:
                            self.hooks.remove(info)
                            # logging.info('Unhooked {}'.format(info))
                        else:
                            logging.error(
                                'Unable to remove hook {}: not found in hooks list'
                                .format(info))

                for function in [
                        function for function in self.modules[module][5]
                        if hasattr(function, 'api')
                ]:
                    for a in list(function.api):
                        function.api.remove(a)
                        api_cmd = a[0]
                        api_host = None if len(a) < 2 else a[1]
                        api_password = None if len(a) < 3 else a[2]
                        info = (api_cmd, function, api_host, api_password,
                                module)
                        try:
                            self.api.remove(info)
                        except ValueError:
                            logging.error(
                                'Callable {} not found in API list.'.format(a))

                # Leftover hooks.
                for h in [h for h in list(self.hooks) if h[2] == module]:
                    logging.error(
                        'Hook {} was not properly removed (or added double). Removing now.'
                        .format(h))
                    self.hooks.remove(h)

                del self.modules[module]

                logging.info('Unloaded: {}'.format(m))
                update_support(self)
                return 1

    except Exception as ex:
        logging.exception(ex)
        return str(ex)
Esempio n. 22
0
    def execute(self, client, recv):
        raw = ' '.join(recv)
        source = list(
            filter(lambda s: s.sid == recv[0][1:] or s.hostname == recv[0][1:],
                   self.ircd.servers))
        if not source:
            logging.error('/SJOIN source not found!')
            return
        source = source[0]

        channel = recv[3]
        if channel[0] == '&':
            logging.error(
                '{}ERROR: received a local channel from remote server: {}{}'.
                format(R, channel, W))
            return client.squit(
                'Sync error! Remote server tried to link local channels.')

        if not client.eos:
            self.ircd.new_sync(self.ircd, client, raw)
        self.ircd.parammodes = self.ircd.chstatus
        for x in range(0, 4):
            for m in [
                    m for m in self.ircd.channel_modes[x]
                    if str(x) in '012' and m not in self.ircd.parammodes
            ]:
                self.ircd.parammodes += m
        memberlist = []
        banlist = []
        excepts = []
        invex = []
        mod_list_data = []  # Store temp data from mods list types.
        c = 0
        if recv[4].startswith('+'):
            modes = recv[4].replace('+', '')
        else:
            modes = ''
        for pos in [pos for pos in recv[1:] if pos]:
            c += 1
            if pos.startswith(':'):
                memberlist = ' '.join(recv[c:]).split('&')[0].split(
                    '"')[0].split("'")[0][1:].split()
                continue
            if pos.startswith('&'):
                banlist.append(pos[1:])
            elif pos.startswith('"'):
                excepts.append(pos[1:])
            elif pos.startswith("'"):
                invex.append(pos[1:])

            elif c > 4 and pos and not pos[0].isalpha() and not pos[0].isdigit(
            ) and pos[0] not in ":&\"'*~@%+":
                if pos in memberlist:
                    memberlist.remove(pos)
                # Unrecognized mode, checking modules.
                # Loop over modules to check if they have a 'mode_prefix' attr.
                try:
                    for m in [
                            m for m in self.ircd.modules
                            if hasattr(m, 'mode_prefix')
                            and pos[0] == m.mode_prefix
                    ]:
                        mod_list_data.append((m.chmode, pos[1:]))
                except Exception as ex:
                    logging.exception(ex)

            # Custom list. In the beginning of SJOIN args.
            custom_mode_list = {}
            list_prefix = pos[0]  # Like ^

            for m in [
                    m for m in self.ircd.channel_mode_class
                    if m.type == 0 and m.mode_prefix == list_prefix
            ]:
                # 2020/02/29 05:31:20 DEBUG [m_sjoin]: Set lokale <ChannelMode 'w'>
                mode = m.mode
                custom_mode_list[mode] = [
                ]  # Params for mode, like +w (whitelist)
                p = pos[1:]
                custom_mode_list[mode].append(p)
                logging.debug(
                    f"Appended {p} to '{mode}' (list_name={m.list_name}) custom mode list."
                )

                continue

        data = []

        giveModes = []
        giveParams = []

        removeModes = []
        removeParams = []

        timestamp = int(recv[2])

        for member in memberlist:
            membernick = []
            for c in member:
                if c not in ':*~@%+':
                    membernick.append(c)
            membernick = ''.join(membernick)

            # userClass = list(filter(lambda c: c.nickname.lower() == membernick.lower() or c.uid == membernick, self.ircd.users))
            user_class = next((c for c in self.ircd.users
                               if c.nickname.lower() == membernick.lower()
                               or c.uid == membernick), None)
            if not user_class:
                logging.error(
                    '{}ERROR: could not fetch userclass for remote user {}. Looks like the user did not sync correctly. Maybe nick collision, or remote leftover from a netsplit.{}'
                    .format(R, membernick, W))
                # continue
                # source.quit('ERROR: could not fetch userclass for remote user {}. Looks like the user did not sync correctly. Maybe nick collision, or remote leftover from a netsplit.'.format(membernick))
                continue

            p = {'override': True, 'sourceServer': client}

            # Making the remote client join local channel, creating if needed.

            user_class.handle('join', channel, params=p)
            localChan = list(
                filter(lambda c: c.name.lower() == channel.lower(),
                       self.ircd.channels))[0]
            local_chan = next(
                (c for c in self.ircd.channels if c.name == channel), None)

            if not local_chan:
                logging.error(
                    f"ERROR: Could not find or create local channel: {channel}"
                )
                return 0

            if len(local_chan.users) == 1:
                # Channel did not exist on self.ircd. Hook channel_create? Sure, why not.
                pass
            if user_class.server != self.ircd:
                logging.info(
                    '{}External user {} joined {} on local server.{}'.format(
                        G, user_class.nickname, channel, W))
            if timestamp < local_chan.creation and not source.eos:
                if '*' in member:
                    giveModes.append('q')
                    giveParams.append(user_class.nickname)
                if '~' in member:
                    giveModes.append('a')
                    giveParams.append(user_class.nickname)
                if '@' in member:
                    giveModes.append('o')
                    giveParams.append(user_class.nickname)
                if '%' in member:
                    giveModes.append('h')
                    giveParams.append(user_class.nickname)
                if '+' in member:
                    giveModes.append('v')
                    giveParams.append(user_class.nickname)

        if timestamp < local_chan.creation and not source.eos:
            # Remote channel is dominant. Replacing modes with remote channel
            # Clear the local modes.
            logging.info(
                'Remote channel {} is dominant. Replacing modes with remote channels\''
                .format(channel))
            local_chan.creation = timestamp
            local_chan.name = channel
            pc = 5
            for m in local_chan.modes:
                if m not in modes and m in list(
                        self.ircd.channel_modes[2]) + list(
                            self.ircd.channel_modes[3]):
                    removeModes.append(m)
                    continue
                # Remote info is different, remove old one first.
                if m in self.ircd.channel_modes[1] and self.ircd.chan_params[
                        local_chan][m] != recv[pc]:
                    removeParams.append(self.ircd.chan_params[local_chan][m])
                    removeModes.append(m)
                if m in self.ircd.parammodes:
                    pc += 1

            pc = 5
            for m in modes:
                if m not in local_chan.modes and m in self.ircd.channel_modes[
                        3]:
                    giveModes.append(m)
                    continue
                if m in self.ircd.parammodes:
                    giveModes.append(m)
                    giveParams.append(recv[pc])
                    logging.debug('SJOIN: Mode {} has param: {}'.format(
                        m, recv[pc]))
                    pc += 1

            # Removing local channel user modes.
            for user in local_chan.users:
                for m in local_chan.usermodes[user]:
                    removeModes.append(m)
                    removeParams.append(user.nickname)

            for b in [b for b in local_chan.bans if b not in banlist]:
                removeModes.append('b')
                removeParams.append(b)

            for e in [e for e in local_chan.excepts if e not in excepts]:
                removeModes.append('e')
                removeParams.append(e)

            for I in [I for I in local_chan.invex if I not in invex]:
                removeModes.append('I')
                removeParams.append(I)

            # Remove modulair lists.
            for m in [m for m in self.ircd.channel_mode_class if m.type == 0]:
                # Remove modulair lists.
                mode = m.mode
                list_name = getattr(m, 'list_name')
                logging.debug(
                    f"Remote takeover, clearing local list: '{list_name}' (if any)"
                )
                l = getattr(local_chan, list_name)
                for local_modulair_mode in l:
                    param = local_modulair_mode
                    logging.debug(
                        f"[SJOIN RT] Removing from local: -{mode} {param}")
                    removeModes.append(mode)
                    removeParams.append(param)

            # Send all remote modes to local_chan
            for b in [b for b in banlist if b not in local_chan.bans]:
                giveModes.append('b')
                giveParams.append(b)

            for e in [e for e in excepts if e not in local_chan.excepts]:
                giveModes.append('e')
                giveParams.append(e)

            for m in custom_mode_list:
                for p in custom_mode_list[m]:
                    logging.debug(f"[SJOIN RT] Syncing from remote: +{m} {p}")
                    giveModes.append(m)
                    giveParams.append(p)

            # ???
            for I in [I for I in invex if I not in local_chan.invex]:
                giveModes.append('I')
                giveParams.append(I)

            for m in [
                    m for m in self.ircd.modules if hasattr(m, 'list_name')
                    and hasattr(local_chan, m.list_name)
            ]:
                remote_temp = []
                for e in mod_list_data:
                    remote_temp.append(e[1])
                for entry in [
                        entry for entry in getattr(local_chan, m.list_name)
                        if entry not in remote_temp
                ]:
                    logging.debug(
                        'Local list entry +{} {} not found in remote data, so removing.'
                        .format(m.chmode, entry, remote_temp))
                    removeModes.append(m.chmode)
                    removeParams.append(entry)

            for entry in [
                    entry for entry in mod_list_data
                    if entry[1] not in getattr(local_chan, m.list_name)
            ]:
                giveModes.append(entry[0])
                giveParams.append(entry[1])

            data = []
            data.append(local_chan.name)
            modes = '{}{}'.format(
                '-' + ''.join(removeModes) if removeModes else '',
                '+' + ''.join(giveModes) if giveModes else '')
            data.append(modes)
            for p in removeParams:
                data.append(p)
            for p in giveParams:
                data.append(p)

        elif timestamp == local_chan.creation and not source.eos:
            if modes:
                logging.info(
                    '{}Equal timestamps for remote channel {} -- merging modes.{}'
                    .format(Y, local_chan.name, W))
                logging.debug(f"Modes: {modes}")
                for member in memberlist:
                    rawUid = re.sub('[:*!~&@%+]', '', member)
                    if '*' in member:
                        giveModes.append('q')
                        giveParams.append(rawUid)
                    if '~' in member:
                        giveModes.append('a')
                        giveParams.append(rawUid)
                    if '@' in member:
                        giveModes.append('o')
                        giveParams.append(rawUid)
                    if '%' in member:
                        giveModes.append('h')
                        giveParams.append(rawUid)
                    if '+' in member:
                        giveModes.append('v')
                        giveParams.append(rawUid)
                pc = 5
                for m in modes:
                    if m not in local_chan.modes:
                        giveModes.append(m)
                        if m in self.ircd.parammodes:
                            giveParams.append(recv[pc])
                            pc += 1
                        continue

                for b in [b for b in banlist if b not in local_chan.bans]:
                    giveModes.append('b')
                    giveParams.append(b)

                for e in [e for e in excepts if e not in local_chan.excepts]:
                    giveModes.append('e')
                    giveParams.append(e)

                for I in [I for I in invex if I not in local_chan.invex]:
                    giveModes.append('I')
                    giveParams.append(I)

                for m in custom_mode_list:
                    for p in custom_mode_list[m]:
                        logging.debug(
                            f"[SJOIN merge] Appending modes: +{m} {p}")
                        giveModes.append(m)
                        giveParams.append(p)

                data = [local_chan.name]
                modes = '{}'.format('+' +
                                    ''.join(giveModes) if giveModes else '')
                data.append(modes)
                for p in removeParams:
                    data.append(p)
                for p in giveParams:
                    data.append(p)

        if modes and data:
            processModes(client,
                         self.ircd,
                         local_chan,
                         data,
                         sync=1,
                         sourceServer=client,
                         sourceUser=client)
Esempio n. 23
0
def check_loops(ircd):
    """
    Checks related to users
    """
    pingfreq = 120
    users = iter([user for user in iter(ircd.users) if user.socket])

    for user in iter([
            user for user in users
            if time.time() - user.ping > pingfreq and time.time() * 1000 -
            user.lastPingSent > pingfreq / 2
    ]):
        user.lastPingSent = time.time() * 1000
        user.lag_measure = user.lastPingSent
        user._send('PING :{}'.format(ircd.hostname))

    ping_users = [user for user in users if time.time() - user.ping >= 180.0]

    for user in list(ping_users):
        user.quit('Ping timeout: {} seconds'.format(
            int(time.time() - user.ping)))

    for t in iter(ircd.tkl):
        for mask in dict(ircd.tkl[t]):
            expire = ircd.tkl[t][mask]['expire']
            if expire == '0':
                continue
            if int(time.time()) > expire:
                mask = '{} {}'.format(mask.split('@')[0], mask.split('@')[1])
                data = '- {} {}'.format(t, mask)
                p = {'expire': True}
                ircd.handle('tkl', data, params=p)

    # Request links
    if ircd.users:
        linkServers = iter([
            link for link in ircd.conf['link']
            if link.lower() != ircd.hostname.lower() and 'outgoing' in
            ircd.conf['link'][link] and 'options' in ircd.conf['link'][link]
            and not list(filter(lambda s: s.hostname == link, ircd.servers))
        ])
        servers = iter(
            [link for link in linkServers if link not in ircd.linkRequests])
        for link in servers:
            ircd.linkRequests[link] = {}
            ircd.linkRequests[link]['ctime'] = int(time.time())

        servers = iter([
            link for link in linkServers
            if 'autoconnect' in ircd.conf['link'][link]['options']
        ])
        for link in iter([
                link for link in servers
                if time.time() - ircd.linkRequests[link]['ctime'] > 900.0
        ]):
            ircd.linkRequests[link] = {}
            ircd.linkRequests[link]['ctime'] = int(time.time())
            logging.info('Auto connecting to {}'.format(link))
            connectTo(None, ircd, link, autoLink=True)

    if len(ircd.dnsblCache) >= 1024:
        del ircd.dnsblCache[ircd.dnsblCache[0]]

    if len(ircd.hostcache) >= 1024:
        del ircd.hostcache[ircd.hostcache[0]]

    if len(ircd.deny_cache) >= 1024:
        del ircd.deny_cache[ircd.deny_cache[0]]

    # Remove cached host look-ups after 6 hours.
    for host in iter([
            host for host in dict(ircd.hostcache)
            if int(time.time()) - ircd.hostcache[host]['ctime'] > 3600.0 * 6
    ]):
        del ircd.hostcache[host]

    # Remove cached DNSBL after 1 day.
    for host in iter([
            host for host in dict(ircd.dnsblCache)
            if int(time.time()) - ircd.dnsblCache[host]['ctime'] > 3600.0 * 24
    ]):
        del ircd.dnsblCache[host]

    # Remove cached Deny entries after 1 day.
    for host in iter([
            host for host in dict(ircd.deny_cache)
            if int(time.time()) - ircd.deny_cache[host]['ctime'] > 3600.0 * 24
    ]):
        del ircd.deny_cache[host]

    # Check for unregistered connections.
    for user in iter([
            user for user in list(ircd.users)
            if user.socket and not user.registered
    ]):
        if time.time() - user.signon >= int(
                ircd.conf['settings']['regtimeout']):
            user.quit('Registration timed out')
            continue

    for throttle in iter(
            throttle for throttle in dict(ircd.throttle)
            if int(time.time()) - ircd.throttle[throttle]['ctime'] > int(
                ircd.conf['settings']['throttle'].split(':')[1])):
        del ircd.throttle[throttle]
        continue

    # Check for timed channels status.
    modify_status = {}
    for chan in channels(ircd):
        try:
            for user in dict(chan.temp_status):
                for mode in chan.temp_status[user]:
                    exp = chan.temp_status[user][mode]['ctime']
                    if int(time.time()) >= exp:
                        param = '{}{} {}'.format(
                            chan.temp_status[user][mode]['action'], mode,
                            user.nickname)
                        if chan not in modify_status:
                            modify_status[chan] = []
                        modify_status[chan].append(param)
                        del chan.temp_status[user]
        except:
            pass
        if chan in modify_status:
            modes = []
            for mode in modify_status[chan]:
                modes.append(mode)
            ircd.handle('MODE', '{} {} 0'.format(chan.name, ' '.join(modes)))
    '''
    Checks related to servers
    '''
    # Send out pings
    pingfreq = 60
    valid_servers = iter([
        server for server in ircd.servers if server.socket and server.hostname
    ])
    for server in iter([
            server for server in valid_servers
            if time.time() - server.ping > pingfreq and time.time() * 1000 -
            server.lastPingSent > pingfreq / 2
    ]):
        server.lastPingSent = time.time() * 1000
        # server.lag_measure = server.lastPingSent
        try:
            server._send(':{} PING {} {}'.format(ircd.sid, ircd.hostname,
                                                 server.hostname))
        except OSError as ex:
            server.quit('Write error: {}'.format(str(ex)))

    # Ping timeouts (only for direct links)
    for server in iter([
            server for server in valid_servers
            if time.time() - server.ping >= 120.0
    ]):
        server.quit('Ping timeout: {} seconds'.format(
            int(time.time() - server.ping)))

    # Registration timeouts
    for server in iter([
            server for server in ircd.servers if not server.eos and (
                (server.introducedBy and not server.introducedBy.eos)
                or server.socket) and time.time() - server.ping >= 10.0
    ]):
        is_silent = False if server.socket else True
        server.quit('Server registration timed out', silent=is_silent)

    # Check for unknown or timed out servers (non-sockets)
    for server in iter([
            server for server in ircd.servers
            if not server.socket and server.uplink and server.uplink.socket
            and time.time() - server.uplink.ping >= 120.0
    ]):
        is_silent = False if server.socket else True
        server.quit('Server uplink ping timed out: {} seconds'.format(
            int(time.time() - server.uplink.ping)))

    for callable in [
            callable for callable in ircd.hooks
            if callable[0].lower() == 'loop'
    ]:
        try:
            callable[2](ircd)
        except Exception as ex:
            logging.exception(ex)

    if not os.path.exists('logs'):
        os.mkdir('logs')

    if not os.path.exists('db'):
        os.mkdir('db')
Esempio n. 24
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
Esempio n. 25
0
def processModes(self,
                 ircd,
                 channel,
                 recv,
                 sync=True,
                 sourceServer=None,
                 sourceUser=None):
    logging.debug('processModes(): {} :: {}'.format(self, recv))
    try:
        if sourceServer != ircd or (type(sourceUser).__name__ == 'User'
                                    and sourceUser.server != ircd):
            hook = 'remote_chanmode'
        else:
            hook = 'local_chanmode'
        rawModes = ' '.join(recv[1:])
        if rawModes.startswith(':'):
            rawModes = rawModes[1:]
        if type(sourceUser).__name__ == 'User':
            displaySource = sourceUser.uid
        else:
            displaySource = sourceUser.sid
            # if not sourceServer.eos and sourceServer != ircd:
            #    sync = False
    except Exception as ex:
        logging.exception(ex)
    try:
        # global modebuf, parambuf, action, prevaction, commandQueue
        modebuf, parambuf, commandQueue = [], [], []
        action = ''
        prevaction = ''
        paramcount = -1
        chmodes = ircd.chstatus
        ircd.parammodes = ircd.chstatus
        for x in range(0, 4):
            for m in [m for m in ircd.channel_modes[x] if m not in chmodes]:
                chmodes += m
                if str(x) in '012' and m not in ircd.parammodes:
                    ircd.parammodes += m

        global oper_override
        extban_prefix = None
        if 'EXTBAN' in ircd.support:
            extban_prefix = ircd.support['EXTBAN'][0]
            # logging.info('Extban prefix set: {}'.format(extban_prefix))

        # Setting some mode level shit.
        # +v = 1
        # +h = 2
        # +o = 3
        # +a = 4
        # +q = 5
        # oper = 6
        # server = 7
        modeLevel = {
            # Channel statuses. Users with +h or higher can always remove their own status.
            # These numbers may seem off, but it's correct. This determines the level requirement to set levels.
            # For example, you can only give voice if you have 2 (+h) or higher, and only give ops with level 3 (+o) or higher.
            'v': 2,
            'h': 3,
            'o': 3,
            'a': 4,
            'q': 5
        }
        for t in ircd.channel_modes:
            for m in ircd.channel_modes[t]:
                level = ircd.channel_modes[t][m][0]
                modeLevel[m] = level
        # for m in [m for m in recv[1] if m in chmodes + '+-' or m in channel.modes]:
        warn = []
        modes = recv[1][1:] if recv[1].startswith(':') else recv[1]
        for m in modes:
            if m not in chmodes + '+-' and m not in channel.modes and type(
                    self).__name__ != 'Server':
                if m not in warn:
                    self.sendraw(ircd.ERR.UNKNOWNMODE,
                                 f"{m} :unknown mode bar")
                    warn.append(m)
                continue
            param_mode = None
            if m in ircd.parammodes:
                if (action == '+') or (action == '-' and m not in list(
                        ircd.channel_modes[2]) + list(ircd.channel_modes[3])):
                    # if (action == '+') or (action == '-' and m in list(ircd.channel_modes[0])+list(ircd.channel_modes[1])):
                    paramcount += 1
                    if len(recv[2:]) > paramcount:
                        param_mode = recv[2:][paramcount]
                        logging.info('Param for {}{}: {}'.format(
                            action, m, param_mode))
                    elif m not in ircd.channel_modes[0]:
                        logging.warning(
                            'Received param mode {}{} without param'.format(
                                action, m))
                        continue
            if m in '+-' and action != m:
                action = m
                # logging.debug('Action set: {}'.format(action))
                if action != prevaction:
                    if modebuf and modebuf[-1] in '-+':
                        modebuf = modebuf[1:]
                    modebuf.append(action)
                    # logging.debug('Modebuf now: {}'.format(modebuf))
                prevaction = action
                continue

            if not action:
                action = '+'
            if m in modeLevel and modeLevel[m] == 6 and (
                    type(self).__name__ != 'Server' and 'o' not in self.modes):
                continue
            if m in modeLevel and modeLevel[m] == 7 and type(
                    self).__name__ != 'Server':
                continue
            if m not in '+-' and action != prevaction and (
                (m in chmodes or m in ircd.chstatus) or
                (action in '+-' and m in channel.modes)):
                modebuf.append(action)
                prevaction = action
            if m not in ircd.chstatus and m not in '+-':
                if m in modeLevel and self.chlevel(
                        channel) < modeLevel[m] and not self.ocheck(
                            'o', 'override'):
                    continue
                elif m in modeLevel and self.chlevel(
                        channel) < modeLevel[m] != 6:
                    oper_override = True

            if m not in ircd.core_chmodes:
                c = next((x for x in ircd.channel_mode_class if x.mode == m),
                         None)
                if c:
                    if not c.check(channel, action, param_mode):
                        continue
                    c.modebuf = modebuf
                    c.parambuf = parambuf
                    if (action == '+' and c.set_mode(self, channel, param_mode)
                        ) or (action == '-'
                              and c.remove_mode(self, channel, param_mode)):
                        pass
                else:
                    # Modules like extbans do not have a mode, so we will check for hooks manually.
                    for callable in [
                            callable for callable in ircd.hooks
                            if callable[0].lower() == 'pre_' +
                            hook and m in callable[1]
                    ]:
                        try:
                            callable[2](self, ircd, channel, modebuf, parambuf,
                                        action, m, param_mode)
                        except Exception as ex:
                            logging.exception(ex)

            if action == '+' and (m in chmodes
                                  or type(self).__name__ == 'Server'):
                # SETTING CHANNEL MODES
                if m == 'l' and len(recv) > 2:
                    if not param_mode.isdigit():
                        continue
                    if int(param_mode) <= 0:
                        continue
                    if m in ircd.chan_params[channel] and int(
                            ircd.chan_params[channel][m]) == int(param_mode):
                        continue
                    else:
                        if m not in channel.modes:
                            channel.modes += m
                        modebuf.append(m)
                        parambuf.append(param_mode)

                elif m == 'k' and m not in ircd.chan_params[channel]:
                    if m not in channel.modes:
                        channel.modes += m
                    modebuf.append(m)
                    parambuf.append(param_mode)

                elif m == 'L':
                    param_mode = param_mode.split(',')[0]
                    if param_mode[0] not in ircd.chantypes:
                        continue
                    redirect = None if 'L' not in channel.modes else ircd.chan_params[
                        channel]['L']
                    if redirect == param_mode or param_mode.lower(
                    ) == channel.name.lower():
                        continue
                    chan_exists = [
                        chan for chan in ircd.channels
                        if chan.name.lower() == param_mode.lower()
                    ]
                    if not chan_exists:
                        self.sendraw(
                            690, ':Target channel {} does not exist.'.format(
                                param_mode))
                        continue
                    if self.chlevel(chan_exists[0]) < 3 and not self.ocheck(
                            'o', 'override'):
                        self.sendraw(
                            690,
                            ':You must be opped on target channel {} to set it as redirect.'
                            .format(chan_exists[0].name))
                        continue
                    if 'L' in chan_exists[0].modes:
                        self.sendraw(690,
                                     ':Destination channel already has +L.')
                        continue
                    elif self.chlevel(channel) < modeLevel[m]:
                        oper_override = True
                    if m not in channel.modes:
                        channel.modes += m
                    modebuf.append(m)
                    parambuf.append(param_mode)

                elif m in 'beI':
                    if extban_prefix and param_mode.startswith(extban_prefix):
                        continue
                    mask = make_mask(ircd, param_mode)
                    if m == 'b':
                        data = channel.bans
                        s = 'ban'
                    elif m == 'e':
                        data = channel.excepts
                        s = 'excepts'
                    elif m == 'I':
                        data = channel.invex
                        s = 'invex'
                    if mask not in data:
                        if len(data) >= ircd.maxlist[m] and type(
                                self).__name__ == 'User':
                            self.sendraw(
                                478, '{} {} :Channel {} list is full'.format(
                                    channel.name, mask, s))
                            continue
                        try:
                            setter = self.fullmask()
                        except:
                            setter = self.hostname
                        modebuf.append(m)
                        parambuf.append(mask)
                        data[mask] = {}
                        data[mask]['setter'] = setter
                        data[mask]['ctime'] = int(time.time())
                        continue

                elif m in ircd.chstatus:
                    timed = False
                    # + status
                    temp_user = param_mode
                    try:
                        t = param_mode.split(':')
                        temp_user = t[0]
                        try:
                            channel.temp_status
                        except:
                            channel.temp_status = {}
                        if valid_expire(t[1]):
                            timed = valid_expire(t[1])
                    except:
                        pass

                    user = list(
                        filter(
                            lambda u: u.uid == temp_user or u.nickname.lower()
                            == temp_user.lower(), channel.users))
                    if not user:
                        continue
                    else:
                        user = user[0]
                    if m in channel.usermodes[user]:
                        continue
                    if type(self).__name__ != 'Server':
                        if self.chlevel(
                                channel) < modeLevel[m] and not self.ocheck(
                                    'o', 'override'):
                            continue
                        elif self.chlevel(channel) < modeLevel[m]:
                            oper_override = True

                    channel.usermodes[user] += m
                    modebuf.append(m)
                    parambuf.append(user.nickname)
                    if timed:
                        channel.temp_status[user] = {}
                        channel.temp_status[user][m] = {}
                        channel.temp_status[user][m]['ctime'] = int(
                            time.time()) + timed
                        channel.temp_status[user][m]['action'] = '-'

                if m not in channel.modes and (m
                                               in list(ircd.channel_modes[3]) +
                                               list(ircd.channel_modes[2])):
                    # If the mode is not handled by modules, do it here.
                    if not next(
                        (x for x in ircd.channel_mode_class if x.mode == m),
                            None):
                        modebuf.append(m)
                        channel.modes += m
                        logging.debug(
                            'Non-modulair mode "{}" has been handled by m_mode'
                            .format(m))

                    if m == 'O' and len(channel.users) > 2:
                        for user in [
                                user for user in channel.users
                                if 'o' not in user.modes
                        ]:
                            cmd = ('KICK', '{} {} :Opers only'.format(
                                channel.name, user.nickname))
                            commandQueue.append(cmd)

            elif action == '-' and ((m in chmodes or m in channel.modes)
                                    or type(self).__name__ == 'Server'):
                # REMOVING CHANNEL MODES
                if m in channel.modes:
                    if m == 'l':
                        # channel.limit = 0
                        if 'L' in channel.modes:  # Also unset -L because we do not need it anymore.
                            channel.modes = channel.modes.replace('L', '')
                            modebuf.append('L')
                            parambuf.append(ircd.chan_params[channel]['L'])

                    elif m == 'k':
                        if param_mode != ircd.chan_params[channel]['k']:
                            continue
                        parambuf.append(ircd.chan_params[channel][m])

                    elif m == 'L':
                        parambuf.append(ircd.chan_params[channel]['L'])
                        # channel.redirect = None

                    elif m == 'P':
                        if len(channel.users) == 0:
                            ircd.channels.remove(channel)

                        try:
                            with open(ircd.rootdir + '/db/chans.db') as f:
                                current_perm = f.read().split('\n')[0]
                                current_perm = json.loads(current_perm)
                                del current_perm[channel.name]
                            with open(ircd.rootdir + '/db/chans.db',
                                      'w+') as f:
                                json.dump(current_perm, f)
                        except Exception as ex:
                            logging.debug(ex)

                    if m in channel.modes:
                        # Only remove mode if it's a core mode. ChannelMode class handles the rest.
                        if m in ircd.core_chmodes:
                            channel.modes = channel.modes.replace(m, '')
                            modebuf.append(m)

                elif m in 'beI':
                    mask = make_mask(ircd, param_mode)
                    if m == 'b':
                        data = channel.bans
                    elif m == 'e':
                        data = channel.excepts
                    elif m == 'I':
                        data = channel.invex
                    if mask in data:
                        del data[mask]
                        parambuf.append(mask)
                        modebuf.append(m)
                    elif param_mode in data:
                        del data[param_mode]
                        parambuf.append(param_mode)
                        modebuf.append(m)
                    # continue
                elif m in ircd.chstatus:
                    timed = False
                    # -qaohv
                    temp_user = param_mode
                    try:
                        t = param_mode.split(':')
                        temp_user = t[0]
                        try:
                            channel.temp_status
                        except:
                            channel.temp_status = {}
                        if valid_expire(t[1]):
                            timed = valid_expire(t[1])
                    except:
                        pass
                    user = list(
                        filter(
                            lambda u: temp_user and u.uid == temp_user or u.
                            nickname.lower() == temp_user.lower(),
                            channel.users))
                    if not user:
                        continue
                    else:
                        user = user[0]
                    if m not in channel.usermodes[user]:
                        continue
                    if 'S' in user.modes and user.server.hostname in ircd.conf[
                            'settings']['ulines'] and not self.ocheck(
                                'o', 'override'):
                        self.sendraw(
                            974, '{} :{} is a protected service bot'.format(
                                m, user.nickname))
                        continue
                    elif 'S' in user.modes:
                        oper_override = True
                    if type(self).__name__ != 'Server':
                        if self.chlevel(
                                channel) < modeLevel[m] and not self.ocheck(
                                    'o', 'override') and user != self:
                            continue
                        elif self.chlevel(channel) < modeLevel[m]:
                            oper_override = True

                    channel.usermodes[user] = channel.usermodes[user].replace(
                        m, '')
                    modebuf.append(m)
                    parambuf.append(user.nickname)
                    if timed:
                        channel.temp_status[user] = {}
                        channel.temp_status[user][m] = {}
                        channel.temp_status[user][m]['ctime'] = int(
                            time.time()) + timed
                        channel.temp_status[user][m]['action'] = '+'

            if m in ircd.core_chmodes:
                # Finally, call modules for core modes.
                for callable in [
                        callable for callable in ircd.hooks
                        if callable[0].lower() == 'pre_' +
                        hook and m in callable[1]
                ]:
                    try:
                        callable[2](self, ircd, channel, modebuf, parambuf,
                                    action, m, param_mode)
                    except Exception as ex:
                        logging.exception(ex)
            continue

        if not re.sub('[+-]', '', ''.join(modebuf)):
            return

        while modebuf[-1] in '+-':
            modebuf = modebuf[:-1]

        if channel.name[0] == '&':
            sync = False

        modes = ''.join(modebuf)
        # total_modes, total_params = [], []
        if len(modebuf) > 1:
            total_modes, total_params = [], []
            paramcount = 0
            action = ''
            for m in modes:
                if m in '+-':
                    action = m
                    total_modes.append(m)
                    continue

                total_modes.append(m)
                if m in list(ircd.channel_modes[1]) + list(
                        ircd.channel_modes[2]):
                    # If a module handles a channel mode with a param, but for some reason forgets to add it to the chan_params dict,
                    # we will add it here. It is really important that param-modes have their params saved.
                    if action == '+':
                        if m in ircd.core_chmodes:
                            logging.debug(
                                '[core] Storing param of {}: {}'.format(
                                    m, parambuf[paramcount]))
                            ircd.chan_params[channel][m] = parambuf[paramcount]
                        elif m not in ircd.chan_params[channel]:
                            logging.debug(
                                '[fallback] Storing param of {}: {}'.format(
                                    m, parambuf[paramcount]))
                            ircd.chan_params[channel][m] = parambuf[paramcount]

                    elif action == '-' and m in ircd.chan_params[channel]:
                        logging.debug(
                            '[fallback] Forgetting param of {}: {}'.format(
                                m, ircd.chan_params[channel][m]))
                        del ircd.chan_params[channel][m]

                for callable in [
                        callable for callable in ircd.hooks
                        if callable[0].lower() == hook
                ]:
                    try:
                        callable[2](self, ircd, channel, modes, parambuf)
                    except Exception as ex:
                        logging.exception(ex)

                if m in ircd.parammodes and (
                        m not in ircd.channel_modes[2]
                        or action == '+') and len(parambuf) > paramcount:
                    # logging.debug(f"Paramcount: {paramcount}")
                    # logging.debug(f"Parambuf: {action}{m} {parambuf}")
                    total_params.append(parambuf[paramcount])
                    paramcount += 1
                    # logging.debug(f"Increased paramcount (now={paramcount}) - moving on")

                totalLength = len(''.join(total_modes) + ' ' +
                                  ' '.join(total_params))
                mode_amount = len(re.sub('[+-]', '', ''.join(total_modes)))
                if mode_amount >= MAXMODES or totalLength >= 400:
                    all_modes = ''.join(total_modes) + ' ' + ' '.join(
                        total_params)
                    if oper_override and type(self).__name__ != 'Server':
                        sourceServer.snotice(
                            's',
                            '*** OperOverride by {} ({}@{}) with MODE {} {}'.
                            format(sourceUser.nickname, sourceUser.ident,
                                   sourceUser.hostname, channel.name,
                                   all_modes))
                    if sync:
                        ircd.new_sync(
                            ircd, sourceServer, ':{} MODE {} :{}'.format(
                                displaySource, channel.name, all_modes if
                                type(self).__name__ == 'User' else rawModes))
                    sourceUser.broadcast(channel.users,
                                         'MODE {} {}'.format(
                                             channel.name, all_modes),
                                         source=sourceUser)
                    total_modes, total_params = [action], []
                    continue
            if len(total_modes) > 1:
                all_modes = ''.join(total_modes) + ' ' + ' '.join(total_params)
                if oper_override and type(self).__name__ != 'Server':
                    sourceServer.snotice(
                        's', '*** OperOverride by {} ({}@{}) with MODE {} {}'.
                        format(sourceUser.nickname, sourceUser.ident,
                               sourceUser.hostname, channel.name, all_modes))
                if sync:
                    ircd.new_sync(
                        ircd, sourceServer, ':{} MODE {} :{}'.format(
                            displaySource, channel.name, all_modes
                            if type(self).__name__ == 'User' else rawModes))
                sourceUser.broadcast(channel.users,
                                     'MODE {} {}'.format(
                                         channel.name, all_modes),
                                     source=sourceUser)

            for cmd, data in commandQueue:
                ircd.handle(cmd, data)

            save_db(ircd)

    except Exception as ex:
        logging.exception(ex)
Esempio n. 26
0
    def handle_recv(self):
        while self.recvbuffer.find("\n") != -1:
            try:
                recv = self.recvbuffer[:self.recvbuffer.find("\n")]
                self.recvbuffer = self.recvbuffer[self.recvbuffer.find("\n") +
                                                  1:]
                recvNoStrip = recv.replace('\r', '').split(' ')
                recv = recv.split()
                if not recv:
                    self.recvbuffer = ''
                    continue
                raw = ' '.join(recv)
                command = recv[0].lower()
                prefix = command[:1]
                localServer = self.localServer
                try:
                    ignore = ['ping', 'pong', 'privmsg', 'notice']
                    # ignore = []
                    if command.lower() not in ignore and recv[1].lower(
                    ) not in ignore:
                        logging.info('{}{} -->>> {}{}'.format(
                            B, self.hostname if self.hostname != '' else self,
                            ' '.join(recvNoStrip), W))
                        pass
                except Exception as ex:
                    pass

                missing_mods = []
                if recv[0].upper() == 'MODLIST':
                    try:
                        remote_modules
                    except:
                        remote_modules = []
                    remote_modules.extend(' '.join(recv[1:])[1:].split())
                    continue
                try:
                    if remote_modules:
                        local_modules = [
                            m.__name__ for m in localServer.modules
                        ]
                        for m in [
                                m for m in remote_modules
                                if m not in local_modules
                        ]:
                            missing_mods.append(m)
                    if missing_mods:
                        ip, port = self.socket.getpeername()
                        # The error is outgoing and will be displayed on the REMOTE server.
                        error = 'Link denied for {}[{}:{}]: they are missing modules: {}'.format(
                            self.hostname, ip, port, ', '.join(missing_mods))

                        string = ', '.join(missing_mods)
                        self._send(':{} ERROR :{}'.format(
                            localServer.sid, error))
                        self.quit('we are missing modules: {}'.format(string))
                        return
                except:
                    pass

                if prefix == '@':
                    # Server only.
                    for s in [
                            s for s in localServer.servers
                            if s != localServer and s != self and s.socket
                    ]:
                        s._send(raw)

                    source = command[1:]
                    serv = list(
                        filter(
                            lambda s: s.hostname == source or s.sid == source,
                            localServer.servers))
                    if not serv:
                        continue
                    target = serv[0]
                    token = recv[1]
                    if token == 'AU':
                        # Send PRIVMSG to all users with given usermode.
                        users = list(
                            filter(lambda u: recv[2] in u.modes,
                                   localServer.users))
                        for user in users:
                            target.broadcast([user], 'PRIVMSG {} {}'.format(
                                user.uid, ' '.join(recv[3:])))

                    elif token == 'Ss':
                        if serv[0] and not serv[0].eos and not serv[
                                0].introducedBy.eos:
                            continue
                        # Send NOTICE to all users with given snomask.
                        msg = ' '.join(recv[3:])[1:]
                        localServer.snotice(recv[2],
                                            msg,
                                            sync=False,
                                            source=serv[0])

                elif prefix == ':':
                    source = command[1:]
                    command = recv[1]

                    if command == 'BW' or command == 'BV' or command == 'SVSSNO':
                        source = list(
                            filter(
                                lambda u: u.uid == recv[0][1:] or u.nickname ==
                                recv[0][1:], localServer.users))
                        if not source:
                            continue
                        if 's' not in source[0].modes:
                            continue
                        snoset = None
                        for m in recv[2]:
                            if m in '+-':
                                snoset = m
                                continue
                            if snoset == '+' and 'm' not in source[0].snomasks:
                                source[0].snomasks += m
                            elif snoset == '-':
                                source[0].snomasks = source[
                                    0].snomasks.replace(m, '')
                        if command == 'BW':
                            source[0]._send(':{} MODE +s :{}'.format(
                                source[0].server.hostname, recv[2:]))
                            source[0].sendraw(
                                8, 'Server notice mask (+{})'.format(
                                    source[0].snomasks))
                        localServer.new_sync(localServer, self, raw)

                    c = next((x for x in localServer.command_class
                              if command.upper() in list(x.command)), None)
                    if c:
                        if c.check(self, recvNoStrip):
                            try:
                                c.execute(self, recvNoStrip)
                            except Exception as ex:
                                logging.exception(ex)

                    for callable in [
                            callable for callable in localServer.commands
                            if callable[0].lower() == command.lower()
                    ]:
                        try:
                            callable[1](self, localServer, recvNoStrip)
                        except Exception as ex:
                            logging.exception(ex)
                            logging.error(
                                'Should we disconnect the server because of this issue?'
                            )
                    continue

                else:
                    c = next((x for x in localServer.command_class
                              if command.upper() in list(x.command)), None)
                    if c:
                        if c.check(self, recvNoStrip):
                            try:
                                c.execute(self, recvNoStrip)
                            except Exception as ex:
                                logging.exception(ex)

            except Exception as ex:
                logging.exception(ex)
                self.quit(str(ex))
Esempio n. 27
0
    def quit(self, reason, silent=False, error=False, source=None, squit=True):
        localServer = self.localServer
        if not hasattr(self, 'socket') or self not in localServer.servers:
            return
        logging.info(f'Server QUIT self: {self} :: reason: {reason}')
        if self in localServer.servers:
            logging.info('Removing self {}'.format(self))
            localServer.servers.remove(self)
        self.recvbuffer = ''
        logging.info('Source: {}'.format(source))
        if self.uplink:
            logging.info('Server was uplinked to {}'.format(self.uplink))
        reason = reason[1:] if reason.startswith(':') else reason
        if self in localServer.introducedTo:
            localServer.introducedTo.remove(self)
        try:
            if self.hostname and self.eos and self.netinfo:
                logging.info(
                    '{}Lost connection to remote server {}: {}{}'.format(
                        R2, self.hostname, reason, W))
                if squit:
                    skip = [self]
                    if self.uplink:
                        skip.append(self.uplink)
                    localServer.new_sync(
                        localServer, skip,
                        ':{} SQUIT {} :{}'.format(localServer.sid,
                                                  self.hostname, reason))

            if not silent and self.hostname and self.socket:
                try:
                    ip, port = self.socket.getpeername()
                except:
                    ip, port = self.socket.getsockname()
                t = 0
                placeholder = ''
                if self.eos and self.netinfo:
                    placeholder = "Lost connection to"
                    t = 1
                elif self.hostname in localServer.pendingLinks:
                    placeholder = "Unable to connect to"
                    t = 2
                elif not self.eos:  # and 'link' in localServer.conf and self.hostname in localServer.conf['link']:
                    placeholder = "Link denied for"
                    t = 3
                if placeholder:
                    msg = '*** {} server {}[{}:{}]: {}'.format(
                        placeholder, self.hostname, ip, port, reason)
                    localServer.snotice('s', msg, local=True)

                if self.is_ssl and t == 2:
                    localServer.snotice(
                        's',
                        '*** Make sure TLS is enabled on both ends and ports are listening for TLS connections.',
                        local=True)

            if self in localServer.linkrequester:
                del localServer.linkrequester[self]

            self.eos = False

            if self.hostname in localServer.linkRequests:
                del localServer.linkRequests[self.hostname]

            if self.hostname in set(localServer.pendingLinks):
                localServer.pendingLinks.remove(self.hostname)

            if self in localServer.sync_queue:
                del localServer.sync_queue[self]

            # if self.socket and reason and self.sid:
            #     logging.debug(f"Sending ERROR from server quit()")
            #     self._send(':{} ERROR :Closing link: [{}] ({})'.format(self.sid, self.socket.getpeername()[0] if not self.hostname else self.hostname, reason))

            while self.sendbuffer:
                logging.debug('Server {} has sendbuffer remaining: {}'.format(
                    self, self.sendbuffer.rstrip()))
                try:
                    sent = self.socket.send(
                        bytes(self.sendbuffer + '\n', 'utf-8'))
                    self.sendbuffer = self.sendbuffer[sent:]
                except:
                    break

            for user in [
                    user for user in localServer.users if not user.server
            ]:
                user.quit('Unknown connection')

            additional_servers = [
                server for server in localServer.servers
                if server.introducedBy == self or server.uplink == self
            ]
            if additional_servers:
                logging.info('Also quitting additional servers: {}'.format(
                    additional_servers))
            users = [
                user for user in localServer.users if user.server and (
                    user.server == self or user.server in additional_servers)
            ]
            for user in users:
                server1 = self.hostname
                server2 = source.hostname if source else localServer.hostname
                user.quit('{} {}'.format(server1, server2), squit=True)

            for server in additional_servers:
                logging.info('Quitting server {}'.format(server))
                server.quit('{} {}'.format(
                    self.hostname,
                    source.hostname if source else localServer.hostname))

            if self.socket:
                if localServer.use_poll:
                    localServer.pollerObject.unregister(self.socket)
                try:
                    self.socket.shutdown(socket.SHUT_WR)
                except:
                    pass
                self.socket.close()

            gc.collect()
            del gc.garbage[:]

            if not localServer.forked:
                try:
                    logging.debug(
                        '[SERVER] Growth after self.quit() (if any):')
                    objgraph.show_growth(limit=10)
                except:
                    pass

            del self

        except Exception as ex:
            logging.exception(ex)
Esempio n. 28
0
    def execute(self, client, recv):
        if not hasattr(client, 'protoctl'):
            client.protoctl = []
        try:
            for p in [p for p in recv[2:] if p not in client.protoctl]:
                try:
                    cap = p.split('=')[0]
                    param = None
                    client.protoctl.append(cap)
                    if '=' in p:
                        param = p.split('=')[1]
                    if cap == 'EAUTH' and param:
                        client.hostname = param.split(',')[0]
                        logging.info('Hostname set from EAUTH: {}'.format(
                            client.hostname))
                        if [
                                s for s in self.ircd.servers + [self.ircd]
                                if s.hostname.lower() ==
                                client.hostname.lower() and s != client
                        ]:
                            ip, port = client.socket.getpeername()
                            error = 'Error connecting to server {}[{}:{}]: server already exists on remote network'.format(
                                self.ircd.hostname, ip, port)
                            client._send(':{} ERROR :{}'.format(
                                self.ircd.sid, error))
                            client.quit(
                                'server already exists on this network')
                            return

                    elif cap == 'SID' and param:
                        for server in [
                                server for server in self.ircd.servers
                                if server.sid == param and server != client
                        ]:
                            client._send(
                                ':{} ERROR :SID {} is already in use on that network'
                                .format(self.ircd.sid, param))
                            client.quit(
                                'SID {} is already in use on that network'.
                                format(param))
                            return
                        client.sid = param

                    elif cap == 'CHANMODES':
                        remote_modes = param.split(',')
                        local_modes = self.ircd.chmodes_string.split(',')
                        missing_modes = []
                        for n in self.ircd.channel_modes:
                            for m in [
                                    m for m in remote_modes[n]
                                    if m not in local_modes[n]
                            ]:
                                missing_modes.append(m)
                        if missing_modes:
                            # The error is outgoing and will be displayed on the REMOTE server.
                            ip, port = client.socket.getpeername()
                            error = 'Link denied for {}[{}:{}]: they are missing channel modes: {}'.format(
                                client.hostname, ip, port,
                                ', '.join(missing_modes))

                            client._send(':{} ERROR :{}'.format(
                                self.ircd.sid, error))

                            client.quit(
                                'we are missing channel modes: {}'.format(
                                    ', '.join(missing_modes)))
                            return

                    elif cap == 'EXTBAN':
                        remote_prefix = param[0]
                        remote_ban_types = param.split(',')[1]
                        local_prefix = None
                        if 'EXTBAN' in self.ircd.support:
                            local_prefix = self.ircd.support['EXTBAN'][0]
                        if remote_prefix != local_prefix:
                            ip, port = client.socket.getpeername()
                            error = 'Link denied for {}[{}:{}]: extban prefixes are not the same. We have: {} but they have: {}'.format(
                                client.hostname, ip, port, remote_prefix,
                                local_prefix)

                            client._send(
                                ':{} ERROR :extban prefixes are not the same. We have: {} but they have: {}'
                                .format(self.ircd.sid, remote_prefix,
                                        local_prefix))
                            client.quit(
                                'extban prefixes are not the same. We have: {} but they have: {}'
                                .format(local_prefix, remote_prefix))
                            return
                        local_ban_types = self.ircd.support['EXTBAN'][1:]
                        logging.info(
                            'We have bantypes: {}'.format(local_ban_types))
                        missing_ext_types = []
                        for m in [
                                m for m in remote_ban_types
                                if m not in local_ban_types
                        ]:
                            missing_ext_types.append(m)
                        if missing_ext_types:
                            error = 'Link denied for {}[{}:{}]: they are missing ext bans: {}'.format(
                                client.hostname, ip, port,
                                ', '.join(missing_ext_types))
                            client._send(':{} ERROR :{}'.format(
                                self.ircd.sid, error))
                            client.quit('we are missing ext bans: {}'.format(
                                ', '.join(missing_ext_types)))
                            return

                except Exception as ex:
                    logging.exception(ex)
                    client.quit(str(ex))

                logging.info(
                    '{}Added PROTOCTL support for {} for server {}{}'.format(
                        P, p, client, W))

        except Exception as ex:
            logging.exception(ex)
Esempio n. 29
0
def whitelist_mode(self, localServer, channel, modebuf, parambuf, action,
                   modebar, param):
    try:
        if (action == '+' or not action) and not param:
            # Requesting list.
            if self.chlevel(channel) < 3 and 'o' not in self.modes:
                return self.sendraw(
                    482,
                    '{} :You are not allowed to view the whitelist'.format(
                        channel.name))
            for entry in OrderedDict(reversed(list(
                    channel.whitelist.items()))):
                self.sendraw(
                    348,
                    '{} {} {} {}'.format(channel.name, entry,
                                         channel.whitelist[entry]['setter'],
                                         channel.whitelist[entry]['ctime']))
            self.sendraw(349,
                         '{} :End of Channel Whitelist'.format(channel.name))
            return 0
        elif not param:
            return
        valid = re.findall("^([1-9][0-9]{0,3}):(.*)", param)
        if not valid:
            logging.info('Invalid param for {}{}: {}'.format(
                action, modebar, param))
            return 0

        mask = make_mask(localServer, param.split(':')[1])
        logging.info('Param for {}{} set: {}'.format(action, modebar, param))
        logging.info('Mask: {}'.format(mask))
        raw_param = param
        param = '{}:{}'.format(':'.join(param.split(':')[:1]), mask)
        if action == '+':
            if param in channel.whitelist:
                return 0
            try:
                setter = self.fullmask()
            except Exception as ex:
                setter = self.hostname
            channel.whitelist[param] = {}
            channel.whitelist[param]['setter'] = setter
            channel.whitelist[param]['ctime'] = int(time.time())
            modebuf.append(modebar)
            parambuf.append(param)

        elif action == '-' and (param in channel.whitelist
                                or raw_param in channel.whitelist):
            if param in channel.whitelist:
                del channel.whitelist[param]
                parambuf.append(param)
            else:
                del channel.whitelist[raw_param]
                parambuf.append(raw_param)

            modebuf.append(modebar)

        return 0

    except Exception as ex:
        logging.exception(ex)