kumistatus/servertools/__init__.py

191 lines
5.7 KiB
Python

import datetime
import encodings.idna
import httptools
import os
import porttools
import setuptools
import skstools
import socket
import socketserver
import ssltools
import sys
import syslog
import threading
SYSLOG = 0
STDOUT = 1
STDDEB = 2 # STDOUT + Debug
SILENT = 9 # Quiet mode
SSL = 0
PORT = 1
SKS = 2
HTTP = 3
logging = STDOUT
try:
exec("logging = " + setuptools.getSetting("Log", "sink"))
except:
pass
def logger(message, prio=syslog.LOG_INFO, sink=logging):
if sink in (STDOUT, STDDEB):
if prio not in (syslog.LOG_NOTICE, syslog.LOG_INFO, syslog.LOG_DEBUG):
print(message)
sys.stderr.write(message)
elif prio != syslog.LOG_DEBUG or sink == STDDEB:
print(message)
elif sink == SYSLOG:
syslog.openlog("KumiStatusServer", syslog.LOG_PID)
syslog.syslog(prio, message)
elif sink != SILENT:
try:
sys.stderr.write("Unknown logging level %s, assuming STDOUT from now on." % str(sink))
except:
pass
logging = STDOUT
logger(message, prio, logging)
def listIncluded(host, section):
if not setuptools.getListSetting("Server", "ignorelist"):
for i in setuptools.getListSetting("HTTP" if section == HTTP else "SSL" if section == SSL else "Ports" if section == PORT else "SKS", "sites" if section == HTTP else "hosts"):
if encodings.idna.ToASCII(i[0].lower()).decode("UTF-8") == encodings.idna.ToASCII(host.lower()).decode("UTF-8"):
return True
return False
return True
class TCPHandler(socketserver.StreamRequestHandler):
def readString(self):
return self.rfile.readline().strip()
def sendString(self, string):
self.request.sendall((string + "\n").encode('utf8'))
def requestHandler(self, request):
pass
def worker(self, message):
content = message.split()
command = content[0].lower()
try:
if command == "hi":
try:
return "HI: Kumi Status v0.8.15 (KSP) at %s" % setuptools.getSetting("Server", "host") or socket.gethostname()
except Exception as e:
print(e)
elif command in ("heartbeat", "hb", "ping"):
return "OK: Still here? Wow."
elif command in ("stat", "status"):
return "UA: Not currently implemented."
elif command in ("ssl", "tls"):
try:
host = encodings.idna.ToASCII(str(content[1])).decode("UTF-8")
try:
port = int(content[2])
except IndexError:
port = 443
if listIncluded(host, SSL):
expiry = ssltools.getRemoteExpiry(host, port)
if expiry > datetime.datetime.now():
dm = "%s certificate is valid until: %s" % (content[1], expiry)
try:
delta = int(content[3])
except:
delta = 0
if expiry < datetime.datetime.now() + datetime.timedelta(days=delta):
return "AL: %s" % dm
return "OK: %s" % dm
else:
return "AL: %s certificate has expired on: %s" % (content[1], expiry)
else:
return "NM: %s is not being monitored!" % content[1]
except Exception as e:
print(e)
return "ER: Could not verify SSL certificate on %s:%i. Is the server down?" % (content[1], int(content[2]))
elif command == "port":
host = encodings.idna.ToASCII(str(content[1])).decode("UTF-8")
port = int(content[2])
if listIncluded(host, PORT):
if porttools.isPortOpen(host, port):
return "OK: Port %i is open on %s." % (port, content[1])
else:
return "ER: Port %i is not open on %s." % (port, content[1])
else:
return "NM: %s is not being monitored!" % content[1]
elif command in ("sks", "keyserver"):
host = encodings.idna.ToASCII(str(content[1])).decode("UTF-8")
if listIncluded(host, SKS):
if skstools.getStatus(host):
return "OK: SKS is running on %s and included in the pools." % content[0]
else:
return "ER: The SKS keyserver at %s is not included in the pools." % content[1]
else:
return "NM: %s is not being monitored!" % content[1]
elif command in ("req", "request"):
return "NI: Requesting monitoring is not yet implemented."
elif command == "help":
return "UA: Not currently implemented."
elif command in ("http", "web"):
site = encodings.idna.ToASCII(str(content[1])).decode("UTF-8")
if listIncluded(site, HTTP):
status = httptools.getStatus(site)
if status == 200:
return "OK: Site %s seems to be working fine." % content[1]
elif status == None:
return "ER: Site %s looks down from here!" % content[1]
else:
return "ER: Site %s returns status code %s!" % (content[1], status)
else:
return "NM: %s is not being monitored!" % content[1]
else:
return "IM: Unknown command %s." % command
except TypeError as e:
return "IM: Invalid values passed to %s. Try HELP %s." % (command, command)
except IndexError as e:
return "IM: Invalid values passed to %s. Try HELP %s." % (command, command)
def handle(self):
try:
remote = self.client_address[0] + ":" + str(self.client_address[1])
logger("New connection from %s." % remote, syslog.LOG_INFO)
self.sendString(self.worker("hi"))
while True:
message = self.readString().decode('utf8')
if not message:
logger("Connection from %s closed." % remote, syslog.LOG_DEBUG)
break
logger("%s said: %s" % (remote, message))
try:
response = self.worker(message)
except:
response = "EM: Something went terribly wrong. Sorry about that."
if response:
self.sendString(response)
logger("Sent to %s: %s" % (remote, response), syslog.LOG_DEBUG)
except Exception as e:
logger(e, syslog.LOG_CRIT)
class TCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
def shutdown(reboot = False, status = 0):
if reboot:
args = sys.argv[:]
args.insert(0, sys.executable)
try:
os.execv(sys.executable, args)
except:
logger("Restart failed. Shutting down.")
exit(status)