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
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'
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
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
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
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
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