recv/send excepts + disconnect reasons

This commit is contained in:
Dpeta 2022-06-13 04:03:39 +02:00
parent 0e1f676acc
commit 5c4d21640d
2 changed files with 138 additions and 70 deletions

49
irc.py
View file

@ -3,6 +3,7 @@ import logging.config
import socket
import random
import time
import json
from PyQt5 import QtCore, QtGui
@ -45,9 +46,19 @@ class PesterIRC(QtCore.QThread):
self.NickServ = services.NickServ()
self.ChanServ = services.ChanServ()
def IRCConnect(self):
with open(_datadir + "server.json", "r") as server_file:
read_file = server_file.read()
server_file.close()
server_obj = json.loads(read_file)
server = self.config.server()
port = self.config.port()
self.cli = IRCClient(PesterHandler, host=server, port=int(port), nick=self.mainwindow.profile().handle, real_name='pcc31', blocking=True, timeout=120)
self.cli = IRCClient(PesterHandler,
host=server,
port=int(port),
nick=self.mainwindow.profile().handle,
real_name='pcc31',
timeout=120,
ssl=server_obj['TLS'])
self.cli.command_handler.parent = self
self.cli.command_handler.mainwindow = self.mainwindow
self.cli.connect()
@ -55,10 +66,10 @@ class PesterIRC(QtCore.QThread):
def run(self):
try:
self.IRCConnect()
except socket.error as se:
except OSError as se:
self.stopIRC = se
return
while 1:
while True:
res = True
try:
PchumLog.debug("updateIRC()")
@ -66,13 +77,10 @@ class PesterIRC(QtCore.QThread):
except socket.timeout as se:
PchumLog.debug("timeout in thread %s" % (self))
self.cli.close()
self.stopIRC = se
self.stopIRC = "%s, %s" % (type(se), se)
return
except socket.error as se:
if self.registeredIRC:
self.stopIRC = None
else:
self.stopIRC = se
except (OSError, IndexError) as se:
self.stopIRC = "%s, %s" % (type(se), se)
PchumLog.debug("socket error, exiting thread")
return
else:
@ -98,6 +106,8 @@ class PesterIRC(QtCore.QThread):
raise se
except socket.error as se:
raise se
except (OSError, IndexError) as se:
raise se
except StopIteration:
self.conn = self.cli.conn()
return True
@ -366,10 +376,6 @@ class PesterIRC(QtCore.QThread):
def quit_dc(self):
helpers.quit(self.cli, _pcVersion + " <3")
#def getMask(self):
# This needs to be updated when our hostname is changed.
# Nevermind this entire thing, actually.
moodUpdated = QtCore.pyqtSignal('QString', Mood)
colorUpdated = QtCore.pyqtSignal('QString', QtGui.QColor)
messageReceived = QtCore.pyqtSignal('QString', 'QString')
@ -439,6 +445,14 @@ class PesterHandler(DefaultCommandHandler):
else:
# Invalid syntax
PchumLog.warning("TAGMSG with invalid syntax.")
def error(self, *params):
# Server is ending connection.
reason = ''
for x in params:
if (x != None) and (x != ''):
reason += x + ' '
self.parent.stopIRC = reason.strip()
self.parent.reconnectIRC()
def privmsg(self, nick, chan, msg):
handle = nick[0:nick.find("!")]
@ -539,6 +553,15 @@ class PesterHandler(DefaultCommandHandler):
helpers.cap(self.client, "REQ", "message-tags")
helpers.cap(self.client, "REQ", "pesterchum-tag")
def erroneusnickname(self, *args):
# Server is not allowing us to connect.
reason = "Handle is not allowed on this server.\n"
for x in args:
if (x != None) and (x != ''):
reason += x + ' '
self.parent.stopIRC = reason.strip()
self.parent.reconnectIRC()
def keyvalue(self, target, handle_us, handle_owner, key, visibility, *value):
# The format of the METADATA server notication is:
# METADATA <Target> <Key> <Visibility> <Value>

View file

