diff --git a/asrc-server.py b/asrc-server.py index a9211be..aa329d7 100755 --- a/asrc-server.py +++ b/asrc-server.py @@ -25,7 +25,7 @@ # aSRC (Aliased Server Remote Control) # - SERVER - # -# program version: 0.0.0.20130424 +# program version: 0.0.0.20130426 # protocol version: 0.2.20130423 # # @@ -35,243 +35,175 @@ import sys, os, socket, socketserver, threading, time -def motd(): - return MOTD - - -#def content(client_address, data): - #ret = "" - - #ret = ret +\ - #"{BEGIN}\n"\ - #"asrcp" + ProtocolVersion + "\n" - - ## Look if the received message is an - ## valid alias or a predefined command - - ## if it's 'version', return the server and protocol version - #if data == "version": - - #if VERBOSITY >= 2: print("Got valid service command from" - #+ str(client_address) + ": ", data) - - #ret = ret +\ - #"202 Valid Service Command\n"\ - #"002 Version\n"\ - #"ServerVersion:" + ServerVersion + "\n"\ - #"ProtocolVersion:" + ProtocolVersion + "\n" - - ## if it's 'help', give a little help - #elif data == 'help': - - #if VERBOSITY >= 2: print("Got valid command from" - #+ str(client_address) + ": ", data) - - ## send status code - #ret = ret + "202 Valid Service Command\n\n" - - ## send the list of aliases - #ret = ret + "Aviable aliases:\n" - #for i in aliases.keys(): - #ret = ret + str(i) + "\n" - - ## if it's a valid userdefined command - #elif data in aliases: - - ## send status code - #ret = ret + "201 Valid Command\n\n" - - ## ohmagawd! a debug message!!1! - #if VERBOSITY >= 2: print("Got valid command from" - #+ str(client_address) + ": ", data) - - ## execute the aliased command - #g_dict, l_dict = {}, {} - #exec(str(aliases[data]), g_dict, l_dict) - - ## send may contain data to send to the client - #if l_dict["send"]: - #content = str(l_dict["send"]).replace('{', '\{') - #content = content.replace('}', '\}') - - #ret = ret + content + "\n" - - ## ALL IS LOST!!1! this has to be invalid! - #else: - - ## send status code - #ret = ret + "203 Invalid Command\n" - - #if VERBOSITY >= 2: print("Got invalid command from", - #str(client_address), ": ", data) - - #ret = ret + "{END}\n" - - #return ret - - class ThreadedRequestHandler(socketserver.StreamRequestHandler): - - def handle(self): - - # Set time for timeout in seconds - self.timeout = TIMEOUT - - # Print a line with the adress of the connected client - if VERBOSITY >=3: - print("Client connected: " + str(self.client_address)) - - # send header line 1 - self.request.sendall(bytes - ("asrpc " + ProtocolVersion + "\n", ENCODING)) - - # send motd - self.request.sendall(bytes(motd() + "\n", ENCODING)) - - # Receive data - self.data = str(self.rfile.readline().strip(), ENCODING) - - # content handler - self.request.sendall(bytes( - content.handler(str(self.client_address), self.data, aliases, - ServerVersion, ProtocolVersion, VERBOSITY), ENCODING)) + + def handle(self): + """ + Handles incoming connections + """ + + # Set time for timeout in seconds + self.timeout = TIMEOUT + + # Print a line with the adress of the connected client + if VERBOSITY >=3: + print("Client connected: " + str(self.client_address)) + + # send motd + self.request.sendall(comm.motd(MOTD) + "\n", ENCODING)) + + # Receive data + self.data = str(self.rfile.readline().strip(), ENCODING) + + # content handler + self.request.sendall(bytes( + comm.handler(str(self.client_address), self.data), ENCODING)) class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): - pass + pass def main(): - global VERBOSITY, HOST, PORT, TIMEOUT, ENCODING - - from include import argparser, content - - parser = argparser - args = parser.parse(ServerVersion, ProtocolVersion) - - if os.path.exists("pid"): - if args.delete_pid_file: os.remove("pid") - elif args.allow_multiple_instances: pass - else: - print( - "\npid file already exists\n"\ - "If the server didn't shut down correctly, just delete this file "\ - "or pass -n\n"\ - "If you want to start multiple instances, pass -m\n") - return 1 - - print(args) - - if args.host: HOST = args.host - if args.port: PORT = args.port - if args.timeout: TIMEOUT = args.timeout - if args.encoding: ENCODING = args.encoding - if args.verbosity: VERBOSITY = args.verbosity - - # write pid file - pidf = open("pid", 'w') - pidf.write(str(os.getpid())) - pidf.close() - - print("aSRC Server\n"\ - "Server version: " + ServerVersion + "\n"\ - "Protocol version: " + ProtocolVersion + "\n\n"\ - "To stop the server, press Ctrl-C\n") - - try: - if VERBOSITY >= 1: print("Initializing server...") - # Create server - server = ThreadedTCPServer((HOST, PORT), ThreadedRequestHandler) - - if VERBOSITY >= 1: print("Starting server on", - server.server_address[0], "port", server.server_address[1], "...") - # Start a thread with the server -- that thread will then start one - # more thread for each request - ServerThread = threading.Thread(target = server.serve_forever) - - # Exit the server thread when the main thread terminates - ServerThread.daemon = True - ServerThread.start() - - while True: - time.sleep(10) - - except KeyboardInterrupt: - print("\nGot Ctrl-C, shutting down server...") - - try: - server.shutdown() - os.remove("pid") - except: print("Failed to shutdown server correctly, "\ - "socket may be still in use or wasn't even started:", - sys.exc_info()) - #except: - # print("\nAn error occured:\n", sys.exc_info(), "\n") - - if VERBOSITY >= 3: input("Press Enter to continue\n") - return 0 + # import global settings + global VERBOSITY, HOST, PORT, TIMEOUT, ENCODING + + from include import argparser, comm + + comm.init(ServerVersion, ProtocolVersion, VERBOSITY, aliases) + + # parse arguments + parser = argparser + args = parser.parse(ServerVersion, ProtocolVersion) + + # look for pid file + if os.path.exists("pid"): + # remove, ... + if args.delete_pid_file: os.remove("pid") + # ... ignore ... + elif args.allow_multiple_instances: pass + # ... or exit + else: + print( + "\npid file already exists\n"\ + "If the server didn't shut down correctly, "\ + "just delete this file or pass -n\n"\ + "If you want to start multiple instances, pass -m\n") + return 1 + + # set settings if command line options are given + if args.host: HOST = args.host + if args.port: PORT = args.port + if args.timeout: TIMEOUT = args.timeout + if args.encoding: ENCODING = args.encoding + if args.verbosity: VERBOSITY = args.verbosity + + # write pid file + pidf = open("pid", 'w') + pidf.write(str(os.getpid())) + pidf.close() + + print("aSRC Server\n"\ + "Server version: " + ServerVersion + "\n"\ + "Protocol version: " + ProtocolVersion + "\n\n"\ + "To stop the server, press Ctrl-C\n") + + try: + if VERBOSITY >= 1: print("Initializing server...") + # Create server + server = ThreadedTCPServer((HOST, PORT), ThreadedRequestHandler) + + if VERBOSITY >= 1: print("Starting server on", + server.server_address[0], "port", server.server_address[1], "...") + # Start a thread with the server -- that thread will then start one + # more thread for each request + ServerThread = threading.Thread(target = server.serve_forever) + + # Exit the server thread when the main thread terminates + ServerThread.daemon = True + ServerThread.start() + + + while True: + time.sleep(10) + + # exit if Ctrl-C is pressed + except KeyboardInterrupt: + print("\nGot Ctrl-C, shutting down server...") + + try: + server.shutdown() + os.remove("pid") + except: print("Failed to shutdown server correctly, "\ + "socket may be still in use or wasn't even started:", + sys.exc_info()) + #except: + # print("\nAn error occured:\n", sys.exc_info(), "\n") + + if VERBOSITY >= 3: input("Press Enter to continue\n") + return 0 if __name__ == '__main__': - - ServerVersion = "0.0.0" - ProtocolVersion = "0.2" - - - ############## - # SETTINGS # - ############## - - # IP or hostname (use 0.0.0.0 for all interfaces) and port, on which - # the server should listen - HOST = '127.0.0.1' - PORT = 24642 - - # Timeout of a connection in seconds - still doesn't work obviously... - TIMEOUT = 15 - - # Encoding to be used when communicating with a client - ENCODING = 'utf-8' - - # Dictionary of aliases. Use python syntax. You can use - # the variable send for text to send to the client. - # - # Shell commands can be executed with: - # import subprocess - # send = subprocess.check_output(["command", "arg", "somemorearg"]) - # - # You don't have to include sys, os, socket, socketserver, - # threading and time, through they are included already. - aliases = dict( - who = ''' + + ServerVersion = "0.0.0" + ProtocolVersion = "0.2" + + + ############## + # SETTINGS # + ############## + + # IP or hostname (use 0.0.0.0 for all interfaces) and port, on which + # the server should listen + HOST = '127.0.0.1' + PORT = 24642 + + # Timeout of a connection in seconds - still doesn't work obviously... + TIMEOUT = 15 + + # Encoding to be used when communicating with a client + ENCODING = 'utf-8' + + # Dictionary of aliases. Use python syntax. You can use + # the variable send for text to send to the client. + # + # Shell commands can be executed with: + # import subprocess + # send = subprocess.check_output(["command", "arg", "somemorearg"]) + # + # You don't have to include sys, os, socket, socketserver, + # threading and time, through they are included already. + aliases = dict( + who = ''' import subprocess send = subprocess.check_output(["whoami"]) ''', - where = ''' + where = ''' import subprocess send = subprocess.check_output(["pwd"]) ''', - uname = ''' + uname = ''' import os send = os.uname() ''', - date = ''' + date = ''' import subprocess send = subprocess.check_output(["date"]) ''', - ping_fanir = ''' + ping_fanir = ''' import subprocess send = subprocess.check_output(["ping", "-c 2", "fanir.de"]) ''') - - # This is sent to the client after the connection is established - MOTD = "Welcome! This is only a test server, for developing purposes.\n"\ - "Here (may) be more text..." - - # Verbosity of logging. - # Can be from 0 (only default output) to 3 (debug messages) - VERBOSITY = 3 - ############## - - - main() + + # This is sent to the client after the connection is established + MOTD = "Welcome! This is only a test server, for developing purposes.\n"\ + "Here (may) be more text..." + + # Verbosity of logging. + # Can be from 0 (only default output) to 3 (debug messages) + VERBOSITY = 3 + ############## + + + main() diff --git a/doc/protocol_reference.odt b/doc/protocol_reference.odt index f5be414..fa01699 100644 Binary files a/doc/protocol_reference.odt and b/doc/protocol_reference.odt differ diff --git a/include/__init__.py b/include/__init__.py index 174e139..6ff74a1 100644 --- a/include/__init__.py +++ b/include/__init__.py @@ -1,2 +1,2 @@ from .argparser import argparser -from .content import content +from .comm import comm diff --git a/include/argparser.py b/include/argparser.py index 517ebf6..bf66b38 100644 --- a/include/argparser.py +++ b/include/argparser.py @@ -1,78 +1,77 @@ # includes/argparser.py # -# module version: 1.0.20130424 +# module version: 1.0.20130425 # class argparser: - - def parse(program_version, protocol_version): - import argparse - - parser = argparse.ArgumentParser( - description = "The server side of the "\ - "aliased server remote control", - add_help = False) - - - parser.add_argument( - "--help", - action = "help", - help = "show this help message and exit") - - parser.add_argument( - "--version", - action = "version", - version = "Server version: " + program_version +\ - " / Protocol version: " + protocol_version) - - grp_pid_file_failure = parser.add_mutually_exclusive_group() - grp_pid_file_failure.add_argument( - "-n", - "--delete-pid-file", - action = "store_true", - help = "deletes the pid file and starts the server") - - grp_pid_file_failure.add_argument( - "-m", - "--allow-multiple-instances", - action = "store_true", - help = "ignores the pid file and starts the server. "\ - "Will not create another pid file.") - - parser.add_argument( - "-v", - "--verbosity", - type = int, - choices = range(0, 4), - # default = -1, - help = "increase output verbosity. Can be from 0 "\ - "(only default output) to 3 (debug messages).") - - parser.add_argument( - "-h", - "--host", - # default = -1, - help = "IP or hostname (use 0.0.0.0 for all interfaces) on which "\ - "the server should listen") - - parser.add_argument( - "-p", - "--port", - # default = -1, - help = "the port on which the server should listen") - - parser.add_argument( - "-t", - "--timeout", - # default = -1, - help = "timeout of a connection in seconds - still "\ - "doesn't work obviously...") - - parser.add_argument( - "-e", - "--encoding", - # default = -1, - help = "encoding to be used when communicating with clients") - - return parser.parse_args() + + def parse(program_version, protocol_version): + """ + Parses comand line arguments + """ + + import argparse + + parser = argparse.ArgumentParser( + description = "The server side of the "\ + "aliased server remote control", + add_help = False) + + + parser.add_argument( + "--help", + action = "help", + help = "show this help message and exit") + + parser.add_argument( + "--version", + action = "version", + version = "Server version: " + program_version +\ + " / Protocol version: " + protocol_version) + + grp_pid_file_failure = parser.add_mutually_exclusive_group() + grp_pid_file_failure.add_argument( + "-n", + "--delete-pid-file", + action = "store_true", + help = "deletes the pid file and starts the server") + + grp_pid_file_failure.add_argument( + "-m", + "--allow-multiple-instances", + action = "store_true", + help = "ignores the pid file and starts the server. "\ + "Will not create another pid file.") + + parser.add_argument( + "-v", + "--verbosity", + type = int, + choices = range(0, 4), + help = "increase output verbosity. Can be from 0 "\ + "(only default output) to 3 (debug messages).") + + parser.add_argument( + "-h", + "--host", + help = "IP or hostname (use 0.0.0.0 for all interfaces) on which "\ + "the server should listen") + + parser.add_argument( + "-p", + "--port", + help = "the port on which the server should listen") + + parser.add_argument( + "-t", + "--timeout", + help = "timeout of a connection in seconds - still "\ + "doesn't work obviously...") + + parser.add_argument( + "-e", + "--encoding", + help = "encoding to be used when communicating with clients") + + return parser.parse_args() diff --git a/include/content.py b/include/comm.py similarity index 64% rename from include/content.py rename to include/comm.py index 79a4545..24b1479 100644 --- a/include/content.py +++ b/include/comm.py @@ -1,18 +1,59 @@ -# includes/content.py +# includes/comm.py # -# module version: 0.0.20130424 +# module version: 0.0.20130426 # -class content: +import statuscodes + + +class comm: - # handler the content - def handler(client_address, data, aliases, server_version, protocol_version, verbosity): - ret = "" + aliases = dict() + server_version = "" + protocol_version = "" + verbosity = 0 + + # initializes global settings + def init(ServerVersion, ProtocolVersion, Verbosity, Aliases): - ret = ret +\ + aliases = Aliases + server_version = ServerVersion + protocol_version = ProtocolVersion + verbosity = Verbosity + + def header(statuscode, AdditionalHeaderLines = ""): + """ + returns the header + """ + + ret =\ "{BEGIN}\n"\ - "asrcp" + protocol_version + "\n" + "asrcp" + protocol_version + "\n"\ + statuscode + " " + statuscodes.description[statuscodes] + "\n"\ + "ServerVersion:" + server_version + "\n" + if AdditionalHeaderLines != "": ret += AdditionalHeaderLines + "\n" + ret += "\n\n" + + return ret + + # returns the motd + def motd(motd): + """ + builds and returns a motd package + """ + + ret = motd + + return ret + + # handles the content + def command(client_address, data): + """ + processes a command + """ + + ret = "" # Look if the received message is an # valid alias or a predefined command diff --git a/include/statuscodes.py b/include/statuscodes.py new file mode 100644 index 0000000..b106ffc --- /dev/null +++ b/include/statuscodes.py @@ -0,0 +1,37 @@ +class statuscodes: + description = dict( + # 000 - 400 server side + # 000 information + 001 = "OK", + 002 = "Version", + 003 = "MOTD", + # 100 authentication and maintenance + 101 = "Challenge", + 102 = "Success", + 103 = "Failure", + 104 = "To Many Tries", + # 200 command + 201 = "Valid", + 202 = "Valid Service Command", + 203 = "Invalid", + 204 = "Failure", + 205 = "Continue", + # 300 server + 301 = "Unhandled Exception", + 302 = "Shutting Down", + 303 = "Restarting", + 304 = "Encoding Error", + 305 = "SSL Error", + # 500 - 900 client side + # 500 information + 501 = "OK", + 502 = "Version", + # 600 authentication and maintenance + 601 = "Response", + 602 = "Failure", + # 700 command + 701 = "Reqiuest", + 702 = "Cancel", + # 800 client + 801 = "SSL Error" + )