Пример #1
0
    def connect(self):
        """ initiates the connection to the server set in self.host:self.port
        and returns a generator object.

        >>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667)
        >>> g = cli.connect()
        >>> while 1:
        ...     next(g)

        """
        try:
            retries = 0
            while True:
                try:
                    self.socket = socket.create_connection(("{0}".format(self.host), self.port))
                    break
                except socket.error as e:
                    retries += 1
                    self.stream_handler('Error: {0}'.format(e), level="warning")
                    if retries > 3:
                        sys.exit(1)

            if self.use_ssl:
                self.socket = ssl.wrap_socket(self.socket)

            if not self.blocking:
                self.socket.setblocking(0)

            self.cap("LS", "302")

            if self.server_pass and (not self.sasl_auth or "{password}" not in self.server_pass):
                message = "PASS :{0}".format(self.server_pass).format(
                    account=self.authname if self.authname else self.nickname,
                    password=self.password)
                self.send(message)

            self.nick(self.nickname)
            self.user(self.ident, self.real_name)

            if self.connect_cb:
                try:
                    self.connect_cb(self)
                except Exception as e:
                    sys.stderr.write(traceback.format_exc())
                    raise e

            buffer = bytes()
            while not self._end:
                try:
                    buffer += self.socket.recv(1024)
                except socket.error as e:
                    if False and not self.blocking and e.errno == 11:
                        pass
                    else:
                        sys.stderr.write(traceback.format_exc())
                        raise e
                else:
                    data = buffer.split(bytes("\n", "utf_8"))
                    buffer = data.pop()

                    for el in data:
                        prefix, command, args = parse_raw_irc_command(el)

                        try:
                            enc = "utf8"
                            fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)]
                        except UnicodeDecodeError:
                            enc = "latin1"
                            fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)]

                        try:
                            largs = list(args)
                            if prefix is not None:
                                prefix = prefix.decode(enc)
                            self.stream_handler("<--- receive {0} {1} ({2})".format(prefix, command, ", ".join(fargs)), level="debug")
                            # for i,arg in enumerate(largs):
                                # if arg is not None: largs[i] = arg.decode(enc)
                            if command in self.command_handler:
                                self.command_handler[command](self, prefix,*fargs)
                            elif "" in self.command_handler:
                                self.command_handler[""](self, prefix, command, *fargs)
                        except Exception as e:
                            sys.stderr.write(traceback.format_exc())
                            raise e  # ?
                yield True
        finally:
            if self.socket:
                self.stream_handler('closing socket')
                self.socket.close()
                yield False
Пример #2
0
def test_parse_raw_irc_command():
    element = bytes(":[email protected] PRIVMSG #mIRC :I feel lucky today", encoding="utf8")
    check = parse.parse_raw_irc_command(element)
    print(check[0].decode())
    assert check[0].decode() == "[email protected]" and check[1] == "privmsg" \
        and check[2][0].decode() == "#mIRC" and check[2][1].decode() == 'I feel lucky today'
Пример #3
0
    def connect(self):
        """ initiates the connection to the server set in self.host:self.port
        and returns a generator object.

        >>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667)
        >>> g = cli.connect()
        >>> while 1:
        ...     next(g)

        """
        try:
            self.stream_handler('connecting to {0}:{1}'.format(self.host, self.port))
            retries = 0
            while True:
                try:
                    self.socket.connect(("{0}".format(self.host), self.port))
                    break
                except socket.error as e:
                    retries += 1
                    self.stream_handler('Error: {0}'.format(e), level="warning")
                    if retries > 3:
                        break
            if not self.blocking:
                self.socket.setblocking(0)

            if not self.sasl_auth:
                self.send("PASS {0}:{1}".format(self.authname if self.authname else self.nickname,
                    self.password if self.password else "NOPASS"))
            else:
                self.cap("LS")

            self.nick(self.nickname)
            self.user(self.ident, self.real_name)

            if self.sasl_auth:
                self.cap("REQ", "multi-prefix")
                self.cap("REQ", "sasl")

            if self.connect_cb:
                try:
                    self.connect_cb(self)
                except Exception as e:
                    traceback.print_exc()
                    raise e

            buffer = bytes()
            while not self._end:
                try:
                    buffer += self.socket.recv(1024)
                except socket.error as e:
                    if False and not self.blocking and e.errno == 11:
                        pass
                    else:
                        raise e
                else:
                    data = buffer.split(bytes("\n", "utf_8"))
                    buffer = data.pop()

                    for el in data:
                        prefix, command, args = parse_raw_irc_command(el)

                        try:
                            enc = "utf8"
                            fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)]
                        except UnicodeDecodeError:
                            enc = "latin1"
                            fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)]

                        self.stream_handler("processCommand ({2}){0}({1})".format(command,
                                                       fargs, prefix), level="debug")
                        try:
                            largs = list(args)
                            if prefix is not None:
                                prefix = prefix.decode(enc)
                            # for i,arg in enumerate(largs):
                                # if arg is not None: largs[i] = arg.decode(enc)
                            if command in self.command_handler:
                                self.command_handler[command](self, prefix,*fargs)
                            elif "" in self.command_handler:
                                self.command_handler[""](self, prefix, command, *fargs)
                        except Exception as e:
                            traceback.print_exc()
                            raise e  # ?
                yield True
        finally:
            if self.socket:
                self.stream_handler('closing socket')
                self.socket.close()
                yield False