@ -27,7 +27,6 @@ import socket
import time
import traceback
import ssl
import json
import select
from oyoyo.parse import parse_raw_irc_command
@ -80,27 +79,7 @@ class IRCClient:
...
"""
# This should be moved to profiles
with open(_datadir + "server.json", "r") as server_file:
read_file = server_file.read()
server_file.close()
server_obj = json.loads(read_file)
TLS = server_obj['TLS']
#print("TLS-status is: " + str(TLS))
if TLS == False:
#print("false")
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
else:
self.context = ssl.create_default_context()
self.context.check_hostname = False
self.context.verify_mode = ssl.CERT_NONE
self.bare_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket = self.context.wrap_socket(self.bare_socket)
#self.socket.setsockopt(socket.IPPROTO_TCP, socket.SO_KEEPALIVE, 1)
self.blocking = True
self.socket.setblocking(self.blocking)
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.nick = None
self.real_name = None
@ -108,6 +87,8 @@ class IRCClient:
self.port = None
self.connect_cb = None
self.timeout = None
self.blocking = None
self.ssl = None
self.__dict__.update(kwargs)
self.command_handler = cmd_handler(self)
@ -146,26 +127,44 @@ class IRCClient:
msg = bytes(" ", "UTF-8").join(bargs)
PchumLog.info('---> send "%s"' % msg)
try:
# Extra block to give a failed write another try, causes a reconnect otherwise
retry = 0
while retry < 5:
tries = 1
while tries < 10:
try:
ready_to_read, ready_to_write, in_error = select.select([], [self.socket], [])
PchumLog.debug("ready_to_write (len %s): " % str(len(ready_to_write)) + str(ready_to_write))
#ready_to_write[0].sendall(msg + bytes("\r\n", "UTF-8"))
for x in ready_to_write:
x.sendall(msg + bytes("\r\n", "UTF-8"))
break
except (socket.error, ValueError) as e:# "file descriptor cannot be a negative integer"
retry += 1
PchumLog.warning("socket.error (retry %s) %s" % (str(retry), str(e)))
except socket.error as se:
PchumLog.warning("socket.error %s" % str(se))
try: # a little dance of compatibility to get the errno
errno = se.errno
except AttributeError:
errno = se[0]
if not self.blocking and errno == 11:
except ssl.SSLWantReadError as e:
PchumLog.warning("ssl.SSLWantReadError on send, " + str(e))
select.select([self.socket], [], [])
if tries >= 9:
raise e
except ssl.SSLWantWriteError as e:
PchumLog.warning("ssl.SSLWantWriteError on send, " + str(e))
select.select([], [self.socket], [])
if tries >= 9:
raise e
except ssl.SSLEOFError as e:
# ssl.SSLEOFError guarantees a broken connection.
PchumLog.warning("ssl.SSLEOFError in on send, " + str(e))
raise ssl.SSLEOFError
except (socket.timeout, TimeoutError) as e:
# socket.timeout is deprecated in 3.10
PchumLog.warning("TimeoutError in on send, " + str(e))
raise socket.timeout
except (OSError, IndexError) as e:
# Unknown error, might as well retry?
PchumLog.warning("Unkown error on send, " + str(e))
if tries >= 9:
raise e
tries += 1
PchumLog.warning("Retrying send. (attempt %s)" % str(tries))
time.sleep(0.1)
PchumLog.debug("ready_to_write (len %s): " % str(len(ready_to_write)) + str(ready_to_write))
except OSError as se:
PchumLog.warning("socket.error %s" % str(se))
if not self.blocking and se.errno == 11:
pass
else:
raise se
@ -173,16 +172,37 @@ class IRCClient:
def connect(self):
""" initiates the connection to the server set in self.host:self.port
"""
PchumLog.info('connecting to %s:%s' % (self.host, self.port))
self.socket.connect(("%s" % self.host, self.port))
if not self.blocking:
self.socket.setblocking(0)
if self.ssl == True:
context = ssl.create_default_context()
bare_sock = socket.create_connection(("%s" % self.host, self.port))
self.socket = context.wrap_socket(bare_sock, server_hostname=self.host, do_handshake_on_connect=False)
while True:
try:
self.socket.do_handshake()
break
except ssl.SSLWantReadError:
select.select([self.socket], [], [])
except ssl.SSLWantWriteError:
select.select([], [self.socket], [])
PchumLog.info("secure sockets version is %s" % self.socket.version())
else:
self.socket = socket.create_connection(("%s" % self.host, self.port))
# setblocking is a shorthand for timeout,
# we shouldn't use both.
if self.timeout:
self.socket.settimeout(self.timeout)
elif not self.blocking:
self.socket.setblocking(False)
elif self.blocking:
self.socket.setblocking(True)
helpers.nick(self, self.nick)
helpers.user(self, self.nick, self.real_name)
if self.connect_cb:
self.connect_cb(self)
@ -191,24 +211,51 @@ class IRCClient:
try:
buffer = bytes()
while not self._end:
# Block for connection-killing exceptions
try:
ready_to_read, ready_to_write, in_error = select.select([self.socket], [], []) # Don't wanna cause an unnecessary timeout
# Though this could probably be 90
PchumLog.debug("ready_to_read (len %s): " % len(ready_to_read) + str(ready_to_read))
for x in ready_to_read:
buffer += x.recv(1024)
#print("pre-recv")
#buffer += self.socket.recv(1024)
#print("post-recv")
#print(buffer)
#raise socket.timeout
tries = 1
while tries < 10:
try:
ready_to_read, ready_to_write, in_error = select.select([self.socket], [], [])
for x in ready_to_read:
buffer += x.recv(1024)
break
except ssl.SSLWantReadError as e:
PchumLog.warning("ssl.SSLWantReadError on send, " + str(e))
select.select([self.socket], [], [])
if tries >= 9:
raise e
except ssl.SSLWantWriteError as e:
PchumLog.warning("ssl.SSLWantWriteError on send, " + str(e))
select.select([], [self.socket], [])
if tries >= 9:
raise e
except ssl.SSLEOFError as e:
# ssl.SSLEOFError guarantees a broken connection.
PchumLog.warning("ssl.SSLEOFError in on send, " + str(e))
raise ssl.SSLEOFError
except (socket.timeout, TimeoutError) as e:
# socket.timeout is deprecated in 3.10
PchumLog.warning("TimeoutError in on send, " + str(e))
raise socket.timeout
except (OSError, IndexError) as e:
# Unknown error, might as well retry?
PchumLog.warning("Unkown error, " + str(e))
if tries >= 9:
raise e
tries += 1
PchumLog.warning("Retrying recv. (attempt %s)" % str(tries))
time.sleep(0.1)
except socket.timeout as e:
PchumLog.warning("timeout in client.py, " + str(e))
if self._end:
break
raise e
except (socket.error, ValueError) as e:
PchumLog.warning("conn socket.error %s in %s" % (e, self))
except ssl.SSLEOFError:
raise ssl.SSLEOFError
except OSError as e:
PchumLog.warning("conn exception %s in %s" % (e, self))
if self._end:
break
try: # a little dance of compatibility to get the errno
@ -222,7 +269,6 @@ class IRCClient:
else:
if self._end:
break
PchumLog.debug("pre buffer check, len(buffer) = " + str(len(buffer)))
if len(buffer) == 0 and self.blocking:
PchumLog.debug("len(buffer) = 0")
raise socket.error("Connection closed")
@ -233,7 +279,6 @@ class IRCClient:
PchumLog.debug("data = " + str(data))
for el in data:
PchumLog.debug("el=%s, data=%s" % (el,data))
tags, prefix, command, args = parse_raw_irc_command(el)
try:
# Only need tags with tagmsg
@ -248,7 +293,7 @@ class IRCClient:
except socket.timeout as se:
PchumLog.debug("passing timeout")
raise se
except socket.error as se:
except (socket.error, ssl.SSLEOFError) as se:
PchumLog.debug("problem: %s" % (str(se)))
if self.socket:
PchumLog.info('error: closing socket')