Beispiel #1
0
    def run(self):
        user = self.user
        blacklist = self.blacklist
        # logging.info('Looking up DNSBL query on {}: {}'.format(self.blacklist, user.ip))
        try:
            result = socket.gethostbyname(RevIP(user.ip) + '.' + blacklist)
            reason = 'Your IP is blacklisted by {}'.format(blacklist)
            if user.ip not in user.server.dnsblCache:
                user.server.dnsblCache[user.ip] = {}
                user.server.dnsblCache[user.ip]['bl'] = blacklist
                user.server.dnsblCache[user.ip]['ctime'] = int(time.time())
                msg = '*** DNSBL match for IP {}: {} [nick: {}]'.format(
                    user.ip, blacklist, user.nickname)
                user.server.snotice('d', msg)
            if user in user.server.users:
                user.sendraw(RPL.TEXT, f"* :{reason}")
            user.sendbuffer = ''
            user.recvbuffer = ''
            user.quit(reason)

        except socket.gaierror:  # socket.gaierror: [Errno -2] Name or service not known -> no match.
            pass

        except Exception as ex:
            logging.exception(ex)
Beispiel #2
0
def history_msg(self, localServer, channel, msg):
    try:
        if chmode not in channel.modes:
            return
        limit = channel.msg_backlog['limit']  # Max lines to remember.
        expire = channel.msg_backlog['expire'] * 60
        while len(channel.msg_backlog['lines']) >= limit:
            channel.msg_backlog['lines'] = channel.msg_backlog['lines'][1:]
        utc_time = float(datetime.utcnow().strftime("%s.%f")) * 10
        data = (self.fullmask(), utc_time, msg)
        if channel.msg_backlog['lines']:
            channel.msg_backlog['previous_last'] = channel.msg_backlog[
                'lines'][-1]
        channel.msg_backlog['lines'].append(data)
        if channel not in localServer.m_history:
            localServer.m_history[channel] = {}
        for user in channel.users:
            if user not in localServer.m_history[channel]:
                localServer.m_history[channel][user] = {}
                localServer.m_history[channel][user]['last'] = None
                localServer.m_history[channel][user]['replay_time'] = int(
                    time.time())
            localServer.m_history[channel][user]['last'] = data

        for user in [
                user for user in list(localServer.m_history[channel])
                if user not in channel.users
        ]:
            del localServer.m_history[channel][user]
    except Exception as ex:
        logging.exception(ex)
Beispiel #3
0
def chmode_H2(self, localServer, channel, modebuf, parambuf, action, modebar,
              param):
    try:
        if action == '+':
            limit = int(param.split(':')[0])
            if limit > 25:
                limit = 25
            expire = int(param.split(':')[1])
            if expire > 10080:
                expire = 10080
            param = '{}:{}'.format(limit, expire)
            if not hasattr(channel, 'msg_backlog'):
                channel.msg_backlog = {}
            elif 'lines' in channel.msg_backlog:
                if limit == channel.msg_backlog[
                        'limit'] and expire == channel.msg_backlog['expire']:
                    return
            channel.msg_backlog['limit'] = limit
            channel.msg_backlog['expire'] = expire
            channel.msg_backlog['lines'] = []
            # modebuf.append(modebar)
            # parambuf.append(param)
            # channel.modes += modebar
            # Actually we should also add the chan_param here. BUT MEH F**K IT.
            # return 0
        else:
            channel.msg_backlog = {}
    except Exception as ex:
        logging.exception(ex)
Beispiel #4
0
    def remove_mode(self, user, channel, param=None):
        # Module hooks.
        process = self.pre_hook(user, channel, param, action='-')
        if not process and process is not None:
            return 0

        # This should be handled by pre_* mode hooks
        for callable in [callable for callable in self.ircd.hooks if callable[0].lower() == 'modechar_del']:
            try:
                result = callable[2](self.ircd, self, channel, self.mode)
                if not result and result is not None:
                    logging.debug('Mode remove denied by module: {}'.format(callable))
                    return 0
            except Exception as ex:
                logging.exception(ex)

        if self.mode in self.ircd.chan_params[channel]:
            logging.debug(f'Forgetting param for {self.mode}: {self.ircd.chan_params[channel][self.mode]}')
            del self.ircd.chan_params[channel][self.mode]

        channel.modes = channel.modes.replace(self.mode, '')

        if ((param and param in self.parambuf) and self.mode in self.modebuf) or (not param and self.mode in self.modebuf):
            if param:
                logging.debug(f'Mode conflict: mode "{self.mode}" and param "{param}" are already stored in the buffer.')
            else:
                logging.debug(f'Mode conflict: mode "{self.mode}"is already stored in the buffer.')
            logging.debug(f'A module probably already handled it. Not adding again.')
        else:
            self.modebuf.append(self.mode)
            if param:
                self.parambuf.append(param)

        logging.debug('Channel mode "{}" removed from {} (param: {})'.format(self.mode, channel, param))
        return 1
