diff --git a/menu-ip-change.py b/menu-ip-change.py index 3ccd6c4..28bf300 100644 --- a/menu-ip-change.py +++ b/menu-ip-change.py @@ -472,21 +472,45 @@ class IPChangerApp: ) self.scan_end_entry.grid(row=0, column=3, sticky="w", padx=5) - # Scan buttons - self.scan_buttons_frame = ttk.Frame(self.scan_frame) - self.scan_buttons_frame.grid(row=0, column=4, columnspan=2, padx=5) - - ttk.Button(self.scan_buttons_frame, text="Start Scan", command=self.start_scan).grid( - row=0, column=0, padx=5 + # Add CIDR field in Network Scan + ttk.Label(self.scan_frame, text="CIDR (/bits):").grid( + row=0, column=4, sticky="w", padx=5 ) - ttk.Button(self.scan_buttons_frame, text="Stop Scan", command=self.stop_scan).grid( - row=0, column=1, padx=5 - ) - ttk.Button(self.scan_buttons_frame, text="Get Host Info", command=self.gather_host_information).grid( - row=1, column=0, columnspan=2, padx=5, pady=3 + self.scan_cidr_bits = tk.StringVar(value="24") + self.scan_cidr_entry = ttk.Entry( + self.scan_frame, textvariable=self.scan_cidr_bits, width=5 ) + self.scan_cidr_entry.grid(row=0, column=5, sticky="w", padx=5) + # Add trace to update the scan range when CIDR changes + self.scan_cidr_bits.trace("w", self.update_scan_range_from_cidr) - # Scan progress + # Display number of nodes to scan + ttk.Label(self.scan_frame, text="Nodes to scan:").grid( + row=1, column=0, sticky="w", padx=5 + ) + self.nodes_to_scan = tk.StringVar(value="0") + nodes_display = ttk.Entry( + self.scan_frame, textvariable=self.nodes_to_scan, width=10, state="readonly" + ) + nodes_display.grid(row=1, column=1, sticky="w", padx=5) + + # Scan buttons - move to row 1, column 2-5 + self.scan_buttons_frame = ttk.Frame(self.scan_frame) + self.scan_buttons_frame.grid(row=1, column=2, columnspan=4, padx=5, sticky="e") + + ttk.Button( + self.scan_buttons_frame, text="Start Scan", command=self.start_scan + ).grid(row=0, column=0, padx=5) + ttk.Button( + self.scan_buttons_frame, text="Stop Scan", command=self.stop_scan + ).grid(row=0, column=1, padx=5) + ttk.Button( + self.scan_buttons_frame, + text="Get Host Info", + command=self.gather_host_information, + ).grid(row=0, column=2, padx=5) + + # Scan progress - move to row 2 self.scan_progress = ttk.Progressbar( self.scan_frame, orient="horizontal", @@ -495,18 +519,18 @@ class IPChangerApp: variable=self.scan_progress_var, ) self.scan_progress.grid( - row=1, column=0, columnspan=6, sticky="ew", padx=5, pady=5 + row=2, column=0, columnspan=6, sticky="ew", padx=5, pady=5 ) - # Scan results + # Scan results - update row numbers ttk.Label(self.scan_frame, text="Scan Results:").grid( - row=2, column=0, sticky="w", padx=5 + row=3, column=0, sticky="w", padx=5 ) # Frame for results list and scrollbar self.results_frame = ttk.Frame(self.scan_frame) self.results_frame.grid( - row=3, column=0, columnspan=6, sticky="nsew", padx=5, pady=5 + row=4, column=0, columnspan=6, sticky="nsew", padx=5, pady=5 ) self.results_frame.columnconfigure(0, weight=1) self.results_frame.rowconfigure(0, weight=1) @@ -516,25 +540,25 @@ class IPChangerApp: self.results_frame, columns=("ip", "hostname", "mac"), show="headings", - height=10 + height=10, ) - + # Define columns self.scan_results_tree.heading("ip", text="IP Address") self.scan_results_tree.heading("hostname", text="Hostname") self.scan_results_tree.heading("mac", text="MAC Address") - + # Set column widths self.scan_results_tree.column("ip", width=120, anchor="w") self.scan_results_tree.column("hostname", width=200, anchor="w") self.scan_results_tree.column("mac", width=150, anchor="w") - + # Add scrollbar self.scan_results_scrollbar = ttk.Scrollbar( self.results_frame, orient="vertical", command=self.scan_results_tree.yview ) self.scan_results_tree.configure(yscrollcommand=self.scan_results_scrollbar.set) - + self.scan_results_tree.grid(row=0, column=0, sticky="nsew") self.scan_results_scrollbar.grid(row=0, column=1, sticky="ns") @@ -711,7 +735,7 @@ class IPChangerApp: ) # Actualizar IP prefix si hay una IP vĂ¡lida - if (interface.ip_address): + if interface.ip_address: prefix = ".".join(interface.ip_address.split(".")[:3]) self.ip_prefix.set(prefix) @@ -1090,7 +1114,7 @@ class IPChangerApp: # Try to get hostname and MAC address hostname = self.get_hostname(target) mac_address = self.get_mac_address(target) - + if hostname: self.log_message(f"Hostname: {hostname}") if mac_address: @@ -1113,10 +1137,10 @@ class IPChangerApp: self.show_error("Start IP must be lower than or equal to End IP") return - # Clear previous results - FIXED: use the treeview method instead of the old listbox + # Clear previous results for item in self.scan_results_tree.get_children(): self.scan_results_tree.delete(item) - + self.scan_results = [] # Calculate total IPs to scan for progress bar @@ -1124,6 +1148,9 @@ class IPChangerApp: self.scan_progress_var.set(0) self.scan_progress["maximum"] = ip_range + # Update nodes to scan count + self.nodes_to_scan.set(str(ip_range)) + self.log_message( f"Starting scan from {start_ip} to {end_ip} ({ip_range} addresses)..." ) @@ -1176,7 +1203,9 @@ class IPChangerApp: self.scan_queue.put(ipaddress.IPv4Address(ip_int)) # Start worker threads for parallel scanning - max_threads = min(20, ip_range) # Limit to 20 threads or fewer if range is small + max_threads = min( + 20, ip_range + ) # Limit to 20 threads or fewer if range is small for i in range(max_threads): thread = threading.Thread(target=self._scan_worker, daemon=True) thread.start() @@ -1215,11 +1244,10 @@ class IPChangerApp: ip_str = str(ip_address) self.log_message(f"Host discovered: {ip_str}") self.scan_results.append(ip_str) - + # Add to results tree with placeholder values self.master.after( - 0, - lambda ip=ip_str: self.add_scan_result(ip, "", "") + 0, lambda ip=ip_str: self.add_scan_result(ip, "", "") ) # Update progress @@ -1237,54 +1265,55 @@ class IPChangerApp: if not self.scan_results: self.show_info("No hosts discovered yet. Run a scan first.") return - + # Start the information gathering in a separate thread - threading.Thread( - target=self._execute_host_gathering, - daemon=True - ).start() + threading.Thread(target=self._execute_host_gathering, daemon=True).start() def _execute_host_gathering(self): """Perform the actual host information gathering""" try: - self.log_message(f"Gathering information for {len(self.scan_results)} hosts...") - + self.log_message( + f"Gathering information for {len(self.scan_results)} hosts..." + ) + # Reset progress bar for this operation total_hosts = len(self.scan_results) self.scan_progress_var.set(0) self.scan_progress["maximum"] = total_hosts - + # Get information for each host for i, ip in enumerate(self.scan_results): if self.scan_stop_event.is_set(): self.log_message("Host information gathering stopped.") break - - self.log_message(f"Getting information for host {ip} ({i+1}/{total_hosts})...") - + + self.log_message( + f"Getting information for host {ip} ({i+1}/{total_hosts})..." + ) + # Get hostname and MAC hostname = self.get_hostname(ip) mac_address = self.get_mac_address(ip) - + # Log what we found if hostname: self.log_message(f" Hostname: {hostname}") else: self.log_message(f" Hostname: Not resolved") - + if mac_address: self.log_message(f" MAC Address: {mac_address}") else: self.log_message(f" MAC Address: Not found") - + # Update the tree self.update_host_in_tree(ip, hostname, mac_address) - + # Update progress self.scan_progress_var.set(i + 1) - + self.log_message("Host information gathering completed.") - + except Exception as e: self.log_message(f"Error gathering host information: {str(e)}") @@ -1293,12 +1322,14 @@ class IPChangerApp: try: # Find the item with this IP for item_id in self.scan_results_tree.get_children(): - values = self.scan_results_tree.item(item_id, 'values') + values = self.scan_results_tree.item(item_id, "values") if values and values[0] == ip: # Update the values hostname_str = hostname if hostname else "Not resolved" mac_str = mac if mac else "Not found" - self.scan_results_tree.item(item_id, values=(ip, hostname_str, mac_str)) + self.scan_results_tree.item( + item_id, values=(ip, hostname_str, mac_str) + ) break except Exception as e: self.log_message(f"Error updating host in treeview: {str(e)}") @@ -1309,7 +1340,7 @@ class IPChangerApp: # Insert the item with all values, even if some are empty hostname = hostname if hostname else "Not resolved" mac = mac if mac else "Not found" - + self.scan_results_tree.insert("", "end", values=(ip, hostname, mac)) except Exception as e: self.log_message(f"Error adding scan result to UI: {str(e)}") @@ -1602,6 +1633,9 @@ class IPChangerApp: mask_bits = bin(int(mask_obj))[2:].zfill(32) network_bits = mask_bits.count("1") + # Update the scan CIDR field + self.scan_cidr_bits.set(str(network_bits)) + # Create network object network = ipaddress.IPv4Network( f"{ip_address}/{network_bits}", strict=False @@ -1621,11 +1655,53 @@ class IPChangerApp: self.scan_start_ip.set(str(start_ip)) self.scan_end_ip.set(str(end_ip)) - self.log_message(f"Updated scan range: {start_ip} - {end_ip}") + # Update the nodes to scan count + nodes_count = int(end_ip) - int(start_ip) + 1 + self.nodes_to_scan.set(str(nodes_count)) + + self.log_message( + f"Updated scan range: {start_ip} - {end_ip} ({nodes_count} nodes)" + ) except Exception as e: self.log_message(f"Error calculating IP range: {str(e)}") + def update_scan_range_from_cidr(self, *args): + """Update scan IP range when CIDR value changes - only modify End IP""" + try: + start_ip = self.scan_start_ip.get().strip() + cidr_bits = self.scan_cidr_bits.get().strip() + + if not start_ip or not cidr_bits.isdigit(): + return + + # Convert CIDR to network + bits = int(cidr_bits) + if 0 <= bits <= 32: + # Use the start IP as the base for the network + network = ipaddress.IPv4Network(f"{start_ip}/{bits}", strict=False) + + # Keep the original start IP and just update the end IP + # For the end IP, use the broadcast address (or last usable address) + if network.num_addresses <= 2: + end_ip = network.broadcast_address + else: + end_ip = network.broadcast_address - 1 + + # Update only the end IP field + self.scan_end_ip.set(str(end_ip)) + + # Update the nodes to scan count + start = ipaddress.IPv4Address(start_ip) + nodes_count = int(end_ip) - int(start) + 1 + self.nodes_to_scan.set(str(nodes_count)) + + self.log_message( + f"Updated scan range from CIDR: {start_ip} - {end_ip} ({nodes_count} nodes)" + ) + except Exception as e: + self.log_message(f"Error updating scan range from CIDR: {str(e)}") + def get_hostname(self, ip_address): """Try to resolve hostname for an IP address with better error handling""" try: @@ -1642,9 +1718,11 @@ class IPChangerApp: self.log_message(f"Timeout resolving hostname for {ip_address}") return "" except Exception as e: - self.log_message(f"Unknown error resolving hostname for {ip_address}: {str(e)}") + self.log_message( + f"Unknown error resolving hostname for {ip_address}: {str(e)}" + ) return "" - + def get_mac_address(self, ip_address): """Get MAC address for an IP using ARP on Windows with improved parsing""" try: @@ -1654,39 +1732,49 @@ class IPChangerApp: shell=True, capture_output=True, text=True, - timeout=2 + timeout=2, ) - + if result.returncode == 0 and result.stdout: # Log the raw output for debugging - self.log_message(f"ARP output for {ip_address}: {result.stdout.strip()}") - + self.log_message( + f"ARP output for {ip_address}: {result.stdout.strip()}" + ) + # Parse the output to find the MAC address # First try the typical format with hyphens or colons - mac_match = re.search(r"([0-9A-F]{2}[-:][0-9A-F]{2}[-:][0-9A-F]{2}[-:][0-9A-F]{2}[-:][0-9A-F]{2}[-:][0-9A-F]{2})", - result.stdout, - re.IGNORECASE) + mac_match = re.search( + r"([0-9A-F]{2}[-:][0-9A-F]{2}[-:][0-9A-F]{2}[-:][0-9A-F]{2}[-:][0-9A-F]{2}[-:][0-9A-F]{2})", + result.stdout, + re.IGNORECASE, + ) if mac_match: return mac_match.group(0) - + # Try an alternative format with spaces (Windows format) - mac_match = re.search(r"([0-9a-f]{2}[\s-][0-9a-f]{2}[\s-][0-9a-f]{2}[\s-][0-9a-f]{2}[\s-][0-9a-f]{2}[\s-][0-9a-f]{2})", - result.stdout, - re.IGNORECASE) + mac_match = re.search( + r"([0-9a-f]{2}[\s-][0-9a-f]{2}[\s-][0-9a-f]{2}[\s-][0-9a-f]{2}[\s-][0-9a-f]{2}[\s-][0-9a-f]{2})", + result.stdout, + re.IGNORECASE, + ) if mac_match: return mac_match.group(0) - + # Try yet another approach - look for any string of hex digits with separators - lines = result.stdout.strip().split('\n') + lines = result.stdout.strip().split("\n") for line in lines: if ip_address in line: parts = line.split() # Usually the MAC address is the second column in the output if len(parts) >= 2: # Check if the second part looks like a MAC address - if re.match(r"([0-9a-f]{2}[^\w]){5}[0-9a-f]{2}", parts[1], re.IGNORECASE): + if re.match( + r"([0-9a-f]{2}[^\w]){5}[0-9a-f]{2}", + parts[1], + re.IGNORECASE, + ): return parts[1] - + self.log_message(f"No MAC address found for {ip_address}") return "" except subprocess.TimeoutExpired: diff --git a/ping_targets.json b/ping_targets.json index acd0e5c..ea0d6d7 100644 --- a/ping_targets.json +++ b/ping_targets.json @@ -1 +1 @@ -{"10.1.22": "10.1.22.11", "10.138.182": "10.138.182.94", "10.1.20": "10.1.20.11"} \ No newline at end of file +{"10.1.22": "10.1.22.11", "10.138.182": "10.138.182.94", "10.1.20": "10.1.20.11", "10.255.255": "10.255.255.1"} \ No newline at end of file