|
|
@ -46,6 +46,21 @@ except ImportError:
|
|
|
|
"WMI module not found. Using alternative method for network interface detection."
|
|
|
|
"WMI module not found. Using alternative method for network interface detection."
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Add MAC vendor lookup library
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
from mac_vendor_lookup import MacLookup
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MAC_LOOKUP_AVAILABLE = True
|
|
|
|
|
|
|
|
# Initialize the MAC lookup service
|
|
|
|
|
|
|
|
mac_lookup = MacLookup()
|
|
|
|
|
|
|
|
# Update the database if needed (this can be time-consuming)
|
|
|
|
|
|
|
|
# mac_lookup.update_vendors() # Uncomment to update database on startup
|
|
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
|
|
|
MAC_LOOKUP_AVAILABLE = False
|
|
|
|
|
|
|
|
print(
|
|
|
|
|
|
|
|
"mac-vendor-lookup module not found. Install with 'pip install mac-vendor-lookup' for MAC vendor lookup functionality."
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
@dataclass
|
|
|
|
class NetworkInterface:
|
|
|
|
class NetworkInterface:
|
|
|
@ -194,6 +209,10 @@ class IPChangerApp:
|
|
|
|
self.master.grid_columnconfigure(0, weight=1)
|
|
|
|
self.master.grid_columnconfigure(0, weight=1)
|
|
|
|
self.master.grid_rowconfigure(1, weight=1) # El log expandible
|
|
|
|
self.master.grid_rowconfigure(1, weight=1) # El log expandible
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Initialize MAC lookup if available
|
|
|
|
|
|
|
|
if MAC_LOOKUP_AVAILABLE:
|
|
|
|
|
|
|
|
self.mac_lookup = MacLookup()
|
|
|
|
|
|
|
|
|
|
|
|
def setup_mask_traces(self):
|
|
|
|
def setup_mask_traces(self):
|
|
|
|
"""Set up traces for subnet mask and CIDR fields safely"""
|
|
|
|
"""Set up traces for subnet mask and CIDR fields safely"""
|
|
|
|
# First initialize variables
|
|
|
|
# First initialize variables
|
|
|
@ -226,6 +245,10 @@ class IPChangerApp:
|
|
|
|
self.ip_setup_tab = ttk.Frame(self.notebook, padding="10")
|
|
|
|
self.ip_setup_tab = ttk.Frame(self.notebook, padding="10")
|
|
|
|
self.tools_tab = ttk.Frame(self.notebook, padding="10")
|
|
|
|
self.tools_tab = ttk.Frame(self.notebook, padding="10")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Configure tab frames to expand
|
|
|
|
|
|
|
|
self.ip_setup_tab.columnconfigure(0, weight=1)
|
|
|
|
|
|
|
|
self.tools_tab.columnconfigure(0, weight=1)
|
|
|
|
|
|
|
|
|
|
|
|
# Add tabs to the notebook
|
|
|
|
# Add tabs to the notebook
|
|
|
|
self.notebook.add(self.ip_setup_tab, text="IP Setup")
|
|
|
|
self.notebook.add(self.ip_setup_tab, text="IP Setup")
|
|
|
|
self.notebook.add(self.tools_tab, text="Tools")
|
|
|
|
self.notebook.add(self.tools_tab, text="Tools")
|
|
|
@ -267,7 +290,7 @@ class IPChangerApp:
|
|
|
|
def create_ip_setup_tab(self):
|
|
|
|
def create_ip_setup_tab(self):
|
|
|
|
# Control frame for IP Setup tab
|
|
|
|
# Control frame for IP Setup tab
|
|
|
|
self.control_frame = ttk.Frame(self.ip_setup_tab)
|
|
|
|
self.control_frame = ttk.Frame(self.ip_setup_tab)
|
|
|
|
self.control_frame.grid(row=0, column=0, sticky="new")
|
|
|
|
self.control_frame.grid(row=0, column=0, sticky="nsew")
|
|
|
|
self.control_frame.columnconfigure(0, weight=1)
|
|
|
|
self.control_frame.columnconfigure(0, weight=1)
|
|
|
|
|
|
|
|
|
|
|
|
# Sección de interfaces
|
|
|
|
# Sección de interfaces
|
|
|
@ -275,6 +298,9 @@ class IPChangerApp:
|
|
|
|
self.control_frame, text="Network Interfaces", padding="5"
|
|
|
|
self.control_frame, text="Network Interfaces", padding="5"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.interfaces_frame.grid(row=0, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
self.interfaces_frame.grid(row=0, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
|
|
|
|
self.interfaces_frame.columnconfigure(
|
|
|
|
|
|
|
|
1, weight=1
|
|
|
|
|
|
|
|
) # Make combo box column expandable
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(self.interfaces_frame, text="Select Interface:").grid(
|
|
|
|
ttk.Label(self.interfaces_frame, text="Select Interface:").grid(
|
|
|
|
row=0, column=0, sticky="w", padx=5
|
|
|
|
row=0, column=0, sticky="w", padx=5
|
|
|
@ -291,22 +317,23 @@ class IPChangerApp:
|
|
|
|
self.control_frame, text="Current Configuration", padding="5"
|
|
|
|
self.control_frame, text="Current Configuration", padding="5"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.current_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
self.current_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
|
|
|
|
self.current_frame.columnconfigure(1, weight=1) # Make value column expandable
|
|
|
|
|
|
|
|
|
|
|
|
self.current_ip_var = tk.StringVar()
|
|
|
|
self.current_ip_var = tk.StringVar()
|
|
|
|
self.current_mask_var = tk.StringVar()
|
|
|
|
self.current_mask_var = tk.StringVar()
|
|
|
|
self.current_gateway_var = tk.StringVar()
|
|
|
|
self.current_gateway_var = tk.StringVar()
|
|
|
|
self.dhcp_status_var = tk.StringVar()
|
|
|
|
self.dhcp_status_var = tk.StringVar()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Current IP and copy button - aligned left
|
|
|
|
ttk.Label(self.current_frame, text="Current IP:").grid(
|
|
|
|
ttk.Label(self.current_frame, text="Current IP:").grid(
|
|
|
|
row=0, column=0, sticky="w", padx=5
|
|
|
|
row=0, column=0, sticky="w", padx=5
|
|
|
|
)
|
|
|
|
)
|
|
|
|
ttk.Label(self.current_frame, textvariable=self.current_ip_var).grid(
|
|
|
|
ip_frame = ttk.Frame(self.current_frame)
|
|
|
|
row=0, column=1, sticky="w"
|
|
|
|
ip_frame.grid(row=0, column=1, sticky="w")
|
|
|
|
|
|
|
|
ttk.Label(ip_frame, textvariable=self.current_ip_var).pack(side=tk.LEFT)
|
|
|
|
|
|
|
|
ttk.Button(ip_frame, text="Copy", width=5, command=self.copy_current_ip).pack(
|
|
|
|
|
|
|
|
side=tk.LEFT, padx=5
|
|
|
|
)
|
|
|
|
)
|
|
|
|
# Add copy button for IP
|
|
|
|
|
|
|
|
ttk.Button(
|
|
|
|
|
|
|
|
self.current_frame, text="Copy", width=5, command=self.copy_current_ip
|
|
|
|
|
|
|
|
).grid(row=0, column=2, padx=5)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(self.current_frame, text="Subnet Mask:").grid(
|
|
|
|
ttk.Label(self.current_frame, text="Subnet Mask:").grid(
|
|
|
|
row=1, column=0, sticky="w", padx=5
|
|
|
|
row=1, column=0, sticky="w", padx=5
|
|
|
@ -315,16 +342,18 @@ class IPChangerApp:
|
|
|
|
row=1, column=1, sticky="w"
|
|
|
|
row=1, column=1, sticky="w"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Gateway and copy button - aligned left
|
|
|
|
ttk.Label(self.current_frame, text="Gateway:").grid(
|
|
|
|
ttk.Label(self.current_frame, text="Gateway:").grid(
|
|
|
|
row=2, column=0, sticky="w", padx=5
|
|
|
|
row=2, column=0, sticky="w", padx=5
|
|
|
|
)
|
|
|
|
)
|
|
|
|
ttk.Label(self.current_frame, textvariable=self.current_gateway_var).grid(
|
|
|
|
gateway_frame = ttk.Frame(self.current_frame)
|
|
|
|
row=2, column=1, sticky="w"
|
|
|
|
gateway_frame.grid(row=2, column=1, sticky="w")
|
|
|
|
|
|
|
|
ttk.Label(gateway_frame, textvariable=self.current_gateway_var).pack(
|
|
|
|
|
|
|
|
side=tk.LEFT
|
|
|
|
)
|
|
|
|
)
|
|
|
|
# Add copy button for Gateway
|
|
|
|
|
|
|
|
ttk.Button(
|
|
|
|
ttk.Button(
|
|
|
|
self.current_frame, text="Copy", width=5, command=self.copy_current_gateway
|
|
|
|
gateway_frame, text="Copy", width=5, command=self.copy_current_gateway
|
|
|
|
).grid(row=2, column=2, padx=5)
|
|
|
|
).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(self.current_frame, text="DHCP Status:").grid(
|
|
|
|
ttk.Label(self.current_frame, text="DHCP Status:").grid(
|
|
|
|
row=3, column=0, sticky="w", padx=5
|
|
|
|
row=3, column=0, sticky="w", padx=5
|
|
|
@ -338,45 +367,52 @@ class IPChangerApp:
|
|
|
|
self.control_frame, text="IP Configuration", padding="5"
|
|
|
|
self.control_frame, text="IP Configuration", padding="5"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.ip_frame.grid(row=2, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
self.ip_frame.grid(row=2, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
|
|
|
|
self.ip_frame.columnconfigure(1, weight=1) # Make IP entry column expandable
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# IP Prefix row - all aligned left
|
|
|
|
ttk.Label(self.ip_frame, text="IP Prefix (first 3 octets):").grid(
|
|
|
|
ttk.Label(self.ip_frame, text="IP Prefix (first 3 octets):").grid(
|
|
|
|
row=0, column=0, sticky="w", padx=5
|
|
|
|
row=0, column=0, sticky="w", padx=5
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.ip_prefix = tk.StringVar(value=self.config.last_ip_prefix)
|
|
|
|
ip_prefix_frame = ttk.Frame(self.ip_frame)
|
|
|
|
self.ip_entry = ttk.Entry(self.ip_frame, textvariable=self.ip_prefix, width=30)
|
|
|
|
ip_prefix_frame.grid(row=0, column=1, sticky="w", padx=5)
|
|
|
|
self.ip_entry.grid(row=0, column=1, sticky="w", padx=5)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(self.ip_frame, text="Last Octet:").grid(
|
|
|
|
self.ip_prefix = tk.StringVar(value=self.config.last_ip_prefix)
|
|
|
|
row=0, column=2, sticky="w", padx=5
|
|
|
|
self.ip_entry = ttk.Entry(
|
|
|
|
|
|
|
|
ip_prefix_frame, textvariable=self.ip_prefix, width=30
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
self.ip_entry.pack(side=tk.LEFT)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(ip_prefix_frame, text="Last Octet:").pack(side=tk.LEFT, padx=5)
|
|
|
|
self.last_octet = tk.StringVar(value="249")
|
|
|
|
self.last_octet = tk.StringVar(value="249")
|
|
|
|
self.last_octet_entry = ttk.Entry(
|
|
|
|
self.last_octet_entry = ttk.Entry(
|
|
|
|
self.ip_frame, textvariable=self.last_octet, width=5
|
|
|
|
ip_prefix_frame, textvariable=self.last_octet, width=5
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.last_octet_entry.grid(row=0, column=3, sticky="w", padx=5)
|
|
|
|
self.last_octet_entry.pack(side=tk.LEFT)
|
|
|
|
|
|
|
|
|
|
|
|
# Add subnet mask configuration
|
|
|
|
# Subnet mask row - all aligned left
|
|
|
|
ttk.Label(self.ip_frame, text="Subnet Mask:").grid(
|
|
|
|
ttk.Label(self.ip_frame, text="Subnet Mask:").grid(
|
|
|
|
row=1, column=0, sticky="w", padx=5
|
|
|
|
row=1, column=0, sticky="w", padx=5
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
subnet_frame = ttk.Frame(self.ip_frame)
|
|
|
|
|
|
|
|
subnet_frame.grid(row=1, column=1, sticky="w", padx=5)
|
|
|
|
|
|
|
|
|
|
|
|
self.subnet_mask = tk.StringVar(value="255.255.255.0")
|
|
|
|
self.subnet_mask = tk.StringVar(value="255.255.255.0")
|
|
|
|
self.subnet_entry = ttk.Entry(
|
|
|
|
self.subnet_entry = ttk.Entry(
|
|
|
|
self.ip_frame, textvariable=self.subnet_mask, width=15
|
|
|
|
subnet_frame, textvariable=self.subnet_mask, width=15
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.subnet_entry.grid(row=1, column=1, sticky="w", padx=5)
|
|
|
|
self.subnet_entry.pack(side=tk.LEFT)
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(self.ip_frame, text="CIDR (/bits):").grid(
|
|
|
|
ttk.Label(subnet_frame, text="CIDR (/bits):").pack(side=tk.LEFT, padx=5)
|
|
|
|
row=1, column=2, sticky="w", padx=5
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
self.cidr_bits = tk.StringVar(value="24")
|
|
|
|
self.cidr_bits = tk.StringVar(value="24")
|
|
|
|
self.cidr_entry = ttk.Entry(self.ip_frame, textvariable=self.cidr_bits, width=5)
|
|
|
|
self.cidr_entry = ttk.Entry(subnet_frame, textvariable=self.cidr_bits, width=5)
|
|
|
|
self.cidr_entry.grid(row=1, column=3, sticky="w", padx=5)
|
|
|
|
self.cidr_entry.pack(side=tk.LEFT)
|
|
|
|
|
|
|
|
|
|
|
|
# Sección de historial
|
|
|
|
# Sección de historial
|
|
|
|
self.history_frame = ttk.LabelFrame(
|
|
|
|
self.history_frame = ttk.LabelFrame(
|
|
|
|
self.control_frame, text="IP History", padding="5"
|
|
|
|
self.control_frame, text="IP History", padding="5"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.history_frame.grid(row=3, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
self.history_frame.grid(row=3, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
|
|
|
|
self.history_frame.columnconfigure(1, weight=1) # Make combo column expandable
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(self.history_frame, text="Previous IPs:").grid(
|
|
|
|
ttk.Label(self.history_frame, text="Previous IPs:").grid(
|
|
|
|
row=0, column=0, sticky="w", padx=5
|
|
|
|
row=0, column=0, sticky="w", padx=5
|
|
|
@ -395,6 +431,10 @@ class IPChangerApp:
|
|
|
|
self.button_frame = ttk.Frame(self.control_frame)
|
|
|
|
self.button_frame = ttk.Frame(self.control_frame)
|
|
|
|
self.button_frame.grid(row=4, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
self.button_frame.grid(row=4, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Make the button frame distribute buttons evenly
|
|
|
|
|
|
|
|
for i in range(4): # For the 4 buttons
|
|
|
|
|
|
|
|
self.button_frame.columnconfigure(i, weight=1)
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Button(
|
|
|
|
ttk.Button(
|
|
|
|
self.button_frame, text="Set Static IP", command=self.set_static_ip
|
|
|
|
self.button_frame, text="Set Static IP", command=self.set_static_ip
|
|
|
|
).grid(row=0, column=0, padx=5)
|
|
|
|
).grid(row=0, column=0, padx=5)
|
|
|
@ -413,14 +453,20 @@ class IPChangerApp:
|
|
|
|
def create_tools_tab(self):
|
|
|
|
def create_tools_tab(self):
|
|
|
|
# Control frame for Tools tab
|
|
|
|
# Control frame for Tools tab
|
|
|
|
self.tools_control_frame = ttk.Frame(self.tools_tab)
|
|
|
|
self.tools_control_frame = ttk.Frame(self.tools_tab)
|
|
|
|
self.tools_control_frame.grid(row=0, column=0, sticky="new")
|
|
|
|
self.tools_control_frame.grid(row=0, column=0, sticky="nsew")
|
|
|
|
self.tools_control_frame.columnconfigure(0, weight=1)
|
|
|
|
self.tools_control_frame.columnconfigure(0, weight=1)
|
|
|
|
|
|
|
|
self.tools_control_frame.rowconfigure(
|
|
|
|
|
|
|
|
1, weight=1
|
|
|
|
|
|
|
|
) # Make the scan frame expandable
|
|
|
|
|
|
|
|
|
|
|
|
# Network Tools section (moved from original layout)
|
|
|
|
# Network Tools section (moved from original layout)
|
|
|
|
self.ping_frame = ttk.LabelFrame(
|
|
|
|
self.ping_frame = ttk.LabelFrame(
|
|
|
|
self.tools_control_frame, text="Ping Tool", padding="5"
|
|
|
|
self.tools_control_frame, text="Ping Tool", padding="5"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.ping_frame.grid(row=0, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
self.ping_frame.grid(row=0, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
|
|
|
|
self.ping_frame.columnconfigure(
|
|
|
|
|
|
|
|
1, weight=1
|
|
|
|
|
|
|
|
) # Make ping target field expandable
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(self.ping_frame, text="Target IP/Host:").grid(
|
|
|
|
ttk.Label(self.ping_frame, text="Target IP/Host:").grid(
|
|
|
|
row=0, column=0, sticky="w", padx=5
|
|
|
|
row=0, column=0, sticky="w", padx=5
|
|
|
@ -446,71 +492,77 @@ class IPChangerApp:
|
|
|
|
row=0, column=4, padx=5
|
|
|
|
row=0, column=4, padx=5
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Network Scan section
|
|
|
|
# Network Scan section - make it expand both horizontally and vertically
|
|
|
|
self.scan_frame = ttk.LabelFrame(
|
|
|
|
self.scan_frame = ttk.LabelFrame(
|
|
|
|
self.tools_control_frame, text="Network Scan", padding="5"
|
|
|
|
self.tools_control_frame, text="Network Scan", padding="5"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.scan_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
self.scan_frame.grid(row=1, column=0, sticky="nsew", pady=(0, 10))
|
|
|
|
|
|
|
|
|
|
|
|
# Start IP
|
|
|
|
# Configure column weights for scan frame to make it expandable
|
|
|
|
ttk.Label(self.scan_frame, text="Start IP:").grid(
|
|
|
|
self.scan_frame.columnconfigure(0, weight=1) # Single column for frames
|
|
|
|
row=0, column=0, sticky="w", padx=5
|
|
|
|
self.scan_frame.rowconfigure(4, weight=1) # Make results row expandable
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Use frames to organize elements in a row and aligned left
|
|
|
|
|
|
|
|
# Start IP and End IP row
|
|
|
|
|
|
|
|
ip_range_frame = ttk.Frame(self.scan_frame)
|
|
|
|
|
|
|
|
ip_range_frame.grid(row=0, column=0, sticky="w", padx=5, pady=2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(ip_range_frame, text="Start IP:").pack(side=tk.LEFT, padx=(0, 5))
|
|
|
|
self.scan_start_ip = tk.StringVar()
|
|
|
|
self.scan_start_ip = tk.StringVar()
|
|
|
|
self.scan_start_entry = ttk.Entry(
|
|
|
|
self.scan_start_entry = ttk.Entry(
|
|
|
|
self.scan_frame, textvariable=self.scan_start_ip, width=15
|
|
|
|
ip_range_frame, textvariable=self.scan_start_ip, width=15
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.scan_start_entry.grid(row=0, column=1, sticky="w", padx=5)
|
|
|
|
self.scan_start_entry.pack(side=tk.LEFT)
|
|
|
|
|
|
|
|
|
|
|
|
# End IP
|
|
|
|
ttk.Label(ip_range_frame, text="End IP:").pack(side=tk.LEFT, padx=(10, 5))
|
|
|
|
ttk.Label(self.scan_frame, text="End IP:").grid(
|
|
|
|
|
|
|
|
row=0, column=2, sticky="w", padx=5
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
self.scan_end_ip = tk.StringVar()
|
|
|
|
self.scan_end_ip = tk.StringVar()
|
|
|
|
self.scan_end_entry = ttk.Entry(
|
|
|
|
self.scan_end_entry = ttk.Entry(
|
|
|
|
self.scan_frame, textvariable=self.scan_end_ip, width=15
|
|
|
|
ip_range_frame, textvariable=self.scan_end_ip, width=15
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.scan_end_entry.grid(row=0, column=3, sticky="w", padx=5)
|
|
|
|
self.scan_end_entry.pack(side=tk.LEFT)
|
|
|
|
|
|
|
|
|
|
|
|
# Add CIDR field in Network Scan
|
|
|
|
ttk.Label(ip_range_frame, text="CIDR (/bits):").pack(side=tk.LEFT, padx=(10, 5))
|
|
|
|
ttk.Label(self.scan_frame, text="CIDR (/bits):").grid(
|
|
|
|
|
|
|
|
row=0, column=4, sticky="w", padx=5
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
self.scan_cidr_bits = tk.StringVar(value="24")
|
|
|
|
self.scan_cidr_bits = tk.StringVar(value="24")
|
|
|
|
self.scan_cidr_entry = ttk.Entry(
|
|
|
|
self.scan_cidr_entry = ttk.Entry(
|
|
|
|
self.scan_frame, textvariable=self.scan_cidr_bits, width=5
|
|
|
|
ip_range_frame, textvariable=self.scan_cidr_bits, width=5
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.scan_cidr_entry.grid(row=0, column=5, sticky="w", padx=5)
|
|
|
|
self.scan_cidr_entry.pack(side=tk.LEFT)
|
|
|
|
# Add trace to update the scan range when CIDR changes
|
|
|
|
# Add trace to update the scan range when CIDR changes
|
|
|
|
self.scan_cidr_bits.trace("w", self.update_scan_range_from_cidr)
|
|
|
|
self.scan_cidr_bits.trace("w", self.update_scan_range_from_cidr)
|
|
|
|
|
|
|
|
|
|
|
|
# Display number of nodes to scan
|
|
|
|
# Nodes to scan and buttons row
|
|
|
|
ttk.Label(self.scan_frame, text="Nodes to scan:").grid(
|
|
|
|
controls_frame = ttk.Frame(self.scan_frame)
|
|
|
|
row=1, column=0, sticky="w", padx=5
|
|
|
|
controls_frame.grid(row=1, column=0, sticky="ew", padx=5, pady=2)
|
|
|
|
)
|
|
|
|
controls_frame.columnconfigure(
|
|
|
|
|
|
|
|
1, weight=1
|
|
|
|
|
|
|
|
) # Make space between nodes and buttons expandable
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nodes_frame = ttk.Frame(controls_frame)
|
|
|
|
|
|
|
|
nodes_frame.grid(row=0, column=0, sticky="w")
|
|
|
|
|
|
|
|
ttk.Label(nodes_frame, text="Nodes to scan:").pack(side=tk.LEFT, padx=(0, 5))
|
|
|
|
self.nodes_to_scan = tk.StringVar(value="0")
|
|
|
|
self.nodes_to_scan = tk.StringVar(value="0")
|
|
|
|
nodes_display = ttk.Entry(
|
|
|
|
nodes_display = ttk.Entry(
|
|
|
|
self.scan_frame, textvariable=self.nodes_to_scan, width=10, state="readonly"
|
|
|
|
nodes_frame, textvariable=self.nodes_to_scan, width=10, state="readonly"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
nodes_display.grid(row=1, column=1, sticky="w", padx=5)
|
|
|
|
nodes_display.pack(side=tk.LEFT)
|
|
|
|
|
|
|
|
|
|
|
|
# Scan buttons - move to row 1, column 2-5
|
|
|
|
# Scan buttons - aligned right
|
|
|
|
self.scan_buttons_frame = ttk.Frame(self.scan_frame)
|
|
|
|
self.scan_buttons_frame = ttk.Frame(controls_frame)
|
|
|
|
self.scan_buttons_frame.grid(row=1, column=2, columnspan=4, padx=5, sticky="e")
|
|
|
|
self.scan_buttons_frame.grid(row=0, column=1, sticky="e")
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Button(
|
|
|
|
ttk.Button(
|
|
|
|
self.scan_buttons_frame, text="Start Scan", command=self.start_scan
|
|
|
|
self.scan_buttons_frame, text="Start Scan", command=self.start_scan
|
|
|
|
).grid(row=0, column=0, padx=5)
|
|
|
|
).pack(side=tk.LEFT, padx=5)
|
|
|
|
ttk.Button(
|
|
|
|
ttk.Button(
|
|
|
|
self.scan_buttons_frame, text="Stop Scan", command=self.stop_scan
|
|
|
|
self.scan_buttons_frame, text="Stop Scan", command=self.stop_scan
|
|
|
|
).grid(row=0, column=1, padx=5)
|
|
|
|
).pack(side=tk.LEFT, padx=5)
|
|
|
|
ttk.Button(
|
|
|
|
ttk.Button(
|
|
|
|
self.scan_buttons_frame,
|
|
|
|
self.scan_buttons_frame,
|
|
|
|
text="Get Host Info",
|
|
|
|
text="Get Host Info",
|
|
|
|
command=self.gather_host_information,
|
|
|
|
command=self.gather_host_information,
|
|
|
|
).grid(row=0, column=2, padx=5)
|
|
|
|
).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
|
|
|
|
# Scan progress - move to row 2
|
|
|
|
# Scan progress - keep full width and make it expand horizontally
|
|
|
|
self.scan_progress = ttk.Progressbar(
|
|
|
|
self.scan_progress = ttk.Progressbar(
|
|
|
|
self.scan_frame,
|
|
|
|
self.scan_frame,
|
|
|
|
orient="horizontal",
|
|
|
|
orient="horizontal",
|
|
|
@ -518,27 +570,23 @@ class IPChangerApp:
|
|
|
|
mode="determinate",
|
|
|
|
mode="determinate",
|
|
|
|
variable=self.scan_progress_var,
|
|
|
|
variable=self.scan_progress_var,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.scan_progress.grid(
|
|
|
|
self.scan_progress.grid(row=2, column=0, sticky="ew", padx=5, pady=5)
|
|
|
|
row=2, column=0, columnspan=6, sticky="ew", padx=5, pady=5
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Scan results - update row numbers
|
|
|
|
# Scan results - keep full width
|
|
|
|
ttk.Label(self.scan_frame, text="Scan Results:").grid(
|
|
|
|
ttk.Label(self.scan_frame, text="Scan Results:").grid(
|
|
|
|
row=3, column=0, sticky="w", padx=5
|
|
|
|
row=3, column=0, sticky="w", padx=5
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Frame for results list and scrollbar
|
|
|
|
# Frame for results list and scrollbar - make it fully expandable
|
|
|
|
self.results_frame = ttk.Frame(self.scan_frame)
|
|
|
|
self.results_frame = ttk.Frame(self.scan_frame)
|
|
|
|
self.results_frame.grid(
|
|
|
|
self.results_frame.grid(row=4, column=0, 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.columnconfigure(0, weight=1)
|
|
|
|
self.results_frame.rowconfigure(0, weight=1)
|
|
|
|
self.results_frame.rowconfigure(0, weight=1)
|
|
|
|
|
|
|
|
|
|
|
|
# Replace Listbox with Treeview that has columns
|
|
|
|
# Replace Listbox with Treeview that has columns - make sure it expands
|
|
|
|
self.scan_results_tree = ttk.Treeview(
|
|
|
|
self.scan_results_tree = ttk.Treeview(
|
|
|
|
self.results_frame,
|
|
|
|
self.results_frame,
|
|
|
|
columns=("ip", "hostname", "mac"),
|
|
|
|
columns=("ip", "hostname", "mac", "vendor"), # Add vendor column
|
|
|
|
show="headings",
|
|
|
|
show="headings",
|
|
|
|
height=10,
|
|
|
|
height=10,
|
|
|
|
)
|
|
|
|
)
|
|
|
@ -547,11 +595,24 @@ class IPChangerApp:
|
|
|
|
self.scan_results_tree.heading("ip", text="IP Address")
|
|
|
|
self.scan_results_tree.heading("ip", text="IP Address")
|
|
|
|
self.scan_results_tree.heading("hostname", text="Hostname")
|
|
|
|
self.scan_results_tree.heading("hostname", text="Hostname")
|
|
|
|
self.scan_results_tree.heading("mac", text="MAC Address")
|
|
|
|
self.scan_results_tree.heading("mac", text="MAC Address")
|
|
|
|
|
|
|
|
self.scan_results_tree.heading(
|
|
|
|
|
|
|
|
"vendor", text="MAC Vendor"
|
|
|
|
|
|
|
|
) # Add vendor heading
|
|
|
|
|
|
|
|
|
|
|
|
# Set column widths
|
|
|
|
# Set column widths
|
|
|
|
self.scan_results_tree.column("ip", width=120, anchor="w")
|
|
|
|
total_width = 650 # Base total width for the columns
|
|
|
|
self.scan_results_tree.column("hostname", width=200, anchor="w")
|
|
|
|
self.scan_results_tree.column(
|
|
|
|
self.scan_results_tree.column("mac", width=150, anchor="w")
|
|
|
|
"ip", width=int(total_width * 0.18), anchor="w", stretch=True
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
self.scan_results_tree.column(
|
|
|
|
|
|
|
|
"hostname", width=int(total_width * 0.30), anchor="w", stretch=True
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
self.scan_results_tree.column(
|
|
|
|
|
|
|
|
"mac", width=int(total_width * 0.22), anchor="w", stretch=True
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
self.scan_results_tree.column(
|
|
|
|
|
|
|
|
"vendor", width=int(total_width * 0.30), anchor="w", stretch=True
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Add scrollbar
|
|
|
|
# Add scrollbar
|
|
|
|
self.scan_results_scrollbar = ttk.Scrollbar(
|
|
|
|
self.scan_results_scrollbar = ttk.Scrollbar(
|
|
|
@ -559,14 +620,10 @@ class IPChangerApp:
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.scan_results_tree.configure(yscrollcommand=self.scan_results_scrollbar.set)
|
|
|
|
self.scan_results_tree.configure(yscrollcommand=self.scan_results_scrollbar.set)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Make sure the treeview fills the available space
|
|
|
|
self.scan_results_tree.grid(row=0, column=0, sticky="nsew")
|
|
|
|
self.scan_results_tree.grid(row=0, column=0, sticky="nsew")
|
|
|
|
self.scan_results_scrollbar.grid(row=0, column=1, sticky="ns")
|
|
|
|
self.scan_results_scrollbar.grid(row=0, column=1, sticky="ns")
|
|
|
|
|
|
|
|
|
|
|
|
# Configure scan frame to expand
|
|
|
|
|
|
|
|
self.scan_frame.columnconfigure(1, weight=1)
|
|
|
|
|
|
|
|
self.scan_frame.columnconfigure(3, weight=1)
|
|
|
|
|
|
|
|
self.scan_frame.rowconfigure(3, weight=1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def clear_log(self):
|
|
|
|
def clear_log(self):
|
|
|
|
self.log_text.delete("1.0", tk.END)
|
|
|
|
self.log_text.delete("1.0", tk.END)
|
|
|
|
|
|
|
|
|
|
|
@ -931,6 +988,7 @@ class IPChangerApp:
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# Save the ping target when used
|
|
|
|
# Save the ping target when used
|
|
|
|
|
|
|
|
|
|
|
|
self.save_current_ping_target()
|
|
|
|
self.save_current_ping_target()
|
|
|
|
|
|
|
|
|
|
|
|
# Check if continuous ping is enabled
|
|
|
|
# Check if continuous ping is enabled
|
|
|
@ -1114,11 +1172,18 @@ class IPChangerApp:
|
|
|
|
# Try to get hostname and MAC address
|
|
|
|
# Try to get hostname and MAC address
|
|
|
|
hostname = self.get_hostname(target)
|
|
|
|
hostname = self.get_hostname(target)
|
|
|
|
mac_address = self.get_mac_address(target)
|
|
|
|
mac_address = self.get_mac_address(target)
|
|
|
|
|
|
|
|
vendor = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Look up vendor if we have a MAC
|
|
|
|
|
|
|
|
if mac_address and MAC_LOOKUP_AVAILABLE:
|
|
|
|
|
|
|
|
vendor = self.get_mac_vendor(mac_address)
|
|
|
|
|
|
|
|
|
|
|
|
if hostname:
|
|
|
|
if hostname:
|
|
|
|
self.log_message(f"Hostname: {hostname}")
|
|
|
|
self.log_message(f"Hostname: {hostname}")
|
|
|
|
if mac_address:
|
|
|
|
if mac_address:
|
|
|
|
self.log_message(f"MAC Address: {mac_address}")
|
|
|
|
self.log_message(f"MAC Address: {mac_address}")
|
|
|
|
|
|
|
|
if vendor:
|
|
|
|
|
|
|
|
self.log_message(f"MAC Vendor: {vendor}")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
self.log_message(f"Error executing ping: {str(e)}")
|
|
|
|
self.log_message(f"Error executing ping: {str(e)}")
|
|
|
@ -1247,7 +1312,7 @@ class IPChangerApp:
|
|
|
|
|
|
|
|
|
|
|
|
# Add to results tree with placeholder values
|
|
|
|
# Add to results tree with placeholder values
|
|
|
|
self.master.after(
|
|
|
|
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
|
|
|
|
# Update progress
|
|
|
@ -1294,6 +1359,15 @@ class IPChangerApp:
|
|
|
|
# Get hostname and MAC
|
|
|
|
# Get hostname and MAC
|
|
|
|
hostname = self.get_hostname(ip)
|
|
|
|
hostname = self.get_hostname(ip)
|
|
|
|
mac_address = self.get_mac_address(ip)
|
|
|
|
mac_address = self.get_mac_address(ip)
|
|
|
|
|
|
|
|
vendor = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Look up vendor information if we have a MAC address
|
|
|
|
|
|
|
|
if mac_address and MAC_LOOKUP_AVAILABLE:
|
|
|
|
|
|
|
|
vendor = self.get_mac_vendor(mac_address)
|
|
|
|
|
|
|
|
if vendor:
|
|
|
|
|
|
|
|
self.log_message(f" MAC Vendor: {vendor}")
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
self.log_message(f" MAC Vendor: Unknown")
|
|
|
|
|
|
|
|
|
|
|
|
# Log what we found
|
|
|
|
# Log what we found
|
|
|
|
if hostname:
|
|
|
|
if hostname:
|
|
|
@ -1306,8 +1380,8 @@ class IPChangerApp:
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
self.log_message(f" MAC Address: Not found")
|
|
|
|
self.log_message(f" MAC Address: Not found")
|
|
|
|
|
|
|
|
|
|
|
|
# Update the tree
|
|
|
|
# Update the tree with vendor information
|
|
|
|
self.update_host_in_tree(ip, hostname, mac_address)
|
|
|
|
self.update_host_in_tree(ip, hostname, mac_address, vendor)
|
|
|
|
|
|
|
|
|
|
|
|
# Update progress
|
|
|
|
# Update progress
|
|
|
|
self.scan_progress_var.set(i + 1)
|
|
|
|
self.scan_progress_var.set(i + 1)
|
|
|
@ -1317,7 +1391,7 @@ class IPChangerApp:
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
self.log_message(f"Error gathering host information: {str(e)}")
|
|
|
|
self.log_message(f"Error gathering host information: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
def update_host_in_tree(self, ip, hostname, mac):
|
|
|
|
def update_host_in_tree(self, ip, hostname, mac, vendor=""):
|
|
|
|
"""Update an existing host in the treeview with obtained information"""
|
|
|
|
"""Update an existing host in the treeview with obtained information"""
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
# Find the item with this IP
|
|
|
|
# Find the item with this IP
|
|
|
@ -1327,21 +1401,24 @@ class IPChangerApp:
|
|
|
|
# Update the values
|
|
|
|
# Update the values
|
|
|
|
hostname_str = hostname if hostname else "Not resolved"
|
|
|
|
hostname_str = hostname if hostname else "Not resolved"
|
|
|
|
mac_str = mac if mac else "Not found"
|
|
|
|
mac_str = mac if mac else "Not found"
|
|
|
|
|
|
|
|
vendor_str = vendor if vendor else "Unknown"
|
|
|
|
|
|
|
|
|
|
|
|
self.scan_results_tree.item(
|
|
|
|
self.scan_results_tree.item(
|
|
|
|
item_id, values=(ip, hostname_str, mac_str)
|
|
|
|
item_id, values=(ip, hostname_str, mac_str, vendor_str)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
break
|
|
|
|
break
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
self.log_message(f"Error updating host in treeview: {str(e)}")
|
|
|
|
self.log_message(f"Error updating host in treeview: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
def add_scan_result(self, ip, hostname, mac):
|
|
|
|
def add_scan_result(self, ip, hostname, mac, vendor=""):
|
|
|
|
"""Helper method to add scan result to the treeview"""
|
|
|
|
"""Helper method to add scan result to the treeview"""
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
# Insert the item with all values, even if some are empty
|
|
|
|
# Insert the item with all values, even if some are empty
|
|
|
|
hostname = hostname if hostname else "Not resolved"
|
|
|
|
hostname = hostname if hostname else "Not resolved"
|
|
|
|
mac = mac if mac else "Not found"
|
|
|
|
mac = mac if mac else "Not found"
|
|
|
|
|
|
|
|
vendor = vendor if vendor else "Unknown"
|
|
|
|
|
|
|
|
|
|
|
|
self.scan_results_tree.insert("", "end", values=(ip, hostname, mac))
|
|
|
|
self.scan_results_tree.insert("", "end", values=(ip, hostname, mac, vendor))
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
self.log_message(f"Error adding scan result to UI: {str(e)}")
|
|
|
|
self.log_message(f"Error adding scan result to UI: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
@ -1784,6 +1861,30 @@ class IPChangerApp:
|
|
|
|
self.log_message(f"Error getting MAC address for {ip_address}: {str(e)}")
|
|
|
|
self.log_message(f"Error getting MAC address for {ip_address}: {str(e)}")
|
|
|
|
return ""
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_mac_vendor(self, mac_address):
|
|
|
|
|
|
|
|
"""Look up the vendor for a MAC address"""
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
if not MAC_LOOKUP_AVAILABLE:
|
|
|
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Clean up the MAC address format - the library expects a standardized format
|
|
|
|
|
|
|
|
# Remove spaces, dashes, colons and ensure uppercase
|
|
|
|
|
|
|
|
clean_mac = (
|
|
|
|
|
|
|
|
mac_address.replace(" ", "").replace("-", "").replace(":", "").upper()
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Add colons to form a standard MAC format for lookup
|
|
|
|
|
|
|
|
formatted_mac = ":".join(
|
|
|
|
|
|
|
|
[clean_mac[i : i + 2] for i in range(0, len(clean_mac), 2)]
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Look up the vendor
|
|
|
|
|
|
|
|
vendor = self.mac_lookup.lookup(formatted_mac)
|
|
|
|
|
|
|
|
return vendor
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
self.log_message(f"Error looking up MAC vendor: {str(e)}")
|
|
|
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
def main():
|
|
|
|
root = tk.Tk()
|
|
|
|
root = tk.Tk()
|
|
|
|