Пример #4
0
    def connect(self):
        """ initiates the connection to the server set in self.host:self.port
        and returns a generator object.

        >>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667)
        >>> g = cli.connect()
        >>> while 1:
        ...     next(g)

        """
        try:
            retries = 0
            while True:
                try:
                    self.socket = socket.create_connection(
                        ("{0}".format(self.host), self.port),
                        source_address=("{0}".format(self.bindhost), 0))
                    break
                except socket.error as e:
                    retries += 1
                    self.stream_handler('Error: {0}'.format(e),
                                        level="warning")
                    if retries > 3:
                        sys.exit(1)

            if self.use_ssl:
                ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)

                if self.cipher_list:
                    try:
                        ctx.set_ciphers(self.cipher_list)
                    except Exception:
                        self.stream_handler(
                            "No ciphers could be selected from the cipher list. TLS is not available.",
                            level="warning")
                        self.stream_handler(
                            "Use `openssl ciphers' to see which ciphers are available on this system.",
                            level="warning")
                        raise

                # explicitly disable old protocols
                ctx.options |= ssl.OP_NO_SSLv2
                ctx.options |= ssl.OP_NO_SSLv3
                ctx.options |= ssl.OP_NO_TLSv1

                # explicitly disable compression (CRIME attack)
                ctx.options |= ssl.OP_NO_COMPRESSION

                # TLS session tickets harm forward secrecy
                ctx.options |= ssl.OP_NO_TICKET

                if self.cert_verify and not self.cert_fp:
                    ctx.verify_mode = ssl.CERT_REQUIRED

                    if not self.cert_fp:
                        ctx.check_hostname = True

                    ctx.load_default_certs()
                elif not self.cert_verify and not self.cert_fp:
                    self.stream_handler(
                        "**NOT** validating the server's TLS certificate! Set SSL_VERIFY or SSL_CERTFP in botconfig.py.",
                        level="warning")

                if self.client_certfile:
                    # if client_keyfile is not specified, the ssl module will look to the client_certfile for it.
                    try:
                        # specify blank password to ensure that encrypted certs will outright fail rather than prompting for password on stdin
                        # in a scenario where a user does !update or !restart, they will be unable to type in such a password and effectively kill the bot
                        # until someone can SSH in to restart it via CLI.
                        ctx.load_cert_chain(self.client_certfile,
                                            self.client_keyfile,
                                            password="")
                        self.stream_handler(
                            "Connecting with a TLS client certificate",
                            level="info")
                    except Exception as error:
                        self.stream_handler(
                            "Unable to load client cert/key pair: {0}".format(
                                error),
                            level="error")
                        raise

                try:
                    self.socket = ctx.wrap_socket(self.socket,
                                                  server_hostname=self.host)
                except Exception as error:
                    self.stream_handler(
                        "Could not connect with TLS: {0}".format(error),
                        level="error")
                    raise

                if self.cert_fp:
                    valid_fps = set(
                        fp.replace(":", "").lower() for fp in self.cert_fp)
                    peercert = self.socket.getpeercert(True)
                    h = hashlib.new("sha256")
                    h.update(peercert)
                    peercertfp = h.hexdigest()

                    if peercertfp not in valid_fps:
                        self.stream_handler(
                            "Certificate fingerprint {0} did not match any expected fingerprints"
                            .format(peercertfp),
                            level="error")
                        raise ssl.CertificateError(
                            "Certificate fingerprint {0} did not match any expected fingerprints"
                            .format(peercertfp))
                    self.stream_handler(
                        "Server certificate fingerprint matched {0}".format(
                            peercertfp),
                        level="info")

                self.stream_handler("Connected with cipher {0}".format(
                    self.socket.cipher()[0]),
                                    level="info")

            if not self.blocking:
                self.socket.setblocking(0)

            self.send("CAP LS 302")

            if self.server_pass and "{password}" in self.server_pass and self.password and not self.sasl_auth:
                # If not using SASL, try to send the NickServ password during connect via PASS
                message = "PASS :{0}".format(self.server_pass).format(
                    account=self.authname if self.authname else self.nickname,
                    password=self.password)
                self.send(message, log="PASS :[redacted]")
            elif self.server_pass and "{password}" not in self.server_pass:
                # If {password} isn't present, then we likely have a connect password, so send that regardless of SASL
                message = "PASS :{0}".format(self.server_pass)
                self.send(message, log="PASS :[redacted]")

            self.send("NICK", self.nickname)
            self.user(self.ident, self.real_name)

            if self.connect_cb:
                try:
                    self.connect_cb(self)
                except Exception as e:
                    sys.stderr.write(traceback.format_exc())
                    raise e

            buffer = bytes()
            while not self._end:
                try:
                    buffer += self.socket.recv(1024)
                except socket.error as e:
                    if False and not self.blocking and e.errno == 11:
                        pass
                    else:
                        sys.stderr.write(traceback.format_exc())
                        raise e
                else:
                    data = buffer.split(bytes("\n", "utf_8"))
                    buffer = data.pop()

                    for el in data:
                        prefix, command, args = parse_raw_irc_command(el)

                        try:
                            enc = "utf8"
                            fargs = [
                                arg.decode(enc) for arg in args
                                if isinstance(arg, bytes)
                            ]
                        except UnicodeDecodeError:
                            enc = "latin1"
                            fargs = [
                                arg.decode(enc) for arg in args
                                if isinstance(arg, bytes)
                            ]

                        try:
                            largs = list(args)
                            if prefix is not None:
                                prefix = prefix.decode(enc)
                            self.stream_handler(
                                "<--- receive {0} {1} ({2})".format(
                                    prefix, command, ", ".join(fargs)),
                                level="debug")
                            # for i,arg in enumerate(largs):
                            # if arg is not None: largs[i] = arg.decode(enc)
                            if command in self.command_handler:
                                self.command_handler[command](self, prefix,
                                                              *fargs)
                            elif "" in self.command_handler:
                                self.command_handler[""](self, prefix, command,
                                                         *fargs)
                        except Exception as e:
                            sys.stderr.write(traceback.format_exc())
                            raise e  # ?
                yield True
        finally:
            if self.socket:
                self.stream_handler('closing socket')
                self.socket.close()
                yield False
