Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclude multicast DNS from the killswitch in mode 2 #248

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ For more detailed information on installing, updating and uninstalling, please v

For the following Linux distribution(s), install the official `protonvpn-cli` package:

#### Guix

```sh
guix install protonvpn-cli
```

#### Fedora

```sh
Expand Down Expand Up @@ -68,6 +74,7 @@ Depending on your distribution, run the appropriate following command to install
|Ubuntu/Linux Mint/Debian and derivatives | `sudo apt install -y openvpn dialog python3-pip python3-setuptools`|
|OpenSUSE/SLES | `sudo zypper in -y openvpn dialog python3-pip python3-setuptools` |
|Arch Linux/Manjaro | `sudo pacman -S openvpn dialog python-pip python-setuptools` |
|Guix | `guix environment protonvpn-cli` |

#### Installing ProtonVPN-CLI

Expand Down
8 changes: 8 additions & 0 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This document provides an extensive guide on how to install and use ProtonVPN-CL
- [Table of Contents](#table-of-contents)
- [Installation & Updating](#installation--updating)
- [Installing from distribution repositories](#installing-from-distribution-repositories)
- [Guix](#guix)
- [Fedora](#fedora)
- [CentOS & RHEL](#centos--rhel)
- [Installing from PyPI](#installing-from-pypi)
Expand Down Expand Up @@ -43,6 +44,12 @@ This document provides an extensive guide on how to install and use ProtonVPN-CL

For the following Linux distribution(s), install the official `protonvpn-cli` package:

#### Guix

```sh
guix install protonvpn-cli
```

#### Fedora

```sh
Expand Down Expand Up @@ -85,6 +92,7 @@ Depending on your distribution, run the appropriate following command to install
|Ubuntu/Linux Mint/Debian and derivatives | `sudo apt install -y openvpn dialog python3-pip python3-setuptools`|
|OpenSUSE/SLES | `sudo zypper in -y openvpn dialog python3-pip python3-setuptools` |
|Arch Linux/Manjaro | `sudo pacman -S openvpn dialog python-pip python-setuptools` |
|Guix | `guix environment protonvpn-cli` |

#### Installing ProtonVPN-CLI

Expand Down
201 changes: 128 additions & 73 deletions protonvpn_cli/cli.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,3 @@
"""
A CLI for ProtonVPN.

Usage:
protonvpn init
protonvpn (c | connect) [<servername>] [-p <protocol>]
protonvpn (c | connect) [-f | --fastest] [-p <protocol>]
protonvpn (c | connect) [--cc <code>] [-p <protocol>]
protonvpn (c | connect) [--sc] [-p <protocol>]
protonvpn (c | connect) [--p2p] [-p <protocol>]
protonvpn (c | connect) [--tor] [-p <protocol>]
protonvpn (c | connect) [-r | --random] [-p <protocol>]
protonvpn (r | reconnect)
protonvpn (d | disconnect)
protonvpn (s | status)
protonvpn configure
protonvpn refresh
protonvpn examples
protonvpn (-h | --help)
protonvpn (-v | --version)

Options:
-f, --fastest Select the fastest ProtonVPN server.
-r, --random Select a random ProtonVPN server.
--cc CODE Determine the country for fastest connect.
--sc Connect to the fastest Secure-Core server.
--p2p Connect to the fastest torrent server.
--tor Connect to the fastest Tor server.
-p PROTOCOL Determine the protocol (UDP or TCP).
-h, --help Show this help message.
-v, --version Display version.

Commands:
init Initialize a ProtonVPN profile.
c, connect Connect to a ProtonVPN server.
r, reconnect Reconnect to the last server.
d, disconnect Disconnect the current session.
s, status Show connection status.
configure Change ProtonVPN-CLI configuration.
refresh Refresh OpenVPN configuration and server data.
examples Print some example commands.

Arguments:
<servername> Servername (CH#4, CH-US-1, HK5-Tor).
"""
# Standard Libraries
import sys
import os
Expand All @@ -51,8 +6,7 @@
import getpass
import shutil
import time
# External Libraries
from docopt import docopt
import argparse
# protonvpn-cli Functions
from . import connection
from .logger import logger
Expand Down Expand Up @@ -88,13 +42,62 @@ def cli():
logger.debug("USER: {0}".format(USER))
logger.debug("CONFIG_DIR: {0}".format(CONFIG_DIR))

args = docopt(__doc__, version="ProtonVPN-CLI v{0}".format(VERSION))
logger.debug("Arguments\n{0}".format(str(args).replace("\n", "")))
ProtonVPNCLI()


class ProtonVPNCLI():
server_features_dict = dict(
p2p=4,
sc=1,
tor=2
)

def __init__(self):
parser = argparse.ArgumentParser(
prog="protonvpn",
add_help=False
)

parser.add_argument("command", nargs="?")
parser.add_argument("-v", "--version", required=False, action="store_true")
parser.add_argument("-h", "--help", required=False, action="store_true")

args = parser.parse_args(sys.argv[1:2])

logger.debug("Main argument\n{0}".format(args))

if args.version:
print("\nProtonVPN CLI v.{}".format(VERSION))
parser.exit(1)
elif not args.command or not hasattr(self, args.command) or args.help:
print(USAGE)
parser.exit(1)

getattr(self, args.command)()

def init(self):
"""CLI command that intialiazes ProtonVPN profile"""
parser = argparse.ArgumentParser(description="Initialize ProtonVPN profile", prog="protonvpn init")
parser.add_argument(
"-i", "--inline", nargs=3, required=False,
help="Inline intialize profile. (username password protocol)", metavar=""
)

args = parser.parse_args(sys.argv[2:])
logger.debug("Sub-arguments\n{0}".format(args))