Beispiel #5
0
def resolve_ip(self):
    ip = self.ip if self.ip.replace('.', '').isdigit() else self.ip[7:]

    try:
        ip_resolved = socket.gethostbyaddr(ip)[0]
    except socket.herror:  # Not a typo.
        ip_resolved = ip
    except Exception as ex:
        logging.exception(ex)

    deny_except = False
    if 'except' in self.server.conf and 'deny' in self.server.conf['except']:
        for e in self.server.conf['except']['deny']:
            if match(e, self.ident + '@' + ip_resolved):
                deny_except = True
                break
    if not deny_except:
        for entry in self.server.deny:
            if match(entry, self.ident + '@' + ip_resolved):
                self.server.deny_cache[ip] = {}
                self.server.deny_cache[ip]['ctime'] = int(time.time())
                self.server.deny_cache[ip]['reason'] = self.server.deny[
                    entry] if self.server.deny[entry] else ''
                if self.server.deny_cache[ip]['reason']:
                    self.server.notice(
                        self, '* Connection denied: {}'.format(
                            self.server.deny_cache[ip]['reason']))
                return self.quit(
                    'Your host matches a deny block, and is therefore not allowed.'
                )
Beispiel #6
0
def savewhowas(self, ircd):
    try:
        if type(self).__name__ == 'Server' or not self.registered:
            return
        if not hasattr(ircd, 'whowas'):
            ircd.whowas = {}
        if self.nickname not in ircd.whowas:
            ircd.whowas[self.nickname] = []
        whowasInfo = {self.nickname: {}}
        whowasInfo[self.nickname]['ident'] = self.ident
        whowasInfo[self.nickname]['cloakhost'] = self.cloakhost
        whowasInfo[self.nickname]['realname'] = self.realname
        whowasInfo[self.nickname]['hostname'] = self.hostname
        whowasInfo[self.nickname]['ip'] = self.ip
        whowasInfo[self.nickname]['server'] = self.server.hostname
        whowasInfo[self.nickname]['signoff'] = int(time.time())

        ircd.whowas[self.nickname].append(whowasInfo[self.nickname])
        if len(ircd.whowas[self.nickname]) > 12:
            del ircd.whowas[self.nickname][0]

        for nick in dict(ircd.whowas):
            info = list(ircd.whowas[nick])
            for data in info:
                signoff = data['signoff']
                if int(time.time()
                       ) - signoff > 3600 * 24 * 30:  # 1 month expire?
                    ircd.whowas[nick].remove(data)

    except Exception as ex:
        logging.exception(ex)
Beispiel #7
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
Beispiel #8
0
def HookToCore(self, callables, reload=False):
    try:
        # Tuple: callables, commands, user_modes, channel_modes, hooks, support, api, module
        hooks = []
        commands = callables[0]
        user_modes = callables[1]
        channel_modes = callables[2]
        module_hooks = callables[3]
        support = callables[4]
        api = callables[5]
        module = callables[6]

        for c in commands + user_modes + channel_modes:
            # c = Command/Mode class
            m = c()
            m.module = module
            m.ircd = self
            m.register()

        if hasattr(module, 'init'):
            module.init(self)

        for callable in [
                callable for callable in api if callable not in hooks
        ]:
            hooks.append(callable)
            for a in [a for a in callable.api if a not in self.api]:
                api_cmd = a[0]
                api_host = None if len(a) < 2 else a[1]
                api_password = None if len(a) < 3 else a[2]
                info = (api_cmd, callable, api_host, api_password, module)
                self.api.append(
                    info
                )  # (cmd, callable, params, req_modes, req_flags, req_class, module)
                # logging.info('Hooked API "{}" (host: {}, password: {}) to function {}'.format(api_cmd, api_host, api_password, callable))

        hooks = []
        for callable in [
                callable for callable in module_hooks if callable not in hooks
        ]:
            hooks.append(callable)
            for h in [h for h in callable.hooks if h]:
                # print(callable)
                info = (h[0], h[1], callable, module)
                self.hooks.append(info)
                # logging.info('Hooked {}'.format(info))

        update_support(self)

    except Exception as ex:
        logging.exception(ex)
        return ex
Beispiel #9
0
 def handle(self, cmd, data, kwargs=None):
     p = ' '.join([':' + self.sid, cmd.upper(), data]).split()
     try:
         c = next((x for x in self.localServer.command_class
                   if cmd.upper() in list(x.command)), None)
         if c:
             if c.check(self, p):
                 if kwargs:
                     c.execute(self, p, **kwargs)
                 else:
                     c.execute(self, p)
     except Exception as ex:
         logging.exception(ex)
Beispiel #10
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))
Beispiel #11
0
    def handle(self, command, data=None, params=None):
        recv = '{} {}'.format(command, data if data else '')
        parsed = self.parse_command(recv)
        command = command.split()[0].lower()
        localServer = self.server if self.socket else self.origin

        c = next((x for x in localServer.command_class
                  if command.upper() in list(x.command)), None)
        if c:
            try:
                if c.check(self, parsed):
                    if not params:
                        c.execute(self, parsed)
                    else:
                        c.execute(self, parsed, **params)
            except Exception as ex:
                logging.exception(ex)