Пример #5
0
    def connect(self):
        """ initiates the connection to the server set in self.host:self.port 
        and returns a generator object. 

        >>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667)
        >>> g = cli.connect()
        >>> while 1:
        ...     next(g)

        """
        try:
            logging.info('connecting to {0}:{1}'.format(self.host, self.port))
            retries = 0
            while True:
                try:
                    self.socket.connect(("{0}".format(self.host), self.port))
                    break
                except socket.error as e:
                    retries += 1
                    logging.warning('Error: {0}'.format(e))
                    if retries > 3:
                        break
            if not self.blocking:
                self.socket.setblocking(0)

            if not self.sasl_auth:
                self.send("PASS {0}:{1}".format(
                    self.authname if self.authname else self.nickname,
                    self.password if self.password else "NOPASS"))
            else:
                self.cap("LS")

            self.nick(self.nickname)
            self.user(self.nickname, self.real_name)

            if self.sasl_auth:
                self.cap("REQ", "multi-prefix")
                self.cap("REQ", "sasl")

            if self.connect_cb:
                try:
                    self.connect_cb(self)
                except Exception as e:
                    traceback.print_exc()
                    raise e

            buffer = bytes()
            while not self._end:
                try:
                    buffer += self.socket.recv(1024)
                except socket.error as e:
                    if False and not self.blocking and e.errno == 11:
                        pass
                    else:
                        raise e
                else:
                    data = buffer.split(bytes("\n", "utf_8"))
                    buffer = data.pop()

                    for el in data:
                        prefix, command, args = parse_raw_irc_command(el)

                        try:
                            enc = "utf8"
                            fargs = [
                                arg.decode(enc) for arg in args
                                if isinstance(arg, bytes)
                            ]
                        except UnicodeDecodeError:
                            enc = "latin1"
                            fargs = [
                                arg.decode(enc) for arg in args
                                if isinstance(arg, bytes)
                            ]

                        logging.debug("processCommand ({2}){0}({1})".format(
                            command, fargs, prefix))
                        try:
                            largs = list(args)
                            if prefix is not None:
                                prefix = prefix.decode(enc)
                            # for i,arg in enumerate(largs):
                            # if arg is not None: largs[i] = arg.decode(enc)
                            if command in self.command_handler:
                                self.command_handler[command](self, prefix,
                                                              *fargs)
                            elif "" in self.command_handler:
                                self.command_handler[""](self, prefix, command,
                                                         *fargs)
                        except Exception as e:
                            traceback.print_exc()
                            raise e  # ?
                yield True
        finally:
            if self.socket:
                logging.info('closing socket')
                self.socket.close()
                yield False
