def get_dissection( self, protocol: optional(is_protocol) = None, ) -> list_of(OrderedDict): """ Function to get dissection of a capture as a list of frames represented as strings :param protocol: Protocol class for filtering purposes :type protocol: type :raises TypeError: If protocol is not a protocol class :raises ReaderError: If the reader couldn't process the file :return: A list of Frame represented as API's dict form :rtype: [OrderedDict] """ # log.debug('Starting dissection.') # Check the protocol is one entered if all((protocol, not is_protocol(protocol))): raise TypeError(protocol.__name__ + ' is not a protocol class') fs = self.frames # For speeding up the process with Data.disable_name_resolution(): # Filter the frames for the selected protocol if protocol: fs, _ = Frame.filter_frames(fs, protocol) if fs is None: raise Error('Empty capture cannot be dissected') # Then return the list of dictionary frame representation return [frame.dict() for frame in fs]
def get_token(tok: optional(str) = None): """ Function to get a token, if there's a valid one entered just return it otherwise generate a new one :param tok: The token if there's already one :type tok: str :return: A token, the same if there's already one, a new one otherwise :rtype: str """ # If the token is already a correct one try: if all(( tok, type(tok) == str, len(tok) == 28, base64.urlsafe_b64decode(tok + '=') # Add '=' only for checking )): return tok except: # If the decode throw an error => Wrong base64 pass # Generate a token because there is none token = hashlib.sha1( str.encode( ("%s%s%04d%s" % (HASH_PREFIX, time.time(), job_id, HASH_SUFFIX)), encoding='utf-8')) token = base64.urlsafe_b64encode(token.digest()).decode() # Remove the '=' at the end of it, it is used by base64 for padding return token.replace('=', '')
def dissect( self, protocol: optional(is_protocol) = None) -> list_of(OrderedDict): """ The dissect function to dissect a pcap file into list of frames :param protocol: Protocol class for filtering purposes :type protocol: type :raises TypeError: If protocol is not a protocol class :raises ReaderError: If the reader couldn't process the file :return: A list of Frame represented as API's dict form :rtype: [OrderedDict] """ # log.debug('Starting dissection.') # Check the protocol is one entered if all((protocol, not is_protocol(protocol))): raise TypeError(protocol.__name__ + ' is not a protocol class') # For speeding up the process with Data.disable_name_resolution(): # Get the list of frames frames = self.__capture.frames # Filter the frames for the selected protocol if protocol is not None: frames, _ = Frame.filter_frames(frames, protocol) # Then return the list of dictionary frame representation return [frame.dict() for frame in frames]
def summary(self, protocol: optional(is_protocol) = None) -> list_of((int, str)): """ The summaries function to get the summary of frames :param protocol: Protocol class for filtering purposes :type protocol: type :Example: from ttproto.core.lib.all import Ieee802154 for s in dissector.summary(protocol = Ieee802154): print(s) :raises TypeError: If protocol is not a protocol class :raises ReaderError: If the reader couldn't process the file :return: Basic informations about frames like the underlying example :rtype: [(int, str)] :Example: [ (13, '[127.0.0.1 -> 127.0.0.1] CoAP [CON 38515] GET /test'), (14, '[127.0.0.1 -> 127.0.0.1] CoAP [ACK 38515] 2.05 Content'), (21, '[127.0.0.1 -> 127.0.0.1] CoAP [CON 38516] PUT /test'), (22, '[127.0.0.1 -> 127.0.0.1] CoAP [ACK 38516] 2.04 Changed')] ] .. note:: With the protocol option we can filter the response """ if all((protocol, not is_protocol(protocol))): raise TypeError(protocol.__name__ + ' is not a protocol class') fs = self.frames # For speeding up the process with Data.disable_name_resolution(): # Filter the frames for the selected protocol if protocol: fs, _ = Frame.filter_frames(fs, protocol) if fs is None: raise Error('Empty capture cannot be dissected') # Return list of frames summary return [frame.summary() for frame in fs]
def summary(self, protocol: optional(is_protocol) = None) -> list_of((int, str)): """ The summaries function to get the summary of frames :param protocol: Protocol class for filtering purposes :type protocol: type :Example: from ttproto.core.lib.all import Ieee802154 for s in dissector.summary(protocol = Ieee802154): print(s) :raises TypeError: If protocol is not a protocol class :raises ReaderError: If the reader couldn't process the file :return: Basic informations about frames like the underlying example :rtype: [(int, str)] :Example: [ (13, '[127.0.0.1 -> 127.0.0.1] CoAP [CON 38515] GET /test'), (14, '[127.0.0.1 -> 127.0.0.1] CoAP [ACK 38515] 2.05 Content'), (21, '[127.0.0.1 -> 127.0.0.1] CoAP [CON 38516] PUT /test'), (22, '[127.0.0.1 -> 127.0.0.1] CoAP [ACK 38516] 2.04 Changed')] ] .. todo:: Filter uninteresting frames ? (to decrease the load) .. note:: With the protocol option we can filter """ # Check the protocol if all((protocol, not is_protocol(protocol))): raise TypeError(protocol.__name__ + ' is not a protocol class') # Disable the name resolution in order to improve performances with Data.disable_name_resolution(): # Get the frames from the capture frames = self.__capture.frames # Filter the frames for the selected protocol if protocol is not None: frames, _ = Frame.filter_frames(frames, protocol) # Then give the summary of every frames return [frame.summary() for frame in frames]
def is_compiled_regex(arg): """ Check if a parameter is a valid regex compiled object. This function is used for the typechecker decorator. :return: True if a valid compiled regex, False if not :rtype: bool """ return all( (arg is not None, isinstance(arg, type(re.compile('dummy_pattern')))))
def get_dissection_simple_format( self, protocol: optional(is_protocol) = None, ) -> list_of(str): """ Function to get dissection of a capture as a list of frames represented as strings :param protocol: Protocol class for filtering purposes :type protocol: type :raises TypeError: If protocol is not a protocol class :raises ReaderError: If the reader couldn't process the file :return: A list of Frame represented as plain non-structured text :rtype: [str] """ # log.debug('Starting dissection.') # Check the protocol is one entered if all((protocol, not is_protocol(protocol))): raise TypeError(protocol.__name__ + ' is not a protocol class') fs = self.frames # For speeding up the process with Data.disable_name_resolution(): # Filter the frames for the selected protocol if protocol: fs, _ = Frame.filter_frames(fs, protocol) if fs is None: raise Error('Empty capture cannot be dissected') # fixme modify Message class from ttproto.data structure so I can get text display wihtout this patch class WritableObj(object): def __init__(self, text=''): self.val = text def __str__(self): return self.val def write(self, text): self.val += text frame_dissection_list = [] for f in fs: text_output = WritableObj() f.message.display(output=text_output) frame_dissection_list.append(str(text_output)) # Then return the list of frames,each as a simple text dissection return frame_dissection_list
def is_layer_value(arg: anything) -> bool: """ Check if a parameter is a valid layer value. This function is used for the typechecker decorator. :param arg: The object to check :type arg: anything :return: True if a valid layer value, False if not :rtype: bool """ return all((arg is not None, isinstance(arg, Value)))
def is_protocol(arg: anything) -> bool: """ Check if a parameter is a valid protocol. This function is used for the typechecker decorator. :param arg: The object to check :type arg: anything :return: True if a valid protocol, False if not :rtype: bool """ return all((arg is not None, type(arg) == type, arg in get_dissectable_protocols()))
def is_verdict(arg) -> bool: """ Check if a parameter is a valid verdict. This function is used for the typechecker decorator. :return: True if a valid verdict, False if not :rtype: bool """ return all(( arg is not None, type(arg) == str, arg in Verdict.values() ))
def correct_get_param(par: list, is_number: optional(bool) = False) -> bool: """ Function to check if a get parameter is correct :param par: The get parameter to check (care it's a list) :param is_number: If we expect it to be a number :type par: list :type is_number: bool :return: True if the get parameter is correct, False if not :rtype: bool """ return all( (len(par) == 1, type(par[0]) == str, par[0] != '', not (is_number and (not par[0].isdigit() or int(par[0]) <= 0))))
def next_skip_ack(self, optional: bool = False): """ Call self.next() but skips possibly interleaved ACKs :param optional: If we have to get a next frame or not :type optional: bool """ # Goes to next frame self.next(optional) # While there is one and that it's an ack, pass it while all((self._frame is not None, self._frame[CoAP] in CoAP(type='ack', code=0))): self.next(optional)
def is_tc_subclass(arg) -> bool: """ Check if a parameter is a valid traceback object. This function is used for the typechecker decorator. :return: True if a valid traceback, False if not :rtype: bool .. note:: Didn't find a better way to check this, isinstance or type seems to not work """ return all(( arg is not None, type(arg) == type, inspect.isclass(arg) and issubclass(arg, TestCase) ))
def is_traceback(arg) -> bool: """ Check if a parameter is a valid traceback object. This function is used for the typechecker decorator. :return: True if a valid traceback, False if not :rtype: bool .. note:: Didn't find a better way to check this, isinstance or type seems to not work """ return all(( arg is not None, hasattr(arg, '__class__'), hasattr(arg.__class__, '__name__'), isinstance(arg.__class__.__name__, str), arg.__class__.__name__ == 'traceback' ))
def do_POST(self): # The job counter global job_id job_id += 1 # ########################## ttproto API ########################### # # POST handler for the analyzer_testCaseAnalyze uri # It will allow users to analyze a pcap file corresponding to a TC # # \param pcap_file => The pcap file that we want to analyze # \param token => The token previously provided # \param testcase_id => The id of the corresponding test case # The pcap_file or the token is required, having both is also forbidden # if self.path == '/api/v1/analyzer_testCaseAnalyze': # Send the header self.send_response(200) self.send_header('Content-Type', 'application/json;charset=utf-8') self.end_headers() # Bind the stdout to the http output os.dup2(self.wfile.fileno(), sys.stdout.fileno()) # Get the content type try: content_type = cgi.parse_header(self.headers['Content-Type']) except TypeError: self.api_error( "Non empty POST datas and format of 'multipart/form-data' expected" ) return # Get post values form = cgi.FieldStorage(fp=self.rfile, headers=self.headers, keep_blank_values=True, environ={ 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': content_type[0] }) # Check that we have the two values if any(( len(form) != 2, 'testcase_id' not in form, all(( # None of the two required => Error 'pcap_file' not in form, 'token' not in form)), all(( # Both of them => Error 'pcap_file' in form, 'token' in form)))): self.api_error( 'Expected POST=([pcap_file={file}|token={text}], testcase_id={text})' ) return # Get the test case and its informations testcase_id = form.getvalue('testcase_id') if not type(testcase_id) == str: self.api_error( 'The value of the testcase_id should be a string from text input' ) return # Try to get the test case try: test_case = get_test_cases(testcase_id) except FileNotFoundError: self.api_error('Test case %s not found' % testcase_id) return # Get the token token = form.getvalue('token') # Get analysis results from the token if token: # Just get the path pcap_path = os.path.join(TMPDIR, token + '.dump') # Get analysis results from the pcap file else: # Check headers if any((len(content_type) == 0, content_type[0] is None, content_type[0] != 'multipart/form-data')): self.api_error( "POST format of 'multipart/form-data' expected, no file input 'pcap_file' found" ) return # Get the same token or generate a new one token = get_token(token) # Get and check the pcap file entered pcap_file = form.getvalue('pcap_file') # Path to save the file pcap_path = os.path.join(TMPDIR, token + '.dump') # Write the pcap file to a temporary destination try: with open(pcap_path, 'wb') as f: f.write(pcap_file) except: self.api_error("Couldn't write the temporary file %s" % pcap_path) return # Get the dissection from analysis tool try: dissection = Dissector(pcap_path).dissect() except pure_pcapy.PcapError: self.api_error( "Expected 'pcap_file' to be a non empty pcap file") except: self.api_error("Couldn't read the temporary file %s" % pcap_path) return # Save the json dissection result into a file json_save = os.path.join(TMPDIR, token + '.json') try: with open(json_save, 'w') as f: json.dump(dissection, f) except: self.api_error("Couldn't write the json file") return # Get the result of the analysis analysis_results = Analyzer('tat_coap').analyse( pcap_path, testcase_id) # self.log_message("###############################################") # self.log_message("Verdict description is : %s", analysis_results[0][3]) # self.log_message("###############################################") # print(analysis_results) # Error for some test cases that the analysis doesn't manage to get try: assert type(analysis_results) == tuple assert len(analysis_results) == 5 assert type(analysis_results[0]) == str assert type(analysis_results[1]) == str assert type(analysis_results[2]) == list assert type(analysis_results[3]) == str assert type(analysis_results[5]) == list for exception_tuple in analysis_results[5]: assert type(exception_tuple) == tuple assert len(exception_tuple) == 3 assert isinstance(exception_tuple[0], type) assert isinstance(exception_tuple[1], Exception) assert isinstance(exception_tuple[2], object) assert analysis_results[0] == test_case['tc_basic']['id'] except AssertionError: self.api_error( 'Problem with the analyse of TC %s, wrong result received' % testcase_id) return # Only take the first verdict = OrderedDict() verdict['_type'] = 'verdict' verdict['verdict'] = analysis_results[1] verdict['description'] = analysis_results[3] verdict['review_frames'] = analysis_results[2] token_res = OrderedDict() token_res['_type'] = 'token' token_res['value'] = token # Prepare the result to return json_result = OrderedDict() json_result['_type'] = 'response' json_result['ok'] = True json_result['content'] = [ token_res, test_case['tc_basic'], verdict ] # Here we will analyze the pcap file and get the results as json print(json.dumps(json_result)) return # POST handler for the analyzer_allMightyAnalyze uri # It will allow users to analyze a pcap file without giving # a corresponding test case # # \param pcap_file => The pcap file that we want to analyze # \param token => The token previously provided # The pcap_file or the token is required, having both is also forbidden # elif self.path == '/api/v1/analyzer_allMightyAnalyze': # Send the header self.send_response(200) self.send_header('Content-Type', 'application/json;charset=utf-8') self.end_headers() # Bind the stdout to the http output os.dup2(self.wfile.fileno(), sys.stdout.fileno()) # Not implemented for the moment self.api_error( "This method is not implemented yet, please come back later") return # POST handler for the dissector_dissectFile uri # It will allow users to analyze a pcap file corresponding to a TC # # \param pcap_file => The pcap file that we want to dissect # \param protocol_selection => The protocol name # elif self.path == '/api/v1/dissector_dissectFile': # Send the header self.send_response(200) self.send_header('Content-Type', 'application/json;charset=utf-8') self.end_headers() # Bind the stdout to the http output os.dup2(self.wfile.fileno(), sys.stdout.fileno()) # Get the content type try: content_type = cgi.parse_header(self.headers['Content-Type']) except TypeError: self.api_error( "Non empty POST datas and format of 'multipart/form-data' expected" ) return # Check headers if any((len(content_type) == 0, content_type[0] is None, content_type[0] != 'multipart/form-data')): self.api_error( "POST format of 'multipart/form-data' expected, no file input 'pcap_file' found" ) return # Get post values form = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ={ 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': content_type[0] }) # Check the parameters passed if any((len(form) != 2, 'pcap_file' not in form, 'protocol_selection' not in form)): self.api_error( 'Expected POST=(pcap_file={file}, protocol_selection={text})' ) return # Check the protocol_selection value protocol_selection = form.getvalue('protocol_selection') if not type(protocol_selection) == str: self.api_error( 'Expected protocol_selection post value to be a text (eq string)' ) return # In function of the protocol asked prot = get_protocol(protocol_selection) if prot is None: self.api_error('Unknown protocol %s' % protocol_selection) return # Generate a new token token = get_token() # Get the pcap file pcap_file = form.getvalue('pcap_file') # Path to save the file pcap_path = os.path.join(TMPDIR, token + '.dump') # Write the pcap file to a temporary destination try: with open(pcap_path, 'wb') as f: f.write(pcap_file) except: self.api_error("Couldn't write the temporary file") return # Prepare the result to return json_result = OrderedDict() json_result['_type'] = 'response' json_result['ok'] = True token_res = OrderedDict() token_res['_type'] = 'token' token_res['value'] = token # Get the dissection from dissector tool try: dissection = Dissector(pcap_path).dissect(eval(prot['name'])) except TypeError as e: self.api_error('Dissector error: ' + str(e)) return except pure_pcapy.PcapError: self.api_error( "Expected 'pcap_file' to be a non empty pcap file") return except: self.api_error( "Couldn't read the temporary file %s and protocol is %s" % (pcap_path, prot['name'])) return # Save the json dissection result into a file json_save = os.path.join(TMPDIR, token + '.json') try: with open(json_save, 'w') as f: json.dump(dissection, f) except: self.api_error("Couldn't write the json file") return # Add the token to the results dissection.insert(0, token_res) # The json result to return json_result['content'] = dissection # Here we will analyze the pcap file and get the results as json print(json.dumps(json_result)) return # ######################## End of API part ######################### # # DEPRECATED # elif (self.path == "/submit"): # if os.fork(): # # close the socket right now(because the # # requesthandler may do a shutdown(), which triggers a # # SIGCHLD in the child process) # self.connection.close() # return # parser = BytesFeedParser() # ct = self.headers.get("Content-Type") # if not ct.startswith("multipart/form-data;"): # self.send_error(400) # return # parser.feed(bytes("Content-Type: %s\r\n\r\n" % ct, "ascii")) # parser.feed(self.rfile.read(int(self.headers['Content-Length']))) # msg = parser.close() # # agree checkbox is selected # for part in msg.get_payload(): # if isinstance(part, email.message.Message): # disposition = part.get("content-disposition") # if disposition and 'name="agree"' in disposition: # agree = True # break # else: # agree = False # # urifilter checkbox is selected # for part in msg.get_payload(): # if isinstance(part, email.message.Message): # disposition = part.get("content-disposition") # if disposition and 'name="urifilter"' in disposition: # urifilter = True # break # else: # urifilter = False # # content of the regex box # for part in msg.get_payload(): # if isinstance(part, email.message.Message): # disposition = part.get("content-disposition") # if disposition and 'name="regex"' in disposition: # regex = part.get_payload() # if not regex: # regex = None # break # else: # regex = None # # profile radio buttons # for part in msg.get_payload(): # if isinstance(part, email.message.Message): # disposition = part.get("content-disposition") # if disposition and 'name="profile"' in disposition: # profile = part.get_payload() # break # else: # profile = "client" # # receive the pcap file # for part in msg.get_payload(): # if isinstance(part, email.message.Message): # disposition = part.get("content-disposition") # if disposition and 'name="file"' in disposition: # mo = re.search('filename="([^"]*)"', disposition) # orig_filename = mo.group(1) if mo else None # timestamp = time.strftime("%y%m%d_%H%M%S") # pcap_file = os.path.join( # (DATADIR if agree else TMPDIR), # "%s_%04d.dump" % (timestamp, job_id) # ) # self.log_message("uploading %s(urifilter=%r, regex=%r)", pcap_file, urifilter, regex) # with open(pcap_file, "wb") as fd: # # FIXME: using hidden API(._payload) because it seems that there is something broken with the encoding when getting the payload using .get_payload() # fd.write(part._payload.encode("ascii", errors="surrogateescape")) # break # else: # self.send_error(400) # return # self.send_response(200) # self.send_header("Content-Type", "text/html;charset=utf-8") # self.end_headers() # out = UTF8Wrapper(self.wfile) # self.wfile.flush() # os.dup2(self.wfile.fileno(), sys.stdout.fileno()) # try: # exceptions = [] # analysis.analyse_file_html(pcap_file, orig_filename, urifilter, exceptions, regex, profile) # for tc in exceptions: # self.log_message("exception in %s", type(tc).__name__, append=tc.exception) # except pure_pcapy.PcapError: # print("Bad file format!") # shutdown() # If we didn't manage to bind the request else: self.send_error(404) return
def run_test_case(self) -> ( str, list_of(int), str, list_of((str, str)), list_of((type, Exception, is_traceback)) ): """ Run the test case :return: A tuple with the informations about the test results which are - The verdict as a string - The list of the result important frames - A string with the log of the test case - A list of all the partial verdicts and their messages - A list of typles representing the exceptions that occurred :rtype: (str, [int], str,[(str,str)], [(type, Exception, traceback)]) """ # Next line is before for 6lowpan TC, where nodes_identification_templates # is not generic. TestCase.get_nodes_identification_templates = self.get_nodes_identification_templates # Pre-process / filter conversations corresponding to the TC self._conversations, self._ignored = self.preprocess( capture=self._capture, expected_frames_pattern=self.get_stimulis() ) # print("----conversations----") # print(self._conversations) # print("----ignored----") # print(self._ignored) if self._conversations == [[]] or self._conversations == []: self.set_verdict( 'inconclusive', 'Capture doesnt match expected pattern: \n\tgot %s, \n\texpected %s' % (str(self._capture.frames), str(self.get_stimulis())) ) else: # Run the test case for every conversations for conv in self._conversations: if logger.getEffectiveLevel() == logging.DEBUG: for frame in conv: logger.debug(frame) try: # Get an iterator on the current conversation frames # and its list of nodes self._iter = iter(conv) self._nodes = conv.nodes self.next() # Run the test case self.run() except self.Stop: # Ignore this testcase result if the first frame gives an # inconclusive verdict if all(( self._verdict.get_value() == 'inconclusive', self._frame == conv[0] )): self.set_verdict('none', 'no match') except Exception as e: # Get the execution information, it's a tuple with # - The type of the exception being handled # - The exception instance # - The traceback object _exception_type, _exception_value, _exception_traceback = sys.exc_info() logger.error(e) traceback.print_exc(file=sys.stdout) # Add those exception information to the list self._exceptions.append(( _exception_type, _exception_value, _exception_traceback )) # Put the verdict and log the exception self.set_verdict('error', 'unhandled exception') self.log(_exception_value) # Return the results return ( self._verdict.get_value(), self._failed_frames, self._text, self._verdict.get_traceback(), self._exceptions, )
def do_POST(self): # The job counter global job_id job_id += 1 # ########################## ttproto API ########################### # # POST handler for the analyzer_testCaseAnalyze uri # It will allow users to analyze a pcap file corresponding to a TC # # \param pcap_file => The pcap file that we want to analyze # \param token => The token previously provided # \param testcase_id => The id of the corresponding test case # The pcap_file or the token is required, having both is also forbidden # if self.path == '/api/v1/analyzer_testCaseAnalyze': # Send the header self.send_response(200) self.send_header('Content-Type', 'application/json;charset=utf-8') self.end_headers() # Bind the stdout to the http output os.dup2(self.wfile.fileno(), sys.stdout.fileno()) # Get the content type try: content_type = cgi.parse_header(self.headers['Content-Type']) except TypeError: self.api_error( "Non empty POST datas and format of 'multipart/form-data' expected" ) return # Get post values form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, keep_blank_values=True, environ={ 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': content_type[0] }) # Check that we have the two values if any(( len(form) != 2, 'testcase_id' not in form, all(( # None of the two required => Error 'pcap_file' not in form, 'token' not in form )), all(( # Both of them => Error 'pcap_file' in form, 'token' in form )) )): self.api_error( 'Expected POST=([pcap_file={file}|token={text}], testcase_id={text})' ) return # Get the test case and its informations testcase_id = form.getvalue('testcase_id') if not type(testcase_id) == str: self.api_error('The value of the testcase_id should be a string from text input') return # Try to get the test case try: test_case = get_test_cases(testcase_id) except FileNotFoundError: self.api_error('Test case %s not found' % testcase_id) return # Get the token token = form.getvalue('token') # Get analysis results from the token if token: # Just get the path pcap_path = os.path.join( TMPDIR, token + '.dump' ) # Get analysis results from the pcap file else: # Check headers if any(( len(content_type) == 0, content_type[0] is None, content_type[0] != 'multipart/form-data' )): self.api_error( "POST format of 'multipart/form-data' expected, no file input 'pcap_file' found" ) return # Get the same token or generate a new one token = get_token(token) # Get and check the pcap file entered pcap_file = form.getvalue('pcap_file') # Path to save the file pcap_path = os.path.join( TMPDIR, token + '.dump' ) # Write the pcap file to a temporary destination try: with open(pcap_path, 'wb') as f: f.write(pcap_file) except: self.api_error( "Couldn't write the temporary file %s" % pcap_path ) return # Get the dissection from analysis tool try: dissection = Dissector(pcap_path).dissect() except pure_pcapy.PcapError: self.api_error( "Expected 'pcap_file' to be a non empty pcap file" ) except: self.api_error( "Couldn't read the temporary file %s" % pcap_path ) return # Save the json dissection result into a file json_save = os.path.join( TMPDIR, token + '.json' ) try: with open(json_save, 'w') as f: json.dump(dissection, f) except: self.api_error("Couldn't write the json file") return # Get the result of the analysis analysis_results = Analyzer('tat_coap').analyse( pcap_path, testcase_id ) self.log_message("Analysis result: " + str(analysis_results)) # Error for some test cases that the analysis doesn't manage to get try: assert type(analysis_results[4]) is list if len(analysis_results[4]) != 0: assert type(analysis_results[4][0]) is tuple assert type(analysis_results) == tuple assert len(analysis_results) == 6 assert type(analysis_results[0]) == str assert type(analysis_results[1]) == str assert type(analysis_results[2]) == list assert type(analysis_results[3]) == str assert type(analysis_results[5]) == list for exception_tuple in analysis_results[5]: assert type(exception_tuple) == tuple assert len(exception_tuple) == 3 assert isinstance(exception_tuple[0], type) assert isinstance(exception_tuple[1], Exception) assert isinstance(exception_tuple[2], object) assert analysis_results[0] == test_case['tc_basic']['id'] except AssertionError: self.api_error( 'Problem with the analyse of TC %s, wrong result received' % testcase_id ) return # Only take the first verdict = OrderedDict() verdict['_type'] = 'verdict' verdict['verdict'] = analysis_results[1] verdict['description'] = analysis_results[3] verdict['review_frames'] = analysis_results[2] verdict['partial_verdicts'] = analysis_results[4] token_res = OrderedDict() token_res['_type'] = 'token' token_res['value'] = token # Prepare the result to return json_result = OrderedDict() json_result['_type'] = 'response' json_result['ok'] = True json_result['content'] = [ token_res, test_case['tc_basic'], verdict ] self.log_message("Analysis response sent: " + str((analysis_results[4]))) self.log_message("Analysis response sent: " + str(json.dumps(json_result))) # Here we will analyze the pcap file and get the results as json print(json.dumps(json_result)) return # POST handler for the analyzer_allMightyAnalyze uri # It will allow users to analyze a pcap file without giving # a corresponding test case # # \param pcap_file => The pcap file that we want to analyze # \param token => The token previously provided # The pcap_file or the token is required, having both is also forbidden # elif self.path == '/api/v1/analyzer_allMightyAnalyze': # Send the header self.send_response(200) self.send_header('Content-Type', 'application/json;charset=utf-8') self.end_headers() # Bind the stdout to the http output os.dup2(self.wfile.fileno(), sys.stdout.fileno()) # Not implemented for the moment self.api_error( "This method is not implemented yet, please come back later" ) return # POST handler for the dissector_dissectFile uri # It will allow users to analyze a pcap file corresponding to a TC # # \param pcap_file => The pcap file that we want to dissect # \param protocol_selection => The protocol name # elif self.path == '/api/v1/dissector_dissectFile': # Send the header self.send_response(200) self.send_header('Content-Type', 'application/json;charset=utf-8') self.end_headers() # Bind the stdout to the http output os.dup2(self.wfile.fileno(), sys.stdout.fileno()) # Get the content type try: content_type = cgi.parse_header(self.headers['Content-Type']) except TypeError: self.api_error( "Non empty POST datas and format of 'multipart/form-data' expected" ) return # Check headers if any(( len(content_type) == 0, content_type[0] is None, content_type[0] != 'multipart/form-data' )): self.api_error( "POST format of 'multipart/form-data' expected, no file input 'pcap_file' found" ) return # Get post values form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={ 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': content_type[0] }) # Check the parameters passed if any(( len(form) != 2, 'pcap_file' not in form, 'protocol_selection' not in form )): self.api_error( 'Expected POST=(pcap_file={file}, protocol_selection={text})' ) return # Check the protocol_selection value protocol_selection = form.getvalue('protocol_selection') if not type(protocol_selection) == str: self.api_error('Expected protocol_selection post value to be a text (eq string)') return # In function of the protocol asked prot = get_protocol(protocol_selection) if prot is None: self.api_error('Unknown protocol %s' % protocol_selection) return # Generate a new token token = get_token() # Get the pcap file pcap_file = form.getvalue('pcap_file') # Path to save the file pcap_path = os.path.join( TMPDIR, token + '.dump' ) # Write the pcap file to a temporary destination try: with open(pcap_path, 'wb') as f: f.write(pcap_file) except: self.api_error("Couldn't write the temporary file") return # Prepare the result to return json_result = OrderedDict() json_result['_type'] = 'response' json_result['ok'] = True token_res = OrderedDict() token_res['_type'] = 'token' token_res['value'] = token # Get the dissection from dissector tool try: dissection = Dissector(pcap_path).dissect(eval(prot['name'])) except TypeError as e: self.api_error('Dissector error: ' + str(e)) return except pure_pcapy.PcapError: self.api_error( "Expected 'pcap_file' to be a non empty pcap file" ) return except: self.api_error( "Couldn't read the temporary file %s and protocol is %s" % ( pcap_path, prot['name'] ) ) return # Save the json dissection result into a file json_save = os.path.join( TMPDIR, token + '.json' ) try: with open(json_save, 'w') as f: json.dump(dissection, f) except: self.api_error("Couldn't write the json file") return # Add the token to the results dissection.insert(0, token_res) # The json result to return json_result['content'] = dissection # Here we will analyze the pcap file and get the results as json print(json.dumps(json_result)) return # If we didn't manage to bind the request else: self.send_error(404) return