Beispiel #12
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)
Beispiel #13
0
    def snotice(self, sno, msg, sync=True, source=None, local=False):
        localServer = self.localServer
        try:
            if sno:
                if sno in localServer.snos:
                    flags = localServer.snos[sno][1]
                    # print('Flags for {}: {}'.format(sno, flags))

            users = list(
                filter(
                    lambda u: 'o' in u.modes and 's' in u.modes and sno in u.
                    snomasks, localServer.users))
            for user in users:
                try:
                    if sno in localServer.conf['opers'][
                            user.operaccount]['ignore']['snomask']:
                        for m in localServer.conf['opers'][
                                user.operaccount]['ignore']['snomask'][sno]:
                            for word in msg.split():
                                if match(
                                        m, word
                                ) and user in users and user.server == localServer:
                                    users.remove(user)
                                    break
                except Exception as ex:
                    pass

            for user in [user for user in users if user.socket]:
                if source:
                    displaySource = source.hostname
                else:
                    displaySource = self.hostname
                user._send(':{} NOTICE {} :{}'.format(displaySource,
                                                      user.nickname, msg))

            localsno = ['d', 'j', 't',
                        'G']  # I removed 's' from localsno. See you soon.
            if sno not in localsno and sync and not local:
                if sno == 'c':
                    sno = 'C'
                data = '@{} Ss {} :{}'.format(self.hostname, sno, msg)
                localServer.new_sync(localServer, self, data)

        except Exception as ex:
            logging.exception(ex)
Beispiel #14
0
 def setinfo(self, info, t='', source=None):
     try:
         if not info or not type:
             return
         if not source:
             return logging.error('No source provided in setinfo()!')
         if type(source) == str or type(source).__name__ != 'Server':
             return logging.error(
                 'Wrong source type provided in setinfo(): {}'.format(
                     source))
         if t not in ['host', 'ident']:
             return logging.error(
                 'Incorrect type received in setinfo(): {}'.format(t))
         valid = 'abcdefghijklmnopqrstuvwxyz0123456789.-'
         for c in str(info):
             if c.lower() not in valid:
                 info = info.replace(c, '')
         if not info:
             return
         updated = []
         if self.registered:
             for user in iter([
                     user for user in self.ircd.users
                     if 'chghost' in user.caplist and user not in updated
                     and user.socket
             ]):
                 common_chan = list(
                     filter(lambda c: user in c.users and self in c.users,
                            self.ircd.channels))
                 if not common_chan:
                     continue
                 user._send(':{} CHGHOST {} {}'.format(
                     self.fullmask(), info if t == 'ident' else self.ident,
                     info if t == 'host' else self.cloakhost))
                 updated.append(user)
             data = ':{} {} {}'.format(
                 self.uid, 'SETHOST' if t == 'host' else 'SETIDENT', info)
             self.ircd.new_sync(self.ircd, source, data)
         if t == 'host':
             self.cloakhost = info
         elif t == 'ident':
             self.ident = info
     except Exception as ex:
         logging.exception(ex)
Beispiel #15
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)
Beispiel #16
0
    def set_mode(self, user, channel, param=None):
        process = self.pre_hook(user, channel, param, action='+')
        if not process and process is not None:
            # Assume the module handled everything correctly.
            logging.debug(f'Mode "{self.mode}" processing blocked by pre_hook(). We assume the module handled everything correctly.')
            return 0

        for callable in [callable for callable in self.ircd.hooks if callable[0].lower() == 'modechar_add']:
            try:
                result = callable[2](self.ircd, self, channel, self.mode)
                if not result and result is not None:
                    logging.debug('Mode set denied by module: {}'.format(callable))
                    return 0
            except Exception as ex:
                logging.exception(ex)

        if self.type in [0, 1, 2] and param:
            if self.mode in self.ircd.core_chmodes or self.type == 2:  # Type 2 modes update when being set.
                logging.debug('Storing param of {}: {}'.format(self.mode, param))
                self.ircd.chan_params[channel][self.mode] = param

            elif self.mode not in self.ircd.chan_params[channel]:
                logging.debug('2 Storing param of {}: {}'.format(self.mode, param))
                self.ircd.chan_params[channel][self.mode] = param

        if ((param and param in self.parambuf) and self.mode in self.modebuf) or (not param and self.mode in self.modebuf):
            if param:
                logging.debug(f'Mode conflict: mode "{self.mode}" and param "{param}" are already stored in the buffer.')
            else:
                logging.debug(f'Mode conflict: mode "{self.mode}"is already stored in the buffer.')
            logging.debug('A module probably already handled it. Not adding again.')
        else:
            self.modebuf.append(self.mode)
            if param:
                self.parambuf.append(param)

        if self.mode not in channel.modes:
            channel.modes += self.mode
            logging.debug('Channel mode "{}" set on {} (param: {})'.format(self.mode, channel, param))
        else:
            logging.debug('Channel mode "{}" updated on {} (param: {})'.format(self.mode, channel, param))
        return 1
