1258 lines
47 KiB
Python
1258 lines
47 KiB
Python
import tkinter as tk
|
|
from tkinter import ttk, messagebox
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import re
|
|
import ctypes
|
|
import sys
|
|
import ipaddress
|
|
from typing import List, Optional, Dict
|
|
from dataclasses import dataclass
|
|
import threading
|
|
import time
|
|
import socket
|
|
|
|
# Add libraries for ping and SNMP functionality
|
|
try:
|
|
from pythonping import ping as pythonping
|
|
|
|
PING_LIBRARY_AVAILABLE = True
|
|
except ImportError:
|
|
PING_LIBRARY_AVAILABLE = False
|
|
print(
|
|
"PythonPing module not found. Install with 'pip install pythonping' for better ping functionality."
|
|
)
|
|
|
|
try:
|
|
from pysnmp.hlapi import *
|
|
|
|
SNMP_AVAILABLE = True
|
|
except ImportError:
|
|
SNMP_AVAILABLE = False
|
|
print(
|
|
"pysnmp module not found. Install with 'pip install pysnmp' for SNMP functionality."
|
|
)
|
|
|
|
try:
|
|
import wmi
|
|
|
|
WMI_AVAILABLE = True
|
|
except ImportError:
|
|
WMI_AVAILABLE = False
|
|
print(
|
|
"WMI module not found. Using alternative method for network interface detection."
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class NetworkInterface:
|
|
name: str
|
|
description: str
|
|
ip_address: str = ""
|
|
subnet_mask: str = ""
|
|
gateway: str = ""
|
|
dhcp_enabled: bool = True
|
|
active: bool = True
|
|
adapter_id: str = ""
|
|
|
|
|
|
class IPChangeConfig:
|
|
def __init__(self):
|
|
self.config_file = "ip_config.json"
|
|
self.history_file = "ip_history.json"
|
|
self.ping_targets_file = "ping_targets.json" # New file for ping targets
|
|
self.load_config()
|
|
self.load_ping_targets() # Load ping targets
|
|
|
|
def load_config(self):
|
|
try:
|
|
if os.path.exists(self.config_file):
|
|
with open(self.config_file, "r") as f:
|
|
config = json.load(f)
|
|
self.last_interface = config.get("last_interface", "")
|
|
self.last_ip_prefix = config.get("last_ip_prefix", "")
|
|
self.previous_config = config.get("previous_config", {})
|
|
else:
|
|
self.last_interface = ""
|
|
self.last_ip_prefix = ""
|
|
self.previous_config = {}
|
|
except Exception as e:
|
|
print(f"Error loading config: {e}")
|
|
self.last_interface = ""
|
|
self.last_ip_prefix = ""
|
|
self.previous_config = {}
|
|
|
|
def save_config(self):
|
|
try:
|
|
config = {
|
|
"last_interface": self.last_interface,
|
|
"last_ip_prefix": self.last_ip_prefix,
|
|
"previous_config": self.previous_config,
|
|
}
|
|
with open(self.config_file, "w") as f:
|
|
json.dump(config, f)
|
|
except Exception as e:
|
|
print(f"Error saving config: {e}")
|
|
|
|
def load_ping_targets(self):
|
|
"""Load saved ping targets for each IP prefix"""
|
|
try:
|
|
if os.path.exists(self.ping_targets_file):
|
|
with open(self.ping_targets_file, "r") as f:
|
|
self.ping_targets = json.load(f)
|
|
else:
|
|
self.ping_targets = {} # Dictionary to store ping targets by IP prefix
|
|
except Exception as e:
|
|
print(f"Error loading ping targets: {e}")
|
|
self.ping_targets = {}
|
|
|
|
def save_ping_targets(self):
|
|
"""Save ping targets for each IP prefix"""
|
|
try:
|
|
with open(self.ping_targets_file, "w") as f:
|
|
json.dump(self.ping_targets, f)
|
|
except Exception as e:
|
|
print(f"Error saving ping targets: {e}")
|
|
|
|
def get_ping_target(self, ip_prefix):
|
|
"""Get the saved ping target for the given IP prefix"""
|
|
if not ip_prefix:
|
|
return ""
|
|
|
|
# Try to find exact match first
|
|
if ip_prefix in self.ping_targets:
|
|
return self.ping_targets[ip_prefix]
|
|
|
|
# If no exact match, try to find a matching IP prefix
|
|
for prefix, target in self.ping_targets.items():
|
|
if ip_prefix.startswith(prefix) or prefix.startswith(ip_prefix):
|
|
return target
|
|
|
|
# Default target: IP prefix + ".1" (usually the gateway)
|
|
return f"{ip_prefix}.1"
|
|
|
|
def set_ping_target(self, ip_prefix, target):
|
|
"""Save a ping target for the given IP prefix"""
|
|
if ip_prefix and target:
|
|
self.ping_targets[ip_prefix] = target
|
|
self.save_ping_targets()
|
|
|
|
|
|
class IPChangerApp:
|
|
def __init__(self, master):
|
|
self.master = master
|
|
self.master.title("Network Interface IP Configuration")
|
|
self.master.geometry("900x700")
|
|
|
|
# Inicializar configuración antes que nada
|
|
self.config = IPChangeConfig()
|
|
|
|
# Inicializar estructuras de datos
|
|
self.interfaces: List[NetworkInterface] = []
|
|
self.ip_history: List[str] = []
|
|
|
|
# Inicializar WMI si está disponible
|
|
if WMI_AVAILABLE:
|
|
self.wmi_client = wmi.WMI()
|
|
|
|
# Cargar datos guardados antes de crear widgets
|
|
self.load_ip_history()
|
|
|
|
# Crear la interfaz
|
|
self.create_widgets()
|
|
|
|
# Initialize trace variables
|
|
self.subnet_trace_id = None
|
|
self.cidr_trace_id = None
|
|
|
|
# Set up initial traces safely
|
|
self.setup_mask_traces()
|
|
|
|
# Actualizar la lista de IPs en el combo
|
|
self.update_history_display()
|
|
|
|
# Refrescar interfaces
|
|
self.refresh_interfaces()
|
|
|
|
# Configurar pesos de la cuadrícula
|
|
self.master.grid_columnconfigure(0, weight=1)
|
|
self.master.grid_rowconfigure(1, weight=1) # El log expandible
|
|
|
|
def setup_mask_traces(self):
|
|
"""Set up traces for subnet mask and CIDR fields safely"""
|
|
# First initialize variables
|
|
self.subnet_mask.set(self.subnet_mask.get()) # Ensure current value is valid
|
|
<<<<<<< HEAD
|
|
self.cidr_bits.set(self.cidr_bits.get()) # Ensure current value is valid
|
|
|
|
# Add traces using a different approach - using add not trace_add
|
|
self.subnet_mask.trace("w", self.on_subnet_mask_changed)
|
|
self.cidr_bits.trace("w", self.on_cidr_bits_changed)
|
|
|
|
self.log_message("Subnet mask traces initialized")
|
|
|
|
=======
|
|
self.cidr_bits.set(self.cidr_bits.get()) # Ensure current value is valid
|
|
|
|
# Add traces using a different approach - using add not trace_add
|
|
self.subnet_mask.trace("w", self.on_subnet_mask_changed)
|
|
self.cidr_bits.trace("w", self.on_cidr_bits_changed)
|
|
|
|
self.log_message("Subnet mask traces initialized")
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
def remove_mask_traces(self):
|
|
"""Not needed - we'll use a different approach"""
|
|
pass # Intentionally empty
|
|
|
|
def get_ip_prefix(self, ip: str) -> str:
|
|
"""Extrae los primeros 3 octetos de una dirección IP"""
|
|
parts = ip.split(".")
|
|
if len(parts) >= 3:
|
|
return ".".join(parts[:3])
|
|
return ip
|
|
|
|
def create_widgets(self):
|
|
# Panel superior para controles
|
|
self.control_frame = ttk.Frame(self.master, padding="10")
|
|
self.control_frame.grid(row=0, column=0, sticky="new")
|
|
|
|
# Sección de interfaces
|
|
self.interfaces_frame = ttk.LabelFrame(
|
|
self.control_frame, text="Network Interfaces", padding="5"
|
|
)
|
|
self.interfaces_frame.grid(row=0, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
ttk.Label(self.interfaces_frame, text="Select Interface:").grid(
|
|
row=0, column=0, sticky="w", padx=5
|
|
)
|
|
self.if_var = tk.StringVar()
|
|
self.if_combo = ttk.Combobox(
|
|
self.interfaces_frame, textvariable=self.if_var, state="readonly", width=50
|
|
)
|
|
self.if_combo.grid(row=0, column=1, sticky="ew", padx=5)
|
|
self.if_combo.bind("<<ComboboxSelected>>", self.on_interface_selected)
|
|
|
|
# Info de configuración actual
|
|
self.current_frame = ttk.LabelFrame(
|
|
self.control_frame, text="Current Configuration", padding="5"
|
|
)
|
|
self.current_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
self.current_ip_var = tk.StringVar()
|
|
self.current_mask_var = tk.StringVar()
|
|
self.current_gateway_var = tk.StringVar()
|
|
self.dhcp_status_var = tk.StringVar()
|
|
|
|
ttk.Label(self.current_frame, text="Current IP:").grid(
|
|
row=0, column=0, sticky="w", padx=5
|
|
)
|
|
ttk.Label(self.current_frame, textvariable=self.current_ip_var).grid(
|
|
row=0, column=1, sticky="w"
|
|
)
|
|
ttk.Label(self.current_frame, text="Subnet Mask:").grid(
|
|
row=1, column=0, sticky="w", padx=5
|
|
)
|
|
ttk.Label(self.current_frame, textvariable=self.current_mask_var).grid(
|
|
row=1, column=1, sticky="w"
|
|
)
|
|
ttk.Label(self.current_frame, text="Gateway:").grid(
|
|
row=2, column=0, sticky="w", padx=5
|
|
)
|
|
ttk.Label(self.current_frame, textvariable=self.current_gateway_var).grid(
|
|
row=2, column=1, sticky="w"
|
|
)
|
|
ttk.Label(self.current_frame, text="DHCP Status:").grid(
|
|
row=3, column=0, sticky="w", padx=5
|
|
)
|
|
ttk.Label(self.current_frame, textvariable=self.dhcp_status_var).grid(
|
|
row=3, column=1, sticky="w"
|
|
)
|
|
|
|
# Sección de configuración IP
|
|
self.ip_frame = ttk.LabelFrame(
|
|
self.control_frame, text="IP Configuration", padding="5"
|
|
)
|
|
self.ip_frame.grid(row=2, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
ttk.Label(self.ip_frame, text="IP Prefix (first 3 octets):").grid(
|
|
row=0, column=0, sticky="w", padx=5
|
|
)
|
|
self.ip_prefix = tk.StringVar(value=self.config.last_ip_prefix)
|
|
self.ip_entry = ttk.Entry(self.ip_frame, textvariable=self.ip_prefix, width=30)
|
|
self.ip_entry.grid(row=0, column=1, sticky="w", padx=5)
|
|
|
|
ttk.Label(self.ip_frame, text="Last Octet:").grid(
|
|
row=0, column=2, sticky="w", padx=5
|
|
)
|
|
self.last_octet = tk.StringVar(value="249")
|
|
self.last_octet_entry = ttk.Entry(
|
|
self.ip_frame, textvariable=self.last_octet, width=5
|
|
)
|
|
self.last_octet_entry.grid(row=0, column=3, sticky="w", padx=5)
|
|
<<<<<<< HEAD
|
|
|
|
=======
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
# Add subnet mask configuration
|
|
ttk.Label(self.ip_frame, text="Subnet Mask:").grid(
|
|
row=1, column=0, sticky="w", padx=5
|
|
)
|
|
self.subnet_mask = tk.StringVar(value="255.255.255.0")
|
|
self.subnet_entry = ttk.Entry(
|
|
self.ip_frame, textvariable=self.subnet_mask, width=15
|
|
)
|
|
self.subnet_entry.grid(row=1, column=1, sticky="w", padx=5)
|
|
<<<<<<< HEAD
|
|
|
|
=======
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
ttk.Label(self.ip_frame, text="CIDR (/bits):").grid(
|
|
row=1, column=2, sticky="w", padx=5
|
|
)
|
|
self.cidr_bits = tk.StringVar(value="24")
|
|
<<<<<<< HEAD
|
|
self.cidr_entry = ttk.Entry(self.ip_frame, textvariable=self.cidr_bits, width=5)
|
|
self.cidr_entry.grid(row=1, column=3, sticky="w", padx=5)
|
|
|
|
=======
|
|
self.cidr_entry = ttk.Entry(
|
|
self.ip_frame, textvariable=self.cidr_bits, width=5
|
|
)
|
|
self.cidr_entry.grid(row=1, column=3, sticky="w", padx=5)
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
# Don't add traces here, we'll do it after widget creation is complete
|
|
|
|
# Sección de historial
|
|
self.history_frame = ttk.LabelFrame(
|
|
self.control_frame, text="IP History", padding="5"
|
|
)
|
|
self.history_frame.grid(row=3, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
ttk.Label(self.history_frame, text="Previous IPs:").grid(
|
|
row=0, column=0, sticky="w", padx=5
|
|
)
|
|
self.history_var = tk.StringVar()
|
|
self.history_combo = ttk.Combobox(
|
|
self.history_frame,
|
|
textvariable=self.history_var,
|
|
state="readonly",
|
|
width=50,
|
|
)
|
|
self.history_combo.grid(row=0, column=1, sticky="ew", padx=5)
|
|
self.history_combo.bind("<<ComboboxSelected>>", self.on_history_selected)
|
|
|
|
# Botones de acción
|
|
self.button_frame = ttk.Frame(self.control_frame)
|
|
self.button_frame.grid(row=4, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
ttk.Button(
|
|
self.button_frame, text="Set Static IP", command=self.set_static_ip
|
|
).grid(row=0, column=0, padx=5)
|
|
ttk.Button(self.button_frame, text="Enable DHCP", command=self.set_dhcp).grid(
|
|
row=0, column=1, padx=5
|
|
)
|
|
ttk.Button(
|
|
self.button_frame, text="Restore Previous", command=self.restore_previous
|
|
).grid(row=0, column=2, padx=5)
|
|
ttk.Button(
|
|
self.button_frame,
|
|
text="Refresh Interfaces",
|
|
command=self.refresh_interfaces,
|
|
).grid(row=0, column=3, padx=5)
|
|
|
|
# Sección de Ping - add initial value and bind event
|
|
self.ping_frame = ttk.LabelFrame(
|
|
self.control_frame, text="Network Tools", padding="5"
|
|
)
|
|
self.ping_frame.grid(row=5, column=0, sticky="ew", pady=(0, 10))
|
|
|
|
ttk.Label(self.ping_frame, text="Target IP/Host:").grid(
|
|
row=0, column=0, sticky="w", padx=5
|
|
)
|
|
self.ping_target = tk.StringVar()
|
|
self.ping_entry = ttk.Entry(
|
|
self.ping_frame, textvariable=self.ping_target, width=40
|
|
)
|
|
self.ping_entry.grid(row=0, column=1, sticky="w", padx=5)
|
|
# Bind the FocusOut event to save the ping target
|
|
self.ping_entry.bind("<FocusOut>", self.save_current_ping_target)
|
|
ttk.Button(self.ping_frame, text="Ping", command=self.do_ping).grid(
|
|
row=0, column=2, padx=5
|
|
)
|
|
|
|
# Log en la parte inferior
|
|
self.log_frame = ttk.LabelFrame(self.master, text="Operation Log", padding="5")
|
|
self.log_frame.grid(row=1, column=0, sticky="nsew", padx=10, pady=(0, 10))
|
|
|
|
self.log_text = tk.Text(self.log_frame, wrap="none", height=10)
|
|
self.log_scrollbar_y = ttk.Scrollbar(
|
|
self.log_frame, orient="vertical", command=self.log_text.yview
|
|
)
|
|
self.log_scrollbar_x = ttk.Scrollbar(
|
|
self.log_frame, orient="horizontal", command=self.log_text.xview
|
|
)
|
|
|
|
self.log_text.configure(
|
|
yscrollcommand=self.log_scrollbar_y.set,
|
|
xscrollcommand=self.log_scrollbar_x.set,
|
|
)
|
|
|
|
self.log_text.grid(row=0, column=0, sticky="nsew")
|
|
self.log_scrollbar_y.grid(row=0, column=1, sticky="ns")
|
|
self.log_scrollbar_x.grid(row=1, column=0, sticky="ew")
|
|
|
|
ttk.Button(self.log_frame, text="Clear Log", command=self.clear_log).grid(
|
|
row=2, column=0, columnspan=2, pady=5
|
|
)
|
|
|
|
self.log_frame.grid_columnconfigure(0, weight=1)
|
|
self.log_frame.grid_rowconfigure(0, weight=1)
|
|
|
|
def clear_log(self):
|
|
self.log_text.delete("1.0", tk.END)
|
|
|
|
def log_message(self, message: str):
|
|
self.log_text.insert(tk.END, f"{message}\n")
|
|
self.log_text.see(tk.END)
|
|
self.log_text.update_idletasks()
|
|
|
|
def load_ip_history(self):
|
|
try:
|
|
if os.path.exists(self.config.history_file):
|
|
with open(self.config.history_file, "r") as f:
|
|
self.ip_history = json.load(f)
|
|
else:
|
|
self.ip_history = []
|
|
except Exception as e:
|
|
self.log_message(f"Error loading IP history: {e}")
|
|
self.ip_history = []
|
|
|
|
def save_ip_history(self):
|
|
try:
|
|
with open(self.config.history_file, "w") as f:
|
|
json.dump(self.ip_history, f)
|
|
except Exception as e:
|
|
self.log_message(f"Error saving IP history: {e}")
|
|
|
|
def add_to_history(self, ip_prefix: str):
|
|
if ip_prefix and ip_prefix not in self.ip_history:
|
|
self.ip_history.insert(0, ip_prefix)
|
|
self.ip_history = self.ip_history[:15] # Mantener solo las últimas 15 IPs
|
|
self.save_ip_history()
|
|
self.update_history_display()
|
|
|
|
def update_history_display(self):
|
|
self.history_combo["values"] = self.ip_history
|
|
if self.ip_history:
|
|
self.history_combo.set(self.ip_history[0])
|
|
|
|
def on_history_selected(self, event):
|
|
selected_ip = self.history_var.get()
|
|
if selected_ip:
|
|
# Ensure we only get the first three octets
|
|
ip_parts = selected_ip.split(".")
|
|
if len(ip_parts) >= 3:
|
|
ip_prefix = ".".join(ip_parts[:3])
|
|
self.ip_prefix.set(ip_prefix)
|
|
self.config.last_ip_prefix = ip_prefix
|
|
self.config.save_config()
|
|
self.log_message(f"Selected IP prefix from history: {ip_prefix}")
|
|
|
|
# Update ping target for the new IP prefix
|
|
self.update_ping_target(ip_prefix)
|
|
|
|
# Update ping target for the new IP prefix
|
|
self.update_ping_target(ip_prefix)
|
|
|
|
def refresh_interfaces(self):
|
|
self.interfaces.clear()
|
|
try:
|
|
if WMI_AVAILABLE:
|
|
self._refresh_interfaces_wmi()
|
|
else:
|
|
self._refresh_interfaces_netsh()
|
|
|
|
# Actualizar combobox
|
|
self.if_combo["values"] = [
|
|
inf.name for inf in self.interfaces if inf.active
|
|
]
|
|
|
|
# Seleccionar última interfaz usada si está disponible
|
|
if self.config.last_interface in self.if_combo["values"]:
|
|
self.if_combo.set(self.config.last_interface)
|
|
self.on_interface_selected()
|
|
|
|
self.log_message("Interfaces refreshed successfully")
|
|
|
|
except Exception as e:
|
|
self.log_message(f"Error refreshing interfaces: {str(e)}")
|
|
self.show_error(f"Error refreshing interfaces: {str(e)}")
|
|
|
|
def _refresh_interfaces_wmi(self):
|
|
try:
|
|
adapters = self.wmi_client.Win32_NetworkAdapter(PhysicalAdapter=True)
|
|
configs = self.wmi_client.Win32_NetworkAdapterConfiguration()
|
|
|
|
for adapter in adapters:
|
|
config = next(
|
|
(
|
|
cfg
|
|
for cfg in configs
|
|
if cfg.InterfaceIndex == adapter.InterfaceIndex
|
|
),
|
|
None,
|
|
)
|
|
|
|
if config and config.IPEnabled:
|
|
interface = NetworkInterface(
|
|
name=adapter.NetConnectionID,
|
|
description=adapter.Description,
|
|
ip_address=config.IPAddress[0] if config.IPAddress else "",
|
|
subnet_mask=config.IPSubnet[0] if config.IPSubnet else "",
|
|
gateway=(
|
|
config.DefaultIPGateway[0]
|
|
if config.DefaultIPGateway
|
|
else ""
|
|
),
|
|
dhcp_enabled=config.DHCPEnabled,
|
|
active=adapter.NetEnabled,
|
|
adapter_id=adapter.GUID,
|
|
)
|
|
self.interfaces.append(interface)
|
|
except Exception as e:
|
|
raise Exception(f"WMI refresh error: {str(e)}")
|
|
|
|
def _refresh_interfaces_netsh(self):
|
|
try:
|
|
output = subprocess.check_output(
|
|
"netsh interface ipv4 show config", shell=True, text=True
|
|
)
|
|
configs = output.split("\n\n")
|
|
|
|
for config in configs:
|
|
if not config.strip():
|
|
continue
|
|
|
|
name_match = re.search(r"Configuración de\s+\"(.+?)\"", config)
|
|
if not name_match:
|
|
continue
|
|
|
|
name = name_match.group(1)
|
|
|
|
ip_match = re.search(r"Dirección IP:\s+(\d+\.\d+\.\d+\.\d+)", config)
|
|
mask_match = re.search(
|
|
r"Máscara de subred:\s+(\d+\.\d+\.\d+\.\d+)", config
|
|
)
|
|
gateway_match = re.search(
|
|
r"Puerta de enlace predeterminada:\s+(\d+\.\d+\.\d+\.\d+)", config
|
|
)
|
|
dhcp_match = re.search(r"DHCP habilitado:\s+(\w+)", config)
|
|
|
|
interface = NetworkInterface(
|
|
name=name,
|
|
description=name,
|
|
ip_address=ip_match.group(1) if ip_match else "",
|
|
subnet_mask=mask_match.group(1) if mask_match else "",
|
|
gateway=gateway_match.group(1) if gateway_match else "",
|
|
dhcp_enabled=(
|
|
dhcp_match.group(1).lower() == "sí" if dhcp_match else True
|
|
),
|
|
active=True,
|
|
)
|
|
self.interfaces.append(interface)
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
raise Exception(f"Error executing netsh: {str(e)}")
|
|
except Exception as e:
|
|
raise Exception(f"Error parsing netsh output: {str(e)}")
|
|
|
|
def on_interface_selected(self, event=None):
|
|
selected = self.if_var.get()
|
|
interface = next((inf for inf in self.interfaces if inf.name == selected), None)
|
|
|
|
if interface:
|
|
self.current_ip_var.set(interface.ip_address)
|
|
self.current_mask_var.set(interface.subnet_mask)
|
|
self.current_gateway_var.set(interface.gateway)
|
|
self.dhcp_status_var.set(
|
|
"Enabled" if interface.dhcp_enabled else "Disabled"
|
|
)
|
|
|
|
# Actualizar IP prefix si hay una IP válida
|
|
if interface.ip_address:
|
|
prefix = ".".join(interface.ip_address.split(".")[:3])
|
|
self.ip_prefix.set(prefix)
|
|
<<<<<<< HEAD
|
|
|
|
=======
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
# Update ping target for the new IP prefix
|
|
self.update_ping_target(prefix)
|
|
|
|
# Guardar interfaz seleccionada
|
|
self.config.last_interface = selected
|
|
self.config.save_config()
|
|
|
|
self.log_message(f"Selected interface: {selected}")
|
|
|
|
def validate_ip_input(self) -> tuple[bool, str]:
|
|
try:
|
|
prefix = self.ip_prefix.get().strip()
|
|
last_octet = self.last_octet.get().strip()
|
|
subnet_mask = self.subnet_mask.get().strip()
|
|
<<<<<<< HEAD
|
|
|
|
=======
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
# Validar formato del prefijo
|
|
if not re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}", prefix):
|
|
return False, "IP prefix must be in format: xxx.xxx.xxx"
|
|
|
|
# Validar último octeto
|
|
if not last_octet.isdigit() or not 0 <= int(last_octet) <= 255:
|
|
return False, "Last octet must be between 0 and 255"
|
|
<<<<<<< HEAD
|
|
|
|
# Validar máscara de subred
|
|
if not self.is_valid_subnet_mask(subnet_mask):
|
|
return False, "Invalid subnet mask format"
|
|
|
|
=======
|
|
|
|
# Validar máscara de subred
|
|
if not self.is_valid_subnet_mask(subnet_mask):
|
|
return False, "Invalid subnet mask format"
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
# Construir y validar IP completa
|
|
ip = f"{prefix}.{last_octet}"
|
|
ipaddress.IPv4Address(ip)
|
|
|
|
return True, ip
|
|
|
|
except ValueError as e:
|
|
return False, f"Invalid IP address: {str(e)}"
|
|
|
|
def execute_admin_script(
|
|
<<<<<<< HEAD
|
|
self,
|
|
interface: str,
|
|
mode: str,
|
|
debug: bool = True,
|
|
subnet_mask: str = "255.255.255.0",
|
|
=======
|
|
self, interface: str, mode: str, debug: bool = True, subnet_mask: str = "255.255.255.0"
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
) -> bool:
|
|
"""
|
|
Ejecuta el script administrativo con los argumentos proporcionados
|
|
mode puede ser una IP o 'dhcp'
|
|
"""
|
|
# Obtener rutas absolutas
|
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
script_path = os.path.join(current_dir, "ip-changer-admin.py")
|
|
python_exe = sys.executable
|
|
|
|
# Verificar que el script existe
|
|
if not os.path.exists(script_path):
|
|
error_msg = f"Admin script not found at: {script_path}"
|
|
self.log_message(error_msg)
|
|
return False
|
|
|
|
# Construir la línea de argumentos - pass both IP and subnet as a single parameter
|
|
if mode != "dhcp":
|
|
# For static IP, create the netsh command with subnet mask
|
|
<<<<<<< HEAD
|
|
gateway = (
|
|
f"{self.get_ip_prefix(mode)}.1" # Use the first three octets of the IP
|
|
)
|
|
=======
|
|
gateway = f"{self.get_ip_prefix(mode)}.1" # Use the first three octets of the IP
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
netsh_cmd = f'netsh interface ip set address "{interface}" static {mode} {subnet_mask} {gateway}'
|
|
args = f'"{script_path}" "{interface}" "{netsh_cmd}"'
|
|
else:
|
|
# For DHCP, keep it simple
|
|
args = f'"{script_path}" "{interface}" "dhcp"'
|
|
|
|
if debug:
|
|
self.log_message("Debug Information:")
|
|
self.log_message(f"Current Directory: {current_dir}")
|
|
self.log_message(f"Script Path: {script_path}")
|
|
self.log_message(f"Python Executable: {python_exe}")
|
|
self.log_message(f"Full Command: {python_exe} {args}")
|
|
|
|
try:
|
|
self.log_message(
|
|
f"Attempting to execute admin script with elevated privileges"
|
|
)
|
|
|
|
ret = ctypes.windll.shell32.ShellExecuteW(
|
|
None, # hwnd
|
|
"runas", # operation
|
|
python_exe, # file
|
|
args, # parameters
|
|
current_dir, # directory
|
|
1, # show command
|
|
)
|
|
|
|
result_code = int(ret)
|
|
if result_code > 32:
|
|
self.log_message(
|
|
f"ShellExecuteW successful (return code: {result_code})"
|
|
)
|
|
return True
|
|
else:
|
|
error_codes = {
|
|
0: "The operating system is out of memory or resources",
|
|
2: "The specified file was not found",
|
|
3: "The specified path was not found",
|
|
5: "Access denied",
|
|
8: "Insufficient memory to complete the operation",
|
|
11: "Bad format",
|
|
26: "A sharing violation occurred",
|
|
27: "The file name association is incomplete or invalid",
|
|
28: "The DDE operation timed out",
|
|
29: "The DDE operation failed",
|
|
30: "The DDE operation is busy",
|
|
31: "The file name association is unavailable",
|
|
32: "No application is associated with the file",
|
|
}
|
|
|
|
self.log_message(
|
|
f"Failed to execute admin script. Error code {result_code}: {error_msg}"
|
|
)
|
|
return False
|
|
|
|
except Exception as e:
|
|
self.log_message(f"Exception while executing admin script: {str(e)}")
|
|
return False
|
|
|
|
def save_previous_config(self, interface_name: str):
|
|
"""Guarda la configuración actual de la interfaz antes de cambiarla"""
|
|
interface = next(
|
|
(inf for inf in self.interfaces if inf.name == interface_name), None
|
|
)
|
|
if interface:
|
|
self.config.previous_config = {
|
|
"name": interface.name,
|
|
"ip_address": interface.ip_address,
|
|
"subnet_mask": interface.subnet_mask,
|
|
"gateway": interface.gateway,
|
|
"dhcp_enabled": interface.dhcp_enabled,
|
|
}
|
|
self.config.save_config()
|
|
self.log_message(f"Previous configuration saved for {interface.name}")
|
|
|
|
def restore_previous(self):
|
|
"""Restaura la configuración IP anterior"""
|
|
prev_config = self.config.previous_config
|
|
if not prev_config:
|
|
self.show_error("No previous configuration available")
|
|
return
|
|
|
|
interface_name = prev_config.get("name")
|
|
if not interface_name:
|
|
self.show_error("Invalid previous configuration")
|
|
return
|
|
|
|
# Verificar que la interfaz existe
|
|
if interface_name not in [inf.name for inf in self.interfaces]:
|
|
self.show_error(f"Interface {interface_name} not found")
|
|
return
|
|
|
|
self.log_message(f"Restoring previous configuration for {interface_name}")
|
|
|
|
if prev_config.get("dhcp_enabled", True):
|
|
# Si la configuración anterior era DHCP
|
|
if self.execute_admin_script(interface_name, "dhcp"):
|
|
self.log_message(f"Successfully restored DHCP on {interface_name}")
|
|
time.sleep(2)
|
|
self.refresh_interfaces()
|
|
else:
|
|
self.show_error("Failed to restore DHCP configuration")
|
|
else:
|
|
# Si la configuración anterior era IP estática
|
|
ip = prev_config.get("ip_address")
|
|
if not ip:
|
|
self.show_error("Previous IP address not available")
|
|
return
|
|
|
|
if self.execute_admin_script(interface_name, ip):
|
|
self.log_message(f"Successfully restored IP {ip} on {interface_name}")
|
|
time.sleep(2)
|
|
self.refresh_interfaces()
|
|
else:
|
|
self.show_error("Failed to restore static IP configuration")
|
|
|
|
def do_ping(self):
|
|
"""Realiza un ping a la dirección especificada usando una biblioteca de Python"""
|
|
target = self.ping_target.get().strip()
|
|
if not target:
|
|
self.show_error("Please enter a target IP or hostname")
|
|
return
|
|
|
|
# Save the ping target when used
|
|
self.save_current_ping_target()
|
|
<<<<<<< HEAD
|
|
|
|
=======
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
self.log_message(f"Pinging {target}...")
|
|
|
|
# Crear un hilo para el ping para no bloquear la interfaz
|
|
threading.Thread(target=self._execute_ping, args=(target,), daemon=True).start()
|
|
|
|
def _execute_ping(self, target: str):
|
|
"""Ejecuta el ping usando bibliotecas de Python en lugar del comando del sistema"""
|
|
try:
|
|
if PING_LIBRARY_AVAILABLE:
|
|
# Usar PythonPing que es multiplataforma y no requiere privilegios
|
|
self.log_message(
|
|
f"Sending 4 ICMP echo requests to {target} using PythonPing..."
|
|
)
|
|
|
|
# Ejecutar el ping
|
|
response = pythonping(target, count=4, timeout=1)
|
|
|
|
# Comprobar si el ping tuvo éxito
|
|
if response.success():
|
|
min_rtt = min(r.time_elapsed_ms for r in response)
|
|
max_rtt = max(r.time_elapsed_ms for r in response)
|
|
avg_rtt = sum(r.time_elapsed_ms for r in response) / len(response)
|
|
|
|
# Mostrar resultados individuales
|
|
for i, reply in enumerate(response):
|
|
self.log_message(
|
|
f"Reply from {target}: time={reply.time_elapsed_ms:.2f}ms"
|
|
)
|
|
|
|
# Resumen
|
|
success_count = len([r for r in response if r.success])
|
|
self.log_message(f"Ping statistics for {target}:")
|
|
self.log_message(
|
|
f" Packets: Sent = 4, Received = {success_count}, Lost = {4 - success_count}"
|
|
)
|
|
self.log_message(f"Approximate round trip times in milliseconds:")
|
|
self.log_message(
|
|
f" Minimum = {min_rtt:.2f}ms, Maximum = {max_rtt:.2f}ms, Average = {avg_rtt:.2f}ms"
|
|
)
|
|
else:
|
|
self.log_message(f"Could not reach host {target}")
|
|
else:
|
|
# Usar socket para hacer un ping básico (solo comprueba si el host está activo)
|
|
self.log_message(
|
|
f"PythonPing not available, checking if host {target} is reachable..."
|
|
)
|
|
self.log_message(
|
|
"Note: This is not a true ICMP ping, just a TCP connection test"
|
|
)
|
|
|
|
for port in [80, 443, 22, 21]:
|
|
try:
|
|
start_time = time.time()
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.settimeout(1)
|
|
result = s.connect_ex((target, port))
|
|
elapsed_time = (time.time() - start_time) * 1000 # ms
|
|
s.close()
|
|
|
|
if result == 0:
|
|
self.log_message(
|
|
f"Port {port} on {target} is open (TCP connect time: {elapsed_time:.2f}ms)"
|
|
)
|
|
else:
|
|
self.log_message(
|
|
f"Port {port} on {target} is not responding"
|
|
)
|
|
except socket.gaierror:
|
|
self.log_message(f"Could not resolve hostname {target}")
|
|
break
|
|
except socket.error as e:
|
|
self.log_message(f"Error connecting to {target}: {str(e)}")
|
|
except AttributeError as e:
|
|
# Handle specifically the attribute error
|
|
self.log_message(f"Error with PythonPing API: {str(e)}")
|
|
|
|
# Fallback to alternative ping implementation
|
|
self.log_message("Falling back to socket-based connectivity test...")
|
|
|
|
# Implement a simple socket-based ping
|
|
success = False
|
|
try:
|
|
# Try to connect to the host - this just checks if it's reachable
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.settimeout(2)
|
|
start_time = time.time()
|
|
s.connect((target, 80)) # Try to connect to port 80
|
|
response_time = (time.time() - start_time) * 1000
|
|
s.close()
|
|
self.log_message(
|
|
f"Host {target} is reachable. Response time: {response_time:.2f}ms"
|
|
)
|
|
success = True
|
|
except socket.error as se:
|
|
self.log_message(f"Could not connect to {target}: {str(se)}")
|
|
|
|
# Try to at least resolve the hostname
|
|
try:
|
|
ip = socket.gethostbyname(target)
|
|
self.log_message(
|
|
f"Hostname {target} resolves to {ip}, but connection failed"
|
|
)
|
|
except socket.gaierror:
|
|
self.log_message(f"Could not resolve hostname {target}")
|
|
|
|
# Alternative implementation using subprocess directly as last resort
|
|
try:
|
|
self.log_message("Attempting to ping using system command...")
|
|
# Use subprocess to run ping directly (won't work if command not available)
|
|
use_count = "-n" if sys.platform.lower() == "win32" else "-c"
|
|
cmd = ["ping", use_count, "4", target]
|
|
process = subprocess.Popen(
|
|
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
)
|
|
stdout, stderr = process.communicate()
|
|
|
|
if process.returncode == 0:
|
|
# Process and display output
|
|
output = stdout.decode("utf-8", errors="replace")
|
|
for line in output.splitlines():
|
|
if line.strip():
|
|
self.log_message(line)
|
|
else:
|
|
self.log_message(
|
|
f"System ping command failed: {stderr.decode('utf-8', errors='replace')}"
|
|
)
|
|
except Exception as subproc_e:
|
|
self.log_message(f"System ping command error: {str(subproc_e)}")
|
|
|
|
except Exception as e:
|
|
self.log_message(f"Error executing ping: {str(e)}")
|
|
self.log_message(
|
|
"PythonPing may need to be reinstalled or updated. Run: pip install -U pythonping"
|
|
)
|
|
|
|
# Try to give more information
|
|
try:
|
|
import pkg_resources
|
|
|
|
version = pkg_resources.get_distribution("pythonping").version
|
|
self.log_message(f"Installed PythonPing version: {version}")
|
|
except Exception:
|
|
self.log_message("Could not determine PythonPing version")
|
|
|
|
def set_static_ip(self):
|
|
interface = self.if_var.get()
|
|
if not interface:
|
|
self.show_error("Please select a network interface")
|
|
return
|
|
<<<<<<< HEAD
|
|
|
|
# Guardar configuración actual antes de cambiarla
|
|
self.save_previous_config(interface)
|
|
|
|
=======
|
|
|
|
# Guardar configuración actual antes de cambiarla
|
|
self.save_previous_config(interface)
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
# Validar IP
|
|
is_valid, result = self.validate_ip_input()
|
|
if not is_valid:
|
|
self.show_error(result)
|
|
return
|
|
|
|
ip = result
|
|
subnet_mask = self.subnet_mask.get()
|
|
<<<<<<< HEAD
|
|
|
|
# Guardar IP actual en el historial
|
|
self.add_to_history(self.ip_prefix.get())
|
|
|
|
=======
|
|
|
|
# Guardar IP actual en el historial
|
|
self.add_to_history(self.ip_prefix.get())
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
# Log the actual command we'll be executing
|
|
gateway = f"{self.ip_prefix.get()}.1"
|
|
command = f'netsh interface ip set address "{interface}" static {ip} {subnet_mask} {gateway}'
|
|
self.log_message(f"Executing network command: {command}")
|
|
<<<<<<< HEAD
|
|
|
|
# Ejecutar script con privilegios - pass subnet mask
|
|
if self.execute_admin_script(
|
|
interface, ip, debug=True, subnet_mask=subnet_mask
|
|
):
|
|
time.sleep(2) # Esperar a que se apliquen los cambios
|
|
self.refresh_interfaces()
|
|
self.log_message(
|
|
f"Successfully set static IP {ip} with mask {subnet_mask} on {interface}"
|
|
)
|
|
=======
|
|
|
|
# Ejecutar script con privilegios - pass subnet mask
|
|
if self.execute_admin_script(interface, ip, debug=True, subnet_mask=subnet_mask):
|
|
time.sleep(2) # Esperar a que se apliquen los cambios
|
|
self.refresh_interfaces()
|
|
self.log_message(f"Successfully set static IP {ip} with mask {subnet_mask} on {interface}")
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
else:
|
|
self.show_error("Failed to set static IP. Check the log for details.")
|
|
|
|
def set_dhcp(self):
|
|
interface = self.if_var.get()
|
|
if not interface:
|
|
self.show_error("Please select a network interface")
|
|
return
|
|
|
|
# Guardar configuración actual antes de cambiarla
|
|
self.save_previous_config(interface)
|
|
|
|
# Ejecutar script con privilegios
|
|
if self.execute_admin_script(interface, "dhcp", debug=True):
|
|
time.sleep(2) # Esperar a que se apliquen los cambios
|
|
self.refresh_interfaces()
|
|
self.log_message(f"Successfully enabled DHCP on {interface}")
|
|
else:
|
|
self.show_error("Failed to enable DHCP. Check the log for details.")
|
|
|
|
def run_command(self, cmd: str) -> bool:
|
|
self.log_message(f"Executing command: {cmd}")
|
|
try:
|
|
process = subprocess.Popen(
|
|
cmd,
|
|
shell=True,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
universal_newlines=True,
|
|
)
|
|
|
|
while True:
|
|
output = process.stdout.readline()
|
|
if output == "" and process.poll() is not None:
|
|
break
|
|
if output:
|
|
self.log_message(output.strip())
|
|
|
|
retcode = process.poll()
|
|
|
|
error_output = process.stderr.read()
|
|
if error_output:
|
|
self.log_message(f"Error output:\n{error_output}")
|
|
|
|
if retcode == 0:
|
|
self.log_message("Command executed successfully")
|
|
return True
|
|
else:
|
|
self.log_message(f"Command failed with return code: {retcode}")
|
|
return False
|
|
|
|
except Exception as e:
|
|
self.log_message(f"Error executing command: {str(e)}")
|
|
return False
|
|
|
|
def is_admin(self):
|
|
try:
|
|
return ctypes.windll.shell32.IsUserAnAdmin()
|
|
except Exception as e:
|
|
self.log_message(f"Error checking admin privileges: {str(e)}")
|
|
return False
|
|
|
|
def elevate_privileges(self):
|
|
if sys.platform == "win32":
|
|
self.show_info(
|
|
"The application needs administrator privileges to change network settings. "
|
|
"Please confirm the UAC prompt."
|
|
)
|
|
script_path = os.path.abspath(sys.argv[0])
|
|
try:
|
|
ctypes.windll.shell32.ShellExecuteW(
|
|
None, "runas", sys.executable, f'"{script_path}"', None, 1
|
|
)
|
|
self.master.quit()
|
|
except Exception as e:
|
|
self.log_message(f"Error elevating privileges: {str(e)}")
|
|
self.show_error("Failed to elevate privileges")
|
|
|
|
def show_error(self, message: str):
|
|
self.log_message(f"ERROR: {message}")
|
|
messagebox.showerror("Error", message)
|
|
|
|
def show_info(self, message: str):
|
|
self.log_message(f"INFO: {message}")
|
|
messagebox.showinfo("Information", message)
|
|
|
|
# Add new methods for subnet mask conversion
|
|
def on_subnet_mask_changed(self, *args):
|
|
"""Update CIDR bits when subnet mask changes"""
|
|
# Use a static variable to prevent recursion
|
|
if hasattr(self, "_updating_mask") and self._updating_mask:
|
|
return
|
|
<<<<<<< HEAD
|
|
|
|
try:
|
|
self._updating_mask = True
|
|
|
|
=======
|
|
|
|
try:
|
|
self._updating_mask = True
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
mask = self.subnet_mask.get()
|
|
if self.is_valid_subnet_mask(mask):
|
|
# Convert mask to bits
|
|
bits = self.subnet_mask_to_cidr(mask)
|
|
self.cidr_bits.set(str(bits))
|
|
except Exception as e:
|
|
self.log_message(f"Error updating CIDR bits: {str(e)}")
|
|
finally:
|
|
self._updating_mask = False
|
|
|
|
def on_cidr_bits_changed(self, *args):
|
|
"""Update subnet mask when CIDR bits change"""
|
|
# Use a static variable to prevent recursion
|
|
if hasattr(self, "_updating_bits") and self._updating_bits:
|
|
return
|
|
<<<<<<< HEAD
|
|
|
|
try:
|
|
self._updating_bits = True
|
|
|
|
=======
|
|
|
|
try:
|
|
self._updating_bits = True
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
bits_str = self.cidr_bits.get()
|
|
if bits_str.isdigit():
|
|
bits = int(bits_str)
|
|
if 0 <= bits <= 32:
|
|
# Convert bits to mask
|
|
mask = self.cidr_to_subnet_mask(bits)
|
|
self.subnet_mask.set(mask)
|
|
except Exception as e:
|
|
self.log_message(f"Error updating subnet mask: {str(e)}")
|
|
finally:
|
|
self._updating_bits = False
|
|
|
|
def is_valid_subnet_mask(self, mask: str) -> bool:
|
|
"""Validate if the string is a valid subnet mask"""
|
|
try:
|
|
# Check if it has the format x.x.x.x
|
|
<<<<<<< HEAD
|
|
parts = mask.split(".")
|
|
if len(parts) != 4:
|
|
return False
|
|
|
|
=======
|
|
parts = mask.split('.')
|
|
if len(parts) != 4:
|
|
return False
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
# Each part should be a number between 0-255
|
|
for part in parts:
|
|
if not part.isdigit() or not 0 <= int(part) <= 255:
|
|
return False
|
|
<<<<<<< HEAD
|
|
|
|
# Check if it's a valid subnet mask pattern
|
|
# Convert to binary and ensure it's a continuous sequence of 1s followed by 0s
|
|
binary = "".join([bin(int(octet))[2:].zfill(8) for octet in parts])
|
|
if "01" in binary: # If there's a 0 followed by 1, it's not valid
|
|
return False
|
|
|
|
=======
|
|
|
|
# Check if it's a valid subnet mask pattern
|
|
# Convert to binary and ensure it's a continuous sequence of 1s followed by 0s
|
|
binary = ''.join([bin(int(octet))[2:].zfill(8) for octet in parts])
|
|
if '01' in binary: # If there's a 0 followed by 1, it's not valid
|
|
return False
|
|
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
def subnet_mask_to_cidr(self, mask: str) -> int:
|
|
"""Convert subnet mask to CIDR notation bits"""
|
|
try:
|
|
# Convert each octet to binary and count the number of 1s
|
|
<<<<<<< HEAD
|
|
parts = mask.split(".")
|
|
binary = "".join([bin(int(octet))[2:].zfill(8) for octet in parts])
|
|
return binary.count("1")
|
|
=======
|
|
parts = mask.split('.')
|
|
binary = ''.join([bin(int(octet))[2:].zfill(8) for octet in parts])
|
|
return binary.count('1')
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
except Exception as e:
|
|
self.log_message(f"Error converting subnet mask to CIDR: {str(e)}")
|
|
return 24 # Default to /24
|
|
|
|
def cidr_to_subnet_mask(self, bits: int) -> str:
|
|
"""Convert CIDR bits to subnet mask in dotted decimal notation"""
|
|
try:
|
|
# Create a binary string with the specified number of 1s followed by 0s
|
|
<<<<<<< HEAD
|
|
binary = "1" * bits + "0" * (32 - bits)
|
|
|
|
# Split the binary string into 4 octets and convert each to decimal
|
|
octets = [binary[i : i + 8] for i in range(0, 32, 8)]
|
|
decimals = [str(int(octet, 2)) for octet in octets]
|
|
|
|
return ".".join(decimals)
|
|
=======
|
|
binary = '1' * bits + '0' * (32 - bits)
|
|
|
|
# Split the binary string into 4 octets and convert each to decimal
|
|
octets = [binary[i:i+8] for i in range(0, 32, 8)]
|
|
decimals = [str(int(octet, 2)) for octet in octets]
|
|
|
|
return '.'.join(decimals)
|
|
>>>>>>> 60f083413a5d4bfd498d24468d6ca2eaa23a5d9d
|
|
except Exception as e:
|
|
self.log_message(f"Error converting CIDR to subnet mask: {str(e)}")
|
|
return "255.255.255.0" # Default to 255.255.255.0
|
|
|
|
def update_ping_target(self, ip_prefix):
|
|
"""Update the ping target field based on the selected IP prefix"""
|
|
if ip_prefix:
|
|
target = self.config.get_ping_target(ip_prefix)
|
|
self.ping_target.set(target)
|
|
|
|
def save_current_ping_target(self, event=None):
|
|
"""Save the current ping target for the current IP prefix"""
|
|
ip_prefix = self.ip_prefix.get()
|
|
target = self.ping_target.get()
|
|
|
|
if ip_prefix and target:
|
|
self.config.set_ping_target(ip_prefix, target)
|
|
self.log_message(f"Saved ping target {target} for IP prefix {ip_prefix}")
|
|
|
|
|
|
def main():
|
|
root = tk.Tk()
|
|
app = IPChangerApp(root)
|
|
root.mainloop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|