@@ -126,6 +126,18 @@ def _require_tool(self, tool_name: str) -> dict[str, Any]:
126126 },
127127 }
128128
129+ def _get_checked_tool_path (self , tool_name : str ) -> str :
130+ """Return a tool path after availability has already been validated."""
131+ tool_path = self ._tool_paths .get (tool_name )
132+ if not self ._tool_is_available (tool_path ):
133+ raise RuntimeError (f"{ tool_name } tool not available" )
134+ assert tool_path is not None
135+ return tool_path
136+
137+ def _select_capture_backend_path (self ) -> str :
138+ """Return the resolved executable for the preferred capture backend."""
139+ return self ._get_checked_tool_path (self ._select_capture_backend ())
140+
129141 # --- Validation Methods ---
130142
131143 def _validate_file (self , filepath : str ) -> dict [str , Any ]:
@@ -212,6 +224,7 @@ async def check_capabilities(self) -> dict[str, Any]:
212224 async def get_version (tool_path : str | None ) -> dict [str , Any ]:
213225 if not self ._tool_is_available (tool_path ):
214226 return {"available" : False }
227+ assert tool_path is not None
215228 try :
216229 proc = await asyncio .create_subprocess_exec (
217230 tool_path ,
@@ -234,7 +247,7 @@ async def get_version(tool_path: str | None) -> dict[str, Any]:
234247
235248 async def list_interfaces (self ) -> str :
236249 """List interfaces (-D)."""
237- backend = self .dumpcap_path if self . _tool_is_available ( self . dumpcap_path ) else self . tshark_path
250+ backend = self ._select_capture_backend_path ()
238251 return await self ._run_command ([backend , "-D" ])
239252
240253 # --- Capture Management ---
@@ -253,7 +266,7 @@ async def capture_packets(
253266 if not output_validation ["success" ]:
254267 return json .dumps (output_validation )
255268
256- backend = self .dumpcap_path if self . _tool_is_available ( self . dumpcap_path ) else self . tshark_path
269+ backend = self ._select_capture_backend_path ()
257270 cmd = [backend , "-i" , interface , "-w" , output_file ]
258271
259272 if capture_filter :
@@ -666,7 +679,8 @@ async def get_file_info(self, pcap_file: str) -> str:
666679 if not required ["success" ]:
667680 return json .dumps (required )
668681
669- return await self ._run_command ([self .capinfos_path , pcap_file ])
682+ capinfos_path = self ._get_checked_tool_path ("capinfos" )
683+ return await self ._run_command ([capinfos_path , pcap_file ])
670684
671685 async def merge_pcap_files (self , output_file : str , input_files : list [str ]) -> str :
672686 """Mergecap: Merge multiple pcaps."""
@@ -683,7 +697,8 @@ async def merge_pcap_files(self, output_file: str, input_files: list[str]) -> st
683697 if not output_validation ["success" ]:
684698 return json .dumps (output_validation )
685699
686- cmd = [self .mergecap_path , "-w" , output_file ] + input_files
700+ mergecap_path = self ._get_checked_tool_path ("mergecap" )
701+ cmd = [mergecap_path , "-w" , output_file ] + input_files
687702 return await self ._run_command (cmd )
688703
689704 async def editcap_trim (
@@ -706,7 +721,8 @@ async def editcap_trim(
706721 if not output_validation ["success" ]:
707722 return json .dumps (output_validation )
708723
709- cmd = [self .editcap_path ]
724+ editcap_path = self ._get_checked_tool_path ("editcap" )
725+ cmd = [editcap_path ]
710726 if start_time :
711727 cmd .extend (["-A" , start_time ])
712728 if stop_time :
@@ -745,7 +761,8 @@ async def editcap_split(
745761 }
746762 )
747763
748- cmd = [self .editcap_path ]
764+ editcap_path = self ._get_checked_tool_path ("editcap" )
765+ cmd = [editcap_path ]
749766 if packets_per_file > 0 :
750767 cmd .extend (["-c" , str (packets_per_file )])
751768 if seconds_per_file > 0 :
@@ -767,7 +784,8 @@ async def editcap_time_shift(self, input_file: str, output_file: str, seconds: f
767784 if not output_validation ["success" ]:
768785 return json .dumps (output_validation )
769786
770- cmd = [self .editcap_path , "-t" , str (seconds ), input_file , output_file ]
787+ editcap_path = self ._get_checked_tool_path ("editcap" )
788+ cmd = [editcap_path , "-t" , str (seconds ), input_file , output_file ]
771789 return await self ._run_command (cmd )
772790
773791 async def editcap_deduplicate (self , input_file : str , output_file : str , duplicate_window : int = 5 ) -> str :
@@ -784,7 +802,8 @@ async def editcap_deduplicate(self, input_file: str, output_file: str, duplicate
784802 if not output_validation ["success" ]:
785803 return json .dumps (output_validation )
786804
787- cmd = [self .editcap_path , "-D" , str (duplicate_window ), input_file , output_file ]
805+ editcap_path = self ._get_checked_tool_path ("editcap" )
806+ cmd = [editcap_path , "-D" , str (duplicate_window ), input_file , output_file ]
788807 return await self ._run_command (cmd )
789808
790809 async def text2pcap_import (
@@ -808,7 +827,8 @@ async def text2pcap_import(
808827 if not output_validation ["success" ]:
809828 return json .dumps (output_validation )
810829
811- cmd = [self .text2pcap_path ]
830+ text2pcap_path = self ._get_checked_tool_path ("text2pcap" )
831+ cmd = [text2pcap_path ]
812832 if timestamp_format :
813833 cmd .extend (["-t" , timestamp_format ])
814834 if ascii_mode :
0 commit comments