Beispiel #17
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)
Beispiel #18
0
def connectTo(self, ircd, name, autoLink=False):
    try:
        host, port = ircd.conf['link'][name]['outgoing']['host'], ircd.conf[
            'link'][name]['outgoing']['port']
        # If the host is local, and you are listening for servers on the port, do not connect to yourself.
        if host in [
                '127.0.0.1', '0.0.0.0', 'localhost'
        ] and (port in ircd.conf['listen']
               and ircd.conf['listen'][str(port)]['options'] == 'servers'):
            return
        pswd = ircd.conf['link'][name]['pass']
        is_ssl = False
        if 'options' in ircd.conf['link'][name] and (
                'tls' in ircd.conf['link'][name]['options']
                or 'ssl' in ircd.conf['link'][name]['options']):
            is_ssl = True
        l = Link(self, ircd, name, host, port, pswd, is_ssl, autoLink)
        l.start()

    except Exception as ex:
        logging.exception(ex)
Beispiel #19
0
    def new_sync(self, ircd, skip, data, direct=None):
        try:
            if type(skip) != list:
                skip = [skip]
            for t in [t for t in skip if type(t).__name__ != 'Server']:
                logging.error(
                    '{}HALT: wrong source type in new_sync(): {} with data: {}{}'
                    .format(R2, t, data, W))
                return
            if data.split()[1] in ['UID', 'SID']:
                data = data.split()
                data = '{} {} {}'.format(' '.join(data[:3]),
                                         str(int(data[3]) + 1),
                                         ' '.join(data[4:]))
            if direct:  # Private messages and notices. direct represents the target.server
                dest = direct if direct.socket else direct.uplink
                # if direct.socket:
                # logging.debug('Directly linked to us, no more hops needed.')
                if not direct.socket:
                    logging.debug(
                        'Server has hopcount of {d.hopcount}, sending to {d.uplink} first.'
                        .format(d=direct))
                dest._send(data)
                return

            for server in [
                    server for server in ircd.servers
                    if server and server.socket and server not in skip
            ]:
                if not server.eos:
                    if server not in ircd.sync_queue:
                        ircd.sync_queue[server] = []
                    ircd.sync_queue[server].append(data)
                    logging.debug(
                        '{}Added to {} sync queue because they are not done syncing: {}{}'
                        .format(R2, server, data, W))
                    continue
                server._send(data)
        except Exception as ex:
            logging.exception(ex)
Beispiel #20
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)
Beispiel #21
0
def read_socket(ircd, sock):
    if not hasattr(sock, 'socket'):
        # Client probably repidly disconnected. Possible causes can be ZNC that have not yet accepted new cert.
        # sock.quit('No socket')
        return

    try:
        if sock.cls:
            buffer_len = int(ircd.conf['class'][sock.cls]['sendq']) * 2
        else:
            buffer_len = 8192 if type(sock).__name__ == 'User' else 65536
        try:
            recv = sock.socket.recv(buffer_len).decode('utf-8')

        except UnicodeDecodeError as ex:  # Do nothing, skip read.
            logging.debug(f'Unable to read socket {sock}: {ex}')

            #####
            sock.quit('')  # Drunk shit, REMOVE THIS!!!!!!!!! #####
            #####

            return
        except Exception as ex:
            # logging.exception(ex)
            sock.quit('Read error: {}'.format(ex))
            return

        if not recv:
            # logging.error('No data received from {}'.format(sock))
            sock.quit('Read error')
            return

        sock.recvbuffer += recv
        check_flood(ircd, sock)
        sock.handle_recv()
        return recv
    except Exception as ex:
        logging.exception(ex)
Beispiel #22
0
    def pre_hook(self, user, channel, param, action=''):
        if type(user).__name__ == 'User':  # Servers can set modes too.
            hook = 'local_chanmode' if user.server != self.ircd else 'remote_chanmode'
        else:
            hook = 'local_chanmode' if user == self.ircd else 'remote_chanmode'
        # logging.debug('Looking for pre_* hooks for {}'.format(self))

        if self.mode not in self.ircd.core_chmodes and self.type == 0:
            if not hasattr(channel, self.list_name):
                setattr(channel, self.list_name, {})

        for callable in [callable for callable in self.ircd.hooks if callable[0].lower() == 'pre_' + hook and self.mode in callable[1]]:
            try:
                # logging.debug('Calling {} with action {}'.format(callable, action))
                # We pass the modebar to the module hook because we need to know which mode to work on.
                ok = callable[2](user, self.ircd, channel, self.modebuf, self.parambuf, action, self.mode, param)
                if not ok and ok is not None:
                    logging.debug('Further processing halted for {}{}{}'.format(action, self.mode, ' ' + param if param else ''))
                    logging.debug('Blocked by: {}'.format(callable))
                    return 0
            except Exception as ex:
                logging.exception(ex)
        return 1