if args.inline:
print("Please intialize without '-i/--inline' as it is not fully supported yet.")
sys.exit(1)

# Parse arguments
if args.get("init"):
init_cli()
elif args.get("c") or args.get("connect"):

def c(self):
"""Short CLI command for connecting to the VPN"""
self.connect()

def connect(self):
"""Full CLI command for connecting to the VPN"""
check_root()
check_init()

Expand All @@ -106,45 +109,97 @@ def cli():
if int(os.environ.get("PVPN_WAIT", 0)) > 0:
wait_for_network(int(os.environ["PVPN_WAIT"]))

protocol = args.get("-p")
if protocol is not None and protocol.lower().strip() in ["tcp", "udp"]:
parser = argparse.ArgumentParser(description="Connect to ProtonVPN", prog="protonvpn c")
group = parser.add_mutually_exclusive_group()
group.add_argument("servername", nargs="?", help="Servername (CH#4, CH-US-1, HK5-Tor).", metavar="")
group.add_argument("-f", "--fastest", help="Connect to the fastest ProtonVPN server.", action="store_true")
group.add_argument("-r", "--random", help="Connect to a random ProtonVPN server.", action="store_true")
group.add_argument("--cc", help="Connect to the specified country code (SE, PT, BR, AR).", metavar="")
group.add_argument("--sc", help="Connect to the fastest Secure-Core server.", action="store_true")
group.add_argument("--p2p", help="Connect to the fastest torrent server.", action="store_true")
group.add_argument("--tor", help="Connect to the fastest Tor server.", action="store_true")
parser.add_argument(
"-p", "--protocol", help="Connect via specified protocol.",
choices=["udp", "tcp"], metavar="", type=str.lower
)

args = parser.parse_args(sys.argv[2:])
logger.debug("Sub-arguments:\n{0}".format(args))

protocol = args.protocol
if protocol and protocol.lower().strip() in ["tcp", "udp"]:
protocol = protocol.lower().strip()

if args.get("--random"):
if args.random:
connection.random_c(protocol)
elif args.get("--fastest"):
elif args.fastest:
connection.fastest(protocol)
elif args.get("<servername>"):
connection.direct(args.get("<servername>"), protocol)
elif args.get("--cc") is not None:
connection.country_f(args.get("--cc"), protocol)
# Features: 1: Secure-Core, 2: Tor, 4: P2P
elif args.get("--p2p"):
connection.feature_f(4, protocol)
elif args.get("--sc"):
connection.feature_f(1, protocol)
elif args.get("--tor"):
connection.feature_f(2, protocol)
elif args.servername:
connection.direct(args.servername, protocol)
elif args.cc:
connection.country_f(args.cc, protocol)
elif args.p2p:
connection.feature_f(self.server_features_dict.get("p2p", None), protocol)
elif args.sc:
connection.feature_f(self.server_features_dict.get("sc", None), protocol)
elif args.tor:
connection.feature_f(self.server_features_dict.get("tor", None), protocol)
else:
connection.dialog()
elif args.get("r") or args.get("reconnect"):

def r(self):
"""Short CLI command to reconnect to the last connected VPN Server"""
self.reconnect()

def reconnect(self):
"""Full CLI command to reconnect to the last connected VPN Server"""
check_root()
check_init()
connection.reconnect()
elif args.get("d") or args.get("disconnect"):

def d(self):
"""Short CLI command to disconnect the VPN if a connection is present"""
self.disconnect()

def disconnect(self):
"""Full CLI command to disconnect the VPN if a connection is present"""
check_root()
check_init()
connection.disconnect()
elif args.get("s") or args.get("status"):

def s(self):
"""Short CLI command to display the current VPN status"""
self.status()

def status(self):
"""Full CLI command to display the current VPN status"""
connection.status()
elif args.get("configure"):

def cf(self):
"""Short CLI command to change single configuration values"""
self.configure()

def configure(self):
"""Full CLI command to change single configuration values"""
check_root()
check_init()
configure_cli()
elif args.get("refresh"):

def rf(self):
"""Short CLI command to refresh server list"""
self.refresh()

def refresh(self):
"""Full CLI command to refresh server list"""
check_init()
pull_server_data(force=True)
elif args.get("examples"):

def ex(self):
"""Short CLI command to display usage examples"""
self.examples()

def examples(self):
"""Full CLI command to display usage examples"""
print_examples()


Expand Down
3 changes: 3 additions & 0 deletions protonvpn_cli/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,9 @@ def manage_killswitch(mode, proto=None, port=None):
exclude_lan_commands = [
"iptables -A OUTPUT -o {0} -d {1} -j ACCEPT".format(default_nic, local_network), # noqa
"iptables -A INPUT -i {0} -s {1} -j ACCEPT".format(default_nic, local_network), # noqa
# multicast DNS
"iptables -A OUTPUT -p udp -o {0} -d 224.0.0.251/32 --dport 5353 -j ACCEPT ".format(default_nic), # noqa
"iptables -A INPUT -p udp -i {0} -s 224.0.0.251/32 --sport 5353 -j ACCEPT".format(default_nic), # noqa
]

for lan_command in exclude_lan_commands:
Expand Down
2 changes: 1 addition & 1 deletion protonvpn_cli/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
OVPN_FILE = os.path.join(CONFIG_DIR, "connect.ovpn")
PASSFILE = os.path.join(CONFIG_DIR, "pvpnpass")
CLIENT_SUFFIX = "plc" # ProtonVPN Linux Community
VERSION = "2.2.6"
VERSION = "2.2.6"
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Call with pip install -r requirements.txt
docopt
requests
pythondialog
jinja2
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
},
install_requires=[
"requests",
"docopt",
"pythondialog",
"jinja2",
],
Expand Down