DNS Changer
#!/usr/bin/env python3
"""
DNS Auto Changer - Complete Package
Combines the main application, build tool, and installer in one file.
Usage:
python dns_auto_changer_complete.py # Run the DNS changer application
python dns_auto_changer_complete.py --build # Build executable
python dns_auto_changer_complete.py --install # Install Task Scheduler (for .exe)
"""
import sys
import os
# Check command line arguments to determine mode
if __name__ == "__main__":
if "--build" in sys.argv:
# Build mode - compile to executable
MODE = "BUILD"
elif "--install" in sys.argv or "--install-auto" in sys.argv:
# Install mode - setup Task Scheduler
MODE = "INSTALL"
else:
# Run mode - normal application
MODE = "RUN"
else:
MODE = "RUN"
# ============================================================================
# BUILD MODE - Executable Compiler
# ============================================================================
if MODE == "BUILD":
import subprocess
import shutil
from pathlib import Path
def find_icon_file():
"""Find an image file in the current directory to use as icon"""
image_extensions = ['.png', '.jpg', '.jpeg', '.bmp', '.gif', '.ico']
current_dir = Path('.')
for ext in image_extensions:
for file in current_dir.glob(f'*{ext}'):
if file.is_file():
print(f"Found image file: {file}")
return str(file)
return None
def convert_image_to_ico(image_path):
"""Convert image to .ico format using PIL"""
try:
from PIL import Image
if image_path.lower().endswith('.ico'):
return image_path
img = Image.open(image_path)
icon_sizes = [(256, 256), (128, 128), (64, 64), (48, 48), (32, 32), (16, 16)]
ico_path = "temp_icon.ico"
img.save(ico_path, format='ICO', sizes=icon_sizes)
print(f"✓ Converted {image_path} to {ico_path}")
return ico_path
except ImportError:
print("Warning: PIL/Pillow not available for icon conversion")
print("Install with: pip install pillow")
return None
except Exception as e:
print(f"Warning: Could not convert image to icon: {e}")
return None
def check_pyinstaller():
"""Check if PyInstaller is installed"""
try:
import PyInstaller
return True
except ImportError:
return False
def install_pyinstaller():
"""Install PyInstaller"""
print("PyInstaller is not installed.")
print("Installing PyInstaller...")
try:
subprocess.check_call([sys.executable, "-m", "pip", "install", "pyinstaller"])
print("✓ PyInstaller installed successfully!")
return True
except Exception as e:
print(f"✗ Failed to install PyInstaller: {e}")
return False
def build_exe():
"""Build the executable using PyInstaller"""
try:
print("\n" + "=" * 70)
print("Building DNS Auto Changer executable...")
print("=" * 70)
script_name = os.path.basename(__file__)
if not os.path.exists(script_name):
print(f"✗ ERROR: {script_name} not found in current directory!")
return False
print("\nSearching for icon file...")
icon_file = find_icon_file()
if icon_file:
print(f"✓ Using icon: {icon_file}")
if not icon_file.lower().endswith('.ico'):
print("Converting image to .ico format...")
ico_file = convert_image_to_ico(icon_file)
if ico_file:
icon_file = ico_file
else:
print("Warning: Icon conversion failed, building without icon")
icon_file = None
else:
print("No icon file found in directory")
print("Supported formats: .png, .jpg, .jpeg, .bmp, .gif, .ico")
print("Building without icon...")
cmd = [
"pyinstaller",
"--onefile",
"--windowed",
"--name=DNS_Auto_Changer",
"--clean",
]
if icon_file:
cmd.append(f"--icon={icon_file}")
cmd.append(script_name)
print(f"\nRunning: {' '.join(cmd)}\n")
print("-" * 70)
result = subprocess.run(cmd, capture_output=True, text=True)
if icon_file and icon_file == "temp_icon.ico" and os.path.exists("temp_icon.ico"):
try:
os.remove("temp_icon.ico")
print("\n✓ Cleaned up temporary icon file")
except:
pass
if result.returncode == 0:
print("-" * 70)
print("\n✓ Build completed successfully!")
exe_path = os.path.join("dist", "DNS_Auto_Changer.exe")
if os.path.exists(exe_path):
file_size = os.path.getsize(exe_path) / (1024 * 1024)
print(f"\n✓ Executable created: {exe_path}")
print(f" Size: {file_size:.2f} MB")
if icon_file:
print(f" Icon: Embedded successfully")
cleanup_build_files()
# Auto-install Task Scheduler
print("\n" + "=" * 70)
print("AUTO-INSTALLING TASK SCHEDULER...")
print("=" * 70)
auto_install_task_scheduler(exe_path)
return True
else:
print("\n✗ Executable was not created!")
return False
else:
print("-" * 70)
print("\n✗ Build failed!")
print(f"\nError output:\n{result.stderr}")
return False
except Exception as e:
print(f"\n✗ Build error: {e}")
import traceback
traceback.print_exc()
return False
def auto_install_task_scheduler(exe_path):
"""Automatically install Task Scheduler without user prompts"""
try:
import ctypes
# Check if running as admin
try:
is_admin = ctypes.windll.shell32.IsUserAnAdmin()
except:
is_admin = False
if not is_admin:
print("\n⚠ Not running as administrator - requesting elevation for Task Scheduler setup...")
# Request elevation and run install with the exe path as parameter
try:
# Pass the exe path as an environment variable since we can't pass complex args
import tempfile
temp_path_file = os.path.join(tempfile.gettempdir(), 'dns_changer_exe_path.txt')
with open(temp_path_file, 'w') as f:
f.write(os.path.abspath(exe_path))
ret = ctypes.windll.shell32.ShellExecuteW(
None,
"runas",
sys.executable,
f'"{os.path.abspath(__file__)}" --install-auto',
None,
1
)
if ret > 32:
print("✓ Task Scheduler installation launched with admin privileges")
# Wait a bit for the elevated process to complete
time.sleep(2)
# Check if installation was successful
if os.path.exists(os.path.join(os.path.dirname(os.path.abspath(exe_path)), "Launch_DNS_Changer.bat")):
print("✓ Task Scheduler installation completed successfully")
return True
else:
print("⚠ Task Scheduler installation may still be in progress")
return True
else:
print("✗ Failed to elevate privileges for Task Scheduler installation")
return False
except Exception as e:
print(f"✗ Could not request elevation: {e}")
return False
# If already admin, install directly
abs_exe_path = os.path.abspath(exe_path)
task_name = "DNSAutoChanger"
task_xml = f'''<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Description>DNS Auto Changer - Runs with administrator privileges without UAC prompts</Description>
<Author>{os.getenv('USERNAME')}</Author>
</RegistrationInfo>
<Triggers />
<Principals>
<Principal id="Author">
<UserId>{os.getenv('USERDOMAIN')}\\{os.getenv('USERNAME')}</UserId>
<LogonType>InteractiveToken</LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>"{abs_exe_path}"</Command>
<WorkingDirectory>{os.path.dirname(abs_exe_path)}</WorkingDirectory>
</Exec>
</Actions>
</Task>'''
temp_xml_path = os.path.join(os.environ['TEMP'], 'dns_changer_task.xml')
with open(temp_xml_path, 'w', encoding='utf-16') as f:
f.write(task_xml)
print(f"\nCreating scheduled task: {task_name}")
print(f"Executable path: {abs_exe_path}")
# Delete existing task if it exists
subprocess.run(
['schtasks', '/Delete', '/TN', task_name, '/F'],
capture_output=True,
shell=True
)
# Create new scheduled task
result = subprocess.run(
['schtasks', '/Create', '/TN', task_name, '/XML', temp_xml_path],
capture_output=True,
text=True,
shell=True
)
try:
os.remove(temp_xml_path)
except:
pass
if result.returncode == 0:
print(f"✓ Scheduled task '{task_name}' created successfully!")
# Create smart launcher bat file that auto-updates Task Scheduler path
launcher_path = os.path.join(os.path.dirname(abs_exe_path), "Launch_DNS_Changer.bat")
launcher_content = f'''@echo off
setlocal EnableDelayedExpansion
REM Get the directory where this bat file is located
set "SCRIPT_DIR=%~dp0"
set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
REM Expected exe path
set "EXE_PATH=%SCRIPT_DIR%\\DNS_Auto_Changer.exe"
REM Check if exe exists
if not exist "!EXE_PATH!" (
echo ERROR: DNS_Auto_Changer.exe not found in current directory!
echo Please make sure the exe is in the same folder as this bat file.
pause
exit /b 1
)
REM Check if Task Scheduler task exists
schtasks /Query /TN "{task_name}" >nul 2>&1
if errorlevel 1 (
echo Task Scheduler not configured. Setting up now...
call :UpdateTaskScheduler
goto :Launch
)
REM Get current task path
for /f "tokens=*" %%i in ('schtasks /Query /TN "{task_name}" /FO LIST /V ^| findstr /C:"Task To Run"') do set "TASK_LINE=%%i"
set "TASK_PATH=!TASK_LINE:*Task To Run:=!"
REM Remove leading/trailing spaces and quotes
set "TASK_PATH=!TASK_PATH:~1!"
set "TASK_PATH=!TASK_PATH: =!"
set "TASK_PATH=!TASK_PATH:"=!"
set "EXE_PATH_CLEAN=!EXE_PATH: =!"
set "EXE_PATH_CLEAN=!EXE_PATH_CLEAN:"=!"
REM Compare paths
if not "!TASK_PATH!"=="!EXE_PATH_CLEAN!" (
echo Detected location change. Updating Task Scheduler...
call :UpdateTaskScheduler
)
:Launch
echo Starting DNS Auto Changer...
schtasks /Run /TN "{task_name}" >nul 2>&1
echo DNS Auto Changer has been launched!
timeout /t 2 >nul
exit /b 0
:UpdateTaskScheduler
echo Updating Task Scheduler with current location...
REM Create temporary XML
set "TEMP_XML=%TEMP%\\dns_task_update.xml"
(
echo ^<?xml version="1.0" encoding="UTF-16"?^>
echo ^<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"^>
echo ^<RegistrationInfo^>
echo ^<Description^>DNS Auto Changer - Runs with administrator privileges without UAC prompts^</Description^>
echo ^<Author^>%USERNAME%^</Author^>
echo ^</RegistrationInfo^>
echo ^<Triggers /^>
echo ^<Principals^>
echo ^<Principal id="Author"^>
echo ^<UserId^>%USERDOMAIN%\\%USERNAME%^</UserId^>
echo ^<LogonType^>InteractiveToken^</LogonType^>
echo ^<RunLevel^>HighestAvailable^</RunLevel^>
echo ^</Principal^>
echo ^</Principals^>
echo ^<Settings^>
echo ^<MultipleInstancesPolicy^>IgnoreNew^</MultipleInstancesPolicy^>
echo ^<DisallowStartIfOnBatteries^>false^</DisallowStartIfOnBatteries^>
echo ^<StopIfGoingOnBatteries^>false^</StopIfGoingOnBatteries^>
echo ^<AllowHardTerminate^>true^</AllowHardTerminate^>
echo ^<StartWhenAvailable^>false^</StartWhenAvailable^>
echo ^<RunOnlyIfNetworkAvailable^>false^</RunOnlyIfNetworkAvailable^>
echo ^<AllowStartOnDemand^>true^</AllowStartOnDemand^>
echo ^<Enabled^>true^</Enabled^>
echo ^<Hidden^>false^</Hidden^>
echo ^<RunOnlyIfIdle^>false^</RunOnlyIfIdle^>
echo ^<WakeToRun^>false^</WakeToRun^>
echo ^<ExecutionTimeLimit^>PT0S^</ExecutionTimeLimit^>
echo ^<Priority^>7^</Priority^>
echo ^</Settings^>
echo ^<Actions Context="Author"^>
echo ^<Exec^>
echo ^<Command^>!EXE_PATH!^</Command^>
echo ^<WorkingDirectory^>!SCRIPT_DIR!^</WorkingDirectory^>
echo ^</Exec^>
echo ^</Actions^>
echo ^</Task^>
) > "!TEMP_XML!"
REM Delete old task and create new one
schtasks /Delete /TN "{task_name}" /F >nul 2>&1
schtasks /Create /TN "{task_name}" /XML "!TEMP_XML!" /F >nul 2>&1
if errorlevel 1 (
echo ERROR: Failed to update Task Scheduler.
echo This requires administrator privileges.
echo Please run this batch file as administrator once.
del "!TEMP_XML!" >nul 2>&1
pause
exit /b 1
)
del "!TEMP_XML!" >nul 2>&1
echo Task Scheduler updated successfully!
exit /b 0
'''
with open(launcher_path, 'w') as f:
f.write(launcher_content)
print(f"✓ Smart launcher created: {launcher_path}")
print(f" → Auto-updates Task Scheduler when moved to new location!")
# Auto-create desktop shortcut
try:
desktop_path = os.path.join(os.path.expanduser('~'), 'Desktop')
shortcut_path = os.path.join(desktop_path, 'DNS Auto Changer.lnk')
vbs_script = f'''Set oWS = WScript.CreateObject("WScript.Shell")
Set oLink = oWS.CreateShortcut("{shortcut_path}")
oLink.TargetPath = "{launcher_path}"
oLink.WorkingDirectory = "{os.path.dirname(launcher_path)}"
oLink.Description = "DNS Auto Changer - No UAC"
oLink.Save
'''
vbs_path = os.path.join(os.environ['TEMP'], 'create_shortcut.vbs')
with open(vbs_path, 'w') as f:
f.write(vbs_script)
subprocess.run(['cscript', '//nologo', vbs_path], shell=True, capture_output=True)
os.remove(vbs_path)
print(f"✓ Desktop shortcut created: {shortcut_path}")
except Exception as e:
print(f"⚠ Could not create desktop shortcut: {e}")
return True
else:
print(f"✗ Failed to create scheduled task")
print(f"Error: {result.stderr}")
return False
except Exception as e:
print(f"✗ Error during auto-install: {e}")
import traceback
traceback.print_exc()
return False
def cleanup_build_files():
"""Clean up build artifacts"""
try:
print("\nCleaning up build files...")
if os.path.exists("build"):
shutil.rmtree("build")
print("✓ Removed build directory")
if os.path.exists("DNS_Auto_Changer.spec"):
os.remove("DNS_Auto_Changer.spec")
print("✓ Removed spec file")
except Exception as e:
print(f"Warning: Could not clean up some files: {e}")
def build_main():
print("\n" + "=" * 70)
print("DNS AUTO CHANGER - BUILD TOOL")
print("=" * 70)
print("\nThis script will compile into a standalone executable")
print("(DNS_Auto_Changer.exe)")
print("\n💡 TIP: Place an image file (png, jpg, ico, etc.) in this")
print(" directory to use it as the executable icon!")
print("\n🚀 Auto-install: Task Scheduler will be configured automatically")
print("=" * 70)
if not check_pyinstaller():
print("\nPyInstaller not found. Installing automatically...")
if not install_pyinstaller():
print("\n✗ Build cancelled. Could not install PyInstaller.")
input("\nPress Enter to exit...")
return
print("\n" + "=" * 70)
print("STARTING BUILD PROCESS...")
print("=" * 70)
if build_exe():
print("\n" + "=" * 70)
print("✓ BUILD AND INSTALLATION SUCCESSFUL!")
print("=" * 70)
print("\n📁 Files created:")
print(" • dist\\DNS_Auto_Changer.exe")
print(" • dist\\Launch_DNS_Changer.bat")
print(" • Desktop shortcut (if successful)")
print("\n🎯 How to use:")
print(" 1. Double-click 'Launch_DNS_Changer.bat' (NO UAC!)")
print(" 2. Or use the desktop shortcut")
print(" 3. Or run DNS_Auto_Changer.exe as administrator")
print("\n✅ Task Scheduler configured - no UAC prompts when using launcher!")
print("=" * 70)
else:
print("\n" + "=" * 70)
print("✗ BUILD FAILED")
print("=" * 70)
print("\nPlease check the error messages above.")
input("\nPress Enter to exit...")
# Execute build mode
try:
build_main()
except KeyboardInterrupt:
print("\n\nBuild cancelled by user.")
except Exception as e:
print(f"\n\nUnexpected error: {e}")
import traceback
traceback.print_exc()
sys.exit(0)
# ============================================================================
# INSTALL MODE - Task Scheduler Setup
# ============================================================================
elif MODE == "INSTALL":
import subprocess
import ctypes
import platform
def is_admin():
"""Check if running with administrator privileges"""
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
def run_as_admin():
"""Restart installer with administrator privileges"""
try:
if platform.system() == "Windows":
if getattr(sys, 'frozen', False):
application_path = sys.executable
params = ' '.join(sys.argv[1:])
else:
application_path = sys.executable
script = os.path.abspath(__file__)
params = f'"{script}" --install'
ret = ctypes.windll.shell32.ShellExecuteW(
None,
"runas",
application_path,
params,
None,
1
)
if ret > 32:
sys.exit(0)
else:
return False
return True
except Exception as e:
print(f"Failed to elevate privileges: {e}")
return False
def create_task_scheduler():
"""Create a scheduled task that runs DNS changer .exe with admin privileges"""
try:
# Check if this is auto-install mode (called from build)
import tempfile
temp_path_file = os.path.join(tempfile.gettempdir(), 'dns_changer_exe_path.txt')
if "--install-auto" in sys.argv and os.path.exists(temp_path_file):
# Read exe path from temp file
with open(temp_path_file, 'r') as f:
dns_changer_path = f.read().strip()
try:
os.remove(temp_path_file)
except:
pass
else:
# Manual install mode - look for exe in current directory
dns_changer_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "DNS_Auto_Changer.exe")
if not os.path.exists(dns_changer_path):
print(f"ERROR: DNS_Auto_Changer.exe not found at: {dns_changer_path}")
print("Please make sure DNS_Auto_Changer.exe is in the same directory.")
print(f"Run 'python {os.path.basename(__file__)} --build' to create it first.")
return False
task_name = "DNSAutoChanger"
task_xml = f'''<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Description>DNS Auto Changer - Runs with administrator privileges without UAC prompts</Description>
<Author>{os.getenv('USERNAME')}</Author>
</RegistrationInfo>
<Triggers />
<Principals>
<Principal id="Author">
<UserId>{os.getenv('USERDOMAIN')}\\{os.getenv('USERNAME')}</UserId>
<LogonType>InteractiveToken</LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>"{dns_changer_path}"</Command>
<WorkingDirectory>{os.path.dirname(dns_changer_path)}</WorkingDirectory>
</Exec>
</Actions>
</Task>'''
temp_xml_path = os.path.join(os.environ['TEMP'], 'dns_changer_task.xml')
with open(temp_xml_path, 'w', encoding='utf-16') as f:
f.write(task_xml)
print("=" * 70)
print("DNS Auto Changer - Installation")
print("=" * 70)
print(f"\nExecutable: {dns_changer_path}")
print(f"\nCreating scheduled task: {task_name}")
subprocess.run(
['schtasks', '/Delete', '/TN', task_name, '/F'],
capture_output=True,
shell=True
)
result = subprocess.run(
['schtasks', '/Create', '/TN', task_name, '/XML', temp_xml_path],
capture_output=True,
text=True,
shell=True
)
try:
os.remove(temp_xml_path)
except:
pass
if result.returncode == 0:
print(f"✓ Scheduled task '{task_name}' created successfully!")
print("\n" + "=" * 70)
print("Installation completed successfully!")
print("=" * 70)
create_launcher(task_name)
return True
else:
print(f"✗ Failed to create scheduled task.")
print(f"Error: {result.stderr}")
return False
except Exception as e:
print(f"ERROR: {e}")
import traceback
traceback.print_exc()
return False
def create_launcher(task_name):
"""Create a smart launcher that auto-updates Task Scheduler path"""
try:
launcher_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Launch_DNS_Changer.bat")
launcher_content = f'''@echo off
setlocal EnableDelayedExpansion
REM Get the directory where this bat file is located
set "SCRIPT_DIR=%~dp0"
set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
REM Expected exe path
set "EXE_PATH=%SCRIPT_DIR%\\DNS_Auto_Changer.exe"
REM Check if exe exists
if not exist "!EXE_PATH!" (
echo ERROR: DNS_Auto_Changer.exe not found in current directory!
echo Please make sure the exe is in the same folder as this bat file.
pause
exit /b 1
)
REM Check if Task Scheduler task exists
schtasks /Query /TN "{task_name}" >nul 2>&1
if errorlevel 1 (
echo Task Scheduler not configured. Setting up now...
call :UpdateTaskScheduler
goto :Launch
)
REM Get current task path
for /f "tokens=*" %%i in ('schtasks /Query /TN "{task_name}" /FO LIST /V ^| findstr /C:"Task To Run"') do set "TASK_LINE=%%i"
set "TASK_PATH=!TASK_LINE:*Task To Run:=!"
REM Remove leading/trailing spaces and quotes
set "TASK_PATH=!TASK_PATH:~1!"
set "TASK_PATH=!TASK_PATH: =!"
set "TASK_PATH=!TASK_PATH:"=!"
set "EXE_PATH_CLEAN=!EXE_PATH: =!"
set "EXE_PATH_CLEAN=!EXE_PATH_CLEAN:"=!"
REM Compare paths
if not "!TASK_PATH!"=="!EXE_PATH_CLEAN!" (
echo Detected location change. Updating Task Scheduler...
call :UpdateTaskScheduler
)
:Launch
echo Starting DNS Auto Changer...
schtasks /Run /TN "{task_name}" >nul 2>&1
echo DNS Auto Changer has been launched!
timeout /t 2 >nul
exit /b 0
:UpdateTaskScheduler
echo Updating Task Scheduler with current location...
REM Create temporary XML
set "TEMP_XML=%TEMP%\\dns_task_update.xml"
(
echo ^<?xml version="1.0" encoding="UTF-16"?^>
echo ^<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"^>
echo ^<RegistrationInfo^>
echo ^<Description^>DNS Auto Changer - Runs with administrator privileges without UAC prompts^</Description^>
echo ^<Author^>%USERNAME%^</Author^>
echo ^</RegistrationInfo^>
echo ^<Triggers /^>
echo ^<Principals^>
echo ^<Principal id="Author"^>
echo ^<UserId^>%USERDOMAIN%\\%USERNAME%^</UserId^>
echo ^<LogonType^>InteractiveToken^</LogonType^>
echo ^<RunLevel^>HighestAvailable^</RunLevel^>
echo ^</Principal^>
echo ^</Principals^>
echo ^<Settings^>
echo ^<MultipleInstancesPolicy^>IgnoreNew^</MultipleInstancesPolicy^>
echo ^<DisallowStartIfOnBatteries^>false^</DisallowStartIfOnBatteries^>
echo ^<StopIfGoingOnBatteries^>false^</StopIfGoingOnBatteries^>
echo ^<AllowHardTerminate^>true^</AllowHardTerminate^>
echo ^<StartWhenAvailable^>false^</StartWhenAvailable^>
echo ^<RunOnlyIfNetworkAvailable^>false^</RunOnlyIfNetworkAvailable^>
echo ^<AllowStartOnDemand^>true^</AllowStartOnDemand^>
echo ^<Enabled^>true^</Enabled^>
echo ^<Hidden^>false^</Hidden^>
echo ^<RunOnlyIfIdle^>false^</RunOnlyIfIdle^>
echo ^<WakeToRun^>false^</WakeToRun^>
echo ^<ExecutionTimeLimit^>PT0S^</ExecutionTimeLimit^>
echo ^<Priority^>7^</Priority^>
echo ^</Settings^>
echo ^<Actions Context="Author"^>
echo ^<Exec^>
echo ^<Command^>!EXE_PATH!^</Command^>
echo ^<WorkingDirectory^>!SCRIPT_DIR!^</WorkingDirectory^>
echo ^</Exec^>
echo ^</Actions^>
echo ^</Task^>
) > "!TEMP_XML!"
REM Delete old task and create new one
schtasks /Delete /TN "{task_name}" /F >nul 2>&1
schtasks /Create /TN "{task_name}" /XML "!TEMP_XML!" /F >nul 2>&1
if errorlevel 1 (
echo ERROR: Failed to update Task Scheduler.
echo This requires administrator privileges.
echo Please run this batch file as administrator once.
del "!TEMP_XML!" >nul 2>&1
pause
exit /b 1
)
del "!TEMP_XML!" >nul 2>&1
echo Task Scheduler updated successfully!
exit /b 0
'''
with open(launcher_path, 'w') as f:
f.write(launcher_content)
print(f"\n✓ Smart launcher created: {launcher_path}")
print(" → Auto-updates Task Scheduler when files are moved!")
print("\nUSAGE:")
print("-" * 70)
print("1. Double-click 'Launch_DNS_Changer.bat' (NO UAC in same location)")
print("2. If you move files: first run will ask for admin to update,")
print(" then subsequent runs will work without UAC!")
print("-" * 70)
create_shortcut_option(launcher_path)
except Exception as e:
print(f"Warning: Could not create launcher: {e}")
def create_shortcut_option(launcher_path):
"""Offer to create a desktop shortcut"""
try:
desktop_path = os.path.join(os.path.expanduser('~'), 'Desktop')
shortcut_path = os.path.join(desktop_path, 'DNS Auto Changer.lnk')
print("\n" + "=" * 70)
response = input("Would you like to create a desktop shortcut? (y/n): ").strip().lower()
if response == 'y':
vbs_script = f'''Set oWS = WScript.CreateObject("WScript.Shell")
Set oLink = oWS.CreateShortcut("{shortcut_path}")
oLink.TargetPath = "{launcher_path}"
oLink.WorkingDirectory = "{os.path.dirname(launcher_path)}"
oLink.Description = "DNS Auto Changer - No UAC"
oLink.Save
'''
vbs_path = os.path.join(os.environ['TEMP'], 'create_shortcut.vbs')
with open(vbs_path, 'w') as f:
f.write(vbs_script)
subprocess.run(['cscript', '//nologo', vbs_path], shell=True)
os.remove(vbs_path)
print(f"✓ Desktop shortcut created: {shortcut_path}")
except Exception as e:
print(f"Warning: Could not create desktop shortcut: {e}")
def install_main():
print("\n" + "=" * 70)
print("DNS AUTO CHANGER - INSTALLER")
print("=" * 70)
print("\nThis installer will configure DNS Auto Changer .exe to run with")
print("administrator privileges WITHOUT UAC prompts.")
print("\nPress Ctrl+C to cancel at any time.")
print("=" * 70)
if platform.system() != "Windows":
print("\nERROR: This installer only works on Windows.")
input("\nPress Enter to exit...")
return
if not is_admin():
print("\nAdministrator privileges required for installation.")
print("Requesting elevation...")
if not run_as_admin():
print("\nERROR: Failed to obtain administrator privileges.")
input("\nPress Enter to exit...")
return
return
print("\n✓ Running with administrator privileges")
if create_task_scheduler():
print("\n" + "=" * 70)
print("INSTALLATION SUCCESSFUL!")
print("=" * 70)
print("\nYou can now launch DNS Auto Changer without UAC prompts by:")
print(" • Double-clicking 'Launch_DNS_Changer.bat'")
print(" • Using the desktop shortcut (if created)")
else:
print("\n" + "=" * 70)
print("INSTALLATION FAILED")
print("=" * 70)
print("\nPlease check the error messages above.")
input("\nPress Enter to exit...")
# Execute install mode
try:
install_main()
except KeyboardInterrupt:
print("\n\nInstallation cancelled by user.")
input("\nPress Enter to exit...")
except Exception as e:
print(f"\n\nUnexpected error: {e}")
import traceback
traceback.print_exc()
input("\nPress Enter to exit...")
sys.exit(0)
# ============================================================================
# RUN MODE - Main DNS Changer Application
# ============================================================================
# All the imports for the main application
import tkinter as tk
from tkinter import ttk, messagebox
import subprocess
import platform
import threading
import time
import ctypes
import json
try:
from PIL import Image, ImageDraw
import pystray
TRAY_AVAILABLE = True
except ImportError:
TRAY_AVAILABLE = False
print("Warning: pystray or PIL not available. System tray functionality disabled.")
print("Install with: pip install pystray pillow")
# Continue with dns_changer.py code...
class DNSChangerApp:
def __init__(self, root, start_as_icon=False):
self.root = root
self.root.title("DNS Auto Changer")
self.root.geometry("600x700")
self.root.resizable(False, False)
# Configuration file path (same directory as executable/script)
if getattr(sys, 'frozen', False):
self.config_path = os.path.join(os.path.dirname(sys.executable), 'dns_changer_config.json')
else:
self.config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dns_changer_config.json')
# Predefined DNS servers (must be defined BEFORE load_config)
self.predefined_dns = {
"Yandex DNS": ("77.88.8.1", "77.88.8.8"),
"Level3 DNS 1": ("209.244.0.3", "209.244.0.4"),
"Level3 DNS 2": ("4.2.2.1", "4.2.2.2"),
"Level3 DNS 3": ("4.2.2.3", "4.2.2.4"),
"Level3 DNS 4": ("4.2.2.5", "4.2.2.6"), # Virgola aggiunta
"UltraDNS": ("204.69.234.1", "204.74.101.1"),
"Censurfridns": ("89.233.43.71", "91.239.100.100"),
"OpenDNS A": ("208.67.222.222", "208.67.220.220"),
"OpenDNS B": ("208.67.222.220", "208.67.220.222"),
"Safe DNS": ("195.46.39.39", "195.46.39.40"),
"Cloudflare DNS": ("1.1.1.1", "1.0.0.1")
}
# Predefined time intervals (in seconds)
self.predefined_intervals = { # Questa linea deve essere correttamente indentata
"5 seconds": 5,
"15 seconds": 15,
"30 seconds": 30,
"1 minute": 60,
"3 minutes": 180,
"5 minutes": 300,
"15 minutes": 900,
"30 minutes": 1800,
"45 minutes": 2700
}
# Load configuration
self.config = self.load_config()
# System tray variables
self.tray_icon = None
self.start_as_icon_var = tk.BooleanVar(value=start_as_icon or self.config.get('start_as_icon', False))
self.is_minimized_to_tray = False
# Auto start variable
self.auto_start_changer_var = tk.BooleanVar(value=self.config.get('auto_start_changer', False))
# Variables
self.is_running = False
self.timer_thread = None
self.remaining_time = 0
# Custom DNS list (merge predefined with user-added)
self.custom_dns_list = self.config.get('custom_dns_list', {})
# Rotation settings
self.rotation_mode_var = tk.StringVar(value=self.config.get('rotation_mode', 'single'))
self.current_rotation_index = 0
# Initialize dns_checkboxes with saved selections
saved_selections = self.config.get('selected_dns_for_rotation', [])
self.dns_checkboxes = {name: tk.BooleanVar(value=(name in saved_selections))
for name in self.get_all_dns().keys()}
self.setup_ui()
def get_all_dns(self):
"""Get combined dictionary of predefined and custom DNS"""
all_dns = self.predefined_dns.copy()
all_dns.update(self.custom_dns_list)
return all_dns
def update_dns_combo(self):
"""Update DNS combobox with all available DNS"""
all_dns = self.get_all_dns()
self.dns_combo['values'] = list(all_dns.keys())
current = self.dns_combo.get()
if current not in all_dns:
if all_dns:
self.dns_combo.current(0)
def load_config(self):
"""Load configuration from file"""
default_config = {
'start_as_icon': False,
'selected_dns': 'Yandex DNS',
'primary_dns': '77.88.8.1',
'secondary_dns': '77.88.8.8',
'interval_preset': '1 minute',
'custom_minutes': 0,
'custom_seconds': 10,
'custom_dns_list': {},
'rotation_mode': 'single',
'selected_dns_for_rotation': [],
'auto_start_changer': False
}
try:
if os.path.exists(self.config_path):
with open(self.config_path, 'r') as f:
loaded_config = json.load(f)
default_config.update(loaded_config)
print(f"Configuration loaded from: {self.config_path}")
else:
print(f"No configuration file found. Using defaults.")
except Exception as e:
print(f"Error loading configuration: {e}")
print("Using default configuration.")
return default_config
def save_config(self):
"""Save current configuration to file"""
try:
config = {
'start_as_icon': self.start_as_icon_var.get(),
'selected_dns': self.dns_combo.get(),
'primary_dns': self.primary_dns.get(),
'secondary_dns': self.secondary_dns.get(),
'interval_preset': self.interval_combo.get(),
'custom_minutes': int(self.minutes_var.get()),
'custom_seconds': int(self.seconds_var.get()),
'custom_dns_list': self.custom_dns_list,
'rotation_mode': self.rotation_mode_var.get(),
'selected_dns_for_rotation': self.get_selected_dns_for_rotation(),
'auto_start_changer': self.auto_start_changer_var.get()
}
with open(self.config_path, 'w') as f:
json.dump(config, f, indent=4)
print(f"Configuration saved to: {self.config_path}")
return True
except Exception as e:
print(f"Error saving configuration: {e}")
return False
def setup_ui(self):
"""Setup the user interface"""
# Main frame
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# DNS Selection Section
dns_frame = ttk.LabelFrame(main_frame, text="DNS Server Selection", padding="10")
dns_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)
# Predefined DNS
ttk.Label(dns_frame, text="Predefined DNS:").grid(row=0, column=0, sticky=tk.W, pady=5)
self.dns_combo = ttk.Combobox(dns_frame, values=list(self.get_all_dns().keys()),
state="readonly", width=30)
self.dns_combo.grid(row=0, column=1, padx=5, pady=5)
saved_dns = self.config.get('selected_dns', 'Yandex DNS')
if saved_dns in self.get_all_dns().keys():
self.dns_combo.set(saved_dns)
else:
self.dns_combo.current(0)
self.dns_combo.bind("<<ComboboxSelected>>", self.on_dns_selected)
# Add new DNS button
add_dns_btn = ttk.Button(dns_frame, text="Add new DNS", command=self.open_add_dns_dialog, width=15)
add_dns_btn.grid(row=0, column=2, padx=5, pady=5)
# Custom DNS
ttk.Label(dns_frame, text="Primary DNS:").grid(row=1, column=0, sticky=tk.W, pady=5)
self.primary_dns = ttk.Entry(dns_frame, width=32)
self.primary_dns.grid(row=1, column=1, padx=5, pady=5)
self.primary_dns.insert(0, self.config.get('primary_dns', '77.88.8.1'))
ttk.Label(dns_frame, text="Secondary DNS:").grid(row=2, column=0, sticky=tk.W, pady=5)
self.secondary_dns = ttk.Entry(dns_frame, width=32)
self.secondary_dns.grid(row=2, column=1, padx=5, pady=5)
self.secondary_dns.insert(0, self.config.get('secondary_dns', '77.88.8.8'))
# Time Interval Section
interval_frame = ttk.LabelFrame(main_frame, text="Time Interval", padding="10")
interval_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)
ttk.Label(interval_frame, text="Predefined:").grid(row=0, column=0, sticky=tk.W, pady=5)
self.interval_combo = ttk.Combobox(interval_frame,
values=list(self.predefined_intervals.keys()),
state="readonly", width=30)
self.interval_combo.grid(row=0, column=1, padx=5, pady=5)
saved_interval = self.config.get('interval_preset', '1 minute')
if saved_interval in self.predefined_intervals.keys():
self.interval_combo.set(saved_interval)
else:
self.interval_combo.current(3)
self.interval_combo.bind("<<ComboboxSelected>>", self.on_interval_selected)
custom_frame = ttk.Frame(interval_frame)
custom_frame.grid(row=1, column=0, columnspan=2, pady=5)
ttk.Label(custom_frame, text="Custom (Min:Sec):").pack(side=tk.LEFT, padx=5)
self.minutes_var = tk.StringVar(value=str(self.config.get('custom_minutes', 0)))
minutes_spinbox = ttk.Spinbox(custom_frame, from_=0, to=999, width=5,
textvariable=self.minutes_var)
minutes_spinbox.pack(side=tk.LEFT, padx=2)
ttk.Label(custom_frame, text=":").pack(side=tk.LEFT)
self.seconds_var = tk.StringVar(value=str(self.config.get('custom_seconds', 10)))
seconds_spinbox = ttk.Spinbox(custom_frame, from_=0, to=59, width=5,
textvariable=self.seconds_var)
seconds_spinbox.pack(side=tk.LEFT, padx=2)
# DNS Rotation Mode Section
rotation_frame = ttk.LabelFrame(main_frame, text="DNS Rotation Mode", padding="10")
rotation_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)
mode_frame = ttk.Frame(rotation_frame)
mode_frame.grid(row=0, column=0, columnspan=2, sticky=tk.W, pady=5)
ttk.Radiobutton(mode_frame, text="Single DNS", variable=self.rotation_mode_var,
value="single", command=self.on_rotation_mode_changed).pack(side=tk.LEFT, padx=5)
ttk.Radiobutton(mode_frame, text="Sequential Rotation", variable=self.rotation_mode_var,
value="sequential", command=self.on_rotation_mode_changed).pack(side=tk.LEFT, padx=5)
ttk.Radiobutton(mode_frame, text="Random Rotation", variable=self.rotation_mode_var,
value="random", command=self.on_rotation_mode_changed).pack(side=tk.LEFT, padx=5)
manage_btn = ttk.Button(rotation_frame, text="Manage DNS Selection",
command=self.open_dns_selection_dialog, width=20)
manage_btn.grid(row=1, column=0, columnspan=2, pady=5)
# Control buttons
control_frame = ttk.Frame(main_frame)
control_frame.grid(row=3, column=0, columnspan=2, pady=10)
self.start_btn = ttk.Button(control_frame, text="Start", command=self.start_timer, width=15)
self.start_btn.pack(side=tk.LEFT, padx=5)
self.stop_btn = ttk.Button(control_frame, text="Stop", command=self.stop_timer,
width=15, state=tk.DISABLED)
self.stop_btn.pack(side=tk.LEFT, padx=5)
self.apply_now_btn = ttk.Button(control_frame, text="Apply DNS Now",
command=self.apply_dns_now, width=15)
self.apply_now_btn.pack(side=tk.LEFT, padx=5)
# System Tray Controls
tray_frame = ttk.Frame(main_frame)
tray_frame.grid(row=4, column=0, columnspan=2, pady=5)
if TRAY_AVAILABLE:
self.reduce_icon_btn = ttk.Button(tray_frame, text="Reduce as Icon",
command=self.minimize_to_tray, width=15)
self.reduce_icon_btn.pack(side=tk.LEFT, padx=5)
start_as_icon_check = ttk.Checkbutton(tray_frame, text="Start as Icon",
variable=self.start_as_icon_var,
command=self.on_start_as_icon_changed)
start_as_icon_check.pack(side=tk.LEFT, padx=5)
else:
info_label = ttk.Label(tray_frame, text="System tray disabled (install pystray & pillow)",
foreground="gray")
info_label.pack(side=tk.LEFT, padx=5)
auto_start_check = ttk.Checkbutton(tray_frame, text="Auto Start Changer",
variable=self.auto_start_changer_var,
command=self.on_auto_start_changed)
auto_start_check.pack(side=tk.LEFT, padx=5)
# Status Section
status_frame = ttk.LabelFrame(main_frame, text="Status", padding="10")
status_frame.grid(row=5, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)
self.status_label = ttk.Label(status_frame, text="Status: Ready", foreground="blue")
self.status_label.pack(anchor=tk.W)
self.timer_label = ttk.Label(status_frame, text="Next change in: --:--", foreground="green")
self.timer_label.pack(anchor=tk.W, pady=5)
# Log Section
log_frame = ttk.LabelFrame(main_frame, text="Log", padding="5")
log_frame.grid(row=6, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
scrollbar = ttk.Scrollbar(log_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.log_text = tk.Text(log_frame, height=8, width=70, yscrollcommand=scrollbar.set)
self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=self.log_text.yview)
# Initialize with first DNS
self.on_dns_selected(None)
# Set up window protocol for close button
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
# Start as icon if requested
if self.start_as_icon_var.get() and TRAY_AVAILABLE:
self.root.after(100, self.minimize_to_tray)
# Auto start changer if enabled
if self.auto_start_changer_var.get():
self.root.after(500, self.start_timer)
self.log("Auto Start Changer: Enabled - Timer will start automatically")
def create_tray_image(self):
"""Create an image for the system tray icon"""
width = 64
height = 64
image = Image.new('RGB', (width, height), color='white')
draw = ImageDraw.Draw(image)
draw.ellipse([4, 4, width-4, height-4], fill='#2ecc71', outline='#27ae60', width=2)
draw.text((width//2 - 10, height//2 - 12), 'D', fill='white')
return image
def create_tray_menu(self):
"""Create the system tray menu"""
return pystray.Menu(
pystray.MenuItem("Show", self.show_window, default=True),
pystray.MenuItem("Apply DNS Now", self.tray_apply_dns),
pystray.MenuItem("Start Auto-Change", self.tray_start_timer,
visible=lambda item: not self.is_running),
pystray.MenuItem("Stop Auto-Change", self.tray_stop_timer,
visible=lambda item: self.is_running),
pystray.Menu.SEPARATOR,
pystray.MenuItem("Exit", self.tray_exit)
)
def minimize_to_tray(self):
"""Minimize the window to system tray"""
if not TRAY_AVAILABLE:
messagebox.showwarning("Not Available",
"System tray functionality requires pystray and pillow.\n"
"Install with: pip install pystray pillow")
return
self.is_minimized_to_tray = True
self.root.withdraw()
if self.tray_icon is None:
image = self.create_tray_image()
self.tray_icon = pystray.Icon(
"DNS_Auto_Changer",
image,
"DNS Auto Changer",
menu=self.create_tray_menu()
)
tray_thread = threading.Thread(target=self.tray_icon.run, daemon=True)
tray_thread.start()
self.log("Minimized to system tray")
def show_window(self, icon=None, item=None):
"""Show the window from system tray"""
self.is_minimized_to_tray = False
self.root.deiconify()
self.root.lift()
self.root.focus_force()
self.log("Restored from system tray")
def tray_apply_dns(self, icon=None, item=None):
"""Apply DNS from tray menu"""
self.root.after(0, self.apply_dns_now)
def tray_start_timer(self, icon=None, item=None):
"""Start timer from tray menu"""
self.root.after(0, self.start_timer)
def tray_stop_timer(self, icon=None, item=None):
"""Stop timer from tray menu"""
self.root.after(0, self.stop_timer)
def tray_exit(self, icon=None, item=None):
"""Exit application from tray menu"""
if self.tray_icon:
self.tray_icon.stop()
self.root.after(0, self.root.quit)
def on_closing(self):
"""Handle window close button"""
self.save_config()
if TRAY_AVAILABLE and messagebox.askyesno("Minimize to Tray?",
"Minimize to system tray instead of closing?\n\n"
"Click 'No' to exit completely."):
self.minimize_to_tray()
else:
if self.tray_icon:
self.tray_icon.stop()
self.root.quit()
def on_dns_selected(self, event):
"""Update custom DNS fields when predefined DNS is selected"""
selected = self.dns_combo.get()
all_dns = self.get_all_dns()
if selected in all_dns:
primary, secondary = all_dns[selected]
self.primary_dns.delete(0, tk.END)
self.primary_dns.insert(0, primary)
self.secondary_dns.delete(0, tk.END)
self.secondary_dns.insert(0, secondary)
self.save_config()
def on_interval_selected(self, event):
"""Update custom time when predefined interval is selected"""
selected = self.interval_combo.get()
if selected in self.predefined_intervals:
total_seconds = self.predefined_intervals[selected]
minutes = total_seconds // 60
seconds = total_seconds % 60
self.minutes_var.set(str(minutes))
self.seconds_var.set(str(seconds))
self.save_config()
def on_start_as_icon_changed(self):
"""Handle start as icon checkbox change"""
self.save_config()
self.log(f"Start as Icon: {'Enabled' if self.start_as_icon_var.get() else 'Disabled'}")
def on_auto_start_changed(self):
"""Handle auto start changer checkbox change"""
self.save_config()
self.log(f"Auto Start Changer: {'Enabled' if self.auto_start_changer_var.get() else 'Disabled'}")
def on_rotation_mode_changed(self):
"""Handle rotation mode change"""
mode = self.rotation_mode_var.get()
mode_names = {'single': 'Single DNS', 'sequential': 'Sequential Rotation', 'random': 'Random Rotation'}
self.log(f"Rotation mode changed to: {mode_names.get(mode, mode)}")
self.save_config()
def open_add_dns_dialog(self):
"""Open dialog to add a new custom DNS"""
dialog = tk.Toplevel(self.root)
dialog.title("Add New DNS")
dialog.geometry("400x200")
dialog.resizable(False, False)
dialog.transient(self.root)
dialog.grab_set()
dialog.update_idletasks()
x = self.root.winfo_x() + (self.root.winfo_width() // 2) - (dialog.winfo_width() // 2)
y = self.root.winfo_y() + (self.root.winfo_height() // 2) - (dialog.winfo_height() // 2)
dialog.geometry(f"+{x}+{y}")
ttk.Label(dialog, text="DNS Name:").grid(row=0, column=0, padx=10, pady=10, sticky=tk.W)
name_entry = ttk.Entry(dialog, width=30)
name_entry.grid(row=0, column=1, padx=10, pady=10)
name_entry.focus()
ttk.Label(dialog, text="Primary DNS:").grid(row=1, column=0, padx=10, pady=10, sticky=tk.W)
primary_entry = ttk.Entry(dialog, width=30)
primary_entry.grid(row=1, column=1, padx=10, pady=10)
ttk.Label(dialog, text="Secondary DNS:").grid(row=2, column=0, padx=10, pady=10, sticky=tk.W)
secondary_entry = ttk.Entry(dialog, width=30)
secondary_entry.grid(row=2, column=1, padx=10, pady=10)
def on_ok():
name = name_entry.get().strip()
primary = primary_entry.get().strip()
secondary = secondary_entry.get().strip()
if not name:
messagebox.showerror("Error", "DNS name cannot be empty", parent=dialog)
return
if not primary or not secondary:
messagebox.showerror("Error", "Both DNS addresses must be specified", parent=dialog)
return
if name in self.get_all_dns():
messagebox.showerror("Error", f"DNS with name '{name}' already exists", parent=dialog)
return
self.custom_dns_list[name] = (primary, secondary)
self.update_dns_combo()
self.dns_combo.set(name)
self.on_dns_selected(None)
self.save_config()
self.log(f"Added new DNS: {name} ({primary}, {secondary})")
dialog.destroy()
def on_cancel():
dialog.destroy()
button_frame = ttk.Frame(dialog)
button_frame.grid(row=3, column=0, columnspan=2, pady=20)
ttk.Button(button_frame, text="OK", command=on_ok, width=10).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="Cancel", command=on_cancel, width=10).pack(side=tk.LEFT, padx=5)
dialog.bind('<Return>', lambda e: on_ok())
dialog.bind('<Escape>', lambda e: on_cancel())
def open_dns_selection_dialog(self):
"""Open dialog to select DNS servers for rotation"""
dialog = tk.Toplevel(self.root)
dialog.title("Select DNS for Rotation")
dialog.geometry("400x400")
dialog.resizable(False, False)
dialog.transient(self.root)
dialog.grab_set()
dialog.update_idletasks()
x = self.root.winfo_x() + (self.root.winfo_width() // 2) - (dialog.winfo_width() // 2)
y = self.root.winfo_y() + (self.root.winfo_height() // 2) - (dialog.winfo_height() // 2)
dialog.geometry(f"+{x}+{y}")
ttk.Label(dialog, text="Select DNS servers to include in rotation:",
font=('Arial', 10, 'bold')).pack(pady=10)
canvas = tk.Canvas(dialog, height=250)
scrollbar = ttk.Scrollbar(dialog, orient="vertical", command=canvas.yview)
scrollable_frame = ttk.Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
canvas.pack(side="left", fill="both", expand=True, padx=10)
scrollbar.pack(side="right", fill="y")
all_dns = self.get_all_dns()
for dns_name in all_dns.keys():
if dns_name not in self.dns_checkboxes:
self.dns_checkboxes[dns_name] = tk.BooleanVar(value=False)
for dns_name in sorted(all_dns.keys()):
var = self.dns_checkboxes[dns_name]
primary, secondary = all_dns[dns_name]
ttk.Checkbutton(scrollable_frame,
text=f"{dns_name} ({primary})",
variable=var).pack(anchor=tk.W, padx=10, pady=2)
def on_ok():
selected = self.get_selected_dns_for_rotation()
if self.rotation_mode_var.get() != 'single' and len(selected) < 2:
messagebox.showwarning("Warning",
"Please select at least 2 DNS servers for rotation mode",
parent=dialog)
return
self.save_config()
mode = self.rotation_mode_var.get()
if mode == 'single':
self.log(f"DNS selection saved (not used in Single DNS mode)")
else:
self.log(f"Selected {len(selected)} DNS for rotation: {', '.join(selected)}")
dialog.destroy()
def on_cancel():
dialog.destroy()
button_frame = ttk.Frame(dialog)
button_frame.pack(pady=10)
ttk.Button(button_frame, text="OK", command=on_ok, width=10).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="Cancel", command=on_cancel, width=10).pack(side=tk.LEFT, padx=5)
dialog.bind('<Return>', lambda e: on_ok())
dialog.bind('<Escape>', lambda e: on_cancel())
def get_selected_dns_for_rotation(self):
"""Get list of DNS names selected for rotation"""
if not hasattr(self, 'dns_checkboxes') or not self.dns_checkboxes:
return self.config.get('selected_dns_for_rotation', [])
selected = [name for name, var in self.dns_checkboxes.items() if var.get()]
return selected
def get_next_dns_for_rotation(self):
"""Get the next DNS to apply based on rotation mode"""
mode = self.rotation_mode_var.get()
if mode == 'single':
selected = self.dns_combo.get()
all_dns = self.get_all_dns()
if selected in all_dns:
return selected, all_dns[selected]
return None, None
selected_dns_list = self.get_selected_dns_for_rotation()
if not selected_dns_list:
self.log("Warning: No DNS selected for rotation. Using current selection.")
selected = self.dns_combo.get()
all_dns = self.get_all_dns()
if selected in all_dns:
return selected, all_dns[selected]
return None, None
all_dns = self.get_all_dns()
if mode == 'sequential':
dns_name = selected_dns_list[self.current_rotation_index % len(selected_dns_list)]
self.current_rotation_index += 1
return dns_name, all_dns[dns_name]
elif mode == 'random':
import random
dns_name = random.choice(selected_dns_list)
return dns_name, all_dns[dns_name]
return None, None
def get_interval_seconds(self):
"""Get the total interval in seconds from custom input"""
try:
minutes = int(self.minutes_var.get())
seconds = int(self.seconds_var.get())
total = minutes * 60 + seconds
if total <= 0:
raise ValueError("Interval must be greater than 0")
return total
except ValueError as e:
messagebox.showerror("Error", f"Invalid time interval: {e}")
return None
def log(self, message):
"""Add message to log (both GUI and console)"""
timestamp = time.strftime("%H:%M:%S")
log_message = f"[{timestamp}] {message}"
self.log_text.insert(tk.END, f"{log_message}\n")
self.log_text.see(tk.END)
self.root.update()
print(log_message)
def get_windows_interfaces(self):
"""Get all network interfaces on Windows"""
try:
print("\n--- Scanning for network interfaces ---")
result = subprocess.run(
['netsh', 'interface', 'ip', 'show', 'config'],
capture_output=True, text=True, shell=True, encoding='utf-8'
)
interfaces = []
lines = result.stdout.split('\n')
for line in lines:
if 'Configuration for interface' in line or 'Configurazione per' in line:
if '"' in line:
start = line.index('"') + 1
end = line.index('"', start)
interface_name = line[start:end]
interfaces.append(interface_name)
print(f" Found interface: {interface_name}")
if not interfaces:
print(" No interfaces found with IP config, trying alternative method...")
result = subprocess.run(
['netsh', 'interface', 'show', 'interface'],
capture_output=True, text=True, shell=True, encoding='utf-8'
)
lines = result.stdout.split('\n')
for line in lines[3:]:
if line.strip():
parts = line.split()
if len(parts) >= 4:
name = ' '.join(parts[3:])
interfaces.append(name)
print(f" Found interface: {name}")
print(f"--- Total interfaces found: {len(interfaces)} ---\n")
return interfaces
except Exception as e:
error_msg = f"Error getting interfaces: {e}"
self.log(error_msg)
print(f"ERROR: {error_msg}")
return []
def set_dns_windows(self, primary, secondary):
"""Set DNS on Windows for all interfaces"""
print(f"\n{'='*60}")
print(f"Starting DNS application process")
print(f"Primary DNS: {primary}")
print(f"Secondary DNS: {secondary}")
print(f"{'='*60}\n")
interfaces = self.get_windows_interfaces()
if not interfaces:
msg = "No network interfaces found"
self.log(msg)
print(f"ERROR: {msg}")
return False
self.log(f"Found {len(interfaces)} network interface(s)")
success_count = 0
failed_count = 0
for i, interface in enumerate(interfaces, 1):
try:
print(f"\n[{i}/{len(interfaces)}] Processing: {interface}")
self.log(f"Applying DNS to: {interface}")
print(f" → Setting primary DNS ({primary})...")
result1 = subprocess.run(
['netsh', 'interface', 'ip', 'set', 'dns', f'name={interface}', 'static', primary],
capture_output=True, text=True, shell=True, encoding='utf-8'
)
print(f" → Setting secondary DNS ({secondary})...")
result2 = subprocess.run(
['netsh', 'interface', 'ip', 'add', 'dns', f'name={interface}', secondary, 'index=2'],
capture_output=True, text=True, shell=True, encoding='utf-8'
)
if result1.returncode == 0 or result2.returncode == 0:
success_msg = f" ✓ Success on {interface}"
self.log(success_msg)
print(f" ✓ SUCCESS")
success_count += 1
else:
fail_msg = f" ✗ Failed on {interface}"
self.log(fail_msg)
print(f" ✗ FAILED")
if result1.stderr:
print(f" Error: {result1.stderr.strip()}")
if result2.stderr:
print(f" Error: {result2.stderr.strip()}")
failed_count += 1
except Exception as e:
error_msg = f" ✗ Error on {interface}: {e}"
self.log(error_msg)
print(f" ✗ EXCEPTION: {e}")
failed_count += 1
summary = f"DNS application completed: {success_count} succeeded, {failed_count} failed"
self.log(summary)
print(f"\n{'='*60}")
print(f"SUMMARY: {success_count} succeeded, {failed_count} failed")
print(f"{'='*60}\n")
return success_count > 0
def set_dns_linux(self, primary, secondary):
"""Set DNS on Linux"""
try:
dns_config = f"nameserver {primary}\nnameserver {secondary}\n"
with open('/etc/resolv.conf', 'w') as f:
f.write(dns_config)
return True
except PermissionError:
self.log("Error: Need root/sudo permissions to change DNS on Linux")
return False
except Exception as e:
self.log(f"Error setting DNS on Linux: {e}")
return False
def set_dns_mac(self, primary, secondary):
"""Set DNS on macOS"""
try:
result = subprocess.run(
['networksetup', '-listallnetworkservices'],
capture_output=True, text=True
)
services = [s for s in result.stdout.split('\n') if s and not s.startswith('*')]
if services:
service = services[0]
subprocess.run(
['networksetup', '-setdnsservers', service, primary, secondary],
check=True
)
return True
else:
self.log("No network service found")
return False
except Exception as e:
self.log(f"Error setting DNS on macOS: {e}")
return False
def apply_dns(self):
"""Apply DNS settings based on OS and rotation mode"""
mode = self.rotation_mode_var.get()
if mode == 'single':
primary = self.primary_dns.get().strip()
secondary = self.secondary_dns.get().strip()
dns_name = self.dns_combo.get()
else:
dns_name, dns_servers = self.get_next_dns_for_rotation()
if dns_servers is None:
self.log("Error: Could not determine DNS for rotation")
return False
primary, secondary = dns_servers
if not primary or not secondary:
self.log("Error: Both primary and secondary DNS must be specified")
return False
system = platform.system()
self.log(f"Applying DNS: {dns_name} ({primary}, {secondary})")
success = False
if system == "Windows":
success = self.set_dns_windows(primary, secondary)
elif system == "Linux":
success = self.set_dns_linux(primary, secondary)
elif system == "Darwin":
success = self.set_dns_mac(primary, secondary)
else:
self.log(f"Unsupported operating system: {system}")
return False
if success:
self.log("DNS applied successfully!")
self.status_label.config(text=f"Status: DNS Applied ({dns_name})", foreground="green")
else:
self.status_label.config(text="Status: DNS Application Failed", foreground="red")
return success
def apply_dns_now(self):
"""Apply DNS immediately without starting timer"""
self.apply_dns()
def timer_worker(self, interval):
"""Worker thread for timer"""
while self.is_running:
self.apply_dns()
self.remaining_time = interval
while self.remaining_time > 0 and self.is_running:
minutes = self.remaining_time // 60
seconds = self.remaining_time % 60
self.timer_label.config(text=f"Next change in: {minutes:02d}:{seconds:02d}")
self.root.update()
time.sleep(1)
self.remaining_time -= 1
def start_timer(self):
"""Start the DNS auto-change timer"""
interval = self.get_interval_seconds()
if interval is None:
return
self.is_running = True
self.start_btn.config(state=tk.DISABLED)
self.stop_btn.config(state=tk.NORMAL)
self.status_label.config(text="Status: Running", foreground="green")
self.log(f"Started auto-change with {interval} seconds interval")
self.timer_thread = threading.Thread(target=self.timer_worker, args=(interval,), daemon=True)
self.timer_thread.start()
def stop_timer(self):
"""Stop the DNS auto-change timer"""
self.is_running = False
self.start_btn.config(state=tk.NORMAL)
self.stop_btn.config(state=tk.DISABLED)
self.status_label.config(text="Status: Stopped", foreground="orange")
self.timer_label.config(text="Next change in: --:--")
self.log("Stopped auto-change")
def is_admin():
"""Check if the program is running with administrator privileges"""
try:
if platform.system() == "Windows":
return ctypes.windll.shell32.IsUserAnAdmin()
else:
return os.geteuid() == 0
except:
return False
def run_as_admin():
"""Restart the program with administrator privileges"""
try:
if platform.system() == "Windows":
if getattr(sys, 'frozen', False):
application_path = sys.executable
params = ' '.join(sys.argv[1:])
else:
application_path = sys.executable
script = os.path.abspath(__file__)
params = f'"{script}"' + ' '.join(sys.argv[1:])
ret = ctypes.windll.shell32.ShellExecuteW(
None,
"runas",
application_path,
params,
None,
1
)
if ret > 32:
sys.exit(0)
else:
return False
else:
print("Please run this program with sudo:")
print(f"sudo python3 {sys.argv[0]}")
sys.exit(1)
return True
except Exception as e:
print(f"Failed to elevate privileges: {e}")
messagebox.showerror("Error",
"Failed to request administrator privileges.\n"
"Please run the program as administrator manually.\n\n"
f"Error: {e}")
return False
def main():
if not is_admin():
print("=" * 60)
print("DNS Auto Changer Tool")
print("=" * 60)
print("Administrator privileges required.")
print("Requesting elevation...")
print("=" * 60)
if not run_as_admin():
print("\nFailed to obtain administrator privileges.")
print("Please run the program manually as administrator.")
input("\nPress Enter to exit...")
sys.exit(1)
return
print("=" * 60)
print("DNS Auto Changer Tool")
print("Running with administrator privileges ✓")
print("=" * 60)
print("Starting application...")
print("All operations will be logged below:")
print("-" * 60)
start_as_icon = "--tray" in sys.argv or "--icon" in sys.argv
root = tk.Tk()
app = DNSChangerApp(root, start_as_icon=start_as_icon)
root.mainloop()
if __name__ == "__main__":
main()
Commenti
Posta un commento