Clarify intentional outages from server linking related things

This commit is contained in:
Firepup Sixfifty 2024-07-17 02:51:04 +00:00
parent 1714010182
commit 7c0c4b0875
Signed by: Firepup650
SSH key fingerprint: SHA256:U0Zp8EhEe3CMqFSrC79CqatzaEiL4sjta80/RSX2XrY

329
server.py
View file

@ -1,17 +1,19 @@
#! /usr/bin/python3 #!/usr/bin/python3
import os, sys, asyncio, re, signal import os, sys, asyncio, re, signal
from platform import uname from platform import uname
from traceback import format_exc from traceback import format_exc
from logs import log from logs import log
class LinksDownException(Exception): ... class LinkDownError(Exception): ...
class FailedLinkError(Exception): ...
class Globals: ... class Globals: ...
# The two below lines are a hacky fix for python 3.10 asyncio
TimeoutErrors = (TimeoutError, asyncio.exceptions.TimeoutError) TimeoutErrors = (TimeoutError, asyncio.exceptions.TimeoutError)
DisconnectErrors = (ConnectionResetError, BrokenPipeError, IndexError, *TimeoutErrors) DisconnectErrors = (ConnectionResetError, BrokenPipeError, IndexError, *TimeoutErrors)
G = Globals() G = Globals()
@ -401,169 +403,179 @@ Please note that this is not network level statistics.\n""".encode(
async def connectServer(hostname: str, port: int): async def connectServer(hostname: str, port: int):
global G global G
reader, writer = await asyncio.open_connection(hostname, port)
await reader.read(1024)
writer.write(f"S2S-{G.remoteID}\n".encode("utf8"))
await writer.drain()
await reader.read(1024)
for client in G.clientsConnected:
writer.write(f"{client}\n".encode("utf8"))
await writer.drain()
resp = raw((await reader.read(1024)).decode("utf8"))
if resp.startswith("K"):
if G.clientsConnected[client] == G.remoteID:
G.killList[client] = True
else:
G.servers[G.clientsConnected[client]][client] = True
writer.write(f"END OF CLIENT LISTING FROM {G.remoteID}\n".encode("utf8"))
await writer.drain()
rID = raw((await reader.read(16)).decode("utf8"))
try: try:
await asyncio.wait_for(reader.read(1024), 0.1) reader, writer = await asyncio.open_connection(hostname, port)
except TimeoutErrors: await reader.read(1024)
pass writer.write(f"S2S-{G.remoteID}\n".encode("utf8"))
if G.servers.get(rID, False) != False: await writer.drain()
writer.close() await reader.read(1024)
await writer.wait_closed() for client in G.clientsConnected:
return writer.write(f"{client}\n".encode("utf8"))
if G.remoteID == rID:
writer.close()
await writer.wait_closed()
return
G.msgs.append(log(f"{rID} has linked to the network"))
G.servers[rID] = {}
writer.write(b"I recieved your remote ID, now awaiting client listing\n")
await writer.drain()
# recieve client list from the other server
while 1:
client = raw((await reader.read(1024)).decode("utf8"))
if client == f"END OF CLIENT LISTING FROM {rID}":
break
if client.lower() in G.servers[rID] or client.lower() in G.clientsConnected:
writer.write(b"K Client rejected: Already exists\n")
await writer.drain() await writer.drain()
continue resp = raw((await reader.read(1024)).decode("utf8"))
writer.write(b"I added that client.\n") if resp.startswith("K"):
if G.clientsConnected[client] == G.remoteID:
G.killList[client] = True
else:
G.servers[G.clientsConnected[client]][client] = True
writer.write(f"END OF CLIENT LISTING FROM {G.remoteID}\n".encode("utf8"))
await writer.drain() await writer.drain()
G.msgs.append(log(f"{client} has connected from {rID}")) rID = raw((await reader.read(16)).decode("utf8"))
G.servers[rID][client.lower()] = False try:
for client in G.servers[rID]: await asyncio.wait_for(reader.read(1024), 0.1)
G.clientsConnected[client] = rID except TimeoutErrors:
msgInd = len(G.S2SLogs) pass
try: if G.servers.get(rID, False) != False:
writer.close()
await writer.wait_closed()
return
if G.remoteID == rID:
writer.close()
await writer.wait_closed()
return
G.msgs.append(log(f"{rID} has linked to the network"))
G.servers[rID] = {}
writer.write(b"I recieved your remote ID, now awaiting client listing\n")
await writer.drain()
# recieve client list from the other server
while 1: while 1:
try: client = raw((await reader.read(1024)).decode("utf8"))
rawMsg = await asyncio.wait_for(reader.read(967), 0.1) if client == f"END OF CLIENT LISTING FROM {rID}":
buffer = raw(rawMsg.decode("utf8")) break
match buffer[0]: if client.lower() in G.servers[rID] or client.lower() in G.clientsConnected:
case "S": writer.write(b"K Client rejected: Already exists\n")
G.msgs.extend([log(buffer[2:])]) await writer.drain()
writer.write(b"I Mmm... Blueberries\n") continue
case "I": writer.write(b"I added that client.\n")
pass await writer.drain()
case "+": G.msgs.append(log(f"{client} has connected from {rID}"))
cName = buffer[2:] G.servers[rID][client.lower()] = False
if cName.lower() not in G.clientsConnected: for client in G.servers[rID]:
G.msgs.append(log(f"{cName} has connected from {rID}")) G.clientsConnected[client] = rID
G.S2SLogs.append(("+", cName, rID)) msgInd = len(G.S2SLogs)
G.servers[rID][cName.lower()] = False try:
G.clientsConnected[cName.lower()] = rID while 1:
writer.write(b"I Mmm... Pineapples\n") try:
else: rawMsg = await asyncio.wait_for(reader.read(967), 0.1)
writer.write(f"K {cName}\n".encode("utf8")) buffer = raw(rawMsg.decode("utf8"))
case "-": match buffer[0]:
cName = buffer[2:] case "S":
if G.clientsConnected.get(cName.lower(), None) == rID: G.msgs.extend([log(buffer[2:])])
G.msgs.append(log(f"{cName} has disconnected from {rID}")) writer.write(b"I Mmm... Blueberries\n")
del G.servers[rID][cName.lower()] case "I":
del G.clientsConnected[cName.lower()] pass
G.S2SLogs.append(("-", cName, rID)) case "+":
else: cName = buffer[2:]
if cName.lower() not in G.clientsConnected:
G.msgs.append(log(f"{cName} has connected from {rID}"))
G.S2SLogs.append(("+", cName, rID))
G.servers[rID][cName.lower()] = False
G.clientsConnected[cName.lower()] = rID
writer.write(b"I Mmm... Pineapples\n")
else:
writer.write(f"K {cName}\n".encode("utf8"))
case "-":
cName = buffer[2:]
if G.clientsConnected.get(cName.lower(), None) == rID:
G.msgs.append(
log(f"{cName} has disconnected from {rID}")
)
del G.servers[rID][cName.lower()]
del G.clientsConnected[cName.lower()]
G.S2SLogs.append(("-", cName, rID))
else:
writer.write(
f"S Your server is LYING about who is connected to it. - {G.remoteID}, a fellow server\n".encode(
"utf8"
)
)
case "M":
cName = buffer[2:].split("|", 1)[0]
message = buffer[2:].split("|", 1)[1]
G.msgs.append(log(f" {cName}: {message}"))
G.S2SLogs.append(("M", (cName, message), rID))
writer.write( writer.write(
f"S Your server is LYING about who is connected to it. - {G.remoteID}, a fellow server\n".encode( b"I Get these damn heretic ghost clients out of my store so i can buy my cult candles in peace.\n"
)
case "A":
cName = buffer[2:].split("|", 1)[0]
message = buffer[2:].split("|", 1)[1]
G.S2SLogs.append(("A", (cName, message), rID))
G.msgs.append(log(f"* {cName} {message}"))
writer.write(b"I Mmm... Strawberries\n")
case "Q":
break
case "K":
cName = buffer[2:]
if G.clientsConnected[cName.lower()] == G.remoteID:
G.killList[cName.lower()] = True
else:
G.servers[G.clientsConnected[cName.lower()]][
cName.lower()
] = True
writer.write(b"I Mmm... Blood\n")
case _:
writer.write(
f"S Your server is doing drugs over here, sending me bullshit messages man - {G.remoteID}, A fellow server\n".encode(
"utf8" "utf8"
) )
) )
case "M": log(
cName = buffer[2:].split("|", 1)[0] f"Recieved invalid message ({buffer}) from {sName}",
message = buffer[2:].split("|", 1)[1] "WARN",
G.msgs.append(log(f" {cName}: {message}"))
G.S2SLogs.append(("M", (cName, message), rID))
writer.write(
b"I Get these damn heretic ghost clients out of my store so i can buy my cult candles in peace.\n"
)
case "A":
cName = buffer[2:].split("|", 1)[0]
message = buffer[2:].split("|", 1)[1]
G.S2SLogs.append(("A", (cName, message), rID))
G.msgs.append(log(f"* {cName} {message}"))
writer.write(b"I Mmm... Strawberries\n")
case "Q":
break
case "K":
cName = buffer[2:]
if G.clientsConnected[cName.lower()] == G.remoteID:
G.killList[cName.lower()] = True
else:
G.servers[G.clientsConnected[cName.lower()]][
cName.lower()
] = True
writer.write(b"I Mmm... Blood\n")
case _:
writer.write(
f"S Your server is doing drugs over here, sending me bullshit messages man - {G.remoteID}, A fellow server\n".encode(
"utf8"
) )
) await writer.drain()
log(f"Recieved invalid message ({buffer}) from {sName}", "WARN") except TimeoutErrors:
await writer.drain() pass
except TimeoutErrors: if any(G.servers[rID].values()):
pass for name in G.servers[rID]:
if any(G.servers[rID].values()): if G.servers[rID][name]:
for name in G.servers[rID]: writer.write(f"K {name}\n".encode("utf8"))
if G.servers[rID][name]: await writer.drain()
writer.write(f"K {name}\n".encode("utf8")) G.servers[rID][name] = False
await writer.drain() while msgInd < len(G.S2SLogs):
G.servers[rID][name] = False type, data, server = G.S2SLogs[msgInd]
while msgInd < len(G.S2SLogs): if server != rID:
type, data, server = G.S2SLogs[msgInd] match type:
if server != rID: case "A":
match type: nick, msg = data
case "A": writer.write(f"A {nick}|{msg}\n".encode("utf8"))
nick, msg = data case "M":
writer.write(f"A {nick}|{msg}\n".encode("utf8")) nick, msg = data
case "M": writer.write(f"M {nick}|{msg}\n".encode("utf8"))
nick, msg = data case "+":
writer.write(f"M {nick}|{msg}\n".encode("utf8")) writer.write(f"+ {data}\n".encode("utf8"))
case "+": case "-":
writer.write(f"+ {data}\n".encode("utf8")) writer.write(f"- {data}\n".encode("utf8"))
case "-": case _:
writer.write(f"- {data}\n".encode("utf8")) pass
case _: await writer.drain()
pass msgInd += 1
await writer.drain() if G.cwlgd:
msgInd += 1 raise LinkDownError
if G.cwlgd: await writer.drain()
raise LinksDownException writer.close()
await writer.drain() await writer.wait_closed()
writer.close() for cName in G.servers[rID]:
await writer.wait_closed() G.msgs.append(log(f"{cName}'s server is going down"))
for cName in G.servers[rID]:
G.msgs.append(log(f"{cName}'s server is going down"))
del G.clientsConnected[cName.lower()]
del G.servers[rID]
G.msgs.append(log(f"{rID} has de-linked from the network"))
except DisconnectErrors:
if G.cwlgd:
raise LinksDownException
for cName in G.servers[rID]:
G.msgs.append(log(f"{cName}'s server is going down"))
try:
del G.clientsConnected[cName.lower()] del G.clientsConnected[cName.lower()]
except Exception: del G.servers[rID]
pass G.msgs.append(log(f"{rID} has de-linked from the network"))
del G.servers[rID] except DisconnectErrors:
G.msgs.append(log(f"{rID} has de-linked from the network")) if G.cwlgd:
raise LinkDownError
for cName in G.servers[rID]:
G.msgs.append(log(f"{cName}'s server is going down"))
try:
del G.clientsConnected[cName.lower()]
except Exception:
pass
del G.servers[rID]
G.msgs.append(log(f"{rID} has de-linked from the network"))
except OSError as E:
log("OSError: " + str(E), "ERROR")
if G.cwlgd:
raise FailedLinkError
async def runServer(address: str, port: int): async def runServer(address: str, port: int):
@ -578,6 +590,11 @@ async def runServer(address: str, port: int):
try: try:
links.append(G.event.wait()) links.append(G.event.wait())
await asyncio.gather(*links) await asyncio.gather(*links)
except LinkDownError:
G.msgs.append(log("Lost a server link, going down", "FATAL")[1:])
crash = True
except FailedLinkError:
G.msgs.append(log("Failed to establish a server link, going down", "FATAL")[1:])
except Exception: except Exception:
crash = True crash = True
G.msgs.append(log("Server crash", "FATAL")[1:]) G.msgs.append(log("Server crash", "FATAL")[1:])