Beispiel #23
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
Beispiel #24
0
def hidejoin(self, ircd, channel, **kwargs):
    try:
        if chmode in channel.modes:
            global can_see
            if channel not in can_see:
                can_see[channel] = {}
                logging.debug('/JOIN: Channel {} added to can_see dict'.format(
                    channel.name))
            if self not in can_see[channel]:
                can_see[channel][self] = []
            # <self> just joined <channel>. They can see everyone currently on the channel.
            can_see[channel][self] = list(
                channel.users
            )  # /!\ Do NOT RE-ASSIGN the list. Make a copy! /!\
            for user in [user for user in channel.users if user != self]:
                if visible_in_chan(
                        user, ircd, self,
                        channel) and self not in can_see[channel][user]:
                    can_see[channel][user].append(self)
                    logging.debug('/join: User {} can see {}'.format(
                        user.nickname, self.nickname))
        return 1, 0
    except Exception as ex:
        logging.exception(ex)
Beispiel #25
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
Beispiel #26
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)
Beispiel #27
0
    def quit(self,
             reason,
             error=True,
             banmsg=None,
             kill=False,
             silent=False,
             api=False,
             squit=False):  # Why source?
        try:
            if not hasattr(self, 'socket'):
                self.socket = None
            self.recvbuffer = ''
            ircd = self.ircd if not self.socket else self.server
            sourceServer = self.server if (self.server.socket or self.server
                                           == ircd) else self.server.uplink
            if self.registered:
                logging.debug('User {} quit. Uplink source: {}'.format(
                    self.nickname, sourceServer))
            for callable in [
                    callable for callable in ircd.hooks
                    if callable[0].lower() == 'pre_local_quit'
            ]:
                try:
                    callable[2](self, ircd)
                except Exception as ex:
                    logging.exception(ex)

            if banmsg:
                ircd.notice(
                    self,
                    '*** You are banned from this server: {}'.format(banmsg))

            if int(
                    time.time()
            ) - self.signon < 60 and self.registered and not error and self.socket:
                reason = str(ircd.conf['settings']['quitprefix']).strip()
                if reason.endswith(':'):
                    reason = reason[:-1]
                reason += ': ' + self.nickname

            if self.socket and reason and not api:
                self._send('ERROR :Closing link: [{}] ({})'.format(
                    self.hostname, reason))

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

            if self in ircd.pings:
                del ircd.pings[self]
                # logging.debug('Removed {} from server PING check'.format(self))

            if self.registered and (self.server == ircd or self.server.eos):
                if reason and not kill:
                    skip = [sourceServer]
                    if squit:
                        for server in [
                                server for server in ircd.servers
                                if hasattr(server, 'protoctl')
                                and 'NOQUIT' in server.protoctl
                        ]:  # and not server.eos]:
                            skip.append(server)
                    ircd.new_sync(ircd, skip,
                                  ':{} QUIT :{}'.format(self.uid, reason))

                if self.socket and reason and not silent:
                    ircd.snotice(
                        'c', '*** Client exiting: {} ({}@{}) ({})'.format(
                            self.nickname, self.ident, self.hostname, reason))

            self.registered = False

            for channel in iter(
                [channel for channel in self.channels
                 if 'j' in channel.modes]):
                self.handle('PART', '{}'.format(channel.name))
                continue

            # Check module hooks for visible_in_channel()

            # FIX: [05:57:26] * vknzvmwlcrcdzz ([email protected]) Quit (Write error: [Errno 110] Connection timed out)
            # IN STATUS WINDOW?

            all_broadcast = [self]
            for channel in self.channels:
                for user in channel.users:
                    if user not in all_broadcast and user != self:
                        all_broadcast.append(user)
            inv_checked = 0
            for u in iter([u for u in all_broadcast if u != self]):
                visible = 0
                for channel in iter(
                    [chan for chan in self.channels if not visible]):
                    for callable in [
                            callable for callable in ircd.hooks
                            if callable[0].lower() == 'visible_in_channel'
                    ]:
                        try:
                            visible = callable[2](u, ircd, self, channel)
                            inv_checked = 1
                            # logging.debug('Is {} visible for {} on {}? :: {}'.format(self.nickname, u.nickname, channel.name, visible))
                        except Exception as ex:
                            logging.exception(ex)
                    if visible:  # Break out of the channels loop. No further checks are required.
                        break
                if not visible and inv_checked:
                    logging.debug(
                        'User {} is not allowed to see {} on any channel, not sending quit.'
                        .format(u.nickname, self.nickname))
                    all_broadcast.remove(u)

            if self.nickname != '*' and self.ident != '' and reason:
                self.broadcast(all_broadcast, 'QUIT :{}'.format(reason))

            for channel in list(self.channels):
                channel.users.remove(self)
                del channel.usermodes[self]
                self.channels.remove(channel)
                if len(channel.users) == 0 and 'P' not in channel.modes:
                    ircd.channels.remove(channel)
                    del ircd.chan_params[channel]
                    for callable in [
                            callable for callable in ircd.hooks
                            if callable[0].lower() == 'channel_destroy'
                    ]:
                        try:
                            callable[2](self, ircd, channel)
                        except Exception as ex:
                            logging.exception(ex)

            watch_notify_offline = iter([
                user for user in ircd.users if self.nickname.lower() in
                [x.lower() for x in user.watchlist]
            ])
            for user in watch_notify_offline:
                user.sendraw(
                    RPL.LOGOFF, '{} {} {} {} :logged offline'.format(
                        self.nickname, self.ident, self.cloakhost,
                        self.signon))

            if self in ircd.users:
                ircd.users.remove(self)

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

            hook = 'local_quit' if self.server == ircd else 'remote_quit'
            for callable in [
                    callable for callable in ircd.hooks
                    if callable[0].lower() == hook
            ]:
                try:
                    callable[2](self, ircd)
                except Exception as ex:
                    logging.exception(ex)

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

            if not ircd.forked:
                try:
                    logging.debug('Growth after self.quit() (if any):')
                    objgraph.show_growth(limit=10)
                except:  # Prevent weird spam shit.
                    pass

            del self

        except Exception as ex:
            logging.exception(ex)
