252 lines
5.6 KiB
Python
252 lines
5.6 KiB
Python
|
#!/usr/bin/env python3
|
||
|
import sys
|
||
|
|
||
|
stopOnException = True
|
||
|
oldexcepthook = sys.excepthook
|
||
|
def newexcepthook(type,value,traceback):
|
||
|
oldexcepthook(type,value,traceback)
|
||
|
if stopOnException: input("Press ENTER to quit.")
|
||
|
sys.excepthook = newexcepthook
|
||
|
|
||
|
import os
|
||
|
p = os.path.join
|
||
|
pUp = os.path.dirname
|
||
|
s = False
|
||
|
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
|
||
|
s = os.path.realpath(sys.executable)
|
||
|
else:
|
||
|
s = os.path.realpath(__file__)
|
||
|
sp = pUp(s)
|
||
|
|
||
|
import socket
|
||
|
import threading
|
||
|
import queue
|
||
|
import traceback
|
||
|
|
||
|
# SETTINGS
|
||
|
serverAddr = ("127.0.0.1",1337)
|
||
|
moduleDir = p(sp,"modules")
|
||
|
heartbeatTime = 300
|
||
|
maxRequestSize = 4096
|
||
|
# SETTINGS END
|
||
|
|
||
|
connections = {}
|
||
|
connectionsLock = threading.Lock()
|
||
|
connectionId = 0
|
||
|
|
||
|
handlers = {}
|
||
|
handlers["modulesLoaded"] = []
|
||
|
handlers["preConnect"] = []
|
||
|
handlers["connect"] = []
|
||
|
handlers["preCommand"] = []
|
||
|
handlers["command"] = []
|
||
|
|
||
|
commands = {}
|
||
|
|
||
|
def runCode(str, lcs = False, description = "loose-code"):
|
||
|
if lcs == False: lcs = {}
|
||
|
code = compile(str,description,"exec")
|
||
|
exec(code,globals(),lcs)
|
||
|
return lcs
|
||
|
|
||
|
def runScript(sf, lcs = False):
|
||
|
if lcs == False: lcs = {}
|
||
|
with open(sf) as script:
|
||
|
runCode(script.read(),lcs,sf)
|
||
|
return lcs
|
||
|
|
||
|
def callHandler(handlerName,env = {},*args,**kwargs):
|
||
|
if handlerName in handlers:
|
||
|
for handlerFunc in handlers[handlerName]:
|
||
|
if handlerFunc(env,*args,**kwargs):
|
||
|
return True
|
||
|
|
||
|
return False
|
||
|
|
||
|
def getResponse(connection):
|
||
|
data = b''
|
||
|
data = connection.recv(4)
|
||
|
if not data: return False
|
||
|
nul = connection.recv(1)
|
||
|
if not nul: return False
|
||
|
if nul != b"\x00": return False
|
||
|
requestLength = int.from_bytes(data,"big")
|
||
|
if requestLength > maxRequestSize: return False
|
||
|
return connection.recv(requestLength)
|
||
|
|
||
|
def sendResponse(connection,data):
|
||
|
connection.sendall(len(data).to_bytes(4,"big") + b"\x00" + data)
|
||
|
|
||
|
def commandlineToList(cmd):
|
||
|
args = []
|
||
|
cArg = ""
|
||
|
escape = False
|
||
|
quoted = False
|
||
|
for letter in cmd:
|
||
|
if escape == True:
|
||
|
cArg += letter
|
||
|
escape = False
|
||
|
continue
|
||
|
|
||
|
if letter == "\\":
|
||
|
escape = True
|
||
|
continue
|
||
|
|
||
|
if letter == ",":
|
||
|
if cArg == "": continue
|
||
|
args.append(cArg)
|
||
|
cArg = ""
|
||
|
continue
|
||
|
|
||
|
cArg += letter
|
||
|
|
||
|
args.append(cArg)
|
||
|
|
||
|
return args
|
||
|
|
||
|
def listToCommandline(lst):
|
||
|
cmd = ""
|
||
|
for arg in lst:
|
||
|
arg = arg.replace("\\","\\\\")
|
||
|
arg = arg.replace(",","\\,")
|
||
|
cmd += arg + ","
|
||
|
|
||
|
return cmd[:-1]
|
||
|
|
||
|
def runCommand(self,command,*args):
|
||
|
callHandler("preCommand",locals())
|
||
|
if not command in commands:
|
||
|
rtn = ["error","nonfatal","invalid_command","Command does not exist"]
|
||
|
callHandler("command",locals())
|
||
|
return rtn
|
||
|
|
||
|
rtn = commands[command](self,command,*args)
|
||
|
if not rtn: rtn = ["OK"]
|
||
|
callHandler("command",locals())
|
||
|
return rtn
|
||
|
|
||
|
class connectionThreadIn(threading.Thread):
|
||
|
def __init__(self,cid,connection,address):
|
||
|
threading.Thread.__init__(self)
|
||
|
self.cid = cid
|
||
|
self.connection = connection
|
||
|
self.address = address
|
||
|
|
||
|
def routine(self):
|
||
|
while True:
|
||
|
data = getResponse(self.connection)
|
||
|
if data == False: return
|
||
|
commandList = commandlineToList(data.decode("utf-8"))
|
||
|
rtn = runCommand(self,*commandList)
|
||
|
with connectionsLock:
|
||
|
connections[self.cid]["threadOut"].queue.put(listToCommandline(rtn))
|
||
|
|
||
|
def run(self):
|
||
|
try:
|
||
|
self.routine()
|
||
|
except:
|
||
|
print(traceback.format_exc())
|
||
|
|
||
|
try:
|
||
|
self.connection.close()
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
with connectionsLock:
|
||
|
try:
|
||
|
connections[self.cid]["threadOut"].queue.put(False)
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
try:
|
||
|
del connections[self.cid]
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
class connectionThreadOut(threading.Thread):
|
||
|
def __init__(self,cid,connection,address):
|
||
|
threading.Thread.__init__(self)
|
||
|
self.cid = cid
|
||
|
self.connection = connection
|
||
|
self.address = address
|
||
|
self.queue = queue.Queue()
|
||
|
|
||
|
def routine(self):
|
||
|
while True:
|
||
|
data = self.queue.get(timeout=heartbeatTime)
|
||
|
if data == False:
|
||
|
return
|
||
|
sendResponse(self.connection,data.encode("utf-8"))
|
||
|
|
||
|
def run(self):
|
||
|
try:
|
||
|
self.routine()
|
||
|
except:
|
||
|
print(traceback.format_exc())
|
||
|
|
||
|
try:
|
||
|
self.connection.close()
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
try:
|
||
|
with connectionsLock:
|
||
|
del connections[self.cid]
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
def main():
|
||
|
if os.path.isfile("modules.txt"):
|
||
|
print("Loading modules...")
|
||
|
with open("modules.txt","r") as modulesFile:
|
||
|
for line in modulesFile:
|
||
|
line = line.split("#",1)[0].strip(" \t\r\n")
|
||
|
if line == "": continue
|
||
|
print("> " +line+ " ...")
|
||
|
moduleFile = p(moduleDir,line)
|
||
|
runScript(moduleFile,locals())
|
||
|
print("OK.\n")
|
||
|
|
||
|
callHandler("modulesLoaded",locals())
|
||
|
|
||
|
global connectionId
|
||
|
global serverSocket
|
||
|
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
|
serverSocket.bind(serverAddr)
|
||
|
serverSocket.listen(65535)
|
||
|
print("Serving at " +str(serverAddr[0])+ ":" +str(serverAddr[1])+ ".")
|
||
|
|
||
|
while True:
|
||
|
connection = False
|
||
|
try:
|
||
|
connection,address = serverSocket.accept()
|
||
|
connection.settimeout(heartbeatTime)
|
||
|
|
||
|
if callHandler("preConnect",locals()):
|
||
|
connection.close()
|
||
|
continue
|
||
|
|
||
|
with connectionsLock:
|
||
|
connectionId += 1
|
||
|
threadIn = connectionThreadIn(str(connectionId),connection,address)
|
||
|
threadOut = connectionThreadOut(str(connectionId),connection,address)
|
||
|
connections[str(connectionId)] = {
|
||
|
"connection": connection,
|
||
|
"address": address,
|
||
|
"threadIn": threadIn,
|
||
|
"threadOut": threadOut
|
||
|
}
|
||
|
|
||
|
threadIn.start()
|
||
|
threadOut.start()
|
||
|
|
||
|
callHandler("connect",locals())
|
||
|
except:
|
||
|
print(traceback.format_exc())
|
||
|
try:
|
||
|
connection.close()
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|