diff --git a/src/pyshark/capture/capture.py b/src/pyshark/capture/capture.py index cb505504..06c81dcb 100644 --- a/src/pyshark/capture/capture.py +++ b/src/pyshark/capture/capture.py @@ -7,6 +7,7 @@ import sys import logging import warnings +import difflib from pyshark import ek_field_mapping from pyshark.packet.packet import Packet @@ -14,7 +15,8 @@ from pyshark.tshark.output_parser import tshark_json from pyshark.tshark.output_parser import tshark_xml from pyshark.tshark.tshark import get_process_path, get_tshark_display_filter_flag, \ - tshark_supports_json, TSharkVersionException, get_tshark_version, tshark_supports_duplicate_keys + tshark_supports_json, TSharkVersionException, get_tshark_version, tshark_supports_duplicate_keys, \ + TSharkProtocolNotSupportedException, tshark_supports_protocol, get_supported_protocols if sys.version_info < (3, 8): @@ -323,6 +325,17 @@ def _get_tshark_version(self): self.__tshark_version = get_tshark_version(self.tshark_path) return self.__tshark_version + def _suggest_protocol_name(self, input_protocol, tshark_path=None, suggestions=1): + """Suggests the correct protocol names based on the user's input. + + :param input_protocol: Protocol name to match. + :param tshark_path: Custom path to the TShark executable. + :param suggestions: Number of suggestions to return. + :return: list: A list of suggested protocol names. + """ + protocols = get_supported_protocols(tshark_path) + return difflib.get_close_matches(input_protocol.lower(), protocols, n=suggestions) + async def _get_tshark_process(self, packet_count=None, stdin=None): """Returns a new tshark process with previously-set parameters.""" self._verify_capture_parameters() @@ -344,6 +357,17 @@ async def _get_tshark_process(self, packet_count=None, stdin=None): parameters = [self._get_tshark_path(), "-l", "-n", "-T", output_type] + \ self.get_parameters(packet_count=packet_count) + output_parameters + if not tshark_supports_protocol(self._display_filter): + suggestions = "or".join(self._suggest_protocol_name(self._display_filter)) + if (suggestions == ""): + raise TSharkProtocolNotSupportedException( + f"Protocol {self._display_filter} is not supported by TShark. " + \ + "Check if protocol is mistyped or it is not loaded to the correct plugins folder") + else: + raise TSharkProtocolNotSupportedException( + f"Protocol {self._display_filter} is not supported by TShark. " + \ + f"Did you mean {suggestions}, or the protocol is not loaded to the correct plugins folder") + self._log.debug( "Creating TShark subprocess with parameters: " + " ".join(parameters)) self._log.debug("Executable: %s", parameters[0]) diff --git a/src/pyshark/tshark/tshark.py b/src/pyshark/tshark/tshark.py index 3d920a10..a706e6c7 100644 --- a/src/pyshark/tshark/tshark.py +++ b/src/pyshark/tshark/tshark.py @@ -13,6 +13,8 @@ class TSharkNotFoundException(Exception): pass +class TSharkProtocolNotSupportedException(Exception): + pass class TSharkVersionException(Exception): pass @@ -95,6 +97,37 @@ def tshark_supports_duplicate_keys(tshark_version): def tshark_supports_json(tshark_version): return tshark_version >= version.parse("2.2.0") +def get_supported_protocols(tshark_path=None): + """Fetches a list of all protocols supported by TShark. + + :param tshark_path: Optional; custom path to the TShark executable. + :return: A list of supported protocol names. + """ + protocols = [] + parameters = [get_process_path(tshark_path), "-G", "protocols"] + result = subprocess.run(parameters, capture_output=True, text=True, check=True) + # List all protocols + for line in result.stdout.splitlines(): + columns = line.split('\t') + # Capture the protocol name + if len(columns) >= 3: + protocols.append(columns[2]) + return protocols + +def tshark_supports_protocol(protocol_name, tshark_path=None): + """Checks if the specified protocol is supported by TShark. + + :param protocol_name: Name of protocol to check. + :param tshark_path: Optional; Custom path to the TShark executable. + :return: True if the protocol is supported, False otherwise + If no protocol is given a True is returned + """ + if not protocol_name: + return True + + supported_protocols = get_supported_protocols(tshark_path=tshark_path) + # Exact-match the protocol name against the third column + return protocol_name in supported_protocols def get_tshark_display_filter_flag(tshark_version): """Returns '-Y' for tshark versions >= 1.10.0 and '-R' for older versions."""