Skip to content

Commit dfdc9f1

Browse files
committed
Fix systemd-resolved issue with dns leak protection
1 parent 3614101 commit dfdc9f1

File tree

1 file changed

+71
-62
lines changed

1 file changed

+71
-62
lines changed

protonvpn_cli/connection.py

Lines changed: 71 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -570,75 +570,84 @@ def manage_dns(mode, dns_server=False):
570570

571571
if mode == "leak_protection":
572572
logger.debug("Leak Protection initiated")
573-
# Restore original resolv.conf if it exists
574-
if os.path.isfile(backupfile):
575-
logger.debug("resolv.conf.backup exists")
576-
manage_dns("restore")
577-
# Check for custom DNS Server
578-
if not int(get_config_value("USER", "dns_leak_protection")):
579-
if get_config_value("USER", "custom_dns") == "None":
580-
logger.debug("DNS Leak Protection is disabled")
581-
return
582-
else:
583-
dns_server = get_config_value("USER", "custom_dns")
584-
logger.debug("Using custom DNS")
573+
if shutil.which("resolvectl") and os.path.islink("/etc/resolv.conf"):
574+
logger.debug("Running resolvectl command for leak_protection")
575+
cmd_args = ["resolvectl", "dns", "proton0", dns_server]
576+
pipes = subprocess.Popen(cmd_args, stderr=subprocess.PIPE)
577+
_, std_err = pipes.communicate()
578+
if pipes.returncode != 0:
579+
raise Exception(f"{' '.join(cmd_args)} failed with code {pipes.returncode} -> {std_err.strip()}")
585580
else:
586-
logger.debug("DNS Leak Protection is enabled")
587-
# Make sure DNS Server has been provided
588-
if not dns_server:
589-
raise Exception("No DNS Server has been provided.")
590-
591-
shutil.copy2(resolvconf_path, backupfile)
592-
logger.debug("{0} (resolv.conf) backed up".format(resolvconf_path))
593-
594-
# Remove previous nameservers
595-
dns_regex = re.compile(r"^nameserver .*$")
596-
597-
with open(backupfile, 'r') as backup_handle:
598-
with open(resolvconf_path, 'w') as resolvconf_handle:
599-
for line in backup_handle:
600-
if not dns_regex.search(line):
601-
resolvconf_handle.write(line)
602-
603-
logger.debug("Removed existing DNS Servers")
604-
605-
# Add ProtonVPN managed DNS Server to resolv.conf
606-
dns_server = dns_server.split()
607-
with open(resolvconf_path, "a") as f:
608-
f.write("# ProtonVPN DNS Servers. Managed by ProtonVPN-CLI.\n")
609-
for dns in dns_server[:3]:
610-
f.write("nameserver {0}\n".format(dns))
611-
logger.debug("Added ProtonVPN or custom DNS")
612-
613-
# Write the hash of the edited file in the configuration
614-
#
615-
# This is so it doesn't restore an old DNS configuration
616-
# if the configuration changes during a VPN session
617-
# (e.g. by switching networks)
618-
619-
with open(resolvconf_path, "rb") as f:
620-
filehash = zlib.crc32(f.read())
621-
set_config_value("metadata", "resolvconf_hash", filehash)
581+
# Restore original resolv.conf if it exists
582+
if os.path.isfile(backupfile):
583+
logger.debug("resolv.conf.backup exists")
584+
manage_dns("restore")
585+
# Check for custom DNS Server
586+
if not int(get_config_value("USER", "dns_leak_protection")):
587+
if get_config_value("USER", "custom_dns") == "None":
588+
logger.debug("DNS Leak Protection is disabled")
589+
return
590+
else:
591+
dns_server = get_config_value("USER", "custom_dns")
592+
logger.debug("Using custom DNS")
593+
else:
594+
logger.debug("DNS Leak Protection is enabled")
595+
# Make sure DNS Server has been provided
596+
if not dns_server:
597+
raise Exception("No DNS Server has been provided.")
598+
599+
shutil.copy2(resolvconf_path, backupfile)
600+
logger.debug("{0} (resolv.conf) backed up".format(resolvconf_path))
601+
602+
# Remove previous nameservers
603+
dns_regex = re.compile(r"^nameserver .*$")
604+
605+
with open(backupfile, 'r') as backup_handle:
606+
with open(resolvconf_path, 'w') as resolvconf_handle:
607+
for line in backup_handle:
608+
if not dns_regex.search(line):
609+
resolvconf_handle.write(line)
610+
611+
logger.debug("Removed existing DNS Servers")
612+
613+
# Add ProtonVPN managed DNS Server to resolv.conf
614+
dns_server = dns_server.split()
615+
with open(resolvconf_path, "a") as f:
616+
f.write("# ProtonVPN DNS Servers. Managed by ProtonVPN-CLI.\n")
617+
for dns in dns_server[:3]:
618+
f.write("nameserver {0}\n".format(dns))
619+
logger.debug("Added ProtonVPN or custom DNS")
620+
621+
# Write the hash of the edited file in the configuration
622+
#
623+
# This is so it doesn't restore an old DNS configuration
624+
# if the configuration changes during a VPN session
625+
# (e.g. by switching networks)
626+
627+
with open(resolvconf_path, "rb") as f:
628+
filehash = zlib.crc32(f.read())
629+
set_config_value("metadata", "resolvconf_hash", filehash)
622630

623631
elif mode == "restore":
624632
logger.debug("Restoring DNS")
625-
if os.path.isfile(backupfile):
633+
if not (shutil.which("resolvectl") and os.path.islink("/etc/resolv.conf")):
634+
if os.path.isfile(backupfile):
626635

627-
# Check if the file changed since connection
628-
oldhash = get_config_value("metadata", "resolvconf_hash")
629-
with open(resolvconf_path, "rb") as f:
630-
filehash = zlib.crc32(f.read())
636+
# Check if the file changed since connection
637+
oldhash = get_config_value("metadata", "resolvconf_hash")
638+
with open(resolvconf_path, "rb") as f:
639+
filehash = zlib.crc32(f.read())
631640

632-
if filehash == int(oldhash):
633-
shutil.copy2(backupfile, resolvconf_path)
634-
logger.debug("resolv.conf restored from backup")
635-
else:
636-
logger.debug("resolv.conf changed. Not restoring.")
641+
if filehash == int(oldhash):
642+
shutil.copy2(backupfile, resolvconf_path)
643+
logger.debug("resolv.conf restored from backup")
644+
else:
645+
logger.debug("resolv.conf changed. Not restoring.")
637646

638-
os.remove(backupfile)
639-
logger.debug("resolv.conf.backup removed")
640-
else:
641-
logger.debug("No Backupfile found")
647+
os.remove(backupfile)
648+
logger.debug("resolv.conf.backup removed")
649+
else:
650+
logger.debug("No Backupfile found")
642651
else:
643652
raise Exception("Invalid argument provided. "
644653
"Mode must be 'restore' or 'leak_protection'")

0 commit comments

Comments
 (0)