Beispiel #28
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()
Beispiel #29
0
    def __init__(self,
                 server,
                 sock=None,
                 address=None,
                 is_ssl=None,
                 server_class=None,
                 params=None):
        try:
            self.socket = sock
            self.server = None
            self.cloakhost = '*'
            self.connected = True
            self.nickname = '*'
            self.ident = ''
            self.hostname = ''
            self.realname = ''
            self.svid = '*'
            self.channels = []
            self.modes = ''
            self.operflags = []
            self.snomasks = ''
            self.swhois = []
            self.watchlist = []
            self.caplist = []
            self.sends_cap = False
            self.cap_end = False
            self.watchC = False
            self.watchS = False
            self.ssl = is_ssl

            self.operaccount = ''
            self.away = False
            self.sendbuffer = ''
            self.operswhois = ''
            self.fingerprint = None

            self.flood_penalty = 0
            self.flood_penalty_time = 0

            if self.socket:
                self.server = server
                self.ircd = server
                self.addr = address
                self.ip, self.hostname = self.addr[0], self.addr[0]
                if self.ip.startswith('::ffff:') and self.ip[7:].replace(
                        '.', '').isdigit():
                    # logging.debug('Invalid IPv6, using {}'.format(self.ip[7:]))
                    self.ip = self.ip[7:]
                if self.hostname.startswith(
                        '::ffff:') and self.hostname[7:].replace('.',
                                                                 '').isdigit():
                    # logging.debug('Invalid IPv6, using {}'.format(self.ip[7:]))
                    self.hostname = self.hostname[7:]

                self.cls = None
                self.signon = int(time.time())
                self.registered = False
                self.ping = int(time.time())
                self.recvbuffer = ''
                self.validping = False
                self.server_pass_accepted = False
                self.uid = '{}{}'.format(
                    self.server.sid, ''.join(
                        random.choice(string.ascii_uppercase + string.digits)
                        for _ in range(6)))
                while [
                        u for u in self.server.users if hasattr(u, 'uid')
                        and u != self and u.uid == self.uid
                ]:
                    # while list(filter(lambda u: u.uid == self.uid, self.server.users)):
                    self.uid = '{}{}'.format(
                        self.server.sid, ''.join(
                            random.choice(string.ascii_uppercase +
                                          string.digits) for _ in range(6)))

                self.lastPingSent = time.time() * 1000
                self.lag_measure = self.lastPingSent

                self.server.users.append(self)
                for callable in [
                        callable for callable in server.hooks
                        if callable[0].lower() == 'new_connection'
                ]:
                    try:
                        callable[2](self, server)
                    except Exception as ex:
                        logging.exception(ex)
                if 'dnsbl' in self.server.conf and self.ip.replace(
                        '.', '').isdigit() and not ipaddress.ip_address(
                            self.ip).is_private:
                    # self.sendraw('020', ':Please wait while we process your connection.')
                    dnsbl_except = False
                    if 'except' in self.server.conf and 'dnsbl' in self.server.conf[
                            'except']:
                        for e in self.server.conf['except']['dnsbl']:
                            if match(e, self.ip):
                                dnsbl_except = True
                                break
                    if not dnsbl_except:
                        DNSBLCheck(self)

                TKL.check(self, self.server, self, 'z')
                TKL.check(self, self.server, self, 'Z')

                throttleTreshhold = int(
                    self.server.conf['settings']['throttle'].split(':')[0])
                throttleTime = int(
                    self.server.conf['settings']['throttle'].split(':')[1])
                total_conns = [
                    u for u in self.server.throttle
                    if u.ip == self.ip and int(time.time()) -
                    self.server.throttle[u]['ctime'] <= throttleTime
                ]
                throttle_except = False
                if 'except' in self.server.conf and 'throttle' in self.server.conf[
                        'except']:
                    for e in self.server.conf['except']['throttle']:
                        if match(e, self.ip):
                            throttle_except = True
                            break
                if len(total_conns
                       ) >= throttleTreshhold and not throttle_except:
                    self.quit('Throttling - You are (re)connecting too fast')

                unknown_conn = [
                    user for user in self.server.users
                    if user.ip == self.ip and not user.registered
                ]
                if len(unknown_conn) > 20:
                    self.quit('Too many unknown connections from your IP')

                self.server.throttle[self] = {}
                self.server.throttle[self]['ip'] = self.ip
                self.server.throttle[self]['ctime'] = int(time.time())
                self.server.totalcons += 1

                if self.ssl and self.socket:
                    try:
                        fp = self.socket.getpeercert(binary_form=True)
                        if fp:
                            self.fingerprint = hashlib.sha256(
                                repr(fp).encode('utf-8')).hexdigest()
                    except Exception as ex:
                        logging.exception(ex)

                self.idle = int(time.time())
                if self.ip in self.server.hostcache:
                    self.hostname = self.server.hostcache[self.ip]['host']
                    self._send(
                        ':{u.server.hostname} NOTICE AUTH :*** Found your hostname ({u.hostname}) [cached]'
                        .format(u=self))
                elif 'dontresolve' not in self.server.conf['settings'] or (
                        'dontresolve' in self.server.conf['settings']
                        and not self.server.conf['settings']['dontresolve']):
                    try:
                        self.hostname = socket.gethostbyaddr(self.ip)[0]
                        if not self.hostname.split('.')[1]:
                            raise
                        self.server.hostcache[self.ip] = {}
                        self.server.hostcache[self.ip]['host'] = self.hostname
                        self.server.hostcache[self.ip]['ctime'] = int(
                            time.time())
                        self._send(
                            ':{u.server.hostname} NOTICE AUTH :*** Found your hostname ({u.hostname})'
                            .format(u=self))
                    except Exception as ex:
                        self.hostname = self.ip
                        # self._send(':{} NOTICE AUTH :*** Couldn\'t resolve your hostname; using IP address instead ({})'.format(self.server.hostname, self.hostname))
                        self._send(
                            ':{u.server.hostname} NOTICE AUTH :*** Couldn\'t resolve your hostname; using IP address instead ({u.hostname})'
                            .format(u=self))
                else:
                    self._send(
                        ':{u.server.hostname} NOTICE AUTH :*** Host resolution is disabled, using IP ({u.ip})'
                        .format(u=self))

                TKL.check(self, self.server, self, 'g')
                TKL.check(self, self.server, self, 'G')

                self.cloakhost = cloak(self)

            else:
                try:
                    self.origin = server_class
                    self.ircd = server_class
                    self.origin.users.append(self)
                    self.cls = 0
                    self.nickname = params[2]
                    self.idle = int(params[4])
                    self.signon = int(params[4])
                    self.ident = params[5]
                    self.hostname = params[6]
                    self.uid = params[7]
                    server = list(
                        filter(lambda s: s.sid == params[0][1:],
                               self.ircd.servers))
                    if not server:
                        logging.debug(
                            f'Quitting {self.nickname} because their server does not exist'
                        )
                        self.quit('Unknown connection')
                        return
                    self.server = server[0]
                    self.modes = params[9].strip('+')
                    if params[11] == '*':
                        self.cloakhost = params[6]
                    else:
                        self.cloakhost = params[11]
                    if params[12] != '*' and not params[12].replace(
                            '.', '').isdigit() and params[12] is not None:
                        self.ip = Base64toIP(params[12])
                    else:
                        self.ip = params[12]
                    self.realname = ' '.join(params[13:])[1:]
                    self.registered = True
                    TKL.check(self, self.origin, self, 'Z')
                    TKL.check(self, self.origin, self, 'G')
                    if len(self.origin.users) > self.origin.maxgusers:
                        self.origin.maxgusers = len(self.origin.users)

                    watch_notify = iter([
                        user for user in self.origin.users
                        if self.nickname.lower() in
                        [x.lower() for x in user.watchlist]
                    ])
                    for user in watch_notify:
                        user.sendraw(
                            RPL.LOGON, '{} {} {} {} :logged online'.format(
                                self.nickname, self.ident, self.cloakhost,
                                self.signon))

                    # msg = '*** Remote client connecting: {} ({}@{}) {{{}}} [{}{}]'.format(self.nickname, self.ident, self.hostname, str(self.cls), 'secure' if 'z' in self.modes else 'plain', ' '+self.socket.cipher()[0] if self.ssl else '')
                    # self.server.snotice('C', msg)
                except Exception as ex:
                    logging.exception(ex)
            # logging.info('New user class {} successfully created'.format(self))
            gc.collect()

        except Exception as ex:
            logging.exception(ex)