Пример #6
0
    def connect(self):
        """ initiates the connection to the server set in self.host:self.port
        and returns a generator object.

        >>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667)
        >>> g = cli.connect()
        >>> while 1:
        ...     next(g)

        """
        try:
            retries = 0
            while True:
                try:
                    self.socket = socket.create_connection(
                        ("{0}".format(self.host), self.port))
                    break
                except socket.error as e:
                    retries += 1
                    self.stream_handler('Error: {0}'.format(e),
                                        level="warning")
                    if retries > 3:
                        sys.exit(1)

            if self.use_ssl:
                self.socket = ssl.wrap_socket(self.socket)

            if not self.blocking:
                self.socket.setblocking(0)

            self.cap("LS", "302")

            if self.server_pass and (not self.sasl_auth
                                     or "{password}" not in self.server_pass):
                message = "PASS :{0}".format(self.server_pass).format(
                    account=self.authname if self.authname else self.nickname,
                    password=self.password)
                self.send(message)

            self.nick(self.nickname)
            self.user(self.ident, self.real_name)

            if self.connect_cb:
                try:
                    self.connect_cb(self)
                except Exception as e:
                    sys.stderr.write(traceback.format_exc())
                    raise e

            buffer = bytes()
            while not self._end:
                try:
                    buffer += self.socket.recv(1024)
                except socket.error as e:
                    if False and not self.blocking and e.errno == 11:
                        pass
                    else:
                        sys.stderr.write(traceback.format_exc())
                        raise e
                else:
                    data = buffer.split(bytes("\n", "utf_8"))
                    buffer = data.pop()

                    for el in data:
                        prefix, command, args = parse_raw_irc_command(el)

                        try:
                            enc = "utf8"
                            fargs = [
                                arg.decode(enc) for arg in args
                                if isinstance(arg, bytes)
                            ]
                        except UnicodeDecodeError:
                            enc = "latin1"
                            fargs = [
                                arg.decode(enc) for arg in args
                                if isinstance(arg, bytes)
                            ]

                        try:
                            largs = list(args)
                            if prefix is not None:
                                prefix = prefix.decode(enc)
                            self.stream_handler(
                                "<--- receive {0} {1} ({2})".format(
                                    prefix, command, ", ".join(fargs)),
                                level="debug")
                            # for i,arg in enumerate(largs):
                            # if arg is not None: largs[i] = arg.decode(enc)
                            if command in self.command_handler:
                                self.command_handler[command](self, prefix,
                                                              *fargs)
                            elif "" in self.command_handler:
                                self.command_handler[""](self, prefix, command,
                                                         *fargs)
                        except Exception as e:
                            sys.stderr.write(traceback.format_exc())
                            raise e  # ?
                yield True
        finally:
            if self.socket:
                self.stream_handler('closing socket')
                self.socket.close()
                yield False
