From: George W. <lt...@us...> - 2014-07-01 23:56:26
|
The following patch replaces unsafe pickler serialization with colander serialization. Because the pickler accepts multiple types, it is able to handle different message formats transparently, including error strings. Colander enforces type checking for safety. Doing so, however, means that the error messages must now be explicitly encapsulated so that they can be deencapsulated like everything else. The pickler also works nicely with sockets by transparently implementing the handshaking for passing arbitrarily sized buffers. Because the protocol is simple enough, this patch just shuts down one side of the socket to synchronize. All this breaks backwards compatibility in a way that likely doesn't allow version detection so cmdvers hasn't been bumped. Signed-off-by: George Wilson <gcw...@us...> amsnet.py | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-) --- --- powerpc-utils-python-1.2.1-orig/scripts/amsvis/powerpcAMS/amsnet.py 2009-10-22 14:02:15.000000000 -0500 +++ powerpc-utils-python-1.2.1/scripts/amsvis/powerpcAMS/amsnet.py 2014-01-16 18:13:17.704795365 -0600 @@ -16,6 +16,8 @@ import types import logging import cPickle as pickle from optparse import OptionParser +import colander +import ast from powerpcAMS.amsdata import * @@ -28,10 +30,26 @@ data_methods = (gather_all_data, gather_ # The update should increment the digits to the right of the decimal point. # The digits to the left of the decimal point should be increased when # backwards compatibility is broken. +orig_cmdvers = 1.0000000 cmdvers = 1.0000000 +class Request(colander.MappingSchema): + command = colander.SchemaNode(colander.Int(), + validator = colander.Range(0, cmd_MAX)) + version = colander.SchemaNode(colander.Float(), + validator = colander.Range(orig_cmdvers, cmdvers)) + +class DataTuple(colander.TupleSchema): + sys_data = colander.SchemaNode(colander.Mapping(unknown='preserve')) + bus_data = colander.SchemaNode(colander.Mapping(unknown='preserve')) + devices_data = colander.SchemaNode(colander.Mapping(unknown='preserve')) + +class Response(colander.MappingSchema): + result = colander.SchemaNode(colander.String()) + data = DataTuple() + def send_data_loop(port): - """Send pickled data to any client that connects to a given network port. + """Send serialized data to any client that connects to a given network port. Keyword arguments: port -- network port number to use for this server @@ -64,12 +82,13 @@ def send_data_loop(port): # request data version we can change what the server will # send in the future. try: - client_data = pickle.Unpickler(sockfile).load() + request_schema = Request() + client_data = request_schema.deserialize(ast.literal_eval(sockfile.read())) except: logging.debug("Unable to parse client request.") logging.info("Bad client request, ignoring.") result = "error" - data = "bad client request" + data = ({"error":"bad client request"}, {}, {}) # Currently the server only expects a dictionary from the client # with the following values to send AMS data: @@ -80,7 +99,7 @@ def send_data_loop(port): int(client_data["version"]) != int(cmdvers))): logging.debug("Unsupported client request version, ignoring.") result = "error" - data = "Unsupported version, server is %f" % cmdvers + data = ({"error":"Unsupported version, server is %f" % cmdvers}, {}, {}) if (result is not "error" and ("command" not in client_data or @@ -88,7 +107,7 @@ def send_data_loop(port): client_data["command"] > cmd_MAX)): logging.debug("Unsupported request from client, ignoring.") result = "error" - data = "Unsupported request" + data = ({"error":"Unsupported request"}, {}, {}) if result is not "error": data_method = data_methods[client_data["command"]] @@ -97,11 +116,11 @@ def send_data_loop(port): data = data_method() if data is None: result = "error" - data = "Unspecified data gathering error, check server log." + data = ({"error":"Unspecified data gathering error, check server log."}, {}, {}) logging.debug("Sending %d data objects to client." % len(data)) - response = {"result":result, "data":data} - sockfile.writelines(pickle.dumps(response, -1)) + response_schema = Response() + sockfile.write(response_schema.serialize(response)) # Clean up sockfile.close() @@ -143,7 +162,7 @@ def send_data_loop(port): # Client def net_get_data(host="localhost", port=50000, cmd=cmd_GET_ALL_DATA): - """Get pickled data from a simple network server. + """Get serialized data from a simple network server. Keywork arguments: host -- server host name (default localhost) @@ -177,20 +196,25 @@ def net_get_data(host="localhost", port= print repr(cmd) return {"result":"error","data":"Client: Bad request."} - sockfile.writelines(pickle.dumps(client_request, -1)) + request_schema = Request() + sockfile.write(request_schema.serialize(client_request)) + sock.shutdown(socket.SHUT_WR) # Get server response - pickler = pickle.Unpickler(sockfile) try: - data = pickler.load() + response_schema = Response() + data = response_schema.deserialize(ast.literal_eval(sockfile.read())); except EOFError: pass sockfile.close() sock.close() - if type(data) is not types.DictType or "result" not in data: + if "result" not in data or "data" not in data: data = {"result":"error", "data":"Unknown server error"} + if data["result"] == "error" and "error" in data["data"][0]: + data["data"] = data["data"][0]["error"] + logging.debug("Data returned to client: " + repr(data)) return data -- George Wilson IBM Linux Technology Center Security Development |