Beispiel #30
0
    def handle_recv(self):
        try:
            while self.recvbuffer.find('\n') != -1:
                recv = self.recvbuffer[:self.recvbuffer.find('\n')]
                self.recvbuffer = self.recvbuffer[self.recvbuffer.find('\n') +
                                                  1:]
                recv = recv.rstrip(' \n\r')
                if not recv:
                    continue

                ircd = self.server
                command = recv.split()[0].lower()

                self.ping = int(time.time())
                if not hasattr(self, 'flood_safe') or not self.flood_safe:
                    self.flood_penalty += 1000 + len(recv)
                check_flood(ircd, self)

                if not self.flood_penalty_time:
                    self.flood_penalty_time = int(time.time())

                dont_parse = ['topic', 'swhois', 'prop']
                if command in dont_parse:
                    parsed = recv.split(' ')
                else:
                    parsed = self.parse_command(recv)

                pre_reg_cmds = [
                    'nick', 'user', 'pass', 'pong', 'cap', 'starttls', 'webirc'
                ]

                if not self.registered and self.cls and not self.server_pass_accepted and 'password' in ircd.conf[
                        'allow'][self.cls] and command not in ['pass', 'cap']:
                    return self.quit('Password required')

                ignore = [
                    'ping', 'pong', 'ison', 'watch', 'who', 'privmsg',
                    'notice', 'ns', 'cs', 'nickserv', 'chanserv', 'id',
                    'identify', 'login', 'auth'
                ]
                # ignore = []
                if command not in ignore:
                    pass

                # Looking for API calls.
                if not self.registered:
                    for callable in [
                            callable for callable in self.server.api
                            if callable[0].lower() == command
                    ]:
                        api_func = callable[1]
                        api_host = callable[2]
                        api_password = callable[3]
                        if api_host and not match(api_host, self.ip):
                            self.quit('API', api=True)
                            break
                        if api_password and recv[1] != api_password:
                            self.quit('API', api=True)
                            break
                        api_func(self, ircd, parsed)
                        self.quit('API', api=True)
                        return

                # print('ik ga zo slaaaaaapen maar jij bent ernie?')
                if type(
                        self
                ).__name__ == 'User' and command not in pre_reg_cmds and not self.registered:
                    return self.sendraw(ERR.NOTREGISTERED,
                                        'You have not registered')
                if command == 'pong':
                    if self in self.server.pings:
                        ping = recv.split()[1]
                        if ping.startswith(':'):
                            ping = ping[1:]
                        if self.server.pings[self] == ping:
                            del self.server.pings[self]
                            self.validping = True
                            if self.ident != '' and self.nickname != '*' and (
                                    self.cap_end or not self.sends_cap):
                                self.welcome()
                        else:
                            return self.quit('Unauthorized connection')

                try:
                    cmd = importlib.import_module('cmds.cmd_' +
                                                  command.lower())
                    getattr(cmd, 'cmd_' + command.upper())(self, ircd, parsed)
                    continue
                except ImportError:
                    try:
                        alias = ircd.conf['aliases']
                        if alias[command.lower()]['type'] == 'services':
                            service = list(
                                filter(
                                    lambda u: u.nickname == alias[
                                        command.lower()]['target'] and
                                    'services' in ircd.conf['settings'] and u.
                                    server.hostname == ircd.conf['settings'][
                                        'services'], ircd.users))
                            if not service:
                                return self.sendraw(
                                    ERR.SERVICESDOWN,
                                    ':Services are currently down. Please try again later.'
                                )
                        data = '{} :{}'.format(
                            alias[command.lower()]['target'],
                            ' '.join(recv.split()[1:]))
                        self.handle('PRIVMSG', data)
                        continue
                    except KeyError:
                        pass

                # pre_command hook.
                allow = 1
                for callable in [
                        callable for callable in self.server.hooks
                        if callable[0].lower() == 'pre_command'
                        and callable[1].lower() == command.lower()
                ]:
                    try:
                        allow = callable[2](self, ircd, parsed)
                    except Exception as ex:
                        logging.exception(ex)
                if not allow and allow is not None:
                    continue

                false_cmd = True
                c = next((x for x in ircd.command_class
                          if command.upper() in list(x.command)), None)
                if c:
                    false_cmd = False
                    if c.check(self, parsed):
                        c.execute(
                            self, parsed
                        )  # <--- instant reply from /stats u (where psutil.Process() is being called)
                        # threading.Thread(target=c.execute, args=([self, parsed])).start() # ~1 second delay in /stats u

                if false_cmd:
                    self.sendraw(ERR.UNKNOWNCOMMAND,
                                 '{} :Unknown command'.format(command.upper()))

        except Exception as ex:
            logging.exception(ex)