Пример #7
0
    def connect(self):
        """ initiates the connection to the server set in self.host:self.port
        and returns a generator object.

        >>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667)
        >>> g = cli.connect()
        >>> while 1:
        ...     next(g)

        """
        try:
            retries = 0
            while True:
                try:
                    self.socket = socket.create_connection(("{0}".format(self.host), self.port))
                    break
                except socket.error as e:
                    retries += 1
                    self.stream_handler('Error: {0}'.format(e), level="warning")
                    if retries > 3:
                        sys.exit(1)

            if self.use_ssl:
                ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)

                if self.cipher_list:
                    try:
                        ctx.set_ciphers(self.cipher_list)
                    except Exception:
                        self.stream_handler("No ciphers could be selected from the cipher list. TLS is not available.", level="warning")
                        self.stream_handler("Use `openssl ciphers' to see which ciphers are available on this system.", level="warning")
                        raise

                # explicitly disable old protocols
                ctx.options |= ssl.OP_NO_SSLv2
                ctx.options |= ssl.OP_NO_SSLv3
                ctx.options |= ssl.OP_NO_TLSv1

                # explicitly disable compression (CRIME attack)
                ctx.options |= ssl.OP_NO_COMPRESSION

                if sys.version_info >= (3, 6):
                    # TLS session tickets harm forward secrecy (this symbol is only defined in 3.6 and later)
                    ctx.options |= ssl.OP_NO_TICKET

                if self.cert_verify and not self.cert_fp:
                    ctx.verify_mode = ssl.CERT_REQUIRED

                    if not self.cert_fp:
                        ctx.check_hostname = True

                    ctx.load_default_certs()
                elif not self.cert_verify and not self.cert_fp:
                    self.stream_handler("**NOT** validating the server's TLS certificate! Set SSL_VERIFY or SSL_CERTFP in botconfig.py.", level="warning")

                if self.client_certfile:
                    # if client_keyfile is not specified, the ssl module will look to the client_certfile for it.
                    try:
                        # specify blank password to ensure that encrypted certs will outright fail rather than prompting for password on stdin
                        # in a scenario where a user does !update or !restart, they will be unable to type in such a password and effectively kill the bot
                        # until someone can SSH in to restart it via CLI.
                        ctx.load_cert_chain(self.client_certfile, self.client_keyfile, password="")
                        self.stream_handler("Connecting with a TLS client certificate", level="info")
                    except Exception as error:
                        self.stream_handler("Unable to load client cert/key pair: {0}".format(error), level="error")
                        raise

                try:
                    self.socket = ctx.wrap_socket(self.socket, server_hostname=self.host)
                except Exception as error:
                    self.stream_handler("Could not connect with TLS: {0}".format(error), level="error")
                    raise

                if self.cert_fp:
                    valid_fps = set(fp.replace(":", "").lower() for fp in self.cert_fp)
                    peercert = self.socket.getpeercert(True)
                    h = hashlib.new("sha256")
                    h.update(peercert)
                    peercertfp = h.hexdigest()

                    if peercertfp not in valid_fps:
                        self.stream_handler("Certificate fingerprint {0} did not match any expected fingerprints".format(peercertfp), level="error")
                        raise ssl.CertificateError("Certificate fingerprint {0} did not match any expected fingerprints".format(peercertfp))
                    self.stream_handler("Server certificate fingerprint matched {0}".format(peercertfp), level="info")

                self.stream_handler("Connected with cipher {0}".format(self.socket.cipher()[0]), level="info")

            if not self.blocking:
                self.socket.setblocking(0)

            self.send("CAP LS 302")

            if (self.server_pass and "{password}" in self.server_pass
                    and self.password and not self.sasl_auth):
                message = "PASS :{0}".format(self.server_pass).format(
                    account=self.authname if self.authname else self.nickname,
                    password=self.password)
                self.send(message, log="PASS :[redacted]")
            elif self.server_pass:
                message = "PASS :{0}".format(self.server_pass)
                self.send(message, log="PASS :[redacted]")

            self.send("NICK", self.nickname)
            self.user(self.ident, self.real_name)

            if self.connect_cb:
                try:
                    self.connect_cb(self)
                except Exception as e:
                    sys.stderr.write(traceback.format_exc())
                    raise e

            buffer = bytes()
            while not self._end:
                try:
                    buffer += self.socket.recv(1024)
                except socket.error as e:
                    if False and not self.blocking and e.errno == 11:
                        pass
                    else:
                        sys.stderr.write(traceback.format_exc())
                        raise e
                else:
                    data = buffer.split(bytes("\n", "utf_8"))
                    buffer = data.pop()

                    for el in data:
                        prefix, command, args = parse_raw_irc_command(el)

                        try:
                            enc = "utf8"
                            fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)]
                        except UnicodeDecodeError:
                            enc = "latin1"
                            fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)]

                        try:
                            largs = list(args)
                            if prefix is not None:
                                prefix = prefix.decode(enc)
                            self.stream_handler("<--- receive {0} {1} ({2})".format(prefix, command, ", ".join(fargs)), level="debug")
                            # for i,arg in enumerate(largs):
                                # if arg is not None: largs[i] = arg.decode(enc)
                            if command in self.command_handler:
                                self.command_handler[command](self, prefix,*fargs)
                            elif "" in self.command_handler:
                                self.command_handler[""](self, prefix, command, *fargs)
                        except Exception as e:
                            sys.stderr.write(traceback.format_exc())
                            raise e  # ?
                yield True
        finally:
            if self.socket:
                self.stream_handler('closing socket')
                self.socket.close()
                yield False