Compare commits

..

31 Commits

Author SHA1 Message Date
Quentin McGaw
1cc0f5fee9 Fix #296 (Cyberghost implementation) (#297)
* Reads the client key from /gluetun/client.key
* Read the client certificate from /gluetun/client.crt
* Additional checks for client key and client certificate validity
* Fix client key file parsing if environment variable isn't present
2020-11-19 08:50:55 -05:00
Quentin McGaw
6896cf4258 Update PIA hardcoded servers information 2020-11-14 22:21:08 +00:00
Quentin McGaw
188d63c6b8 Fix #298 2020-11-13 01:14:05 +00:00
Quentin McGaw
cbc5d466f6 Fix Shadowsocks UDP logs 2020-11-13 00:42:14 +00:00
Quentin McGaw
aef14a9f6d Assimilate PIA v4 as PIA in code 2020-11-10 13:35:49 +00:00
Quentin McGaw
f48392064e Update issue templates 2020-11-10 01:29:47 +00:00
Quentin McGaw
994bdd0ca7 Update Gituhb labels 2020-11-10 01:16:12 +00:00
Quentin McGaw
40ed070f21 Filter Privado servers by hostnames only 2020-11-09 23:17:22 +00:00
Quentin McGaw
f1e4b9937b Privado support, fix #285 (#288) 2020-11-08 20:56:49 -05:00
Quentin McGaw
0423388b52 Fix build information setting at build time 2020-11-07 22:31:20 +00:00
Quentin McGaw
096a9c5fc0 Fix #289 2020-11-06 02:54:27 +00:00
Quentin McGaw
7518f74729 Refactor HTTP control server code 2020-11-05 22:26:53 +00:00
Quentin McGaw
854401a150 PureVPN servers json tag fix 2020-11-05 02:22:33 +00:00
Quentin McGaw
a7a7efe9c3 Remove PIA v3 servers support 2020-11-05 02:10:34 +00:00
Quentin McGaw
31883f9adb Windscribe API and more servers filter options, fixes #197 (#282)
- Use Windscribe API to fetch servers information
- More data on servers about region, city and hostname
- Add optional server filters with `REGION`, `CITY` and `HOSTNAME` csv environment variables
2020-11-04 20:38:35 -05:00
Quentin McGaw
3b04677f8f HTTP control server /version endpoint 2020-11-04 14:07:04 +00:00
Quentin McGaw
b5fb2b849a DOT listens on all interfaces, refers to #281 2020-11-04 03:14:27 +00:00
Quentin McGaw
0c9f74ffa4 HTTP proxy written in Go to replace Tinyproxy (#269) 2020-10-31 21:50:31 -04:00
Quentin McGaw
58da55da1e Retrocompatiblity with EXTRA_SUBNETS key 2020-10-29 23:32:15 +00:00
Quentin McGaw
db64dea664 Fix #273 (#277), adding FIREWALL_OUTBOUND_SUBNETS 2020-10-29 19:23:44 -04:00
Quentin McGaw
f7bff247aa Fix #275 2020-10-28 22:09:58 +00:00
Quentin McGaw
edc08c46d4 Health server runs on 127.0.0.1:9999, fix #272 2020-10-27 03:28:25 +00:00
Quentin McGaw
78d83145ba Increase http timeout to 30s for piav4
- Refers to #271
2020-10-26 21:53:18 +00:00
Quentin McGaw
0c81154f36 No firewall setup needed for Shadowsocks 2020-10-26 02:52:10 +00:00
Quentin McGaw
53fe08ea26 Fix Mullvad CSV city bug 2020-10-25 21:13:26 +00:00
Quentin McGaw
a6cb1a7052 Remove x/net Go dependency 2020-10-25 20:45:14 +00:00
Quentin McGaw
c64fe7e45d Routing: use 0.0.0.0/0 instead of nil 2020-10-25 20:41:09 +00:00
Quentin McGaw
a062135148 Add routing verbose option in code 2020-10-25 20:40:17 +00:00
Quentin McGaw
5ae7c15211 Unused shadowsocks code cleanup 2020-10-25 20:38:16 +00:00
Quentin McGaw
f29707fa9f Remove unneeded openvpn remote-random 2020-10-24 22:26:15 +00:00
Quentin McGaw
e97d1e4a9a Set default for DOT_CACHING in code 2020-10-24 22:24:20 +00:00
112 changed files with 2965 additions and 1950 deletions

View File

@@ -7,49 +7,31 @@ assignees: qdm12
---
**TLDR**: *Describe your issue in a one liner here*
**Host OS** (approximate answer is fine too): Ubuntu 18
1. Is this urgent?
**Is this urgent?**: No
- [ ] Yes
- [x] No
**What VPN provider are you using**:
2. What VPN service provider are you using?
**What are you using to run your container?**: Docker Compose
- [x] PIA
- [ ] Mullvad
- [ ] Windscribe
- [ ] Surfshark
- [ ] Cyberghost
**What is the version of the program** (See the line at the top of your logs)
3. What's the version of the program?
```
Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
```
**See the line at the top of your logs**
**What's the problem** 🤔
`Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)`
That feature doesn't work
4. What are you using to run the container?
**Share your logs...**
- [ ] Docker run
- [x] Docker Compose
- [ ] Kubernetes
- [ ] Docker stack
- [ ] Docker swarm
- [ ] Podman
- [ ] Other:
5. Extra information
Logs:
...*careful to remove i.e. token information with PIA port forwarding*
```log
```
Configuration file:
```yml
PASTE YOUR LOGS
IN THERE
```
Host OS:

View File

@@ -1,14 +1,17 @@
---
name: Feature request
about: Suggest a feature to add to this project
title: 'Feature request: ...'
title: 'Feature request: FILL THIS TEXT!'
labels: ":bulb: feature request"
assignees: qdm12
---
1. What's the feature?
**What's the feature?** 🧐
2. Why do you need this feature?
- Support this new feature because that and that
3. Extra information?
**Optional extra information** 🚀
- I tried `docker run something` and it doesn't work
- That [url](https://github.com/qdm12/gluetun) is interesting

View File

@@ -7,49 +7,47 @@ assignees:
---
**TLDR**: *Describe your issue in a one liner here*
**Host OS** (approximate answer is fine too): Ubuntu 18
1. Is this urgent?
**Is this urgent?**: No
- [ ] Yes
- [x] No
**What VPN provider are you using**:
2. What VPN service provider are you using?
**What is the version of the program** (See the line at the top of your logs)
- [x] PIA
- [ ] Mullvad
- [ ] Windscribe
- [ ] Surfshark
- [ ] Cyberghost
```
Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
```
3. What's the version of the program?
**What's the problem** 🤔
**See the line at the top of your logs**
That feature doesn't work
`Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)`
**Share your logs...**
4. What are you using to run the container?
- [ ] Docker run
- [x] Docker Compose
- [ ] Kubernetes
- [ ] Docker stack
- [ ] Docker swarm
- [ ] Podman
- [ ] Other:
5. Extra information
Logs:
...*careful to remove i.e. token information with PIA port forwarding*
```log
PASTE YOUR LOGS
IN THERE
```
Configuration file:
**What are you using to run your container?**: Docker Compose
Please also share your configuration file:
```yml
your .yml
content
in here
```
Host OS:
or
```sh
# your docker
# run command
# in here
```

96
.github/labels.yml vendored
View File

@@ -1,51 +1,67 @@
- name: ":robot: bot"
color: "69cde9"
description: ""
- name: ":bug: bug"
- name: "Bug :bug:"
color: "b60205"
description: ""
- name: ":game_die: dependencies"
color: "0366d6"
description: ""
- name: ":memo: documentation"
color: "c5def5"
description: ""
- name: ":busts_in_silhouette: duplicate"
color: "cccccc"
description: ""
- name: ":sparkles: enhancement"
color: "0054ca"
description: ""
- name: ":bulb: feature request"
- name: "Feature request :bulb:"
color: "0e8a16"
description: ""
- name: ":mega: feedback"
color: "03a9f4"
description: ""
- name: ":rocket: future maybe"
color: "fef2c0"
description: ""
- name: ":hatching_chick: good first issue"
color: "7057ff"
description: ""
- name: ":pray: help wanted"
- name: "Help wanted :pray:"
color: "4caf50"
description: ""
- name: ":hand: hold"
color: "24292f"
- name: "Documentation :memo:"
color: "c5def5"
description: ""
- name: ":no_entry_sign: invalid"
color: "e6e6e6"
description: ""
- name: ":interrobang: maybe bug"
color: "ff5722"
description: ""
- name: ":thinking: needs more info"
- name: "Needs more info :thinking:"
color: "795548"
description: ""
- name: ":question: question"
color: "3f51b5"
# VPN providers
- name: ":cloud: Cyberghost"
color: "cfe8d4"
description: ""
- name: ":coffin: wontfix"
color: "ffffff"
- name: ":cloud: Mullvad"
color: "cfe8d4"
description: ""
- name: ":cloud: NordVPN"
color: "cfe8d4"
description: ""
- name: ":cloud: PIA"
color: "cfe8d4"
description: ""
- name: ":cloud: Privado"
color: "cfe8d4"
description: ""
- name: ":cloud: PureVPN"
color: "cfe8d4"
description: ""
- name: ":cloud: Surfshark"
color: "cfe8d4"
description: ""
- name: ":cloud: Vyprvpn"
color: "cfe8d4"
description: ""
- name: ":cloud: Windscribe"
color: "cfe8d4"
description: ""
# Problem category
- name: "Openvpn"
color: "ffc7ea"
description: ""
- name: "Unbound (DNS over TLS)"
color: "ffc7ea"
description: ""
- name: "Firewall"
color: "ffc7ea"
description: ""
- name: "HTTP proxy"
color: "ffc7ea"
description: ""
- name: "Shadowsocks"
color: "ffc7ea"
description: ""
- name: "Healthcheck server"
color: "ffc7ea"
description: ""
- name: "Control server"
color: "ffc7ea"
description: ""

View File

@@ -4,6 +4,13 @@ linters-settings:
misspell:
locale: US
issues:
exclude-rules:
- path: _test\.go
linters:
- dupl
- maligned
linters:
disable-all: true
enable:
@@ -57,4 +64,4 @@ run:
skip-dirs:
- .devcontainer
- .github
- postgres
- doc

View File

@@ -58,7 +58,10 @@ ENV VPNSP=pia \
PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
# Mullvad and PureVPN only
COUNTRY= \
# Mullvad, PureVPN, Windscribe only
CITY= \
# Windscribe only
HOSTNAME= \
# Mullvad only
ISP= \
OWNED=no \
@@ -91,13 +94,14 @@ ENV VPNSP=pia \
FIREWALL=on \
FIREWALL_VPN_INPUT_PORTS= \
FIREWALL_INPUT_PORTS= \
FIREWALL_OUTBOUND_SUBNETS= \
FIREWALL_DEBUG=off \
# Tinyproxy
TINYPROXY=off \
TINYPROXY_LOG=Info \
TINYPROXY_PORT=8888 \
TINYPROXY_USER= \
TINYPROXY_PASSWORD= \
# HTTP proxy
HTTPPROXY= \
HTTPPROXY_LOG=off \
HTTPPROXY_PORT=8888 \
HTTPPROXY_USER= \
HTTPPROXY_PASSWORD= \
# Shadowsocks
SHADOWSOCKS=off \
SHADOWSOCKS_LOG=off \
@@ -108,10 +112,9 @@ ENV VPNSP=pia \
ENTRYPOINT ["/entrypoint"]
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
HEALTHCHECK --interval=10m --timeout=10s --start-period=30s --retries=2 CMD /entrypoint healthcheck
RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables ip6tables unbound tinyproxy tzdata && \
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* /etc/tinyproxy/tinyproxy.conf && \
RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* && \
deluser openvpn && \
deluser tinyproxy && \
deluser unbound && \
mkdir /gluetun
# TODO remove once SAN is added to PIA servers certificates, see https://github.com/pia-foss/manual-connections/issues/10

View File

@@ -1,8 +1,7 @@
# Gluetun VPN client
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access,
Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN and PureVPN VPN servers, using Go, OpenVPN,
iptables, DNS over TLS, ShadowSocks and Tinyproxy*
Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN and Privado VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
**ANNOUNCEMENT**: *Github Wiki reworked*
@@ -29,14 +28,14 @@ iptables, DNS over TLS, ShadowSocks and Tinyproxy*
## Features
- Based on Alpine 3.12 for a small Docker image of 52MB
- Supports **Private Internet Access** (new and old), **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN** and **PureVPN** servers
- Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN**, **PureVPN** and **Privado** servers
- Supports Openvpn only for now
- DNS over TLS baked in with service provider(s) of your choice
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
- Choose the vpn network protocol, `udp` or `tcp`
- Built in firewall kill switch to allow traffic only with needed the VPN servers and LAN devices
- Built in Shadowsocks proxy (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP)
- Built in HTTP proxy (Tinyproxy, tunnels TCP)
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
- [Connect other containers to it](https://github.com/qdm12/gluetun#connect-to-it)
- [Connect LAN devices to it](https://github.com/qdm12/gluetun#connect-to-it)
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7 🎆
@@ -97,7 +96,7 @@ docker run --rm --network=container:gluetun alpine:3.12 wget -qO- https://ipinfo
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `VPNSP` | `private internet access` | `private internet access`, `private internet access old`, `mullvad`, `windscribe`, `surfshark`, `vyprvpn`, `nordvpn`, `purevpn` | VPN Service Provider |
| 🏁 `VPNSP` | `private internet access` | `private internet access`, `mullvad`, `windscribe`, `surfshark`, `vyprvpn`, `nordvpn`, `purevpn`, `privado` | VPN Service Provider |
| `IP_STATUS_FILE` | `/tmp/gluetun/ip` | Any filepath | Filepath to store the public IP address assigned |
| `PROTOCOL` | `udp` | `udp` or `tcp` | Network protocol to use |
| `OPENVPN_VERBOSITY` | `1` | `0` to `6` | Openvpn verbosity level |
@@ -135,13 +134,15 @@ docker run --rm --network=container:gluetun alpine:3.12 wget -qO- https://ipinfo
For **port forwarding**, obtain a port from [here](https://mullvad.net/en/account/#/ports) and add it to `FIREWALL_VPN_INPUT_PORTS`
- Windscribe
- Windscribe (see [this](https://github.com/qdm12/gluetun/blob/master/internal/constants/windscribe.go#L43) for the choices of regions, cities and hostnames)
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password |
| `REGION` | | One of the [Windscribe regions](https://windscribe.com/status) | VPN server region |
| `REGION` | | | Comma separated list of regions to choose the VPN server |
| `CITY` | | | Comma separated list of cities to choose the VPN server |
| `HOSTNAME` | | | Comma separated list of hostnames to choose the VPN server |
| `PORT` | | One from the [this list of ports](https://windscribe.com/getconfig/openvpn) | Custom VPN port to use |
- Surfshark
@@ -158,20 +159,15 @@ docker run --rm --network=container:gluetun alpine:3.12 wget -qO- https://ipinfo
| --- | --- | --- | --- |
| 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password |
| 🏁 `CLIENT_KEY` | | | Your device client key content, **see below** |
| 🏁 | | | **See additional setup steps below** |
| `REGION` | | One of the Cyberghost regions, [Wiki page](https://github.com/qdm12/gluetun/wiki/Cyberghost-Servers) | VPN server country |
| `CYBERGHOST_GROUP` | `Premium UDP Europe` | One of the server groups (see above Wiki page) | Server group |
To specify your client key, you can either:
**Additional setup steps**: Bind mount your `client.key` file to `/gluetun/client.key` and your `client.crt` file to `/gluetun/client.crt`. For example, you can use with your `docker run` command:
- Bind mount it at `/files/client.key`, for example with `-v /yourpath/client.key:/files/client.key:ro`
- Convert it to a single line value using:
```sh
docker run -it --rm -v /yourpath/client.key:/files/client.key:ro qmcgaw/private-internet-access clientkey
```
And use the line produced as the value for the environment variable `CLIENT_KEY`.
```sh
-v /yourpath/client.key:/gluetun/client.key:ro -v /yourpath/client.crt:/gluetun/client.crt:ro
```
- Vyprvpn
@@ -201,6 +197,14 @@ docker run --rm --network=container:gluetun alpine:3.12 wget -qO- https://ipinfo
| `COUNTRY` | | One of the [PureVPN countries](https://support.purevpn.com/vpn-servers) | VPN server country |
| `CITY` | | One of the [PureVPN cities](https://support.purevpn.com/vpn-servers) | VPN server city |
- Privado
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password |
| `HOSTNAME` | | [One of the Privado hostname](internal/constants/privado.go#L26), i.e. `ams-001.vpn.privado.io` | VPN server hostname |
### DNS over TLS
None of the following values are required.
@@ -223,9 +227,7 @@ None of the following values are required.
| `DNS_PLAINTEXT_ADDRESS` | `1.1.1.1` | Any IP address | IP address to use as DNS resolver if `DOT` is `off` |
| `DNS_KEEP_NAMESERVER` | `off` | `on` or `off` | Keep the nameservers in /etc/resolv.conf untouched, but disabled DNS blocking features |
### Firewall
That one is important if you want to connect to the container from your LAN for example, using Shadowsocks or Tinyproxy.
### Firewall and routing
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
@@ -233,6 +235,7 @@ That one is important if you want to connect to the container from your LAN for
| `FIREWALL_VPN_INPUT_PORTS` | | i.e. `1000,8080` | Comma separated list of ports to allow from the VPN server side (useful for **vyprvpn** port forwarding) |
| `FIREWALL_INPUT_PORTS` | | i.e. `1000,8000` | Comma separated list of ports to allow through the default interface. This seems needed for Kubernetes sidecars. |
| `FIREWALL_DEBUG` | `off` | `on` or `off` | Prints every firewall related command. You should use it for **debugging purposes** only. |
| `FIREWALL_OUTBOUND_SUBNETS` | | i.e. `192.168.1.0/24,192.168.10.121,10.0.0.5/28` | Comma separated subnets that Gluetun and the containers sharing its network stack are allowed to access. This involves firewall and routing modifications. |
### Shadowsocks
@@ -244,15 +247,16 @@ That one is important if you want to connect to the container from your LAN for
| `SHADOWSOCKS_PASSWORD` | | | Password to use to connect to Shadowsocks |
| `SHADOWSOCKS_METHOD` | `chacha20-ietf-poly1305` | `chacha20-ietf-poly1305`, `aes-128-gcm`, `aes-256-gcm` | Method to use for Shadowsocks |
### Tinyproxy
### HTTP proxy
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| `TINYPROXY` | `off` | `on`, `off` | Enable the internal HTTP proxy tinyproxy |
| `TINYPROXY_LOG` | `Info` | `Info`, `Connect`, `Notice`, `Warning`, `Error`, `Critical` | Tinyproxy log level |
| `TINYPROXY_PORT` | `8888` | `1024` to `65535` | Internal port number for Tinyproxy to listen on |
| `TINYPROXY_USER` | | | Username to use to connect to Tinyproxy |
| `TINYPROXY_PASSWORD` | | | Password to use to connect to Tinyproxy |
| `HTTPPROXY` | `off` | `on`, `off` | Enable the internal HTTP proxy |
| `HTTPPROXY_LOG` | `off` | `on` or `off` | Logs every tunnel requests |
| `HTTPPROXY_PORT` | `8888` | `1024` to `65535` | Internal port number for the HTTP proxy to listen on |
| `HTTPPROXY_USER` | | | Username to use to connect to the HTTP proxy |
| `HTTPPROXY_PASSWORD` | | | Password to use to connect to the HTTP proxy |
| `HTTPPROXY_STEALTH` | `off` | `on` or `off` | Stealth mode means HTTP proxy headers are not added to your requests |
### System
@@ -296,15 +300,16 @@ There are various ways to achieve this, depending on your use case.
Add `network_mode: "container:gluetun"` to your *docker-compose.yml*, provided Gluetun is already running
</p></details>
- <details><summary>Connect LAN devices through the built-in HTTP proxy *Tinyproxy* (i.e. with Chrome, Kodi, etc.)</summary><p>
- <details><summary>Connect LAN devices through the built-in HTTP proxy (i.e. with Chrome, Kodi, etc.)</summary><p>
You might want to use Shadowsocks instead which tunnels UDP as well as TCP, whereas Tinyproxy only tunnels TCP.
⚠️ You might want to use Shadowsocks instead which tunnels UDP as well as TCP and does not leak your credentials.
The HTTP proxy will not encrypt your username and password every time you send a request to the HTTP proxy server.
1. Setup a HTTP proxy client, such as [SwitchyOmega for Chrome](https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif?hl=en)
1. Setup an HTTP proxy client, such as [SwitchyOmega for Chrome](https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif?hl=en)
1. Ensure the Gluetun container is launched with:
- port `8888` published `-p 8888:8888/tcp`
1. With your HTTP proxy client, connect to the Docker host (i.e. `192.168.1.10`) on port `8888`. You need to enter your credentials if you set them with `TINYPROXY_USER` and `TINYPROXY_PASSWORD`.
1. If you set `TINYPROXY_LOG` to `Info`, more information will be logged in the Docker logs
1. With your HTTP proxy client, connect to the Docker host (i.e. `192.168.1.10`) on port `8888`. You need to enter your credentials if you set them with `HTTPPROXY_USER` and `HTTPPROXY_PASSWORD`. Note that Chrome does not support authentication.
1. If you set `HTTPPROXY_LOG` to `on`, more information will be logged in the Docker logs
</p></details>
- <details><summary>Connect LAN devices through the built-in *Shadowsocks* proxy (per app, system wide, etc.)</summary><p>

View File

@@ -17,7 +17,10 @@ import (
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/dns"
"github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/healthcheck"
"github.com/qdm12/gluetun/internal/httpproxy"
gluetunLogging "github.com/qdm12/gluetun/internal/logging"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/openvpn"
"github.com/qdm12/gluetun/internal/params"
"github.com/qdm12/gluetun/internal/publicip"
@@ -26,7 +29,6 @@ import (
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/gluetun/internal/shadowsocks"
"github.com/qdm12/gluetun/internal/storage"
"github.com/qdm12/gluetun/internal/tinyproxy"
"github.com/qdm12/gluetun/internal/updater"
versionpkg "github.com/qdm12/gluetun/internal/version"
"github.com/qdm12/golibs/command"
@@ -37,12 +39,16 @@ import (
//nolint:gochecknoglobals
var (
buildInfo models.BuildInformation
version = "unknown"
commit = "unknown"
buildDate = "an unknown date"
)
func main() {
buildInfo.Version = version
buildInfo.Commit = commit
buildInfo.BuildDate = buildDate
ctx := context.Background()
os.Exit(_main(ctx, os.Args))
}
@@ -52,8 +58,7 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go
var err error
switch args[1] {
case "healthcheck":
client := &http.Client{Timeout: time.Second}
err = cli.HealthCheck(background, client)
err = cli.HealthCheck(background)
case "clientkey":
err = cli.ClientKey(args[2:])
case "openvpnconfig":
@@ -83,17 +88,15 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go
dnsConf := dns.NewConfigurator(logger, client, fileManager)
routingConf := routing.NewRouting(logger)
firewallConf := firewall.NewConfigurator(logger, routingConf, fileManager)
tinyProxyConf := tinyproxy.NewConfigurator(fileManager, logger)
streamMerger := command.NewStreamMerger()
paramsReader := params.NewReader(logger, fileManager)
fmt.Println(gluetunLogging.Splash(version, commit, buildDate))
fmt.Println(gluetunLogging.Splash(buildInfo))
printVersions(ctx, logger, map[string]func(ctx context.Context) (string, error){
"OpenVPN": ovpnConf.Version,
"Unbound": dnsConf.Version,
"IPtables": firewallConf.Version,
"TinyProxy": tinyProxyConf.Version,
"OpenVPN": ovpnConf.Version,
"Unbound": dnsConf.Version,
"IPtables": firewallConf.Version,
})
allSettings, err := settings.GetAllSettings(paramsReader)
@@ -125,11 +128,6 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go
logger.Error(err)
return 1
}
err = fileManager.SetOwnership("/etc/tinyproxy", uid, gid)
if err != nil {
logger.Error(err)
return 1
}
if allSettings.Firewall.Debug {
firewallConf.SetDebug()
@@ -148,18 +146,34 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go
return 1
}
firewallConf.SetNetworkInformation(defaultInterface, defaultGateway, localSubnet)
defaultIP, err := routingConf.DefaultIP()
if err != nil {
logger.Error(err)
return 1
}
firewallConf.SetNetworkInformation(defaultInterface, defaultGateway, localSubnet, defaultIP)
if err := routingConf.Setup(); err != nil {
logger.Error(err)
return 1
}
defer func() {
routingConf.SetVerbose(false)
if err := routingConf.TearDown(); err != nil {
logger.Error(err)
}
}()
if err := firewallConf.SetOutboundSubnets(ctx, allSettings.Firewall.OutboundSubnets); err != nil {
logger.Error(err)
return 1
}
if err := routingConf.SetOutboundRoutes(allSettings.Firewall.OutboundSubnets); err != nil {
logger.Error(err)
return 1
}
if err := ovpnConf.CheckTUN(); err != nil {
logger.Warn(err)
err = ovpnConf.CreateTUN()
@@ -229,19 +243,17 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go
go publicIPLooper.RunRestartTicker(ctx, wg)
publicIPLooper.SetPeriod(allSettings.PublicIPPeriod) // call after RunRestartTicker
tinyproxyLooper := tinyproxy.NewLooper(tinyProxyConf, firewallConf,
allSettings.TinyProxy, logger, streamMerger, uid, gid, defaultInterface)
restartTinyproxy := tinyproxyLooper.Restart
httpProxyLooper := httpproxy.NewLooper(httpClient, logger, allSettings.HTTPProxy)
wg.Add(1)
go tinyproxyLooper.Run(ctx, wg)
go httpProxyLooper.Run(ctx, wg)
shadowsocksLooper := shadowsocks.NewLooper(firewallConf, allSettings.ShadowSocks, logger, defaultInterface)
shadowsocksLooper := shadowsocks.NewLooper(allSettings.ShadowSocks, logger, defaultInterface)
restartShadowsocks := shadowsocksLooper.Restart
wg.Add(1)
go shadowsocksLooper.Run(ctx, wg)
if allSettings.TinyProxy.Enabled {
restartTinyproxy()
if allSettings.HTTPProxy.Enabled {
httpProxyLooper.Restart()
}
if allSettings.ShadowSocks.Enabled {
restartShadowsocks()
@@ -255,10 +267,15 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go
controlServerAddress := fmt.Sprintf("0.0.0.0:%d", allSettings.ControlServer.Port)
controlServerLogging := allSettings.ControlServer.Log
httpServer := server.New(controlServerAddress, controlServerLogging,
logger, openvpnLooper, unboundLooper, updaterLooper)
logger, buildInfo, openvpnLooper, unboundLooper, updaterLooper)
wg.Add(1)
go httpServer.Run(ctx, wg)
healthcheckServer := healthcheck.NewServer(
constants.HealthcheckAddress, logger)
wg.Add(1)
go healthcheckServer.Run(ctx, wg)
// Start openvpn for the first time
openvpnLooper.Restart()
@@ -336,7 +353,7 @@ func printVersions(ctx context.Context, logger logging.Logger,
//nolint:lll
func collectStreamLines(ctx context.Context, streamMerger command.StreamMerger,
logger logging.Logger, signalTunnelReady func()) {
// Blocking line merging paramsReader for all programs: openvpn, tinyproxy, unbound and shadowsocks
// Blocking line merging paramsReader for openvpn and unbound
logger.Info("Launching standard output merger")
streamMerger.CollectLines(ctx, func(line string) {
line, level := gluetunLogging.PostProcessLine(line)
@@ -398,31 +415,20 @@ func routeReadyEvents(ctx context.Context, wg *sync.WaitGroup, tunnelReadyCh, dn
logger.Info("VPN routing IP address: %s", vpnDestination)
}
if portForwardingEnabled {
// TODO make instantaneous once v3 go out of service
const waitDuration = 5 * time.Second
timer := time.NewTimer(waitDuration)
select {
case <-ctx.Done():
if !timer.Stop() {
<-timer.C
}
continue
case <-timer.C:
// vpnGateway required only for PIA v4
vpnGateway, err := routing.VPNLocalGatewayIP()
if err != nil {
logger.Error(err)
}
logger.Info("VPN gateway IP address: %s", vpnGateway)
startPortForward(vpnGateway)
// vpnGateway required only for PIA
vpnGateway, err := routing.VPNLocalGatewayIP()
if err != nil {
logger.Error(err)
}
logger.Info("VPN gateway IP address: %s", vpnGateway)
startPortForward(vpnGateway)
}
case <-dnsReadyCh:
publicIPLooper.Restart() // TODO do not restart if disabled
if !versionInformation {
break
}
message, err := versionpkg.GetMessage(ctx, version, commit, httpClient)
message, err := versionpkg.GetMessage(ctx, buildInfo, httpClient)
if err != nil {
logger.Error(err)
break

View File

@@ -7,7 +7,7 @@ services:
- NET_ADMIN
network_mode: bridge
ports:
- 8888:8888/tcp # Tinyproxy
- 8888:8888/tcp # HTTP proxy
- 8388:8388/tcp # Shadowsocks
- 8388:8388/udp # Shadowsocks
- 8000:8000/tcp # Built-in HTTP control server

5
go.mod
View File

@@ -6,10 +6,9 @@ require (
github.com/fatih/color v1.9.0
github.com/golang/mock v1.4.4
github.com/kyokomi/emoji v2.2.4+incompatible
github.com/qdm12/golibs v0.0.0-20201024185935-092412448c2c
github.com/qdm12/ss-server v0.0.0-20200819124651-6428e626ee83
github.com/qdm12/golibs v0.0.0-20201025221346-fe352060c25a
github.com/qdm12/ss-server v0.1.0
github.com/stretchr/testify v1.6.1
github.com/vishvananda/netlink v1.1.0
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0
golang.org/x/sys v0.0.0-20201018121011-98379d014ca7
)

21
go.sum
View File

@@ -72,18 +72,16 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/qdm12/golibs v0.0.0-20201024185935-092412448c2c h1:9EQyDXbeapnPeMeO8Yq7PE6zqYPGkHp/qijNBBTU74c=
github.com/qdm12/golibs v0.0.0-20201024185935-092412448c2c/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
github.com/qdm12/ss-server v0.0.0-20200819124651-6428e626ee83 h1:b7sNsgsKxH0mbl9L1hdUp5KSDkZ/1kOQ+iHiBVgFElM=
github.com/qdm12/ss-server v0.0.0-20200819124651-6428e626ee83/go.mod h1:ABVUkxubboL3vqBkOwDV9glX1/x7SnYrckBe5d+M/zw=
github.com/qdm12/golibs v0.0.0-20201025221346-fe352060c25a h1:v0zUA1FWeVkTEd9KyxfehbRVJeFGOqyMY6FHO/Q9ITU=
github.com/qdm12/golibs v0.0.0-20201025221346-fe352060c25a/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
github.com/qdm12/ss-server v0.1.0 h1:WV9MkHCDEWRwe4WpnYFeR/zcZAxYoTbfntLDnw9AQ50=
github.com/qdm12/ss-server v0.1.0/go.mod h1:ABVUkxubboL3vqBkOwDV9glX1/x7SnYrckBe5d+M/zw=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -102,7 +100,6 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
@@ -111,25 +108,17 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0 h1:5kGOVHlq0euqwzgTC9Vu15p6fV1Wi0ArVi8da2urnVg=
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201018121011-98379d014ca7 h1:CNOpL+H7PSxBI7dF/EIUsfOguRSzWp6CQ91yxZE6PG4=
golang.org/x/sys v0.0.0-20201018121011-98379d014ca7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
@@ -137,13 +126,11 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -4,12 +4,12 @@ import (
"context"
"flag"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/healthcheck"
"github.com/qdm12/gluetun/internal/params"
"github.com/qdm12/gluetun/internal/provider"
"github.com/qdm12/gluetun/internal/settings"
@@ -17,12 +17,11 @@ import (
"github.com/qdm12/gluetun/internal/updater"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
"golang.org/x/net/context/ctxhttp"
)
func ClientKey(args []string) error {
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
filepath := flagSet.String("path", "/files/client.key", "file path to the client.key file")
filepath := flagSet.String("path", string(constants.ClientKey), "file path to the client.key file")
if err := flagSet.Parse(args); err != nil {
return err
}
@@ -40,21 +39,14 @@ func ClientKey(args []string) error {
return nil
}
func HealthCheck(ctx context.Context, client *http.Client) error {
const url = "http://localhost:8000/health"
response, err := ctxhttp.Get(ctx, client, url)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode == http.StatusOK {
return nil
}
b, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
return fmt.Errorf("HTTP status code %s with message: %s", response.Status, string(b))
func HealthCheck(ctx context.Context) error {
const timeout = 3 * time.Second
httpClient := &http.Client{Timeout: timeout}
healthchecker := healthcheck.NewChecker(httpClient)
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
const url = "http://" + constants.HealthcheckAddress
return healthchecker.Check(ctx, url)
}
func OpenvpnConfig() error {
@@ -101,7 +93,7 @@ func Update(args []string) error {
flagSet.BoolVar(&options.Mullvad, "mullvad", false, "Update Mullvad servers")
flagSet.BoolVar(&options.Nordvpn, "nordvpn", false, "Update Nordvpn servers")
flagSet.BoolVar(&options.PIA, "pia", false, "Update Private Internet Access post-summer 2020 servers")
flagSet.BoolVar(&options.PIAold, "piaold", false, "Update Private Internet Access pre-summer 2020 servers")
flagSet.BoolVar(&options.Privado, "privado", false, "Update Privado servers")
flagSet.BoolVar(&options.Purevpn, "purevpn", false, "Update Purevpn servers")
flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers")
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")

View File

@@ -0,0 +1,5 @@
package constants
const (
HealthcheckAddress = "127.0.0.1:9999"
)

View File

@@ -6,18 +6,6 @@ func ColorUnbound() *color.Color {
return color.New(color.FgCyan)
}
func ColorTinyproxy() *color.Color {
return color.New(color.FgHiGreen)
}
func ColorShadowsocks() *color.Color {
return color.New(color.FgHiYellow)
}
func ColorShadowsocksError() *color.Color {
return color.New(color.FgHiRed)
}
func ColorOpenvpn() *color.Color {
return color.New(color.FgHiMagenta)
}

View File

@@ -9,8 +9,7 @@ import (
//nolint:lll
const (
CyberghostCertificate = "MIIGWjCCBEKgAwIBAgIJAJxUG61mxDS7MA0GCSqGSIb3DQEBDQUAMHsxCzAJBgNVBAYTAlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5BLjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJpbmZvQGN5YmVyZ2hvc3Qucm8wHhcNMTcwNjE5MDgxNzI1WhcNMzcwNjE0MDgxNzI1WjB7MQswCQYDVQQGEwJSTzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4xGzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7O8+mji2FlQhJXn/G4VLrKPjGtxgQBAdjo0dZEQzKX08q14dLkslmOLgShStWKrOiLXGAvB1rPvvk613jtA0KjQLpgyLy9lIWohQKYjj5jrJYXMZMkbSHBYI9L8L7iezBEFYrjYKdDo51nq99wRFhKdbyKKjDh3e2L2SVEZLT1ogkK5gWzjvH+mjjtjUUicK+YjGwWOz6I+KKaG4Ve/D/cE6nCLbhHIMMnargZEu7sqA6BFeS4kEP/ZdCZoTSX2n43XV1q63nJt/v0KDetbZDciFVW9h9SVPG4qT44p0550N+Mom7zTX7S/ID5T9dplgU8sRGtIMrG0cIMD9zmpFgUnMusCrR7jJFr0sMAveTbgZg95LmstV6R6WKZkSFdUrE0DHl4dHoZvTFX+1LhwhHgjgDLaosX0vhG/C/7LpoVWimd6RRQT3M9o4Fa1TuhfvBzQ20QHrmRV/yKvGNK0xckZ6EZ/QY7Z55ORU15Tgab4ebnblYPWoEmn0mIYP3LFFeoR5OS1EX7+j4kPv+bwPGsmpHjxmZyq2Y7sJBpbOCJgbkn52WZdPBIRDpPdIHQ8pAJC4T0iMK9xvAwWNl/V6EYYNpR97osyEDXn+BTdAHlhJ5fck9KlwI9mb1Kg1bhbvbmaIAiOLenSULYf3j6rI1ygo3R2cCyybtuAq8M7z0OECAwEAAaOB4DCB3TAdBgNVHQ4EFgQU6tdK1g/He5qzjeAoM5eHt4in9iUwga0GA1UdIwSBpTCBooAU6tdK1g/He5qzjeAoM5eHt4in9iWhf6R9MHsxCzAJBgNVBAYTAlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5BLjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJpbmZvQGN5YmVyZ2hvc3Qucm+CCQCcVButZsQ0uzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4ICAQDNyQ92kj4qiNjnHk99qvnFw9qGfwB9ofaPL74zh0G5hEe3Wgb2o4fqUGnvUNgOu53gJksz3DcPQ8t40wfmm9I1Z8tiM9qrqvkuQ+nKcLgdooXtEsTybPIYDZ2cWR/5E0TKRvC7RFzKgQ4D77Vbi4TdaHiDV7ZNfU1iLCoBGcYm80hcUHEs5KIVLwUmcSOTmbZBySJxcSD0yUpS7nlZGwLY6VQrU+JFwDSisbXT4DXf3iSzp7FzW0/u/SFvWsPHrjE0hkPoZPalYvouaJEHKAhip0ZwSmitlxbBnmm8+K/3c9mLA5/uXrirfpuhhs8V3lyV2mczVtSiTl6gpi88gc//JY80JeHdupjO25T3XEzY9cpxecmkWaUEjLMx4wVoXQuUiPonfILM6OLwi+zUS8gQErdFeGvcQXbncPa4SdJuHkF8lgiX2i8S8fPGdXvU37E9bdAXwP5nZriYq1s0D59Qfvz+vLXVkmyZp6ztxjKjKolemPMak0Y5c1Q4RjNF6tmQoFuy/ACSkWy14Tzu2dFp7UiVbGg1FOvKhfs48zC2/IUQv1arqmPT/9LVq3B2DVT9UKXRUXX/f/jSSsVjkz4uUe2jUyL+XHX1nSmROTPHSAJ+oKf0BLnfqUxFkEUTwLnayssP2nwGgq35b7wEbTFIXdrjHGFUVQIDeERz8UThew=="
CyberghostClientCertificate = "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"
CyberghostCertificate = "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"
)
func CyberghostRegionChoices() (choices []string) {

View File

@@ -66,15 +66,16 @@ func MullvadServers() []models.MullvadServer {
{Country: "Australia", City: "Sydney", ISP: "Intergrid", Owned: false, IPs: []net.IP{{43, 245, 162, 130}, {103, 77, 232, 130}, {103, 77, 232, 146}}, IPsV6: []net.IP{{0x24, 0x0, 0xfa, 0x80, 0x0, 0x1, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x24, 0x0, 0xfa, 0x80, 0x0, 0x1, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x24, 0x0, 0xfa, 0x80, 0x0, 0x1, 0x0, 0x15, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "Australia", City: "Sydney", ISP: "M247", Owned: false, IPs: []net.IP{{89, 44, 10, 18}, {89, 44, 10, 34}, {89, 44, 10, 50}, {89, 44, 10, 194}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x84, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x84, 0x0, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x84, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x84, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
{Country: "Austria", City: "Vienna", ISP: "M247", Owned: false, IPs: []net.IP{{5, 253, 207, 34}, {86, 107, 21, 210}, {86, 107, 21, 226}, {86, 107, 21, 242}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x29, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x29, 0x0, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x29, 0x0, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x29, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x4f}}},
{Country: "Belgium", City: "Brussels", ISP: "M247", Owned: false, IPs: []net.IP{{37, 120, 143, 138}, {37, 120, 218, 138}, {37, 120, 218, 146}, {91, 207, 57, 50}, {185, 104, 186, 202}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0x32, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0x55, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
{Country: "Belgium", City: "Brussels", ISP: "M247", Owned: false, IPs: []net.IP{{37, 120, 143, 138}, {37, 120, 218, 138}, {37, 120, 218, 146}, {91, 207, 57, 50}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0x32, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x27, 0x0, 0x55, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
{Country: "Brazil", City: "Sao Paulo", ISP: "Heficed", Owned: false, IPs: []net.IP{{191, 101, 62, 178}}, IPsV6: []net.IP{{0x28, 0x3, 0x0, 0x80, 0x80, 0x3, 0x80, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "Brazil", City: "Sao Paulo", ISP: "Qnax", Owned: false, IPs: []net.IP{{177, 67, 80, 186}}, IPsV6: []net.IP{{0x28, 0x4, 0x53, 0x64, 0x21, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Bulgaria", City: "Sofia", ISP: "M247", Owned: false, IPs: []net.IP{{37, 120, 152, 114}, {37, 120, 152, 146}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x30, 0x0, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x30, 0x0, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Canada", City: "Montreal", ISP: "M247", Owned: false, IPs: []net.IP{{89, 36, 78, 18}, {89, 36, 78, 34}, {89, 36, 78, 50}, {89, 36, 78, 66}, {89, 36, 78, 82}, {89, 36, 78, 98}, {89, 36, 78, 114}, {89, 36, 78, 130}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xb6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xb7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xba, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x0, 0xc9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x9, 0x1, 0x61, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}}},
{Country: "Canada", City: "Toronto", ISP: "Amanah", Owned: false, IPs: []net.IP{{162, 219, 176, 250}}, IPsV6: []net.IP{{0x26, 0x6, 0x60, 0x80, 0x10, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "Canada", City: "Toronto", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 132, 34}, {198, 54, 132, 50}, {198, 54, 132, 66}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x60, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x60, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x60, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}}},
{Country: "Canada", City: "Vancouver", ISP: "100TB", Owned: false, IPs: []net.IP{{172, 83, 40, 34}, {172, 83, 40, 38}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0xd, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0xd, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Canada", City: "Vancouver", ISP: "Esecuredata", Owned: false, IPs: []net.IP{{71, 19, 248, 240}, {71, 19, 249, 81}}, IPsV6: []net.IP{{0x26, 0x5, 0x0, 0x80, 0x0, 0x18, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4}, {0x26, 0x5, 0x0, 0x80, 0x0, 0x19, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5}}},
{Country: "Czech Republic", City: "Prague", ISP: "M247", Owned: false, IPs: []net.IP{{185, 156, 174, 170}, {185, 216, 35, 242}, {217, 138, 199, 74}, {217, 138, 199, 82}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0xb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
{Country: "Czech Republic", City: "Prague", ISP: "M247", Owned: false, IPs: []net.IP{{185, 156, 174, 146}, {185, 156, 174, 170}, {185, 216, 35, 242}, {217, 138, 199, 74}, {217, 138, 199, 82}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0xb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x33, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
{Country: "Denmark", City: "Copenhagen", ISP: "31173", Owned: true, IPs: []net.IP{{45, 129, 56, 81}, {141, 98, 254, 71}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x8, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x8, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "Denmark", City: "Copenhagen", ISP: "Asergo", Owned: false, IPs: []net.IP{{82, 103, 140, 213}}, IPsV6: []net.IP{{0x2a, 0x0, 0x90, 0x80, 0x0, 0x1, 0x9, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Denmark", City: "Copenhagen", ISP: "Blix", Owned: false, IPs: []net.IP{{134, 90, 149, 138}}, IPsV6: []net.IP{{0x2a, 0x2, 0xed, 0x1, 0x41, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
@@ -89,52 +90,53 @@ func MullvadServers() []models.MullvadServer {
{Country: "Hong Kong", City: "Hong Kong", ISP: "M247", Owned: false, IPs: []net.IP{{89, 45, 6, 50}, {89, 45, 6, 66}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x92, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x92, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Hungary", City: "Budapest", ISP: "M247", Owned: false, IPs: []net.IP{{86, 106, 74, 34}, {86, 106, 74, 50}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x26, 0x0, 0xab, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x26, 0x0, 0xac, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
{Country: "Ireland", City: "Dublin", ISP: "M247", Owned: false, IPs: []net.IP{{217, 138, 222, 82}, {217, 138, 222, 90}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x88, 0x0, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x88, 0x0, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "Israel", City: "Tel Aviv", ISP: "HQServ", Owned: false, IPs: []net.IP{{185, 191, 207, 210}}, IPsV6: []net.IP{{0x2a, 0xa, 0x1d, 0xc4, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Italy", City: "Milan", ISP: "M247", Owned: false, IPs: []net.IP{{89, 40, 182, 146}, {89, 40, 182, 210}, {192, 145, 127, 98}, {192, 145, 127, 114}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x24, 0x0, 0x76, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x24, 0x0, 0x77, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x24, 0x0, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x24, 0x0, 0x79, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
{Country: "Japan", City: "Tokyo", ISP: "M247", Owned: false, IPs: []net.IP{{217, 138, 252, 50}, {217, 138, 252, 162}, {217, 138, 252, 178}, {217, 138, 252, 194}, {217, 138, 252, 210}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x40, 0x0, 0xb1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x40, 0x0, 0xb2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x40, 0x0, 0xb3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x40, 0x0, 0xb4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x40, 0x0, 0xb5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
{Country: "Latvia", City: "Riga", ISP: "Makonix", Owned: false, IPs: []net.IP{{31, 170, 22, 2}}, IPsV6: []net.IP{{0x2a, 0x0, 0xc, 0x68, 0x0, 0x0, 0xcb, 0xcf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Luxembourg", City: "Luxembourg", ISP: "Evoluso", Owned: false, IPs: []net.IP{{92, 223, 89, 160}, {92, 223, 89, 182}}, IPsV6: []net.IP{{0x2a, 0x3, 0x90, 0xc0, 0x0, 0x83, 0x29, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x90, 0xc0, 0x0, 0x83, 0x29, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "Luxembourg", City: "Luxembourg", ISP: "Evoluso", Owned: false, IPs: []net.IP{{92, 223, 89, 182}}, IPsV6: []net.IP{{0x2a, 0x3, 0x90, 0xc0, 0x0, 0x83, 0x29, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "Moldova", City: "Chisinau", ISP: "Trabia", Owned: false, IPs: []net.IP{{178, 175, 142, 194}}, IPsV6: []net.IP{{0x2a, 0x0, 0x1d, 0xc0, 0x29, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Netherlands", City: "Amsterdam", ISP: "31173", Owned: true, IPs: []net.IP{{185, 65, 134, 131}, {185, 65, 134, 132}, {185, 65, 134, 133}, {185, 65, 134, 134}, {185, 65, 134, 135}, {185, 65, 134, 136}, {185, 65, 134, 139}, {185, 65, 134, 140}, {185, 65, 134, 141}, {185, 65, 134, 142}, {185, 65, 134, 143}, {185, 65, 134, 144}, {185, 65, 134, 145}, {185, 65, 134, 146}, {185, 65, 134, 147}, {185, 65, 134, 148}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x3, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8f}}},
{Country: "New Zealand", City: "Auckland", ISP: "Intergrid", Owned: false, IPs: []net.IP{{103, 231, 91, 114}}, IPsV6: []net.IP{{0x24, 0x0, 0xfa, 0x80, 0x0, 0x4, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Norway", City: "Oslo", ISP: "Blix", Owned: true, IPs: []net.IP{{91, 90, 44, 11}, {91, 90, 44, 12}, {91, 90, 44, 13}, {91, 90, 44, 14}, {91, 90, 44, 15}, {91, 90, 44, 16}, {91, 90, 44, 17}, {91, 90, 44, 18}}, IPsV6: []net.IP{{0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0x2, 0x20, 0xc8, 0x41, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}}},
{Country: "Poland", City: "Warsaw", ISP: "M247", Owned: false, IPs: []net.IP{{37, 120, 156, 162}, {37, 120, 211, 186}, {37, 120, 211, 194}, {37, 120, 211, 202}, {185, 244, 214, 210}, {185, 244, 214, 215}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x13, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x13, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x13, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x13, 0x0, 0x3a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x13, 0x0, 0x3b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x13, 0xb, 0xb1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Portugal", City: "Lisbon", ISP: "Dotsi", Owned: false, IPs: []net.IP{{5, 206, 231, 214}}, IPsV6: []net.IP{{0x2a, 0x0, 0x1c, 0x60, 0x0, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Romania", City: "Bucharest", ISP: "M247", Owned: false, IPs: []net.IP{{185, 163, 110, 66}, {185, 163, 110, 82}, {185, 163, 110, 98}, {185, 163, 110, 114}}, IPsV6: []net.IP{{0x2a, 0x4, 0x9d, 0xc0, 0x0, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1f}, {0x2a, 0x4, 0x9d, 0xc0, 0x0, 0x0, 0x0, 0x91, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x2f}, {0x2a, 0x4, 0x9d, 0xc0, 0x0, 0x0, 0x0, 0x92, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}, {0x2a, 0x4, 0x9d, 0xc0, 0x0, 0x0, 0x0, 0x93, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x4f}}},
{Country: "Serbia", City: "Belgrade", ISP: "M247", Owned: false, IPs: []net.IP{{89, 38, 224, 98}, {89, 38, 224, 114}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x7d, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x7d, 0x0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}}},
{Country: "Serbia", City: "Nis", ISP: "ninet", Owned: false, IPs: []net.IP{{176, 104, 107, 118}}, IPsV6: []net.IP{{0x2a, 0x6, 0x1, 0x85, 0x0, 0x1, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Singapore", City: "Singapore", ISP: "Leaseweb", Owned: false, IPs: []net.IP{{103, 254, 153, 82}}, IPsV6: []net.IP{{0x20, 0x1, 0xd, 0xf1, 0x8, 0x0, 0xa0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1}}},
{Country: "Singapore", City: "Singapore", ISP: "M247", Owned: false, IPs: []net.IP{{89, 38, 225, 34}, {94, 198, 43, 2}, {94, 198, 43, 18}}, IPsV6: []net.IP{{0x2a, 0xa, 0xb6, 0x40, 0x0, 0x1, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0xa, 0xb6, 0x40, 0x0, 0x1, 0x0, 0x55, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xa, 0xb6, 0x40, 0x0, 0x1, 0x0, 0x56, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "Spain", City: "Madrid", ISP: "M247", Owned: false, IPs: []net.IP{{45, 152, 183, 26}, {45, 152, 183, 42}, {89, 238, 178, 34}, {89, 238, 178, 74}, {195, 206, 107, 146}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x23, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x23, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf2}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x23, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x23, 0x0, 0x58, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x23, 0x0, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
{Country: "Sweden", City: "Gothenburg", ISP: "31173", Owned: true, IPs: []net.IP{{185, 213, 154, 131}, {185, 213, 154, 132}, {185, 213, 154, 133}, {185, 213, 154, 134}, {185, 213, 154, 135}, {185, 213, 154, 136}, {185, 213, 154, 137}, {185, 213, 154, 138}, {185, 213, 154, 139}, {185, 213, 154, 140}, {185, 213, 154, 141}, {185, 213, 154, 142}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x5, 0xf0, 0x11, 0x0, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}}},
{Country: "Sweden", City: "Helsingborg", ISP: "31173", Owned: true, IPs: []net.IP{{185, 213, 152, 131}, {185, 213, 152, 132}, {185, 213, 152, 133}, {185, 213, 152, 134}, {185, 213, 152, 137}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x2, 0xf7, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x2, 0xf7, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x2, 0xf7, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x2, 0xf7, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x2, 0xf7, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}}},
{Country: "Sweden", City: "Malmö", ISP: "31173", Owned: true, IPs: []net.IP{{45, 83, 220, 81}, {45, 83, 220, 82}, {45, 83, 220, 83}, {45, 83, 220, 84}, {45, 83, 220, 85}, {45, 83, 220, 86}, {45, 83, 220, 87}, {45, 83, 220, 88}, {45, 83, 220, 89}, {45, 83, 220, 90}, {45, 83, 220, 91}, {45, 83, 220, 92}, {45, 83, 220, 93}, {141, 98, 255, 83}, {141, 98, 255, 84}, {141, 98, 255, 85}, {141, 98, 255, 86}, {141, 98, 255, 87}, {141, 98, 255, 88}, {141, 98, 255, 89}, {141, 98, 255, 90}, {141, 98, 255, 91}, {141, 98, 255, 92}, {141, 98, 255, 93}, {141, 98, 255, 94}, {193, 138, 218, 131}, {193, 138, 218, 132}, {193, 138, 218, 133}, {193, 138, 218, 134}, {193, 138, 218, 135}, {193, 138, 218, 136}, {193, 138, 218, 137}, {193, 138, 218, 138}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}}},
{Country: "Sweden", City: "Stockholm", ISP: "31173", Owned: true, IPs: []net.IP{{185, 65, 135, 131}, {185, 65, 135, 133}, {185, 65, 135, 134}, {185, 65, 135, 135}, {185, 65, 135, 136}, {185, 65, 135, 137}, {185, 65, 135, 138}, {185, 65, 135, 139}, {185, 65, 135, 140}, {185, 65, 135, 141}, {185, 65, 135, 142}, {185, 65, 135, 143}, {185, 65, 135, 144}, {185, 65, 135, 145}, {185, 65, 135, 146}, {185, 65, 135, 147}, {185, 65, 135, 148}, {185, 65, 135, 149}, {185, 65, 135, 150}, {185, 65, 135, 151}, {185, 65, 135, 152}, {185, 65, 135, 153}, {185, 65, 135, 154}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4e}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x4f}}},
{Country: "Sweden", City: "Helsingborg", ISP: "31173", Owned: true, IPs: []net.IP{{185, 213, 152, 131}, {185, 213, 152, 132}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x2, 0xf7, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x2, 0xf7, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "Sweden", City: "Malmö", ISP: "31173", Owned: true, IPs: []net.IP{{45, 83, 220, 87}, {45, 83, 220, 88}, {45, 83, 220, 89}, {45, 83, 220, 90}, {45, 83, 220, 91}, {45, 83, 220, 92}, {45, 83, 220, 93}, {141, 98, 255, 83}, {141, 98, 255, 84}, {141, 98, 255, 85}, {141, 98, 255, 86}, {141, 98, 255, 87}, {141, 98, 255, 88}, {141, 98, 255, 89}, {141, 98, 255, 90}, {141, 98, 255, 91}, {141, 98, 255, 92}, {141, 98, 255, 93}, {141, 98, 255, 94}, {193, 138, 218, 131}, {193, 138, 218, 132}, {193, 138, 218, 133}, {193, 138, 218, 134}, {193, 138, 218, 135}, {193, 138, 218, 136}, {193, 138, 218, 137}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xe0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x1, 0xf4, 0x10, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}}},
{Country: "Sweden", City: "Stockholm", ISP: "31173", Owned: true, IPs: []net.IP{{185, 65, 135, 136}, {185, 65, 135, 137}, {185, 65, 135, 138}, {185, 65, 135, 139}, {185, 65, 135, 140}, {185, 65, 135, 141}, {185, 65, 135, 142}, {185, 65, 135, 143}, {185, 65, 135, 144}, {185, 65, 135, 145}, {185, 65, 135, 146}, {185, 65, 135, 147}, {185, 65, 135, 148}, {185, 65, 135, 149}, {185, 65, 135, 150}, {185, 65, 135, 151}, {185, 65, 135, 152}, {185, 65, 135, 153}, {185, 65, 135, 154}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4e}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xf}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x4, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x4f}}},
{Country: "Switzerland", City: "Zurich", ISP: "31173", Owned: true, IPs: []net.IP{{193, 32, 127, 81}, {193, 32, 127, 82}, {193, 32, 127, 83}, {193, 32, 127, 84}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0xa, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0xa, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0xa, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0xa, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
{Country: "Switzerland", City: "Zurich", ISP: "M247", Owned: false, IPs: []net.IP{{91, 193, 4, 2}, {91, 193, 4, 18}, {91, 193, 4, 34}, {91, 193, 4, 50}, {91, 193, 4, 66}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x28, 0x0, 0x84, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x28, 0x0, 0x85, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x28, 0x0, 0x86, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x28, 0x0, 0x87, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x28, 0x0, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "Switzerland", City: "Zurich", ISP: "PrivateLayer", Owned: false, IPs: []net.IP{{81, 17, 20, 34}, {179, 43, 128, 170}}, IPsV6: []net.IP{{0x2a, 0x2, 0x29, 0xb8, 0xdc, 0x1, 0x5, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x2, 0x29, 0xb8, 0xdc, 0x1, 0x18, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "Switzerland", City: "Zurich", ISP: "PrivateLayer", Owned: false, IPs: []net.IP{{179, 43, 128, 170}}, IPsV6: []net.IP{{0x2a, 0x2, 0x29, 0xb8, 0xdc, 0x1, 0x5, 0x97, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "UK", City: "London", ISP: "31173", Owned: true, IPs: []net.IP{{141, 98, 252, 131}, {141, 98, 252, 132}, {141, 98, 252, 133}, {141, 98, 252, 138}, {141, 98, 252, 139}, {141, 98, 252, 140}, {185, 195, 232, 84}, {185, 195, 232, 85}, {185, 195, 232, 86}}, IPsV6: []net.IP{{0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0x3, 0x1b, 0x20, 0x0, 0x7, 0xf0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}}},
{Country: "UK", City: "London", ISP: "M247", Owned: false, IPs: []net.IP{{45, 87, 215, 50}, {185, 200, 118, 178}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x31, 0x2, 0x35, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x31, 0x2, 0x36, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "UK", City: "Manchester", ISP: "M247", Owned: false, IPs: []net.IP{{37, 120, 159, 164}, {89, 238, 132, 36}, {194, 37, 96, 180}, {217, 151, 98, 68}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x21, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x21, 0x0, 0x1b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x21, 0x0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x21, 0x0, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "USA", City: "Atlanta, GA", ISP: "100TB", Owned: false, IPs: []net.IP{{66, 115, 180, 227}, {66, 115, 180, 228}, {66, 115, 180, 229}, {66, 115, 180, 230}, {107, 152, 108, 62}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0x1, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x1, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x1, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x1, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x6, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}}},
{Country: "USA", City: "Atlanta, GA", ISP: "Quadranet", Owned: false, IPs: []net.IP{{104, 129, 24, 242}}, IPsV6: []net.IP{{0x26, 0x7, 0xfc, 0xd0, 0xaa, 0x80, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "USA", City: "Chicago, IL", ISP: "Quadranet", Owned: false, IPs: []net.IP{{104, 129, 31, 26}}, IPsV6: []net.IP{{0x26, 0x7, 0xfc, 0xd0, 0xbb, 0x80, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "USA", City: "Chicago, IL", ISP: "Tzulo", Owned: false, IPs: []net.IP{{68, 235, 43, 10}, {68, 235, 43, 18}, {68, 235, 43, 26}, {68, 235, 43, 34}, {68, 235, 43, 42}, {68, 235, 43, 50}, {68, 235, 43, 58}, {68, 235, 43, 66}, {68, 235, 43, 74}, {68, 235, 43, 122}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x51, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x55, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x56, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x57, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x58, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "USA", City: "Dallas, TX", ISP: "100TB", Owned: false, IPs: []net.IP{{174, 127, 113, 3}, {174, 127, 113, 4}, {174, 127, 113, 5}, {174, 127, 113, 6}, {174, 127, 113, 7}}, IPsV6: []net.IP{{0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
{Country: "USA", City: "Dallas, TX", ISP: "M247", Owned: false, IPs: []net.IP{{193, 27, 13, 34}, {193, 27, 13, 50}, {193, 27, 13, 66}, {193, 27, 13, 82}, {193, 27, 13, 178}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "USA", City: "Dallas, TX", ISP: "Quadranet", Owned: false, IPs: []net.IP{{96, 44, 145, 18}, {96, 44, 147, 130}}, IPsV6: []net.IP{{0x26, 0x7, 0xfc, 0xd0, 0xda, 0x80, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8}, {0x26, 0x7, 0xfc, 0xd0, 0xda, 0x80, 0x18, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9}}},
{Country: "USA", City: "Denver, CO", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 128, 66}, {198, 54, 128, 74}, {198, 54, 128, 106}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x20, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x20, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x20, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}}},
{Country: "USA", City: "Los Angeles, CA", ISP: "100TB", Owned: false, IPs: []net.IP{{104, 200, 152, 66}, {107, 181, 168, 130}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0x3, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x3, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}}},
{Country: "USA", City: "Los Angeles, CA", ISP: "M247", Owned: false, IPs: []net.IP{{89, 46, 114, 15}, {89, 46, 114, 28}, {89, 46, 114, 41}, {89, 46, 114, 54}, {89, 46, 114, 67}, {89, 46, 114, 80}, {89, 46, 114, 93}, {89, 46, 114, 106}, {89, 46, 114, 119}, {89, 46, 114, 132}, {89, 46, 114, 145}, {89, 46, 114, 158}, {89, 46, 114, 171}, {89, 46, 114, 184}, {89, 46, 114, 197}, {89, 46, 114, 210}, {89, 46, 114, 223}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}}},
{Country: "USA", City: "Los Angeles, CA", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 129, 74}, {198, 54, 129, 82}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x30, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x30, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "USA", City: "Miami, FL", ISP: "M247", Owned: false, IPs: []net.IP{{94, 198, 42, 50}, {94, 198, 42, 66}, {94, 198, 42, 82}, {94, 198, 42, 98}, {193, 27, 12, 2}, {193, 27, 12, 18}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0x0, 0x33, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0x0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0x0, 0x35, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0x0, 0x36, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0xa, 0xd6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0xa, 0xd7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "USA", City: "New York, NY", ISP: "100TB", Owned: false, IPs: []net.IP{{107, 182, 226, 206}, {107, 182, 226, 218}}, IPsV6: []net.IP{{0x26, 0x6, 0x2e, 0x0, 0x80, 0x3, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x3, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}}},
{Country: "USA", City: "New York, NY", ISP: "M247", Owned: false, IPs: []net.IP{{86, 106, 121, 15}, {86, 106, 121, 28}, {86, 106, 121, 41}, {86, 106, 121, 54}, {86, 106, 121, 67}, {86, 106, 121, 80}, {86, 106, 121, 93}, {86, 106, 121, 106}, {86, 106, 121, 119}, {86, 106, 121, 132}, {89, 46, 62, 15}, {89, 46, 62, 28}, {89, 46, 62, 41}, {89, 46, 62, 54}, {89, 46, 62, 67}, {89, 46, 62, 80}, {89, 46, 62, 93}, {89, 46, 62, 106}, {89, 46, 62, 119}, {89, 46, 62, 132}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x71, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x72, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x73, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x75, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x76, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x77, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x79, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x7a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x99, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0xa1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0xa2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x9f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0xa3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xf}}},
{Country: "USA", City: "Phoenix, AZ", ISP: "100TB", Owned: false, IPs: []net.IP{{107, 152, 99, 86}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0x5, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
{Country: "USA", City: "Raleigh, NC", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 130, 34}, {198, 54, 130, 50}, {198, 54, 130, 66}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x40, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x40, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x40, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}}},
{Country: "USA", City: "Salt Lake City, UT", ISP: "100TB", Owned: false, IPs: []net.IP{{69, 4, 234, 132}, {69, 4, 234, 133}, {69, 4, 234, 134}, {69, 4, 234, 135}, {69, 4, 234, 136}, {69, 4, 234, 137}}, IPsV6: []net.IP{{0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}}},
{Country: "USA", City: "Seattle, WA", ISP: "100TB", Owned: false, IPs: []net.IP{{104, 200, 129, 42}, {104, 200, 129, 110}, {104, 200, 129, 150}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0xc, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0xc, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0xc, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "USA", City: "Seattle, WA", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 131, 34}, {198, 54, 131, 50}, {198, 54, 131, 66}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x50, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x50, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x50, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}}},
{Country: "USA", City: "Secaucus, NJ", ISP: "Quadranet", Owned: false, IPs: []net.IP{{23, 226, 131, 130}, {23, 226, 131, 154}}, IPsV6: []net.IP{{0x26, 0x7, 0xfc, 0xd0, 0xcc, 0xc0, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x7, 0xfc, 0xd0, 0xcc, 0xc0, 0x1d, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "USA", City: "Atlanta GA", ISP: "100TB", Owned: false, IPs: []net.IP{{66, 115, 180, 227}, {66, 115, 180, 228}, {66, 115, 180, 229}, {66, 115, 180, 230}, {107, 152, 108, 62}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0x1, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x1, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x1, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x1, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x6, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}}},
{Country: "USA", City: "Atlanta GA", ISP: "Quadranet", Owned: false, IPs: []net.IP{{104, 129, 24, 242}}, IPsV6: []net.IP{{0x26, 0x7, 0xfc, 0xd0, 0xaa, 0x80, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "USA", City: "Chicago IL", ISP: "Quadranet", Owned: false, IPs: []net.IP{{104, 129, 31, 26}}, IPsV6: []net.IP{{0x26, 0x7, 0xfc, 0xd0, 0xbb, 0x80, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "USA", City: "Chicago IL", ISP: "Tzulo", Owned: false, IPs: []net.IP{{68, 235, 43, 10}, {68, 235, 43, 18}, {68, 235, 43, 26}, {68, 235, 43, 34}, {68, 235, 43, 42}, {68, 235, 43, 50}, {68, 235, 43, 58}, {68, 235, 43, 66}, {68, 235, 43, 74}, {68, 235, 43, 122}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x51, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x55, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x56, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x57, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x58, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x26, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "USA", City: "Dallas TX", ISP: "100TB", Owned: false, IPs: []net.IP{{174, 127, 113, 3}, {174, 127, 113, 4}, {174, 127, 113, 5}, {174, 127, 113, 6}, {174, 127, 113, 7}}, IPsV6: []net.IP{{0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x7, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}}},
{Country: "USA", City: "Dallas TX", ISP: "M247", Owned: false, IPs: []net.IP{{193, 27, 13, 34}, {193, 27, 13, 50}, {193, 27, 13, 66}, {193, 27, 13, 82}, {193, 27, 13, 178}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x20, 0x1, 0xa, 0xc8, 0x0, 0x9a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "USA", City: "Dallas TX", ISP: "Quadranet", Owned: false, IPs: []net.IP{{96, 44, 145, 18}, {96, 44, 147, 130}}, IPsV6: []net.IP{{0x26, 0x7, 0xfc, 0xd0, 0xda, 0x80, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8}, {0x26, 0x7, 0xfc, 0xd0, 0xda, 0x80, 0x18, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9}}},
{Country: "USA", City: "Denver CO", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 128, 66}, {198, 54, 128, 74}, {198, 54, 128, 106}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x20, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x20, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x20, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}}},
{Country: "USA", City: "Los Angeles CA", ISP: "100TB", Owned: false, IPs: []net.IP{{104, 200, 152, 66}, {107, 181, 168, 130}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0x3, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0x3, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}}},
{Country: "USA", City: "Los Angeles CA", ISP: "M247", Owned: false, IPs: []net.IP{{89, 46, 114, 15}, {89, 46, 114, 28}, {89, 46, 114, 41}, {89, 46, 114, 54}, {89, 46, 114, 67}, {89, 46, 114, 80}, {89, 46, 114, 93}, {89, 46, 114, 106}, {89, 46, 114, 119}, {89, 46, 114, 132}, {89, 46, 114, 145}, {89, 46, 114, 158}, {89, 46, 114, 171}, {89, 46, 114, 184}, {89, 46, 114, 197}, {89, 46, 114, 210}, {89, 46, 114, 223}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x8, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}}},
{Country: "USA", City: "Los Angeles CA", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 129, 74}, {198, 54, 129, 82}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x30, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x30, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "USA", City: "Miami FL", ISP: "M247", Owned: false, IPs: []net.IP{{94, 198, 42, 50}, {94, 198, 42, 66}, {94, 198, 42, 82}, {94, 198, 42, 98}, {193, 27, 12, 2}, {193, 27, 12, 18}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0x0, 0x33, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0x0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0x0, 0x35, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0x0, 0x36, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0xa, 0xd6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x6, 0xa, 0xd7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
{Country: "USA", City: "New York NY", ISP: "100TB", Owned: false, IPs: []net.IP{{107, 182, 226, 206}, {107, 182, 226, 218}}, IPsV6: []net.IP{{0x26, 0x6, 0x2e, 0x0, 0x80, 0x3, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x26, 0x6, 0x2e, 0x0, 0x80, 0x3, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}}},
{Country: "USA", City: "New York NY", ISP: "M247", Owned: false, IPs: []net.IP{{86, 106, 121, 15}, {86, 106, 121, 28}, {86, 106, 121, 41}, {86, 106, 121, 54}, {86, 106, 121, 67}, {86, 106, 121, 80}, {86, 106, 121, 93}, {86, 106, 121, 106}, {86, 106, 121, 119}, {86, 106, 121, 132}, {89, 46, 62, 15}, {89, 46, 62, 28}, {89, 46, 62, 41}, {89, 46, 62, 54}, {89, 46, 62, 67}, {89, 46, 62, 80}, {89, 46, 62, 93}, {89, 46, 62, 106}, {89, 46, 62, 119}, {89, 46, 62, 132}}, IPsV6: []net.IP{{0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x71, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x72, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x73, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x75, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x76, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x77, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x79, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x7a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x99, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0x9f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0xa1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0xa2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x9f}, {0x2a, 0xd, 0x56, 0x0, 0x0, 0x24, 0xa, 0xa3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xf}}},
{Country: "USA", City: "Phoenix AZ", ISP: "100TB", Owned: false, IPs: []net.IP{{107, 152, 99, 86}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0x5, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}}},
{Country: "USA", City: "Phoenix AZ", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 133, 34}, {198, 54, 133, 50}, {198, 54, 133, 66}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x70, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x70, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x70, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}}},
{Country: "USA", City: "Raleigh NC", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 130, 34}, {198, 54, 130, 50}, {198, 54, 130, 66}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x40, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x40, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x40, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}}},
{Country: "USA", City: "Salt Lake City UT", ISP: "100TB", Owned: false, IPs: []net.IP{{69, 4, 234, 132}, {69, 4, 234, 133}, {69, 4, 234, 134}, {69, 4, 234, 135}, {69, 4, 234, 136}, {69, 4, 234, 137}}, IPsV6: []net.IP{{0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f}, {0x26, 0x6, 0x2e, 0x0, 0x0, 0x0, 0x0, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f}}},
{Country: "USA", City: "San Jose CA", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 134, 34}, {198, 54, 134, 50}, {198, 54, 134, 66}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x80, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x80, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x80, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}}},
{Country: "USA", City: "Seattle WA", ISP: "100TB", Owned: false, IPs: []net.IP{{104, 200, 129, 42}, {104, 200, 129, 110}, {104, 200, 129, 150}}, IPsV6: []net.IP{{0x26, 0x7, 0xf7, 0xa0, 0x0, 0xc, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0xc, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f}, {0x26, 0x7, 0xf7, 0xa0, 0x0, 0xc, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "USA", City: "Seattle WA", ISP: "Tzulo", Owned: false, IPs: []net.IP{{198, 54, 131, 34}, {198, 54, 131, 50}, {198, 54, 131, 66}}, IPsV6: []net.IP{{0x26, 0x7, 0x90, 0x0, 0x50, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1f}, {0x26, 0x7, 0x90, 0x0, 0x50, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x2f}, {0x26, 0x7, 0x90, 0x0, 0x50, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3f}}},
{Country: "USA", City: "Secaucus NJ", ISP: "Quadranet", Owned: false, IPs: []net.IP{{23, 226, 131, 130}, {23, 226, 131, 154}}, IPsV6: []net.IP{{0x26, 0x7, 0xfc, 0xd0, 0xcc, 0xc0, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}, {0x26, 0x7, 0xfc, 0xd0, 0xcc, 0xc0, 0x1d, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f}}},
{Country: "United Arab Emirates", City: "Dubai", ISP: "M247", Owned: false, IPs: []net.IP{{45, 9, 249, 34}}, IPsV6: []net.IP{{0x20, 0x1, 0xa, 0xc8, 0x0, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f}}},
}
}

View File

@@ -15,18 +15,18 @@ const (
OpenVPNAuthConf models.Filepath = "/etc/openvpn/auth.conf"
// OpenVPNConf is the file path to the OpenVPN client configuration file.
OpenVPNConf models.Filepath = "/etc/openvpn/target.ovpn"
// PIAPortForward is the file path to the port forwarding JSON information for PIA v4 servers.
// PIAPortForward is the file path to the port forwarding JSON information for PIA servers.
PIAPortForward models.Filepath = "/gluetun/piaportforward.json"
// TunnelDevice is the file path to tun device.
TunnelDevice models.Filepath = "/dev/net/tun"
// NetRoute is the path to the file containing information on the network route.
NetRoute models.Filepath = "/proc/net/route"
// TinyProxyConf is the filepath to the tinyproxy configuration file.
TinyProxyConf models.Filepath = "/etc/tinyproxy/tinyproxy.conf"
// ShadowsocksConf is the filepath to the shadowsocks configuration file.
ShadowsocksConf models.Filepath = "/etc/shadowsocks.json"
// RootHints is the filepath to the root.hints file used by Unbound.
RootHints models.Filepath = "/etc/unbound/root.hints"
// RootKey is the filepath to the root.key file used by Unbound.
RootKey models.Filepath = "/etc/unbound/root.key"
// Client key filepath, used by Cyberghost.
ClientKey models.Filepath = "/gluetun/client.key"
// Client certificate filepath, used by Cyberghost.
ClientCertificate models.Filepath = "/gluetun/client.crt"
)

View File

@@ -28,185 +28,99 @@ func PIAGeoChoices() (choices []string) {
//nolint:lll
func PIAServers() []models.PIAServer {
return []models.PIAServer{
{Region: "AU Melbourne", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "melbourne405", IPs: []net.IP{{103, 2, 198, 108}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "melbourne405", IPs: []net.IP{{103, 2, 198, 103}}}},
{Region: "AU Perth", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "perth404", IPs: []net.IP{{43, 250, 205, 186}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "perth404", IPs: []net.IP{{43, 250, 205, 188}}}},
{Region: "AU Sydney", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "sydney405", IPs: []net.IP{{27, 50, 76, 132}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "sydney405", IPs: []net.IP{{27, 50, 76, 132}}}},
{Region: "Albania", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "tirana401", IPs: []net.IP{{31, 171, 154, 131}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "tirana401", IPs: []net.IP{{31, 171, 154, 137}}}},
{Region: "Algeria", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "algiers402", IPs: []net.IP{{45, 133, 91, 209}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "algiers402", IPs: []net.IP{{45, 133, 91, 227}}}},
{Region: "Andorra", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "andorra401", IPs: []net.IP{{45, 139, 49, 232}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "andorra401", IPs: []net.IP{{45, 139, 49, 238}}}},
{Region: "Argentina", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "buenosaires401", IPs: []net.IP{{190, 106, 134, 92}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "buenosaires401", IPs: []net.IP{{190, 106, 134, 89}}}},
{Region: "Armenia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "armenia402", IPs: []net.IP{{45, 139, 50, 229}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "armenia402", IPs: []net.IP{{45, 139, 50, 213}}}},
{Region: "Austria", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "vienna403", IPs: []net.IP{{156, 146, 60, 104}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "vienna403", IPs: []net.IP{{156, 146, 60, 100}}}},
{Region: "Bahamas", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "bahamas402", IPs: []net.IP{{45, 132, 143, 206}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "bahamas402", IPs: []net.IP{{45, 132, 143, 229}}}},
{Region: "Belgium", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "brussels403", IPs: []net.IP{{5, 253, 205, 147}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "brussels403", IPs: []net.IP{{5, 253, 205, 153}}}},
{Region: "Bosnia and Herzegovina", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "sarajevo401", IPs: []net.IP{{185, 212, 111, 76}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "sarajevo401", IPs: []net.IP{{185, 212, 111, 77}}}},
{Region: "Brazil", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "saopaolo402", IPs: []net.IP{{188, 241, 177, 56}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "saopaolo402", IPs: []net.IP{{188, 241, 177, 51}}}},
{Region: "Bulgaria", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "sofia401", IPs: []net.IP{{217, 138, 221, 131}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "sofia401", IPs: []net.IP{{217, 138, 221, 133}}}},
{Region: "CA Montreal", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "montreal403", IPs: []net.IP{{172, 98, 71, 62}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "montreal403", IPs: []net.IP{{172, 98, 71, 59}}}},
{Region: "CA Ontario", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "ontario402", IPs: []net.IP{{172, 83, 47, 138}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "ontario402", IPs: []net.IP{{172, 83, 47, 196}}}},
{Region: "CA Toronto", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "toronto405", IPs: []net.IP{{172, 83, 47, 250}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "toronto405", IPs: []net.IP{{172, 83, 47, 251}}}},
{Region: "CA Vancouver", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "vancouver407", IPs: []net.IP{{172, 98, 89, 70}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "vancouver407", IPs: []net.IP{{172, 98, 89, 18}}}},
{Region: "Cambodia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "cambodia401", IPs: []net.IP{{188, 215, 235, 105}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "cambodia401", IPs: []net.IP{{188, 215, 235, 102}}}},
{Region: "China", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "china403", IPs: []net.IP{{86, 107, 104, 212}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "china403", IPs: []net.IP{{86, 107, 104, 216}}}},
{Region: "Cyprus", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "cyprus402", IPs: []net.IP{{45, 132, 137, 220}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "cyprus402", IPs: []net.IP{{45, 132, 137, 225}}}},
{Region: "Czech Republic", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "prague402", IPs: []net.IP{{212, 102, 39, 148}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "prague402", IPs: []net.IP{{212, 102, 39, 149}}}},
{Region: "DE Berlin", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "berlin410", IPs: []net.IP{{89, 36, 76, 153}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "berlin410", IPs: []net.IP{{89, 36, 76, 149}}}},
{Region: "DE Frankfurt", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "frankfurt406", IPs: []net.IP{{212, 102, 57, 96}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "frankfurt406", IPs: []net.IP{{212, 102, 57, 106}}}},
{Region: "Denmark", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "copenhagen402", IPs: []net.IP{{188, 126, 94, 93}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "copenhagen402", IPs: []net.IP{{188, 126, 94, 93}}}},
{Region: "Egypt", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "cairo401", IPs: []net.IP{{188, 214, 122, 106}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "cairo401", IPs: []net.IP{{188, 214, 122, 104}}}},
{Region: "Estonia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "talinn402", IPs: []net.IP{{95, 153, 31, 73}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "talinn402", IPs: []net.IP{{95, 153, 31, 73}}}},
{Region: "Finland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "helsinki402", IPs: []net.IP{{188, 126, 89, 45}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "helsinki402", IPs: []net.IP{{188, 126, 89, 45}}}},
{Region: "France", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "paris402", IPs: []net.IP{{156, 146, 63, 159}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "paris402", IPs: []net.IP{{156, 146, 63, 159}}}},
{Region: "Georgia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "georgia401", IPs: []net.IP{{45, 132, 138, 245}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "georgia401", IPs: []net.IP{{45, 132, 138, 236}}}},
{Region: "Greece", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "athens401", IPs: []net.IP{{154, 57, 3, 80}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "athens401", IPs: []net.IP{{154, 57, 3, 84}}}},
{Region: "Greenland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "greenland402", IPs: []net.IP{{45, 131, 209, 222}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "greenland402", IPs: []net.IP{{45, 131, 209, 208}}}},
{Region: "Hong Kong", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "hongkong402", IPs: []net.IP{{86, 107, 104, 234}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "hongkong402", IPs: []net.IP{{86, 107, 104, 240}}}},
{Region: "Hungary", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "budapest402", IPs: []net.IP{{86, 106, 74, 121}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "budapest402", IPs: []net.IP{{86, 106, 74, 125}}}},
{Region: "Iceland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "reykjavik402", IPs: []net.IP{{45, 133, 193, 86}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "reykjavik402", IPs: []net.IP{{45, 133, 193, 86}}}},
{Region: "India", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "mumbai405", IPs: []net.IP{{45, 120, 139, 97}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "mumbai405", IPs: []net.IP{{45, 120, 139, 97}}}},
{Region: "Iran", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "iran402", IPs: []net.IP{{45, 131, 4, 219}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "iran402", IPs: []net.IP{{45, 131, 4, 218}}}},
{Region: "Ireland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "dublin404", IPs: []net.IP{{193, 56, 252, 28}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "dublin404", IPs: []net.IP{{193, 56, 252, 24}}}},
{Region: "Isle of Man", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "douglas401", IPs: []net.IP{{45, 132, 140, 236}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "douglas401", IPs: []net.IP{{45, 132, 140, 244}}}},
{Region: "Israel", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "jerusalem401", IPs: []net.IP{{185, 77, 248, 19}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "jerusalem401", IPs: []net.IP{{185, 77, 248, 17}}}},
{Region: "Italy", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "milano402", IPs: []net.IP{{156, 146, 41, 20}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "milano402", IPs: []net.IP{{156, 146, 41, 42}}}},
{Region: "Japan", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "tokyo401", IPs: []net.IP{{156, 146, 34, 135}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "tokyo401", IPs: []net.IP{{156, 146, 34, 157}}}},
{Region: "Kazakhstan", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "kazakhstan402", IPs: []net.IP{{45, 133, 88, 209}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "kazakhstan402", IPs: []net.IP{{45, 133, 88, 229}}}},
{Region: "Latvia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "riga401", IPs: []net.IP{{109, 248, 149, 12}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "riga401", IPs: []net.IP{{109, 248, 149, 12}}}},
{Region: "Liechtenstein", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "liechtenstein401", IPs: []net.IP{{45, 139, 48, 236}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "liechtenstein401", IPs: []net.IP{{45, 139, 48, 242}}}},
{Region: "Lithuania", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "vilnius401", IPs: []net.IP{{85, 206, 165, 163}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "vilnius401", IPs: []net.IP{{85, 206, 165, 163}}}},
{Region: "Luxembourg", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "luxembourg401", IPs: []net.IP{{92, 223, 89, 74}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "luxembourg401", IPs: []net.IP{{92, 223, 89, 78}}}},
{Region: "Macedonia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "macedonia401", IPs: []net.IP{{185, 225, 28, 115}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "macedonia401", IPs: []net.IP{{185, 225, 28, 115}}}},
{Region: "Malta", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "malta401", IPs: []net.IP{{45, 137, 198, 238}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "malta401", IPs: []net.IP{{45, 137, 198, 244}}}},
{Region: "Mexico", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "mexico403", IPs: []net.IP{{77, 81, 142, 8}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "mexico403", IPs: []net.IP{{77, 81, 142, 7}}}},
{Region: "Moldova", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "chisinau401", IPs: []net.IP{{178, 175, 129, 43}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "chisinau401", IPs: []net.IP{{178, 175, 129, 44}}}},
{Region: "Monaco", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "monaco402", IPs: []net.IP{{45, 137, 199, 226}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "monaco402", IPs: []net.IP{{45, 137, 199, 218}}}},
{Region: "Montenegro", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "montenegro402", IPs: []net.IP{{45, 131, 208, 212}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "montenegro402", IPs: []net.IP{{45, 131, 208, 212}}}},
{Region: "Morocco", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "morocco401", IPs: []net.IP{{45, 131, 211, 233}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "morocco401", IPs: []net.IP{{45, 131, 211, 248}}}},
{Region: "Netherlands", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "amsterdam416", IPs: []net.IP{{212, 102, 35, 136}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "amsterdam416", IPs: []net.IP{{212, 102, 35, 136}}}},
{Region: "New Zealand", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "newzealand403", IPs: []net.IP{{43, 250, 207, 89}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "newzealand403", IPs: []net.IP{{43, 250, 207, 94}}}},
{Region: "Norway", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "oslo403", IPs: []net.IP{{46, 246, 122, 124}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "oslo403", IPs: []net.IP{{46, 246, 122, 99}}}},
{Region: "Panama", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "panama401", IPs: []net.IP{{45, 131, 210, 248}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "panama401", IPs: []net.IP{{45, 131, 210, 231}}}},
{Region: "Philippines", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "philippines401", IPs: []net.IP{{188, 214, 125, 142}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "philippines401", IPs: []net.IP{{188, 214, 125, 142}}}},
{Region: "Poland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "warsaw402", IPs: []net.IP{{194, 110, 114, 13}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "warsaw402", IPs: []net.IP{{194, 110, 114, 13}}}},
{Region: "Portugal", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "lisbon401", IPs: []net.IP{{89, 26, 241, 72}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "lisbon401", IPs: []net.IP{{89, 26, 241, 76}}}},
{Region: "Qatar", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "qatar401", IPs: []net.IP{{45, 131, 7, 234}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "qatar401", IPs: []net.IP{{45, 131, 7, 232}}}},
{Region: "Romania", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "romania408", IPs: []net.IP{{143, 244, 54, 93}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "romania408", IPs: []net.IP{{143, 244, 54, 92}}}},
{Region: "Saudi Arabia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "saudiarabia401", IPs: []net.IP{{45, 131, 6, 238}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "saudiarabia401", IPs: []net.IP{{45, 131, 6, 231}}}},
{Region: "Serbia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "belgrade401", IPs: []net.IP{{37, 120, 193, 254}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "belgrade401", IPs: []net.IP{{37, 120, 193, 254}}}},
{Region: "Singapore", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "singapore401", IPs: []net.IP{{156, 146, 57, 210}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "singapore401", IPs: []net.IP{{156, 146, 57, 190}}}},
{Region: "Slovakia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "bratislava401", IPs: []net.IP{{37, 120, 221, 93}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "bratislava401", IPs: []net.IP{{37, 120, 221, 83}}}},
{Region: "South Africa", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "johannesburg401", IPs: []net.IP{{154, 16, 93, 46}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "johannesburg401", IPs: []net.IP{{154, 16, 93, 44}}}},
{Region: "Spain", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "madrid402", IPs: []net.IP{{212, 102, 49, 33}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "madrid402", IPs: []net.IP{{212, 102, 49, 29}}}},
{Region: "Sri Lanka", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "srilanka402", IPs: []net.IP{{45, 132, 136, 224}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "srilanka402", IPs: []net.IP{{45, 132, 136, 216}}}},
{Region: "Sweden", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "stockholm404", IPs: []net.IP{{195, 246, 120, 140}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "stockholm404", IPs: []net.IP{{195, 246, 120, 116}}}},
{Region: "Switzerland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "zurich404", IPs: []net.IP{{212, 102, 37, 104}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "zurich404", IPs: []net.IP{{212, 102, 37, 84}}}},
{Region: "Taiwan", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "taiwan401", IPs: []net.IP{{188, 214, 106, 76}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "taiwan401", IPs: []net.IP{{188, 214, 106, 71}}}},
{Region: "Turkey", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "istanbul401", IPs: []net.IP{{188, 213, 34, 71}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "istanbul401", IPs: []net.IP{{188, 213, 34, 76}}}},
{Region: "UK London", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "london412", IPs: []net.IP{{37, 235, 96, 109}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "london412", IPs: []net.IP{{37, 235, 96, 109}}}},
{Region: "UK Manchester", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "manchester460", IPs: []net.IP{{37, 120, 159, 136}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "manchester460", IPs: []net.IP{{37, 120, 159, 122}}}},
{Region: "UK Southampton", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "southampton401", IPs: []net.IP{{143, 244, 37, 223}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "southampton401", IPs: []net.IP{{143, 244, 37, 189}}}},
{Region: "US Atlanta", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "atlanta421", IPs: []net.IP{{154, 21, 21, 77}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "atlanta421", IPs: []net.IP{{154, 21, 21, 70}}}},
{Region: "US California", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "losangeles401", IPs: []net.IP{{37, 235, 107, 62}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "losangeles401", IPs: []net.IP{{37, 235, 107, 17}}}},
{Region: "US Chicago", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "chicago416", IPs: []net.IP{{154, 21, 114, 12}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "chicago416", IPs: []net.IP{{154, 21, 114, 12}}}},
{Region: "US Denver", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "denver402", IPs: []net.IP{{70, 39, 126, 157}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "denver402", IPs: []net.IP{{70, 39, 126, 175}}}},
{Region: "US East", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "newjersey402", IPs: []net.IP{{37, 235, 103, 74}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "newjersey402", IPs: []net.IP{{37, 235, 103, 131}}}},
{Region: "US Florida", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "miami405", IPs: []net.IP{{37, 235, 98, 169}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "miami405", IPs: []net.IP{{37, 235, 98, 188}}}},
{Region: "US Houston", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "houston418", IPs: []net.IP{{205, 251, 154, 205}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "houston418", IPs: []net.IP{{205, 251, 154, 208}}}},
{Region: "US Las Vegas", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "lasvegas402", IPs: []net.IP{{45, 89, 173, 178}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "lasvegas402", IPs: []net.IP{{45, 89, 173, 181}}}},
{Region: "US New York", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "newyork403", IPs: []net.IP{{156, 146, 54, 108}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "newyork403", IPs: []net.IP{{156, 146, 54, 63}}}},
{Region: "US Seattle", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "seattle417", IPs: []net.IP{{154, 21, 20, 187}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "seattle417", IPs: []net.IP{{154, 21, 20, 169}}}},
{Region: "US Silicon Valley", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "siliconvalley401", IPs: []net.IP{{154, 21, 212, 40}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "siliconvalley401", IPs: []net.IP{{154, 21, 212, 14}}}},
{Region: "US Texas", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "dallas401", IPs: []net.IP{{156, 146, 53, 180}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "dallas401", IPs: []net.IP{{156, 146, 53, 186}}}},
{Region: "US Washington DC", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "washington412", IPs: []net.IP{{23, 105, 168, 143}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "washington412", IPs: []net.IP{{23, 105, 168, 150}}}},
{Region: "US West", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "phoenix407", IPs: []net.IP{{184, 170, 241, 67}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "phoenix407", IPs: []net.IP{{184, 170, 241, 121}}}},
{Region: "Ukraine", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "kiev402", IPs: []net.IP{{62, 149, 20, 23}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "kiev402", IPs: []net.IP{{62, 149, 20, 22}}}},
{Region: "United Arab Emirates", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "dubai403", IPs: []net.IP{{217, 138, 193, 146}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "dubai403", IPs: []net.IP{{217, 138, 193, 148}}}},
{Region: "Venezuela", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "venezuela402", IPs: []net.IP{{45, 133, 89, 217}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "venezuela402", IPs: []net.IP{{45, 133, 89, 217}}}},
{Region: "Vietnam", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "vietnam401", IPs: []net.IP{{188, 214, 152, 76}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "vietnam401", IPs: []net.IP{{188, 214, 152, 70}}}},
{Region: "AU Melbourne", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "melbourne406", IPs: []net.IP{{27, 50, 74, 148}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "melbourne406", IPs: []net.IP{{27, 50, 74, 156}}}},
{Region: "AU Perth", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "perth404", IPs: []net.IP{{43, 250, 205, 186}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "perth404", IPs: []net.IP{{43, 250, 205, 190}}}},
{Region: "AU Sydney", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "sydney403", IPs: []net.IP{{180, 92, 192, 156}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "sydney403", IPs: []net.IP{{117, 120, 9, 7}}}},
{Region: "Albania", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "tirana401", IPs: []net.IP{{31, 171, 154, 134}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "tirana401", IPs: []net.IP{{31, 171, 154, 136}}}},
{Region: "Algeria", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "algiers401", IPs: []net.IP{{45, 133, 91, 248}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "algiers401", IPs: []net.IP{{45, 133, 91, 234}}}},
{Region: "Andorra", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "andorra403", IPs: []net.IP{{188, 241, 82, 4}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "andorra403", IPs: []net.IP{{188, 241, 82, 7}}}},
{Region: "Argentina", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "buenosaires401", IPs: []net.IP{{190, 106, 134, 86}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "buenosaires401", IPs: []net.IP{{190, 106, 134, 87}}}},
{Region: "Armenia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "armenia401", IPs: []net.IP{{45, 139, 50, 244}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "armenia401", IPs: []net.IP{{45, 139, 50, 249}}}},
{Region: "Austria", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "vienna402", IPs: []net.IP{{156, 146, 60, 46}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "vienna402", IPs: []net.IP{{156, 146, 60, 53}}}},
{Region: "Bahamas", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "bahamas402", IPs: []net.IP{{45, 132, 143, 226}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "bahamas402", IPs: []net.IP{{45, 132, 143, 208}}}},
{Region: "Belgium", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "brussels408", IPs: []net.IP{{91, 90, 123, 46}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "brussels408", IPs: []net.IP{{91, 90, 123, 46}}}},
{Region: "Brazil", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "saopaolo404", IPs: []net.IP{{45, 133, 180, 248}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "saopaolo404", IPs: []net.IP{{45, 133, 180, 244}}}},
{Region: "Bulgaria", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "sofia403", IPs: []net.IP{{217, 138, 221, 70}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "sofia403", IPs: []net.IP{{217, 138, 221, 70}}}},
{Region: "CA Montreal", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "montreal410", IPs: []net.IP{{199, 36, 223, 235}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "montreal401", IPs: []net.IP{{176, 113, 74, 45}}}},
{Region: "CA Ontario", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "ontario403", IPs: []net.IP{{172, 98, 92, 5}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "ontario403", IPs: []net.IP{{172, 98, 92, 45}}}},
{Region: "CA Toronto", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "toronto405", IPs: []net.IP{{172, 83, 47, 226}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "toronto405", IPs: []net.IP{{172, 83, 47, 234}}}},
{Region: "CA Vancouver", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "vancouver407", IPs: []net.IP{{172, 98, 89, 18}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "vancouver407", IPs: []net.IP{{172, 98, 89, 43}}}},
{Region: "Cambodia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "cambodia402", IPs: []net.IP{{188, 215, 235, 120}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "cambodia401", IPs: []net.IP{{188, 215, 235, 100}}}},
{Region: "China", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "china404", IPs: []net.IP{{188, 241, 80, 10}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "china404", IPs: []net.IP{{188, 241, 80, 10}}}},
{Region: "Cyprus", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "cyprus401", IPs: []net.IP{{45, 132, 137, 253}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "cyprus401", IPs: []net.IP{{45, 132, 137, 234}}}},
{Region: "Czech Republic", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "prague402", IPs: []net.IP{{212, 102, 39, 163}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "prague402", IPs: []net.IP{{212, 102, 39, 147}}}},
{Region: "DE Berlin", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "berlin421", IPs: []net.IP{{154, 13, 1, 87}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "berlin421", IPs: []net.IP{{154, 13, 1, 80}}}},
{Region: "DE Frankfurt", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "frankfurt406", IPs: []net.IP{{212, 102, 57, 115}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "frankfurt406", IPs: []net.IP{{212, 102, 57, 67}}}},
{Region: "Denmark", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "copenhagen402", IPs: []net.IP{{188, 126, 94, 72}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "copenhagen402", IPs: []net.IP{{188, 126, 94, 72}}}},
{Region: "Egypt", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "cairo402", IPs: []net.IP{{188, 214, 122, 119}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "cairo402", IPs: []net.IP{{188, 214, 122, 123}}}},
{Region: "Estonia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "talinn402", IPs: []net.IP{{95, 153, 31, 77}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "talinn402", IPs: []net.IP{{95, 153, 31, 73}}}},
{Region: "Finland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "helsinki402", IPs: []net.IP{{188, 126, 89, 51}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "helsinki402", IPs: []net.IP{{188, 126, 89, 53}}}},
{Region: "France", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "paris406", IPs: []net.IP{{143, 244, 57, 136}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "paris406", IPs: []net.IP{{143, 244, 57, 184}}}},
{Region: "Georgia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "georgia402", IPs: []net.IP{{45, 132, 138, 208}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "georgia402", IPs: []net.IP{{45, 132, 138, 228}}}},
{Region: "Greece", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "athens403", IPs: []net.IP{{154, 57, 3, 108}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "athens403", IPs: []net.IP{{154, 57, 3, 118}}}},
{Region: "Greenland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "greenland402", IPs: []net.IP{{45, 131, 209, 206}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "greenland402", IPs: []net.IP{{45, 131, 209, 226}}}},
{Region: "Hong Kong", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "china403", IPs: []net.IP{{86, 107, 104, 217}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "china403", IPs: []net.IP{{86, 107, 104, 216}}}},
{Region: "Hungary", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "budapest401", IPs: []net.IP{{217, 138, 192, 218}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "budapest401", IPs: []net.IP{{217, 138, 192, 220}}}},
{Region: "Iceland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "reykjavik402", IPs: []net.IP{{45, 133, 193, 93}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "reykjavik402", IPs: []net.IP{{45, 133, 193, 86}}}},
{Region: "India", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "mumbai402", IPs: []net.IP{{45, 120, 139, 128}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "mumbai402", IPs: []net.IP{{45, 120, 139, 128}}}},
{Region: "Ireland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "dublin409", IPs: []net.IP{{188, 241, 178, 35}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "dublin409", IPs: []net.IP{{188, 241, 178, 40}}}},
{Region: "Isle of Man", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "douglas402", IPs: []net.IP{{45, 132, 140, 220}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "douglas402", IPs: []net.IP{{45, 132, 140, 214}}}},
{Region: "Israel", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "jerusalem402", IPs: []net.IP{{185, 77, 248, 25}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "jerusalem402", IPs: []net.IP{{185, 77, 248, 27}}}},
{Region: "Italy", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "milano403", IPs: []net.IP{{156, 146, 41, 115}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "milano403", IPs: []net.IP{{156, 146, 41, 115}}}},
{Region: "Japan", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "tokyo404", IPs: []net.IP{{156, 146, 34, 227}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "tokyo402", IPs: []net.IP{{156, 146, 34, 126}}}},
{Region: "Kazakhstan", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "kazakhstan402", IPs: []net.IP{{45, 133, 88, 227}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "kazakhstan402", IPs: []net.IP{{45, 133, 88, 227}}}},
{Region: "Latvia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "riga401", IPs: []net.IP{{109, 248, 149, 4}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "riga401", IPs: []net.IP{{109, 248, 149, 14}}}},
{Region: "Liechtenstein", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "liechtenstein402", IPs: []net.IP{{45, 139, 48, 227}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "liechtenstein402", IPs: []net.IP{{45, 139, 48, 207}}}},
{Region: "Lithuania", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "vilnius401", IPs: []net.IP{{85, 206, 165, 167}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "vilnius401", IPs: []net.IP{{85, 206, 165, 167}}}},
{Region: "Luxembourg", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "luxembourg403", IPs: []net.IP{{92, 223, 89, 245}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "luxembourg403", IPs: []net.IP{{92, 223, 89, 103}}}},
{Region: "Macedonia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "macedonia401", IPs: []net.IP{{185, 225, 28, 122}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "macedonia401", IPs: []net.IP{{185, 225, 28, 126}}}},
{Region: "Malta", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "malta402", IPs: []net.IP{{45, 137, 198, 219}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "malta402", IPs: []net.IP{{45, 137, 198, 208}}}},
{Region: "Mexico", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "mexico410", IPs: []net.IP{{77, 81, 142, 114}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "mexico410", IPs: []net.IP{{77, 81, 142, 113}}}},
{Region: "Moldova", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "chisinau401", IPs: []net.IP{{178, 175, 129, 38}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "chisinau401", IPs: []net.IP{{178, 175, 129, 37}}}},
{Region: "Monaco", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "monaco402", IPs: []net.IP{{45, 137, 199, 208}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "monaco402", IPs: []net.IP{{45, 137, 199, 206}}}},
{Region: "Montenegro", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "montenegro401", IPs: []net.IP{{45, 131, 208, 250}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "montenegro401", IPs: []net.IP{{45, 131, 208, 249}}}},
{Region: "Morocco", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "morocco401", IPs: []net.IP{{45, 131, 211, 233}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "morocco401", IPs: []net.IP{{45, 131, 211, 243}}}},
{Region: "Netherlands", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "amsterdam402", IPs: []net.IP{{212, 102, 34, 144}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "amsterdam402", IPs: []net.IP{{212, 102, 35, 84}}}},
{Region: "New Zealand", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "newzealand404", IPs: []net.IP{{43, 250, 207, 25}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "newzealand403", IPs: []net.IP{{43, 250, 207, 94}}}},
{Region: "Nigeria", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "nigeria401", IPs: []net.IP{{45, 137, 196, 243}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "nigeria402", IPs: []net.IP{{45, 137, 196, 217}}}},
{Region: "Norway", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "oslo402", IPs: []net.IP{{46, 246, 122, 75}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "oslo402", IPs: []net.IP{{46, 246, 122, 88}}}},
{Region: "Panama", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "panama402", IPs: []net.IP{{45, 131, 210, 220}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "panama402", IPs: []net.IP{{45, 131, 210, 220}}}},
{Region: "Philippines", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "philippines402", IPs: []net.IP{{188, 214, 125, 153}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "philippines401", IPs: []net.IP{{188, 214, 125, 136}}}},
{Region: "Poland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "warsaw406", IPs: []net.IP{{194, 110, 114, 74}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "warsaw406", IPs: []net.IP{{194, 110, 114, 68}}}},
{Region: "Portugal", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "lisbon403", IPs: []net.IP{{89, 26, 241, 132}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "lisbon403", IPs: []net.IP{{89, 26, 241, 132}}}},
{Region: "Qatar", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "qatar402", IPs: []net.IP{{45, 131, 7, 208}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "qatar402", IPs: []net.IP{{45, 131, 7, 210}}}},
{Region: "Romania", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "romania407", IPs: []net.IP{{143, 244, 54, 136}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "romania407", IPs: []net.IP{{143, 244, 54, 170}}}},
{Region: "Saudi Arabia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "saudiarabia401", IPs: []net.IP{{45, 131, 6, 236}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "saudiarabia401", IPs: []net.IP{{45, 131, 6, 236}}}},
{Region: "Serbia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "belgrade401", IPs: []net.IP{{37, 120, 193, 246}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "belgrade401", IPs: []net.IP{{37, 120, 193, 254}}}},
{Region: "Singapore", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "singapore401", IPs: []net.IP{{156, 146, 57, 177}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "singapore401", IPs: []net.IP{{156, 146, 57, 223}}}},
{Region: "Slovakia", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "bratislava402", IPs: []net.IP{{37, 120, 221, 216}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "bratislava402", IPs: []net.IP{{37, 120, 221, 218}}}},
{Region: "South Africa", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "johannesburg401", IPs: []net.IP{{154, 16, 93, 39}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "johannesburg401", IPs: []net.IP{{154, 16, 93, 45}}}},
{Region: "Spain", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "madrid402", IPs: []net.IP{{212, 102, 49, 25}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "madrid402", IPs: []net.IP{{212, 102, 49, 17}}}},
{Region: "Sri Lanka", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "srilanka402", IPs: []net.IP{{45, 132, 136, 210}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "srilanka402", IPs: []net.IP{{45, 132, 136, 214}}}},
{Region: "Sweden", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "stockholm403", IPs: []net.IP{{195, 246, 120, 94}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "stockholm401", IPs: []net.IP{{195, 246, 120, 14}}}},
{Region: "Switzerland", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "zurich404", IPs: []net.IP{{212, 102, 37, 73}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "zurich408", IPs: []net.IP{{212, 102, 37, 6}}}},
{Region: "Taiwan", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "taiwan402", IPs: []net.IP{{188, 214, 106, 93}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "taiwan402", IPs: []net.IP{{188, 214, 106, 94}}}},
{Region: "Turkey", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "istanbul401", IPs: []net.IP{{188, 213, 34, 70}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "istanbul401", IPs: []net.IP{{188, 213, 34, 73}}}},
{Region: "UK London", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "london402", IPs: []net.IP{{212, 102, 63, 137}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "london402", IPs: []net.IP{{212, 102, 63, 154}}}},
{Region: "UK Manchester", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "manchester410", IPs: []net.IP{{194, 37, 96, 40}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "manchester410", IPs: []net.IP{{194, 37, 96, 43}}}},
{Region: "UK Southampton", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "southampton403", IPs: []net.IP{{143, 244, 37, 113}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "southampton403", IPs: []net.IP{{143, 244, 37, 99}}}},
{Region: "US Atlanta", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "atlanta424", IPs: []net.IP{{154, 21, 21, 193}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "atlanta424", IPs: []net.IP{{154, 21, 21, 175}}}},
{Region: "US California", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "losangeles409", IPs: []net.IP{{143, 244, 49, 144}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "losangeles409", IPs: []net.IP{{143, 244, 49, 186}}}},
{Region: "US Chicago", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "chicago410", IPs: []net.IP{{154, 21, 28, 239}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "chicago410", IPs: []net.IP{{154, 21, 28, 247}}}},
{Region: "US Denver", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "denver404", IPs: []net.IP{{70, 39, 111, 204}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "denver404", IPs: []net.IP{{70, 39, 111, 236}}}},
{Region: "US East", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "newjersey405", IPs: []net.IP{{37, 235, 104, 13}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "newjersey405", IPs: []net.IP{{37, 235, 104, 17}}}},
{Region: "US Florida", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "miami403", IPs: []net.IP{{37, 235, 98, 3}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "miami403", IPs: []net.IP{{37, 235, 98, 64}}}},
{Region: "US Houston", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "houston406", IPs: []net.IP{{205, 251, 138, 134}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "houston406", IPs: []net.IP{{205, 251, 138, 137}}}},
{Region: "US Las Vegas", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "lasvegas406", IPs: []net.IP{{82, 102, 31, 187}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "lasvegas406", IPs: []net.IP{{82, 102, 31, 180}}}},
{Region: "US New York", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "newyork420", IPs: []net.IP{{138, 199, 10, 115}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "newyork418", IPs: []net.IP{{156, 146, 58, 217}}}},
{Region: "US Seattle", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "seattle413", IPs: []net.IP{{154, 21, 20, 44}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "seattle413", IPs: []net.IP{{154, 21, 20, 55}}}},
{Region: "US Silicon Valley", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "siliconvalley407", IPs: []net.IP{{154, 21, 212, 209}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "siliconvalley407", IPs: []net.IP{{154, 21, 212, 197}}}},
{Region: "US Texas", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "dallas418", IPs: []net.IP{{154, 3, 250, 180}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "dallas418", IPs: []net.IP{{154, 3, 250, 179}}}},
{Region: "US Washington DC", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "washington420", IPs: []net.IP{{70, 32, 6, 69}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "washington420", IPs: []net.IP{{70, 32, 6, 68}}}},
{Region: "US West", PortForward: false, OpenvpnUDP: models.PIAServerOpenvpn{CN: "phoenix407", IPs: []net.IP{{184, 170, 241, 93}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "phoenix407", IPs: []net.IP{{184, 170, 241, 69}}}},
{Region: "Ukraine", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "kiev401", IPs: []net.IP{{62, 149, 20, 61}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "kiev401", IPs: []net.IP{{62, 149, 20, 58}}}},
{Region: "United Arab Emirates", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "dubai403", IPs: []net.IP{{217, 138, 193, 156}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "dubai403", IPs: []net.IP{{217, 138, 193, 148}}}},
{Region: "Venezuela", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "venezuela401", IPs: []net.IP{{45, 133, 89, 239}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "venezuela401", IPs: []net.IP{{45, 133, 89, 246}}}},
{Region: "Vietnam", PortForward: true, OpenvpnUDP: models.PIAServerOpenvpn{CN: "vietnam402", IPs: []net.IP{{188, 214, 152, 83}}}, OpenvpnTCP: models.PIAServerOpenvpn{CN: "vietnam402", IPs: []net.IP{{188, 214, 152, 83}}}},
}
}
func PIAOldGeoChoices() (choices []string) {
servers := PIAOldServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
}
return choices
}
//nolint:lll
func PIAOldServers() []models.PIAOldServer {
return []models.PIAOldServer{
{Region: "AU Melbourne", IPs: []net.IP{{27, 50, 82, 131}, {43, 250, 204, 105}, {43, 250, 204, 107}, {43, 250, 204, 109}, {43, 250, 204, 111}, {43, 250, 204, 113}, {43, 250, 204, 115}, {43, 250, 204, 117}, {43, 250, 204, 119}, {43, 250, 204, 123}, {43, 250, 204, 125}}},
{Region: "AU Perth", IPs: []net.IP{{43, 250, 205, 59}, {43, 250, 205, 91}, {43, 250, 205, 93}, {43, 250, 205, 95}}},
{Region: "AU Sydney", IPs: []net.IP{{27, 50, 68, 23}, {27, 50, 70, 87}, {27, 50, 77, 251}, {27, 50, 81, 117}, {103, 13, 102, 123}, {103, 13, 102, 127}, {118, 127, 60, 51}, {221, 121, 145, 135}, {221, 121, 145, 137}, {221, 121, 145, 145}, {221, 121, 145, 147}, {221, 121, 145, 159}, {221, 121, 146, 203}, {221, 121, 148, 221}, {221, 121, 152, 215}}},
{Region: "Albania", IPs: []net.IP{{31, 171, 154, 114}}},
{Region: "Argentina", IPs: []net.IP{{190, 106, 134, 100}}},
{Region: "Austria", IPs: []net.IP{{89, 187, 168, 6}, {156, 146, 60, 129}}},
{Region: "Belgium", IPs: []net.IP{{77, 243, 191, 18}, {77, 243, 191, 19}, {77, 243, 191, 20}, {185, 232, 21, 26}}},
{Region: "Bosnia and Herzegovina", IPs: []net.IP{{185, 164, 35, 54}}},
{Region: "Bulgaria", IPs: []net.IP{{217, 138, 221, 66}}},
{Region: "CA Montreal", IPs: []net.IP{{172, 98, 71, 194}, {199, 36, 223, 130}, {199, 36, 223, 194}}},
{Region: "CA Ontario", IPs: []net.IP{{162, 219, 176, 26}, {162, 219, 176, 42}, {184, 75, 208, 2}, {184, 75, 208, 90}, {184, 75, 208, 114}, {184, 75, 208, 122}, {184, 75, 208, 130}, {184, 75, 208, 146}, {184, 75, 208, 170}, {184, 75, 208, 202}, {184, 75, 210, 18}, {184, 75, 210, 98}, {184, 75, 210, 106}, {184, 75, 213, 186}, {184, 75, 213, 218}, {184, 75, 214, 18}, {184, 75, 215, 18}, {184, 75, 215, 26}, {184, 75, 215, 66}, {184, 75, 215, 74}}},
{Region: "CA Toronto", IPs: []net.IP{{66, 115, 142, 130}, {66, 115, 145, 199}, {172, 98, 92, 66}, {172, 98, 92, 130}, {172, 98, 92, 194}}},
{Region: "CA Vancouver", IPs: []net.IP{{162, 216, 47, 66}, {162, 216, 47, 194}, {172, 98, 89, 130}, {172, 98, 89, 194}}},
{Region: "Czech Republic", IPs: []net.IP{{212, 102, 39, 1}}},
{Region: "DE Berlin", IPs: []net.IP{{185, 230, 127, 238}, {193, 176, 86, 122}, {193, 176, 86, 123}, {193, 176, 86, 134}, {193, 176, 86, 178}, {194, 36, 108, 6}}},
{Region: "DE Frankfurt", IPs: []net.IP{{195, 181, 170, 239}, {195, 181, 170, 240}, {195, 181, 170, 241}, {195, 181, 170, 242}, {195, 181, 170, 243}, {195, 181, 170, 244}, {212, 102, 57, 138}}},
{Region: "Denmark", IPs: []net.IP{{188, 126, 94, 34}}},
{Region: "Estonia", IPs: []net.IP{{77, 247, 111, 82}, {77, 247, 111, 98}, {77, 247, 111, 114}, {77, 247, 111, 130}}},
{Region: "Finland", IPs: []net.IP{{188, 126, 89, 4}, {188, 126, 89, 194}}},
{Region: "France", IPs: []net.IP{{156, 146, 63, 1}, {156, 146, 63, 65}}},
{Region: "Greece", IPs: []net.IP{{154, 57, 3, 91}, {154, 57, 3, 106}, {154, 57, 3, 145}}},
{Region: "Hungary", IPs: []net.IP{{185, 128, 26, 18}, {185, 128, 26, 19}, {185, 128, 26, 20}, {185, 128, 26, 21}, {185, 128, 26, 22}, {185, 128, 26, 23}, {185, 128, 26, 24}, {185, 189, 114, 98}}},
{Region: "Iceland", IPs: []net.IP{{45, 133, 193, 50}}},
{Region: "India", IPs: []net.IP{{45, 120, 139, 108}, {45, 120, 139, 109}, {150, 242, 12, 155}, {150, 242, 12, 171}, {150, 242, 12, 187}}},
{Region: "Ireland", IPs: []net.IP{{193, 56, 252, 210}, {193, 56, 252, 226}, {193, 56, 252, 242}, {193, 56, 252, 250}, {193, 56, 252, 251}, {193, 56, 252, 252}}},
{Region: "Israel", IPs: []net.IP{{31, 168, 172, 142}, {31, 168, 172, 143}, {31, 168, 172, 145}, {31, 168, 172, 146}}},
{Region: "Italy", IPs: []net.IP{{156, 146, 41, 129}, {156, 146, 41, 193}}},
{Region: "Japan", IPs: []net.IP{{156, 146, 34, 1}, {156, 146, 34, 65}}},
{Region: "Latvia", IPs: []net.IP{{46, 183, 217, 34}, {46, 183, 218, 130}, {46, 183, 218, 146}}},
{Region: "Lithuania", IPs: []net.IP{{85, 206, 165, 96}, {85, 206, 165, 112}, {85, 206, 165, 128}}},
{Region: "Luxembourg", IPs: []net.IP{{92, 223, 89, 133}, {92, 223, 89, 134}, {92, 223, 89, 135}, {92, 223, 89, 136}, {92, 223, 89, 137}, {92, 223, 89, 138}, {92, 223, 89, 140}, {92, 223, 89, 142}}},
{Region: "Moldova", IPs: []net.IP{{178, 17, 172, 242}, {178, 17, 173, 194}, {178, 175, 128, 34}}},
{Region: "Netherlands", IPs: []net.IP{{89, 187, 174, 198}, {212, 102, 35, 101}, {212, 102, 35, 102}, {212, 102, 35, 103}, {212, 102, 35, 104}}},
{Region: "New Zealand", IPs: []net.IP{{43, 250, 207, 1}, {43, 250, 207, 3}}},
{Region: "North Macedonia", IPs: []net.IP{{185, 225, 28, 130}}},
{Region: "Norway", IPs: []net.IP{{46, 246, 122, 34}, {46, 246, 122, 162}}},
{Region: "Poland", IPs: []net.IP{{185, 244, 214, 195}, {185, 244, 214, 196}, {185, 244, 214, 197}, {185, 244, 214, 198}, {185, 244, 214, 199}, {185, 244, 214, 200}}},
{Region: "Portugal", IPs: []net.IP{{89, 26, 241, 86}, {89, 26, 241, 102}, {89, 26, 241, 130}}},
{Region: "Romania", IPs: []net.IP{{86, 105, 25, 69}, {86, 105, 25, 70}, {86, 105, 25, 74}, {86, 105, 25, 75}, {86, 105, 25, 76}, {86, 105, 25, 77}, {86, 105, 25, 78}, {89, 33, 8, 38}, {89, 33, 8, 42}, {93, 115, 7, 70}, {94, 176, 148, 35}, {143, 244, 54, 1}, {185, 45, 12, 126}, {185, 210, 218, 98}, {185, 210, 218, 99}, {185, 210, 218, 100}, {185, 210, 218, 101}, {185, 210, 218, 102}, {185, 210, 218, 105}, {188, 240, 220, 26}}},
{Region: "Serbia", IPs: []net.IP{{37, 120, 193, 226}}},
{Region: "Singapore", IPs: []net.IP{{156, 146, 56, 193}, {156, 146, 57, 38}, {156, 146, 57, 235}, {156, 146, 57, 244}}},
{Region: "Slovakia", IPs: []net.IP{{37, 120, 221, 82}, {37, 120, 221, 98}}},
{Region: "South Africa", IPs: []net.IP{{102, 165, 20, 133}}},
{Region: "Spain", IPs: []net.IP{{212, 102, 49, 185}, {212, 102, 49, 251}}},
{Region: "Sweden", IPs: []net.IP{{46, 246, 3, 254}}},
{Region: "Switzerland", IPs: []net.IP{{156, 146, 62, 193}, {212, 102, 36, 1}, {212, 102, 36, 166}, {212, 102, 37, 240}, {212, 102, 37, 241}, {212, 102, 37, 242}, {212, 102, 37, 243}}},
{Region: "Turkey", IPs: []net.IP{{185, 195, 79, 34}, {185, 195, 79, 82}}},
{Region: "UAE", IPs: []net.IP{{45, 9, 250, 46}}},
{Region: "UK London", IPs: []net.IP{{212, 102, 52, 1}}},
{Region: "UK Manchester", IPs: []net.IP{{89, 238, 137, 36}, {89, 238, 137, 37}, {89, 238, 137, 38}, {89, 238, 137, 39}, {89, 238, 139, 52}, {89, 238, 139, 53}, {89, 238, 139, 54}, {89, 238, 139, 55}, {89, 238, 139, 56}, {89, 238, 139, 57}, {89, 238, 139, 58}, {89, 249, 67, 220}}},
{Region: "UK Southampton", IPs: []net.IP{{143, 244, 36, 58}, {143, 244, 37, 1}, {143, 244, 38, 1}, {143, 244, 38, 60}, {143, 244, 38, 119}}},
{Region: "US Atlanta", IPs: []net.IP{{156, 146, 46, 1}, {156, 146, 46, 134}, {156, 146, 46, 198}, {156, 146, 47, 11}}},
{Region: "US California", IPs: []net.IP{{37, 235, 108, 208}, {89, 187, 187, 129}, {89, 187, 187, 162}, {91, 207, 175, 194}, {91, 207, 175, 195}, {91, 207, 175, 197}, {91, 207, 175, 198}, {91, 207, 175, 199}, {91, 207, 175, 200}, {91, 207, 175, 205}, {91, 207, 175, 206}, {91, 207, 175, 207}, {91, 207, 175, 209}, {91, 207, 175, 210}, {91, 207, 175, 212}}},
{Region: "US Chicago", IPs: []net.IP{{156, 146, 50, 1}, {156, 146, 50, 65}, {156, 146, 50, 134}, {156, 146, 50, 198}, {156, 146, 51, 11}, {212, 102, 58, 113}, {212, 102, 59, 54}, {212, 102, 59, 129}}},
{Region: "US Dallas", IPs: []net.IP{{156, 146, 38, 65}, {156, 146, 38, 161}, {156, 146, 39, 1}, {156, 146, 39, 6}, {156, 146, 52, 6}, {156, 146, 52, 70}, {156, 146, 52, 139}, {156, 146, 52, 203}}},
{Region: "US Denver", IPs: []net.IP{{70, 39, 77, 130}, {70, 39, 92, 2}, {70, 39, 113, 194}, {174, 128, 225, 2}, {174, 128, 226, 10}, {174, 128, 226, 18}, {174, 128, 227, 2}, {174, 128, 227, 226}, {174, 128, 236, 98}, {174, 128, 242, 234}, {174, 128, 242, 250}, {174, 128, 243, 98}, {174, 128, 244, 74}, {174, 128, 245, 122}, {174, 128, 246, 10}, {199, 115, 98, 146}, {199, 115, 98, 234}, {199, 115, 101, 178}, {199, 115, 101, 186}, {199, 115, 102, 146}}},
{Region: "US East", IPs: []net.IP{{156, 146, 58, 202}, {156, 146, 58, 203}, {156, 146, 58, 204}, {156, 146, 58, 205}, {156, 146, 58, 207}, {156, 146, 58, 208}, {156, 146, 58, 209}, {193, 37, 253, 115}, {193, 37, 253, 134}, {194, 59, 251, 8}, {194, 59, 251, 11}, {194, 59, 251, 22}, {194, 59, 251, 28}, {194, 59, 251, 56}, {194, 59, 251, 62}, {194, 59, 251, 69}, {194, 59, 251, 82}, {194, 59, 251, 84}, {194, 59, 251, 91}, {194, 59, 251, 112}}},
{Region: "US Florida", IPs: []net.IP{{193, 37, 252, 6}, {193, 37, 252, 7}, {193, 37, 252, 8}, {193, 37, 252, 9}, {193, 37, 252, 10}, {193, 37, 252, 11}, {193, 37, 252, 12}, {193, 37, 252, 14}, {193, 37, 252, 15}, {193, 37, 252, 16}, {193, 37, 252, 17}, {193, 37, 252, 18}, {193, 37, 252, 19}, {193, 37, 252, 20}, {193, 37, 252, 21}, {193, 37, 252, 23}, {193, 37, 252, 24}, {193, 37, 252, 25}, {193, 37, 252, 26}, {193, 37, 252, 27}}},
{Region: "US Houston", IPs: []net.IP{{74, 81, 88, 26}, {74, 81, 88, 42}, {74, 81, 88, 66}, {74, 81, 88, 74}, {205, 251, 148, 66}, {205, 251, 148, 90}, {205, 251, 148, 98}, {205, 251, 148, 122}, {205, 251, 148, 130}, {205, 251, 148, 138}, {205, 251, 148, 186}, {205, 251, 150, 146}, {205, 251, 150, 170}}},
{Region: "US Las Vegas", IPs: []net.IP{{79, 110, 53, 50}, {79, 110, 53, 66}, {79, 110, 53, 98}, {79, 110, 53, 114}, {79, 110, 53, 130}, {79, 110, 53, 146}, {79, 110, 53, 162}, {79, 110, 53, 178}, {79, 110, 53, 194}, {79, 110, 53, 210}, {162, 251, 236, 7}, {199, 127, 56, 83}, {199, 127, 56, 84}, {199, 127, 56, 87}, {199, 127, 56, 89}, {199, 127, 56, 90}}},
{Region: "US New York City", IPs: []net.IP{{156, 146, 36, 225}, {156, 146, 37, 129}, {156, 146, 58, 1}, {156, 146, 58, 134}}},
{Region: "US Seattle", IPs: []net.IP{{156, 146, 48, 65}, {156, 146, 48, 135}, {156, 146, 48, 200}, {156, 146, 49, 13}, {212, 102, 46, 129}, {212, 102, 46, 193}, {212, 102, 47, 134}}},
{Region: "US Silicon Valley", IPs: []net.IP{{199, 116, 118, 130}, {199, 116, 118, 132}, {199, 116, 118, 134}, {199, 116, 118, 136}, {199, 116, 118, 145}, {199, 116, 118, 148}, {199, 116, 118, 149}, {199, 116, 118, 157}, {199, 116, 118, 166}, {199, 116, 118, 169}, {199, 116, 118, 172}}},
{Region: "US Washington DC", IPs: []net.IP{{70, 32, 0, 46}, {70, 32, 0, 51}, {70, 32, 0, 53}, {70, 32, 0, 62}, {70, 32, 0, 64}, {70, 32, 0, 68}, {70, 32, 0, 69}, {70, 32, 0, 72}, {70, 32, 0, 76}, {70, 32, 0, 77}, {70, 32, 0, 106}, {70, 32, 0, 107}, {70, 32, 0, 114}, {70, 32, 0, 116}, {70, 32, 0, 120}, {70, 32, 0, 167}, {70, 32, 0, 168}, {70, 32, 0, 170}, {70, 32, 0, 172}, {70, 32, 0, 173}}},
{Region: "US West", IPs: []net.IP{{184, 170, 241, 130}, {184, 170, 241, 194}, {184, 170, 242, 135}, {184, 170, 242, 199}}},
{Region: "Ukraine", IPs: []net.IP{{62, 149, 20, 10}, {62, 149, 20, 40}}},
}
}
const (
PIAPortForwardURL models.URL = "http://209.222.18.222:2000"
)

View File

@@ -0,0 +1,206 @@
package constants
import (
"net"
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll
const (
PrivadoCertificate = "MIIFKDCCAxCgAwIBAgIJAMtrmqZxIV/OMA0GCSqGSIb3DQEBDQUAMBIxEDAOBgNVBAMMB1ByaXZhZG8wHhcNMjAwMTA4MjEyODQ1WhcNMzUwMTA5MjEyODQ1WjASMRAwDgYDVQQDDAdQcml2YWRvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxPwOgiwNJzZTnKIXwAB0TSu/Lu2qt2U2I8obtQjwhi/7OrfmbmYykSdro70al2XPhnwAGGdCxW6LDnp0UN/IOhD11mgBPo14f5CLkBQjSJ6VN5miPbvK746LsNZl9H8rQGvDuPo4CG9BfPZMiDRGlsMxij/jztzgT1gmuxQ7WHfFRcNzBas1dHa9hV/d3TU6/t47x4SE/ljdcCtJiu7Zn6ODKQoys3mB7Luz2ngqUJWvkqsg+E4+3eJ0M8Hlbn5TPaRJBID7DAdYo6Vs6xGCYr981ThFcmoIQ10js10yANrrfGAzd03b3TnLAgko0uQMHjliMZL6L8sWOPHxyxJI0us88SFh4UgcFyRHKHPKux7w24SxAlZUYoUcTHp9VjG5XvDKYxzgV2RdM4ulBGbQRQ3y3/CyddsyQYMvA55Ets0LfPaBvDIcct70iXijGsdvlX1du3ArGpG7Vaje/RU4nbbGT6HYRdt5YyZfof288ukMOSj20nVcmS+c/4tqsxSerRb1aq5LOi1IemSkTMeC5gCbexk+L1vl7NT/58sxjGmu5bXwnvev/lIItfi2AlITrfUSEv19iDMKkeshwn/+sFJBMWYyluP+yJ56yR+MWoXvLlSWphLDTqq19yx3BZn0P1tgbXoR0g8PTdJFcz8z3RIb7myVLYulV1oGG/3rka0CAwEAAaOBgDB+MB0GA1UdDgQWBBTFtJkZCVDuDAD6k5bJzefjJdO3DTBCBgNVHSMEOzA5gBTFtJkZCVDuDAD6k5bJzefjJdO3DaEWpBQwEjEQMA4GA1UEAwwHUHJpdmFkb4IJAMtrmqZxIV/OMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBDQUAA4ICAQB7MUSXMeBb9wlSv4sUaT1JHEwE26nlBw+TKmezfuPU5pBlY0LYr6qQZY95DHqsRJ7ByUzGUrGo17dNGXlcuNc6TAaQQEDRPo6y+LVh2TWMk15TUMI+MkqryJtCret7xGvDigKYMJgBy58HN3RAVr1B7cL9youwzLgc2Y/NcFKvnQJKeiIYAJ7g0CcnJiQvgZTS7xdwkEBXfsngmUCIG320DLPEL+Ze0HiUrxwWljMRya6i40AeH3Zu2i532xX1wV5+cjA4RJWIKg6ri/Q54iFGtZrA9/nc6y9uoQHkmz8cGyVUmJxFzMrrIICVqUtVRxLhkTMe4UzwRWTBeGgtW4tS0yq1QonAKfOyjgRw/CeY55D2UGvnAFZdTadtYXS4Alu2P9zdwoEk3fzHiVmDjqfJVr5wz9383aABUFrPI3nz6ed/Z6LZflKh1k+DUDEp8NxU4klUULWsSOKoa5zGX51G8cdHxwQLImXvtGuN5eSR8jCTgxFZhdps/xes4KkyfIz9FMYG748M+uOTgKITf4zdJ9BAyiQaOufVQZ8WjhWzWk9YHec9VqPkzpWNGkVjiRI5ewuXwZzZ164tMv2hikBXSuUCnFz37/ZNwGlDi0oBdDszCk2GxccdFHHaCSmpjU5MrdJ+5IhtTKGeTx+US2hTIVHQFIO99DmacxSYvLNcSQ=="
)
func PrivadoHostnameChoices() (choices []string) {
servers := PrivadoServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
return choices
}
//nolint:gomnd
func PrivadoServers() []models.PrivadoServer {
return []models.PrivadoServer{
{Hostname: "akl-001.vpn.privado.io", IP: net.IP{23, 254, 104, 114}},
{Hostname: "akl-002.vpn.privado.io", IP: net.IP{23, 254, 104, 120}},
{Hostname: "akl-003.vpn.privado.io", IP: net.IP{23, 254, 104, 51}},
{Hostname: "ams-001.vpn.privado.io", IP: net.IP{91, 148, 224, 10}},
{Hostname: "ams-002.vpn.privado.io", IP: net.IP{91, 148, 224, 20}},
{Hostname: "ams-003.vpn.privado.io", IP: net.IP{91, 148, 224, 30}},
{Hostname: "ams-004.vpn.privado.io", IP: net.IP{91, 148, 224, 40}},
{Hostname: "ams-005.vpn.privado.io", IP: net.IP{91, 148, 224, 50}},
{Hostname: "ams-006.vpn.privado.io", IP: net.IP{91, 148, 224, 60}},
{Hostname: "ams-007.vpn.privado.io", IP: net.IP{91, 148, 224, 70}},
{Hostname: "ams-008.vpn.privado.io", IP: net.IP{91, 148, 224, 80}},
{Hostname: "ams-009.vpn.privado.io", IP: net.IP{91, 148, 228, 10}},
{Hostname: "ams-010.vpn.privado.io", IP: net.IP{91, 148, 228, 20}},
{Hostname: "ams-011.vpn.privado.io", IP: net.IP{91, 148, 228, 30}},
{Hostname: "ams-012.vpn.privado.io", IP: net.IP{91, 148, 228, 40}},
{Hostname: "ams-013.vpn.privado.io", IP: net.IP{91, 148, 228, 50}},
{Hostname: "ams-014.vpn.privado.io", IP: net.IP{91, 148, 228, 60}},
{Hostname: "ams-015.vpn.privado.io", IP: net.IP{91, 148, 228, 70}},
{Hostname: "ams-016.vpn.privado.io", IP: net.IP{91, 148, 228, 80}},
{Hostname: "arn-001.vpn.privado.io", IP: net.IP{86, 106, 103, 67}},
{Hostname: "arn-002.vpn.privado.io", IP: net.IP{86, 106, 103, 74}},
{Hostname: "arn-003.vpn.privado.io", IP: net.IP{86, 106, 103, 81}},
{Hostname: "ath-001.vpn.privado.io", IP: net.IP{188, 123, 126, 61}},
{Hostname: "ath-002.vpn.privado.io", IP: net.IP{188, 123, 126, 64}},
{Hostname: "ath-003.vpn.privado.io", IP: net.IP{188, 123, 126, 68}},
{Hostname: "ath-004.vpn.privado.io", IP: net.IP{188, 123, 126, 72}},
{Hostname: "beg-001.vpn.privado.io", IP: net.IP{89, 38, 224, 19}},
{Hostname: "beg-002.vpn.privado.io", IP: net.IP{89, 38, 224, 25}},
{Hostname: "bkk-001.vpn.privado.io", IP: net.IP{119, 59, 111, 3}},
{Hostname: "bkk-002.vpn.privado.io", IP: net.IP{119, 59, 111, 11}},
{Hostname: "bom-001.vpn.privado.io", IP: net.IP{103, 26, 204, 61}},
{Hostname: "bom-002.vpn.privado.io", IP: net.IP{103, 26, 204, 70}},
{Hostname: "bru-001.vpn.privado.io", IP: net.IP{217, 138, 211, 163}},
{Hostname: "bru-002.vpn.privado.io", IP: net.IP{217, 138, 211, 170}},
{Hostname: "bru-003.vpn.privado.io", IP: net.IP{217, 138, 211, 177}},
{Hostname: "bru-004.vpn.privado.io", IP: net.IP{217, 138, 211, 184}},
{Hostname: "bts-001.vpn.privado.io", IP: net.IP{37, 120, 221, 227}},
{Hostname: "bts-002.vpn.privado.io", IP: net.IP{37, 120, 221, 233}},
{Hostname: "bud-001.vpn.privado.io", IP: net.IP{185, 128, 26, 194}},
{Hostname: "bud-002.vpn.privado.io", IP: net.IP{185, 128, 26, 200}},
{Hostname: "cdg-001.vpn.privado.io", IP: net.IP{89, 40, 183, 99}},
{Hostname: "cdg-002.vpn.privado.io", IP: net.IP{89, 40, 183, 106}},
{Hostname: "cdg-003.vpn.privado.io", IP: net.IP{89, 40, 183, 113}},
{Hostname: "cdg-004.vpn.privado.io", IP: net.IP{89, 40, 183, 120}},
{Hostname: "cph-001.vpn.privado.io", IP: net.IP{2, 58, 46, 35}},
{Hostname: "cph-002.vpn.privado.io", IP: net.IP{2, 58, 46, 42}},
{Hostname: "cph-003.vpn.privado.io", IP: net.IP{2, 58, 46, 49}},
{Hostname: "cph-004.vpn.privado.io", IP: net.IP{2, 58, 46, 56}},
{Hostname: "dca-001.vpn.privado.io", IP: net.IP{85, 12, 61, 10}},
{Hostname: "dca-002.vpn.privado.io", IP: net.IP{85, 12, 61, 20}},
{Hostname: "dca-003.vpn.privado.io", IP: net.IP{85, 12, 61, 30}},
{Hostname: "dca-004.vpn.privado.io", IP: net.IP{85, 12, 61, 40}},
{Hostname: "dca-005.vpn.privado.io", IP: net.IP{85, 12, 61, 50}},
{Hostname: "dca-006.vpn.privado.io", IP: net.IP{85, 12, 61, 60}},
{Hostname: "dca-007.vpn.privado.io", IP: net.IP{85, 12, 61, 70}},
{Hostname: "dca-008.vpn.privado.io", IP: net.IP{85, 12, 61, 80}},
{Hostname: "dca-013.vpn.privado.io", IP: net.IP{185, 247, 68, 3}},
{Hostname: "dca-014.vpn.privado.io", IP: net.IP{185, 247, 68, 10}},
{Hostname: "dca-015.vpn.privado.io", IP: net.IP{185, 247, 68, 17}},
{Hostname: "dca-016.vpn.privado.io", IP: net.IP{185, 247, 68, 24}},
{Hostname: "dfw-001.vpn.privado.io", IP: net.IP{23, 105, 32, 243}},
{Hostname: "dfw-002.vpn.privado.io", IP: net.IP{23, 105, 32, 244}},
{Hostname: "dub-001.vpn.privado.io", IP: net.IP{84, 247, 48, 227}},
{Hostname: "dub-002.vpn.privado.io", IP: net.IP{84, 247, 48, 234}},
{Hostname: "dub-003.vpn.privado.io", IP: net.IP{84, 247, 48, 241}},
{Hostname: "dub-004.vpn.privado.io", IP: net.IP{84, 247, 48, 248}},
{Hostname: "eze-001.vpn.privado.io", IP: net.IP{168, 205, 93, 211}},
{Hostname: "eze-002.vpn.privado.io", IP: net.IP{168, 205, 93, 217}},
{Hostname: "fra-001.vpn.privado.io", IP: net.IP{91, 148, 232, 10}},
{Hostname: "fra-002.vpn.privado.io", IP: net.IP{91, 148, 232, 20}},
{Hostname: "fra-003.vpn.privado.io", IP: net.IP{91, 148, 232, 30}},
{Hostname: "fra-004.vpn.privado.io", IP: net.IP{91, 148, 232, 40}},
{Hostname: "fra-005.vpn.privado.io", IP: net.IP{91, 148, 233, 7}},
{Hostname: "fra-006.vpn.privado.io", IP: net.IP{91, 148, 233, 8}},
{Hostname: "fra-007.vpn.privado.io", IP: net.IP{91, 148, 233, 9}},
{Hostname: "fra-008.vpn.privado.io", IP: net.IP{91, 148, 233, 10}},
{Hostname: "gru-001.vpn.privado.io", IP: net.IP{177, 54, 145, 193}},
{Hostname: "gru-002.vpn.privado.io", IP: net.IP{177, 54, 145, 197}},
{Hostname: "hel-001.vpn.privado.io", IP: net.IP{194, 34, 134, 219}},
{Hostname: "hel-002.vpn.privado.io", IP: net.IP{194, 34, 134, 227}},
{Hostname: "hkg-001.vpn.privado.io", IP: net.IP{209, 58, 185, 88}},
{Hostname: "hkg-002.vpn.privado.io", IP: net.IP{209, 58, 185, 97}},
{Hostname: "hkg-003.vpn.privado.io", IP: net.IP{209, 58, 185, 108}},
{Hostname: "hkg-004.vpn.privado.io", IP: net.IP{209, 58, 185, 120}},
{Hostname: "icn-001.vpn.privado.io", IP: net.IP{169, 56, 73, 146}},
{Hostname: "icn-002.vpn.privado.io", IP: net.IP{169, 56, 73, 153}},
{Hostname: "iev-001.vpn.privado.io", IP: net.IP{176, 103, 52, 40}},
{Hostname: "iev-002.vpn.privado.io", IP: net.IP{176, 103, 53, 40}},
{Hostname: "ist-001.vpn.privado.io", IP: net.IP{185, 84, 183, 3}},
{Hostname: "ist-002.vpn.privado.io", IP: net.IP{185, 84, 183, 4}},
{Hostname: "jfk-001.vpn.privado.io", IP: net.IP{217, 138, 208, 99}},
{Hostname: "jfk-002.vpn.privado.io", IP: net.IP{217, 138, 208, 106}},
{Hostname: "jfk-003.vpn.privado.io", IP: net.IP{217, 138, 208, 113}},
{Hostname: "jfk-004.vpn.privado.io", IP: net.IP{217, 138, 208, 120}},
{Hostname: "jnb-001.vpn.privado.io", IP: net.IP{172, 107, 93, 131}},
{Hostname: "jnb-002.vpn.privado.io", IP: net.IP{172, 107, 93, 137}},
{Hostname: "lax-009.vpn.privado.io", IP: net.IP{45, 152, 182, 227}},
{Hostname: "lax-010.vpn.privado.io", IP: net.IP{45, 152, 182, 234}},
{Hostname: "lax-011.vpn.privado.io", IP: net.IP{45, 152, 182, 241}},
{Hostname: "lax-012.vpn.privado.io", IP: net.IP{45, 152, 182, 248}},
{Hostname: "lis-001.vpn.privado.io", IP: net.IP{89, 26, 243, 153}},
{Hostname: "lis-002.vpn.privado.io", IP: net.IP{89, 26, 243, 154}},
{Hostname: "lon-001.vpn.privado.io", IP: net.IP{217, 138, 195, 163}},
{Hostname: "lon-002.vpn.privado.io", IP: net.IP{217, 138, 195, 170}},
{Hostname: "lon-003.vpn.privado.io", IP: net.IP{217, 138, 195, 177}},
{Hostname: "lon-004.vpn.privado.io", IP: net.IP{217, 138, 195, 184}},
{Hostname: "mad-001.vpn.privado.io", IP: net.IP{217, 138, 218, 131}},
{Hostname: "man-001.vpn.privado.io", IP: net.IP{217, 138, 196, 131}},
{Hostname: "man-002.vpn.privado.io", IP: net.IP{217, 138, 196, 138}},
{Hostname: "man-003.vpn.privado.io", IP: net.IP{217, 138, 196, 145}},
{Hostname: "man-004.vpn.privado.io", IP: net.IP{217, 138, 196, 152}},
{Hostname: "mex-001.vpn.privado.io", IP: net.IP{169, 57, 96, 52}},
{Hostname: "mex-002.vpn.privado.io", IP: net.IP{169, 57, 96, 57}},
{Hostname: "mia-001.vpn.privado.io", IP: net.IP{86, 106, 87, 131}},
{Hostname: "mia-002.vpn.privado.io", IP: net.IP{86, 106, 87, 138}},
{Hostname: "mia-003.vpn.privado.io", IP: net.IP{86, 106, 87, 145}},
{Hostname: "mia-004.vpn.privado.io", IP: net.IP{86, 106, 87, 152}},
{Hostname: "mxp-001.vpn.privado.io", IP: net.IP{89, 40, 182, 195}},
{Hostname: "mxp-002.vpn.privado.io", IP: net.IP{89, 40, 182, 201}},
{Hostname: "nrt-001.vpn.privado.io", IP: net.IP{217, 138, 252, 3}},
{Hostname: "nrt-002.vpn.privado.io", IP: net.IP{217, 138, 252, 10}},
{Hostname: "nrt-003.vpn.privado.io", IP: net.IP{217, 138, 252, 17}},
{Hostname: "nrt-004.vpn.privado.io", IP: net.IP{217, 138, 252, 24}},
{Hostname: "ord-001.vpn.privado.io", IP: net.IP{23, 108, 95, 129}},
{Hostname: "ord-002.vpn.privado.io", IP: net.IP{23, 108, 95, 167}},
{Hostname: "osl-001.vpn.privado.io", IP: net.IP{84, 247, 50, 115}},
{Hostname: "osl-002.vpn.privado.io", IP: net.IP{84, 247, 50, 119}},
{Hostname: "osl-003.vpn.privado.io", IP: net.IP{84, 247, 50, 123}},
{Hostname: "otp-001.vpn.privado.io", IP: net.IP{89, 46, 102, 179}},
{Hostname: "otp-002.vpn.privado.io", IP: net.IP{89, 46, 102, 185}},
{Hostname: "phx-001.vpn.privado.io", IP: net.IP{91, 148, 236, 10}},
{Hostname: "phx-002.vpn.privado.io", IP: net.IP{91, 148, 236, 20}},
{Hostname: "phx-003.vpn.privado.io", IP: net.IP{91, 148, 236, 30}},
{Hostname: "phx-004.vpn.privado.io", IP: net.IP{91, 148, 236, 40}},
{Hostname: "phx-005.vpn.privado.io", IP: net.IP{91, 148, 236, 50}},
{Hostname: "phx-006.vpn.privado.io", IP: net.IP{91, 148, 236, 60}},
{Hostname: "phx-007.vpn.privado.io", IP: net.IP{91, 148, 236, 70}},
{Hostname: "phx-008.vpn.privado.io", IP: net.IP{91, 148, 236, 80}},
{Hostname: "prg-001.vpn.privado.io", IP: net.IP{185, 216, 35, 99}},
{Hostname: "prg-002.vpn.privado.io", IP: net.IP{185, 216, 35, 105}},
{Hostname: "rix-001.vpn.privado.io", IP: net.IP{109, 248, 149, 35}},
{Hostname: "rix-002.vpn.privado.io", IP: net.IP{109, 248, 149, 40}},
{Hostname: "rkv-001.vpn.privado.io", IP: net.IP{82, 221, 131, 78}},
{Hostname: "rkv-002.vpn.privado.io", IP: net.IP{82, 221, 131, 127}},
{Hostname: "sea-001.vpn.privado.io", IP: net.IP{23, 81, 208, 96}},
{Hostname: "sea-002.vpn.privado.io", IP: net.IP{23, 81, 208, 104}},
{Hostname: "sin-001.vpn.privado.io", IP: net.IP{92, 119, 178, 131}},
{Hostname: "sin-002.vpn.privado.io", IP: net.IP{92, 119, 178, 138}},
{Hostname: "sin-003.vpn.privado.io", IP: net.IP{92, 119, 178, 145}},
{Hostname: "sin-004.vpn.privado.io", IP: net.IP{92, 119, 178, 152}},
{Hostname: "sof-001.vpn.privado.io", IP: net.IP{217, 138, 221, 163}},
{Hostname: "sof-002.vpn.privado.io", IP: net.IP{217, 138, 221, 169}},
{Hostname: "stl-001.vpn.privado.io", IP: net.IP{148, 72, 170, 145}},
{Hostname: "stl-002.vpn.privado.io", IP: net.IP{148, 72, 172, 82}},
{Hostname: "syd-001.vpn.privado.io", IP: net.IP{93, 115, 35, 35}},
{Hostname: "syd-002.vpn.privado.io", IP: net.IP{93, 115, 35, 42}},
{Hostname: "syd-003.vpn.privado.io", IP: net.IP{93, 115, 35, 49}},
{Hostname: "syd-004.vpn.privado.io", IP: net.IP{93, 115, 35, 56}},
{Hostname: "vie-001.vpn.privado.io", IP: net.IP{5, 253, 207, 227}},
{Hostname: "vie-002.vpn.privado.io", IP: net.IP{5, 253, 207, 234}},
{Hostname: "vie-003.vpn.privado.io", IP: net.IP{5, 253, 207, 241}},
{Hostname: "vie-004.vpn.privado.io", IP: net.IP{5, 253, 207, 248}},
{Hostname: "vno-001.vpn.privado.io", IP: net.IP{185, 64, 104, 176}},
{Hostname: "vno-002.vpn.privado.io", IP: net.IP{185, 64, 104, 180}},
{Hostname: "waw-001.vpn.privado.io", IP: net.IP{217, 138, 209, 163}},
{Hostname: "waw-002.vpn.privado.io", IP: net.IP{217, 138, 209, 164}},
{Hostname: "waw-003.vpn.privado.io", IP: net.IP{217, 138, 209, 165}},
{Hostname: "waw-004.vpn.privado.io", IP: net.IP{217, 138, 209, 166}},
{Hostname: "yul-001.vpn.privado.io", IP: net.IP{217, 138, 213, 67}},
{Hostname: "yul-002.vpn.privado.io", IP: net.IP{217, 138, 213, 74}},
{Hostname: "yul-003.vpn.privado.io", IP: net.IP{217, 138, 213, 81}},
{Hostname: "yul-004.vpn.privado.io", IP: net.IP{217, 138, 213, 88}},
{Hostname: "yvr-001.vpn.privado.io", IP: net.IP{71, 19, 248, 57}},
{Hostname: "yvr-002.vpn.privado.io", IP: net.IP{71, 19, 248, 113}},
{Hostname: "yyz-003.vpn.privado.io", IP: net.IP{199, 189, 27, 19}},
{Hostname: "zrh-001.vpn.privado.io", IP: net.IP{185, 156, 175, 195}},
{Hostname: "zrh-002.vpn.privado.io", IP: net.IP{185, 156, 175, 202}},
{Hostname: "zrh-003.vpn.privado.io", IP: net.IP{185, 156, 175, 209}},
{Hostname: "zrh-004.vpn.privado.io", IP: net.IP{185, 156, 175, 216}},
}
}

View File

@@ -13,7 +13,7 @@ func GetAllServers() (allServers models.AllServers) {
},
Mullvad: models.MullvadServers{
Version: 1,
Timestamp: 1600438544,
Timestamp: 1603660367,
Servers: MullvadServers(),
},
Nordvpn: models.NordvpnServers{
@@ -23,19 +23,19 @@ func GetAllServers() (allServers models.AllServers) {
},
Pia: models.PiaServers{
Version: 2,
Timestamp: 1602531173,
Timestamp: 1605392393,
Servers: PIAServers(),
},
PiaOld: models.PiaOldServers{
Version: 1,
Timestamp: 1602523433,
Servers: PIAOldServers(),
},
Purevpn: models.PurevpnServers{
Version: 1,
Timestamp: 1599323261,
Servers: PurevpnServers(),
},
Privado: models.PrivadoServers{
Version: 2,
Timestamp: 1604963273,
Servers: PrivadoServers(),
},
Surfshark: models.SurfsharkServers{
Version: 1,
Timestamp: 1599957644,
@@ -47,8 +47,8 @@ func GetAllServers() (allServers models.AllServers) {
Servers: VyprvpnServers(),
},
Windscribe: models.WindscribeServers{
Version: 1,
Timestamp: 1599323261,
Version: 2,
Timestamp: 1604019438,
Servers: WindscribeServers(),
},
}

View File

@@ -54,10 +54,10 @@ func Test_versions(t *testing.T) {
version: allServers.Pia.Version,
digest: "f1e01afe",
},
"Private Internet Access Old": {
model: models.PIAOldServer{},
version: allServers.PiaOld.Version,
digest: "4e25ce4a",
"Privado": {
model: models.PrivadoServer{},
version: allServers.Privado.Version,
digest: "1d5aeb23",
},
"Purevpn": {
model: models.PurevpnServer{},
@@ -77,7 +77,7 @@ func Test_versions(t *testing.T) {
"Windscribe": {
model: models.WindscribeServer{},
version: allServers.Windscribe.Version,
digest: "042bef64",
digest: "6e3ca639",
},
}
for name, testCase := range testCases {
@@ -123,7 +123,7 @@ func Test_timestamps(t *testing.T) {
"Mullvad": {
servers: allServers.Mullvad.Servers,
timestamp: allServers.Mullvad.Timestamp,
digest: "e1fee56f",
digest: "93859652",
},
"Nordvpn": {
servers: allServers.Nordvpn.Servers,
@@ -133,18 +133,18 @@ func Test_timestamps(t *testing.T) {
"Private Internet Access": {
servers: allServers.Pia.Servers,
timestamp: allServers.Pia.Timestamp,
digest: "1571e777",
},
"Private Internet Access Old": {
servers: allServers.PiaOld.Servers,
timestamp: allServers.PiaOld.Timestamp,
digest: "3566a800",
digest: "4a172b0a",
},
"Purevpn": {
servers: allServers.Purevpn.Servers,
timestamp: allServers.Purevpn.Timestamp,
digest: "cdf9b708",
},
"Privado": {
servers: allServers.Privado.Servers,
timestamp: allServers.Privado.Timestamp,
digest: "df2a046d",
},
"Surfshark": {
servers: allServers.Surfshark.Servers,
timestamp: allServers.Surfshark.Timestamp,
@@ -158,7 +158,7 @@ func Test_timestamps(t *testing.T) {
"Windscribe": {
servers: allServers.Windscribe.Servers,
timestamp: allServers.Windscribe.Timestamp,
digest: "eacad593",
digest: "fd87502e",
},
}
for name, testCase := range testCases {

View File

@@ -2,9 +2,9 @@ package constants
const (
// Announcement is a message announcement.
Announcement = "Port forwarding is working for PIA v4 servers"
Announcement = "Support for Privado"
// AnnouncementExpiration is the expiration date of the announcement in format yyyy-mm-dd.
AnnouncementExpiration = "2020-11-15"
AnnouncementExpiration = "2020-11-25"
)
const (

View File

@@ -1,20 +0,0 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
const (
// TinyProxyInfoLevel is the info log level for TinyProxy.
TinyProxyInfoLevel models.TinyProxyLogLevel = "Info"
// TinyProxyConnectLevel is the info log level for TinyProxy.
TinyProxyConnectLevel models.TinyProxyLogLevel = "Connect"
// TinyProxyNoticeLevel is the info log level for TinyProxy.
TinyProxyNoticeLevel models.TinyProxyLogLevel = "Notice"
// TinyProxyWarnLevel is the warning log level for TinyProxy.
TinyProxyWarnLevel models.TinyProxyLogLevel = "Warning"
// TinyProxyErrorLevel is the error log level for TinyProxy.
TinyProxyErrorLevel models.TinyProxyLogLevel = "Error"
// TinyProxyCriticalLevel is the critical log level for TinyProxy.
TinyProxyCriticalLevel models.TinyProxyLogLevel = "Critical"
)

View File

@@ -7,8 +7,6 @@ import (
const (
// PrivateInternetAccess is a VPN provider.
PrivateInternetAccess models.VPNProvider = "private internet access"
// PrivateInternetAccessOld is the pre summer 2020 PIA provider.
PrivateInternetAccessOld models.VPNProvider = "private internet access old"
// Mullvad is a VPN provider.
Mullvad models.VPNProvider = "mullvad"
// Windscribe is a VPN provider.
@@ -23,6 +21,8 @@ const (
Nordvpn models.VPNProvider = "nordvpn"
// PureVPN is a VPN provider.
Purevpn models.VPNProvider = "purevpn"
// Privado is a VPN provider.
Privado models.VPNProvider = "privado"
)
const (

View File

@@ -21,79 +21,319 @@ func WindscribeRegionChoices() (choices []string) {
return choices
}
func WindscribeCityChoices() (choices []string) {
servers := WindscribeServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
}
return choices
}
func WindscribeHostnameChoices() (choices []string) {
servers := WindscribeServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
return choices
}
//nolint:lll
func WindscribeServers() []models.WindscribeServer {
return []models.WindscribeServer{
{Region: "Albania", IPs: []net.IP{{31, 171, 152, 179}}},
{Region: "Argentina", IPs: []net.IP{{167, 250, 6, 121}, {190, 105, 236, 19}, {190, 105, 236, 32}, {190, 105, 236, 50}}},
{Region: "Australia", IPs: []net.IP{{45, 121, 208, 160}, {45, 121, 209, 160}, {45, 121, 210, 208}, {103, 62, 50, 208}, {103, 77, 233, 67}, {103, 77, 234, 211}, {116, 90, 72, 243}, {116, 206, 228, 67}}},
{Region: "Austria", IPs: []net.IP{{89, 187, 168, 66}, {217, 64, 127, 11}}},
{Region: "Azerbaijan", IPs: []net.IP{{85, 132, 61, 123}}},
{Region: "Belgium", IPs: []net.IP{{185, 232, 21, 131}, {194, 187, 251, 147}}},
{Region: "Bosnia", IPs: []net.IP{{185, 99, 3, 24}}},
{Region: "Brazil", IPs: []net.IP{{177, 54, 144, 68}, {177, 67, 80, 59}, {189, 1, 172, 12}}},
{Region: "Bulgaria", IPs: []net.IP{{185, 94, 192, 35}}},
{Region: "Canada East", IPs: []net.IP{{23, 154, 160, 177}, {66, 70, 148, 80}, {104, 227, 235, 129}, {104, 254, 92, 11}, {104, 254, 92, 91}, {144, 168, 163, 160}, {144, 168, 163, 193}, {184, 75, 212, 91}, {192, 190, 19, 65}, {192, 190, 19, 97}, {198, 8, 85, 195}, {198, 8, 85, 210}, {199, 204, 208, 158}}},
{Region: "Canada West", IPs: []net.IP{{104, 218, 61, 1}, {104, 218, 61, 33}, {162, 221, 207, 95}, {208, 78, 41, 1}, {208, 78, 41, 131}, {208, 78, 41, 163}}},
{Region: "Colombia", IPs: []net.IP{{138, 121, 203, 203}, {138, 186, 141, 155}}},
{Region: "Croatia", IPs: []net.IP{{85, 10, 56, 252}}},
{Region: "Cyprus", IPs: []net.IP{{157, 97, 132, 43}}},
{Region: "Czech republic", IPs: []net.IP{{185, 156, 174, 11}, {185, 246, 210, 2}}},
{Region: "Denmark", IPs: []net.IP{{134, 90, 149, 147}, {185, 206, 224, 195}}},
{Region: "Estonia", IPs: []net.IP{{46, 22, 211, 251}, {196, 196, 216, 131}}},
{Region: "Fake antarctica", IPs: []net.IP{{23, 154, 160, 212}, {23, 154, 160, 222}}},
{Region: "Finland", IPs: []net.IP{{185, 112, 82, 227}, {194, 34, 133, 82}}},
{Region: "France", IPs: []net.IP{{45, 89, 174, 35}, {82, 102, 18, 35}, {84, 17, 42, 2}, {84, 17, 42, 34}, {185, 156, 173, 187}}},
{Region: "Germany", IPs: []net.IP{{45, 87, 212, 51}, {89, 249, 65, 19}, {185, 130, 184, 195}, {195, 181, 170, 66}, {195, 181, 175, 98}, {217, 138, 194, 115}}},
{Region: "Greece", IPs: []net.IP{{78, 108, 38, 155}, {185, 226, 64, 111}, {188, 123, 126, 146}}},
{Region: "Guinea-Bissau", IPs: []net.IP{{149, 56, 10, 82}}},
{Region: "Hong kong", IPs: []net.IP{{84, 17, 57, 114}, {103, 10, 197, 99}}},
{Region: "Hungary", IPs: []net.IP{{185, 104, 187, 43}}},
{Region: "Iceland", IPs: []net.IP{{82, 221, 139, 38}, {185, 165, 170, 2}}},
{Region: "India", IPs: []net.IP{{103, 205, 140, 227}, {169, 38, 68, 188}, {169, 38, 72, 12}, {169, 38, 72, 14}}},
{Region: "Indonesia", IPs: []net.IP{{45, 127, 134, 91}}},
{Region: "Ireland", IPs: []net.IP{{185, 24, 232, 146}, {185, 104, 219, 2}}},
{Region: "Israel", IPs: []net.IP{{160, 116, 0, 27}, {185, 191, 205, 139}}},
{Region: "Italy", IPs: []net.IP{{37, 120, 135, 83}, {37, 120, 207, 19}, {84, 17, 59, 66}, {87, 101, 94, 195}, {89, 40, 182, 3}}},
{Region: "Japan", IPs: []net.IP{{89, 187, 161, 114}, {193, 148, 16, 243}}},
{Region: "Latvia", IPs: []net.IP{{85, 254, 72, 23}, {89, 111, 33, 220}}},
{Region: "Lithuania", IPs: []net.IP{{85, 206, 163, 225}}},
{Region: "Macedonia", IPs: []net.IP{{185, 225, 28, 51}}},
{Region: "Madagascar", IPs: []net.IP{{104, 20, 26, 217}, {104, 20, 27, 217}, {172, 67, 17, 175}}},
{Region: "Malaysia", IPs: []net.IP{{103, 106, 250, 31}, {103, 212, 69, 232}}},
{Region: "Mexico", IPs: []net.IP{{143, 255, 57, 67}, {190, 103, 179, 211}, {190, 103, 179, 217}, {201, 131, 125, 107}}},
{Region: "Moldova", IPs: []net.IP{{178, 175, 144, 123}}},
{Region: "Netherlands", IPs: []net.IP{{37, 120, 192, 19}, {46, 166, 143, 98}, {72, 11, 157, 35}, {72, 11, 157, 67}, {84, 17, 46, 2}, {185, 212, 171, 131}, {185, 253, 96, 3}}},
{Region: "New zealand", IPs: []net.IP{{103, 62, 49, 113}}},
{Region: "Norway", IPs: []net.IP{{37, 120, 203, 67}, {185, 206, 225, 131}}},
{Region: "Panama", IPs: []net.IP{{138, 186, 142, 203}}},
{Region: "Peru", IPs: []net.IP{{190, 120, 229, 139}}},
{Region: "Philippines", IPs: []net.IP{{103, 103, 0, 118}, {141, 98, 215, 211}}},
{Region: "Poland", IPs: []net.IP{{5, 133, 11, 116}, {84, 17, 55, 98}, {185, 244, 214, 35}}},
{Region: "Portugal", IPs: []net.IP{{94, 46, 13, 215}, {185, 15, 21, 66}}},
{Region: "Romania", IPs: []net.IP{{89, 46, 103, 147}, {91, 207, 102, 147}}},
{Region: "Russia", IPs: []net.IP{{94, 242, 62, 19}, {94, 242, 62, 67}, {95, 213, 193, 195}, {95, 213, 193, 227}, {185, 22, 175, 132}, {188, 124, 42, 99}, {188, 124, 42, 115}}},
{Region: "Serbia", IPs: []net.IP{{141, 98, 103, 19}}},
{Region: "Singapore", IPs: []net.IP{{82, 102, 25, 131}, {103, 62, 48, 224}, {156, 146, 56, 98}, {156, 146, 56, 111}, {185, 200, 117, 163}}},
{Region: "Slovakia", IPs: []net.IP{{185, 245, 85, 3}}},
{Region: "South Africa", IPs: []net.IP{{129, 232, 167, 211}, {165, 73, 248, 91}, {197, 242, 157, 235}}},
{Region: "South Korea", IPs: []net.IP{{27, 255, 92, 52}, {103, 212, 223, 3}, {218, 232, 76, 179}}},
{Region: "Spain", IPs: []net.IP{{37, 120, 142, 227}, {89, 238, 178, 43}, {185, 253, 99, 131}, {217, 138, 218, 99}}},
{Region: "Sweden", IPs: []net.IP{{31, 13, 191, 67}, {79, 142, 76, 198}, {195, 181, 166, 129}}},
{Region: "Switzerland", IPs: []net.IP{{31, 7, 57, 242}, {37, 120, 213, 163}, {84, 17, 53, 2}, {89, 187, 165, 98}, {185, 156, 175, 179}}},
{Region: "Taiwan", IPs: []net.IP{{103, 4, 29, 77}, {185, 189, 160, 12}, {185, 189, 160, 27}, {185, 189, 160, 32}}},
{Region: "Thailand", IPs: []net.IP{{27, 254, 130, 221}, {202, 129, 16, 147}, {202, 129, 16, 155}}},
{Region: "Tunisia", IPs: []net.IP{{41, 231, 5, 23}}},
{Region: "Turkey", IPs: []net.IP{{45, 123, 118, 156}, {45, 123, 119, 11}, {79, 98, 131, 43}, {176, 53, 113, 163}, {185, 125, 33, 227}}},
{Region: "US Central", IPs: []net.IP{{67, 212, 238, 196}, {69, 12, 94, 67}, {104, 129, 18, 3}, {104, 129, 18, 131}, {104, 223, 92, 163}, {107, 150, 31, 3}, {107, 150, 31, 67}, {107, 150, 31, 131}, {107, 161, 86, 131}, {107, 182, 234, 240}, {161, 129, 70, 195}, {162, 222, 198, 67}, {172, 241, 26, 78}, {172, 241, 131, 129}, {198, 12, 76, 211}, {198, 54, 128, 116}, {198, 55, 125, 195}, {199, 115, 96, 83}, {204, 44, 112, 67}, {204, 44, 112, 131}, {206, 217, 139, 19}, {206, 217, 139, 195}, {206, 217, 143, 131}}},
{Region: "US West", IPs: []net.IP{{23, 83, 130, 166}, {23, 83, 131, 187}, {23, 94, 74, 99}, {37, 120, 147, 163}, {64, 120, 2, 174}, {66, 115, 176, 3}, {82, 102, 30, 67}, {89, 187, 185, 34}, {89, 187, 187, 98}, {104, 129, 3, 67}, {104, 129, 3, 163}, {104, 129, 56, 67}, {104, 129, 56, 131}, {104, 152, 222, 33}, {167, 88, 60, 227}, {167, 88, 60, 243}, {172, 241, 214, 202}, {172, 241, 250, 131}, {172, 255, 125, 141}, {185, 236, 200, 35}, {192, 3, 20, 51}, {198, 12, 116, 195}, {198, 23, 242, 147}, {209, 58, 129, 121}, {212, 103, 49, 67}, {216, 45, 53, 131}, {217, 138, 217, 51}, {217, 138, 217, 211}}},
{Region: "Ukraine", IPs: []net.IP{{45, 141, 156, 11}, {45, 141, 156, 50}}},
{Region: "United Arab Emirates", IPs: []net.IP{{45, 9, 249, 43}}},
{Region: "United Kingdom", IPs: []net.IP{{2, 58, 29, 17}, {2, 58, 29, 145}, {81, 92, 207, 69}, {84, 17, 50, 130}, {89, 44, 201, 99}, {89, 238, 135, 133}, {89, 238, 150, 229}, {185, 212, 168, 133}, {212, 102, 63, 32}, {212, 102, 63, 62}, {217, 138, 254, 51}}},
{Region: "Vietnam", IPs: []net.IP{{103, 9, 76, 197}, {103, 9, 79, 186}, {103, 9, 79, 219}}},
{Region: "Windflix CA", IPs: []net.IP{{104, 218, 60, 111}, {104, 254, 92, 99}}},
{Region: "Windflix JP", IPs: []net.IP{{5, 181, 235, 67}}},
{Region: "Windflix UK", IPs: []net.IP{{45, 9, 248, 3}, {81, 92, 200, 85}, {89, 47, 62, 83}}},
{Region: "Windflix US", IPs: []net.IP{{38, 132, 101, 211}, {38, 132, 122, 131}, {38, 132, 122, 195}, {77, 81, 136, 99}, {185, 232, 22, 131}, {217, 138, 206, 211}}},
{Region: "Albania", City: "Tirana", Hostname: "al-002.whiskergalaxy.com", IP: net.IP{31, 171, 152, 179}},
{Region: "Argentina", City: "Buenos Aires", Hostname: "ar-001.whiskergalaxy.com", IP: net.IP{200, 85, 152, 110}},
{Region: "Argentina", City: "Buenos Aires", Hostname: "ar-003.whiskergalaxy.com", IP: net.IP{167, 250, 6, 121}},
{Region: "Argentina", City: "Buenos Aires", Hostname: "ar-004.whiskergalaxy.com", IP: net.IP{190, 105, 236, 50}},
{Region: "Argentina", City: "Buenos Aires", Hostname: "ar-005.whiskergalaxy.com", IP: net.IP{190, 105, 236, 32}},
{Region: "Argentina", City: "Buenos Aires", Hostname: "ar-006.whiskergalaxy.com", IP: net.IP{190, 105, 236, 19}},
{Region: "Australia", City: "Adelaide ", Hostname: "au-011.whiskergalaxy.com", IP: net.IP{103, 108, 92, 83}},
{Region: "Australia", City: "Adelaide", Hostname: "au-008.whiskergalaxy.com", IP: net.IP{116, 90, 72, 243}},
{Region: "Australia", City: "Brisbane", Hostname: "au-007.whiskergalaxy.com", IP: net.IP{103, 62, 50, 208}},
{Region: "Australia", City: "Brisbane", Hostname: "au-014.whiskergalaxy.com", IP: net.IP{43, 245, 160, 35}},
{Region: "Australia", City: "Canberra", Hostname: "au-010.whiskergalaxy.com", IP: net.IP{116, 206, 229, 131}},
{Region: "Australia", City: "Melbourne ", Hostname: "au-005.whiskergalaxy.com", IP: net.IP{45, 121, 209, 160}},
{Region: "Australia", City: "Melbourne ", Hostname: "au-013.whiskergalaxy.com", IP: net.IP{116, 206, 228, 67}},
{Region: "Australia", City: "Perth", Hostname: "au-004.whiskergalaxy.com", IP: net.IP{45, 121, 208, 160}},
{Region: "Australia", City: "Perth", Hostname: "au-012.whiskergalaxy.com", IP: net.IP{103, 77, 234, 211}},
{Region: "Australia", City: "Sydney", Hostname: "au-009.whiskergalaxy.com", IP: net.IP{103, 77, 233, 67}},
{Region: "Australia", City: "Sydney", Hostname: "au-015.whiskergalaxy.com", IP: net.IP{103, 1, 213, 211}},
{Region: "Austria", City: "Vienna", Hostname: "at-001.whiskergalaxy.com", IP: net.IP{217, 64, 127, 11}},
{Region: "Austria", City: "Vienna", Hostname: "at-002.whiskergalaxy.com", IP: net.IP{89, 187, 168, 66}},
{Region: "Belgium", City: "Brussels", Hostname: "be-001.whiskergalaxy.com", IP: net.IP{194, 187, 251, 147}},
{Region: "Belgium", City: "Brussels", Hostname: "be-002.whiskergalaxy.com", IP: net.IP{185, 232, 21, 131}},
{Region: "Brazil", City: "Sao Paulo", Hostname: "br-004.whiskergalaxy.com", IP: net.IP{177, 67, 80, 59}},
{Region: "Brazil", City: "Sao Paulo", Hostname: "br-005.whiskergalaxy.com", IP: net.IP{177, 54, 157, 178}},
{Region: "Brazil", City: "Sao Paulo", Hostname: "br-006.whiskergalaxy.com", IP: net.IP{177, 54, 148, 247}},
{Region: "Bulgaria", City: "Sofia", Hostname: "bg-001.whiskergalaxy.com", IP: net.IP{185, 94, 192, 35}},
{Region: "Canada East", City: "Halifax", Hostname: "ca-029.whiskergalaxy.com", IP: net.IP{199, 204, 208, 158}},
{Region: "Canada East", City: "Montreal", Hostname: "ca-004.whiskergalaxy.com", IP: net.IP{66, 70, 148, 80}},
{Region: "Canada East", City: "Montreal", Hostname: "ca-027.whiskergalaxy.com", IP: net.IP{144, 168, 163, 160}},
{Region: "Canada East", City: "Montreal", Hostname: "ca-028.whiskergalaxy.com", IP: net.IP{144, 168, 163, 193}},
{Region: "Canada East", City: "Montreal", Hostname: "ca-032.whiskergalaxy.com", IP: net.IP{104, 227, 235, 129}},
{Region: "Canada East", City: "Montreal", Hostname: "ca-033.whiskergalaxy.com", IP: net.IP{198, 8, 85, 195}},
{Region: "Canada East", City: "Montreal", Hostname: "ca-034.whiskergalaxy.com", IP: net.IP{198, 8, 85, 210}},
{Region: "Canada East", City: "Toronto", Hostname: "ca-002.whiskergalaxy.com", IP: net.IP{104, 254, 92, 11}},
{Region: "Canada East", City: "Toronto", Hostname: "ca-009.whiskergalaxy.com", IP: net.IP{104, 254, 92, 91}},
{Region: "Canada East", City: "Toronto", Hostname: "ca-017.whiskergalaxy.com", IP: net.IP{184, 75, 212, 91}},
{Region: "Canada East", City: "Toronto", Hostname: "ca-025.whiskergalaxy.com", IP: net.IP{192, 190, 19, 65}},
{Region: "Canada East", City: "Toronto", Hostname: "ca-026.whiskergalaxy.com", IP: net.IP{192, 190, 19, 97}},
{Region: "Canada East", City: "Toronto", Hostname: "ca-030.whiskergalaxy.com", IP: net.IP{23, 154, 160, 177}},
{Region: "Canada West", City: "Vancouver", Hostname: "ca-west-005.whiskergalaxy.com", IP: net.IP{162, 221, 207, 95}},
{Region: "Canada West", City: "Vancouver", Hostname: "ca-west-011.whiskergalaxy.com", IP: net.IP{104, 218, 61, 1}},
{Region: "Canada West", City: "Vancouver", Hostname: "ca-west-012.whiskergalaxy.com", IP: net.IP{104, 218, 61, 33}},
{Region: "Canada West", City: "Vancouver", Hostname: "ca-west-016.whiskergalaxy.com", IP: net.IP{208, 78, 41, 1}},
{Region: "Canada West", City: "Vancouver", Hostname: "ca-west-017.whiskergalaxy.com", IP: net.IP{208, 78, 41, 131}},
{Region: "Canada West", City: "Vancouver", Hostname: "ca-west-019.whiskergalaxy.com", IP: net.IP{208, 78, 41, 163}},
{Region: "Colombia", City: "Bogota", Hostname: "co-001.whiskergalaxy.com", IP: net.IP{138, 121, 203, 203}},
{Region: "Colombia", City: "Bogota", Hostname: "co-002.whiskergalaxy.com", IP: net.IP{138, 186, 141, 155}},
{Region: "Croatia", City: "Zagreb", Hostname: "hr-002.whiskergalaxy.com", IP: net.IP{85, 10, 56, 129}},
{Region: "Cyprus", City: "Nicosia", Hostname: "cy-001.whiskergalaxy.com", IP: net.IP{157, 97, 132, 43}},
{Region: "Czech Republic", City: "Prague ", Hostname: "cz-002.whiskergalaxy.com", IP: net.IP{185, 246, 210, 2}},
{Region: "Czech Republic", City: "Prague", Hostname: "cz-001.whiskergalaxy.com", IP: net.IP{185, 156, 174, 11}},
{Region: "Denmark", City: "Copenhagen", Hostname: "dk-001.whiskergalaxy.com", IP: net.IP{185, 206, 224, 195}},
{Region: "Denmark", City: "Copenhagen", Hostname: "dk-002.whiskergalaxy.com", IP: net.IP{134, 90, 149, 147}},
{Region: "Estonia", City: "Tallinn", Hostname: "ee-001.whiskergalaxy.com", IP: net.IP{46, 22, 211, 251}},
{Region: "Estonia", City: "Tallinn", Hostname: "ee-002.whiskergalaxy.com", IP: net.IP{196, 196, 216, 131}},
{Region: "Fake Antarctica", City: "Troll", Hostname: "aq-001.whiskergalaxy.com", IP: net.IP{23, 154, 160, 212}},
{Region: "Fake Antarctica", City: "Troll", Hostname: "aq-002.whiskergalaxy.com", IP: net.IP{23, 154, 160, 222}},
{Region: "Finland", City: "Helsinki", Hostname: "fi-002.whiskergalaxy.com", IP: net.IP{185, 112, 82, 227}},
{Region: "Finland", City: "Helsinki", Hostname: "fi-003.whiskergalaxy.com", IP: net.IP{194, 34, 133, 82}},
{Region: "Finland", City: "Helsinki", Hostname: "fi-004.whiskergalaxy.com", IP: net.IP{196, 244, 192, 51}},
{Region: "France", City: "Paris", Hostname: "fr-004.whiskergalaxy.com", IP: net.IP{185, 156, 173, 187}},
{Region: "France", City: "Paris", Hostname: "fr-005.whiskergalaxy.com", IP: net.IP{82, 102, 18, 35}},
{Region: "France", City: "Paris", Hostname: "fr-008.whiskergalaxy.com", IP: net.IP{84, 17, 42, 34}},
{Region: "France", City: "Paris", Hostname: "fr-009.whiskergalaxy.com", IP: net.IP{84, 17, 42, 2}},
{Region: "France", City: "Paris", Hostname: "fr-011.whiskergalaxy.com", IP: net.IP{45, 89, 174, 35}},
{Region: "Germany", City: "Frankfurt", Hostname: "de-003.whiskergalaxy.com", IP: net.IP{89, 249, 65, 19}},
{Region: "Germany", City: "Frankfurt", Hostname: "de-006.whiskergalaxy.com", IP: net.IP{185, 130, 184, 195}},
{Region: "Germany", City: "Frankfurt", Hostname: "de-009.whiskergalaxy.com", IP: net.IP{195, 181, 170, 66}},
{Region: "Germany", City: "Frankfurt", Hostname: "de-010.whiskergalaxy.com", IP: net.IP{195, 181, 175, 98}},
{Region: "Germany", City: "Frankfurt", Hostname: "de-011.whiskergalaxy.com", IP: net.IP{217, 138, 194, 115}},
{Region: "Germany", City: "Frankfurt", Hostname: "de-012.whiskergalaxy.com", IP: net.IP{45, 87, 212, 51}},
{Region: "Greece", City: "Athens", Hostname: "gr-002.whiskergalaxy.com", IP: net.IP{78, 108, 38, 155}},
{Region: "Greece", City: "Athens", Hostname: "gr-004.whiskergalaxy.com", IP: net.IP{185, 226, 64, 111}},
{Region: "Greece", City: "Athens", Hostname: "gr-005.whiskergalaxy.com", IP: net.IP{188, 123, 126, 146}},
{Region: "Hong Kong", City: "Hong Kong", Hostname: "hk-005.whiskergalaxy.com", IP: net.IP{103, 10, 197, 99}},
{Region: "Hong Kong", City: "Hong Kong", Hostname: "hk-006.whiskergalaxy.com", IP: net.IP{84, 17, 57, 114}},
{Region: "Hungary", City: "Budapest", Hostname: "hu-001.whiskergalaxy.com", IP: net.IP{185, 104, 187, 43}},
{Region: "Iceland", City: "Reykjavik", Hostname: "is-001.whiskergalaxy.com", IP: net.IP{82, 221, 139, 38}},
{Region: "Iceland", City: "Reykjavik", Hostname: "is-002.whiskergalaxy.com", IP: net.IP{185, 165, 170, 2}},
{Region: "India", City: "Chennai", Hostname: "in-005.whiskergalaxy.com", IP: net.IP{169, 38, 68, 188}},
{Region: "India", City: "Chennai", Hostname: "in-006.whiskergalaxy.com", IP: net.IP{169, 38, 72, 14}},
{Region: "India", City: "Chennai", Hostname: "in-007.whiskergalaxy.com", IP: net.IP{169, 38, 72, 12}},
{Region: "India", City: "Mumbai", Hostname: "in-009.whiskergalaxy.com", IP: net.IP{165, 231, 253, 211}},
{Region: "India", City: "New Delhi", Hostname: "in-008.whiskergalaxy.com", IP: net.IP{103, 205, 140, 227}},
{Region: "Indonesia", City: "Jakarta", Hostname: "id-002.whiskergalaxy.com", IP: net.IP{45, 127, 134, 91}},
{Region: "Ireland", City: "Dublin", Hostname: "ie-001.whiskergalaxy.com", IP: net.IP{185, 24, 232, 146}},
{Region: "Ireland", City: "Dublin", Hostname: "ie-002.whiskergalaxy.com", IP: net.IP{185, 104, 219, 2}},
{Region: "Ireland", City: "Dublin", Hostname: "ie-003.whiskergalaxy.com", IP: net.IP{23, 92, 127, 35}},
{Region: "Israel", City: "Ashdod", Hostname: "il-002.whiskergalaxy.com", IP: net.IP{185, 191, 205, 139}},
{Region: "Israel", City: "Jerusalem", Hostname: "il-001.whiskergalaxy.com", IP: net.IP{160, 116, 0, 27}},
{Region: "Italy", City: "Milan", Hostname: "it-001.whiskergalaxy.com", IP: net.IP{37, 120, 135, 83}},
{Region: "Italy", City: "Milan", Hostname: "it-004.whiskergalaxy.com", IP: net.IP{84, 17, 59, 66}},
{Region: "Italy", City: "Milan", Hostname: "it-005.whiskergalaxy.com", IP: net.IP{89, 40, 182, 3}},
{Region: "Italy", City: "Rome", Hostname: "it-003.whiskergalaxy.com", IP: net.IP{87, 101, 94, 195}},
{Region: "Italy", City: "Rome", Hostname: "it-006.whiskergalaxy.com", IP: net.IP{37, 120, 207, 19}},
{Region: "Japan", City: "Tokyo", Hostname: "jp-004.whiskergalaxy.com", IP: net.IP{193, 148, 16, 243}},
{Region: "Japan", City: "Tokyo", Hostname: "jp-005.whiskergalaxy.com", IP: net.IP{89, 187, 161, 114}},
{Region: "Latvia", City: "Riga", Hostname: "lv-003.whiskergalaxy.com", IP: net.IP{85, 254, 72, 23}},
{Region: "Latvia", City: "Riga", Hostname: "lv-004.whiskergalaxy.com", IP: net.IP{89, 111, 33, 220}},
{Region: "Lithuania", City: "Siauliai", Hostname: "lt-003.whiskergalaxy.com", IP: net.IP{85, 206, 163, 225}},
{Region: "Malaysia", City: "Kuala Lumpur", Hostname: "my-001.whiskergalaxy.com", IP: net.IP{103, 106, 250, 31}},
{Region: "Malaysia", City: "Kuala Lumpur", Hostname: "my-003.whiskergalaxy.com", IP: net.IP{103, 212, 69, 232}},
{Region: "Mexico", City: "Guadalajara", Hostname: "mx-007.whiskergalaxy.com", IP: net.IP{201, 131, 125, 107}},
{Region: "Mexico", City: "Guadalajara", Hostname: "mx-008.whiskergalaxy.com", IP: net.IP{143, 255, 57, 67}},
{Region: "Mexico", City: "Mexico City", Hostname: "mx-009.whiskergalaxy.com", IP: net.IP{190, 103, 179, 211}},
{Region: "Mexico", City: "Mexico City", Hostname: "mx-010.whiskergalaxy.com", IP: net.IP{190, 103, 179, 217}},
{Region: "Moldova", City: "Chisinau", Hostname: "md-002.whiskergalaxy.com", IP: net.IP{178, 175, 144, 123}},
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-001.whiskergalaxy.com", IP: net.IP{46, 166, 143, 98}},
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-005.whiskergalaxy.com", IP: net.IP{185, 212, 171, 131}},
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-008.whiskergalaxy.com", IP: net.IP{185, 253, 96, 3}},
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-011.whiskergalaxy.com", IP: net.IP{84, 17, 46, 2}},
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-012.whiskergalaxy.com", IP: net.IP{37, 120, 192, 19}},
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-013.whiskergalaxy.com", IP: net.IP{72, 11, 157, 67}},
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-014.whiskergalaxy.com", IP: net.IP{72, 11, 157, 35}},
{Region: "Netherlands", City: "Amsterdam", Hostname: "nl-015.whiskergalaxy.com", IP: net.IP{109, 201, 130, 2}},
{Region: "New Zealand", City: "Auckland ", Hostname: "nz-003.whiskergalaxy.com", IP: net.IP{103, 108, 94, 163}},
{Region: "New Zealand", City: "Auckland", Hostname: "nz-002.whiskergalaxy.com", IP: net.IP{103, 62, 49, 113}},
{Region: "North Macedonia", City: "Skopje", Hostname: "mk-001.whiskergalaxy.com", IP: net.IP{185, 225, 28, 51}},
{Region: "Norway", City: "Oslo", Hostname: "no-003.whiskergalaxy.com", IP: net.IP{185, 206, 225, 131}},
{Region: "Norway", City: "Oslo", Hostname: "no-006.whiskergalaxy.com", IP: net.IP{37, 120, 203, 67}},
{Region: "Panama", City: "Panama City", Hostname: "pa-001.whiskergalaxy.com", IP: net.IP{138, 186, 142, 203}},
{Region: "Peru", City: "Lima", Hostname: "pe-002.whiskergalaxy.com", IP: net.IP{190, 120, 229, 139}},
{Region: "Philippines", City: "Manila", Hostname: "ph-003.whiskergalaxy.com", IP: net.IP{141, 98, 215, 211}},
{Region: "Philippines", City: "San Antonio", Hostname: "ph-002.whiskergalaxy.com", IP: net.IP{103, 103, 0, 118}},
{Region: "Poland", City: "Warsaw", Hostname: "pl-002.whiskergalaxy.com", IP: net.IP{185, 244, 214, 35}},
{Region: "Poland", City: "Warsaw", Hostname: "pl-004.whiskergalaxy.com", IP: net.IP{84, 17, 55, 98}},
{Region: "Poland", City: "Warsaw", Hostname: "pl-005.whiskergalaxy.com", IP: net.IP{5, 133, 11, 116}},
{Region: "Portugal", City: "Lisbon", Hostname: "pt-002.whiskergalaxy.com", IP: net.IP{94, 46, 13, 215}},
{Region: "Portugal", City: "Lisbon", Hostname: "pt-003.whiskergalaxy.com", IP: net.IP{185, 15, 21, 66}},
{Region: "Romania", City: "Bucharest", Hostname: "ro-006.whiskergalaxy.com", IP: net.IP{89, 46, 103, 147}},
{Region: "Romania", City: "Bucharest", Hostname: "ro-008.whiskergalaxy.com", IP: net.IP{91, 207, 102, 147}},
{Region: "Russia", City: "Moscow", Hostname: "ru-010.whiskergalaxy.com", IP: net.IP{95, 213, 193, 227}},
{Region: "Russia", City: "Moscow", Hostname: "ru-011.whiskergalaxy.com", IP: net.IP{95, 213, 193, 195}},
{Region: "Russia", City: "Saint Petersburg", Hostname: "ru-008.whiskergalaxy.com", IP: net.IP{94, 242, 62, 19}},
{Region: "Russia", City: "Saint Petersburg", Hostname: "ru-009.whiskergalaxy.com", IP: net.IP{94, 242, 62, 67}},
{Region: "Russia", City: "Saint Petersburg", Hostname: "ru-012.whiskergalaxy.com", IP: net.IP{188, 124, 42, 115}},
{Region: "Russia", City: "Saint Petersburg", Hostname: "ru-013.whiskergalaxy.com", IP: net.IP{188, 124, 42, 99}},
{Region: "Serbia", City: "Belgrade", Hostname: "rs-003.whiskergalaxy.com", IP: net.IP{141, 98, 103, 19}},
{Region: "Singapore", City: "Singapore", Hostname: "sg-003.whiskergalaxy.com", IP: net.IP{185, 200, 117, 163}},
{Region: "Singapore", City: "Singapore", Hostname: "sg-004.whiskergalaxy.com", IP: net.IP{82, 102, 25, 131}},
{Region: "Singapore", City: "Singapore", Hostname: "sg-005.whiskergalaxy.com", IP: net.IP{103, 62, 48, 224}},
{Region: "Singapore", City: "Singapore", Hostname: "sg-006.whiskergalaxy.com", IP: net.IP{156, 146, 56, 98}},
{Region: "Singapore", City: "Singapore", Hostname: "sg-007.whiskergalaxy.com", IP: net.IP{156, 146, 56, 111}},
{Region: "Slovakia", City: "Bratislava", Hostname: "sk-001.whiskergalaxy.com", IP: net.IP{185, 245, 85, 3}},
{Region: "South Africa", City: "Johannesburg", Hostname: "za-001.whiskergalaxy.com", IP: net.IP{197, 242, 157, 235}},
{Region: "South Africa", City: "Johannesburg", Hostname: "za-002.whiskergalaxy.com", IP: net.IP{129, 232, 167, 211}},
{Region: "South Africa", City: "Johannesburg", Hostname: "za-003.whiskergalaxy.com", IP: net.IP{197, 242, 156, 53}},
{Region: "South Africa", City: "Johannesburg", Hostname: "za-004.whiskergalaxy.com", IP: net.IP{165, 73, 248, 91}},
{Region: "South Korea", City: "Seoul", Hostname: "kr-001.whiskergalaxy.com", IP: net.IP{103, 212, 223, 3}},
{Region: "South Korea", City: "Seoul", Hostname: "kr-002.whiskergalaxy.com", IP: net.IP{218, 232, 76, 179}},
{Region: "South Korea", City: "Seoul", Hostname: "kr-005.whiskergalaxy.com", IP: net.IP{45, 133, 194, 235}},
{Region: "Spain", City: "Barcelona", Hostname: "es-004.whiskergalaxy.com", IP: net.IP{37, 120, 142, 227}},
{Region: "Spain", City: "Madrid", Hostname: "es-002.whiskergalaxy.com", IP: net.IP{89, 238, 178, 43}},
{Region: "Spain", City: "Madrid", Hostname: "es-003.whiskergalaxy.com", IP: net.IP{217, 138, 218, 99}},
{Region: "Sweden", City: "Stockholm", Hostname: "se-001.whiskergalaxy.com", IP: net.IP{31, 13, 191, 67}},
{Region: "Sweden", City: "Stockholm", Hostname: "se-002.whiskergalaxy.com", IP: net.IP{79, 142, 76, 198}},
{Region: "Sweden", City: "Stockholm", Hostname: "se-003.whiskergalaxy.com", IP: net.IP{195, 181, 166, 129}},
{Region: "Switzerland", City: "Zurich", Hostname: "ch-001.whiskergalaxy.com", IP: net.IP{31, 7, 57, 242}},
{Region: "Switzerland", City: "Zurich", Hostname: "ch-003.whiskergalaxy.com", IP: net.IP{185, 156, 175, 179}},
{Region: "Switzerland", City: "Zurich", Hostname: "ch-005.whiskergalaxy.com", IP: net.IP{89, 187, 165, 98}},
{Region: "Switzerland", City: "Zurich", Hostname: "ch-006.whiskergalaxy.com", IP: net.IP{84, 17, 53, 2}},
{Region: "Switzerland", City: "Zurich", Hostname: "ch-008.whiskergalaxy.com", IP: net.IP{37, 120, 213, 163}},
{Region: "Taiwan", City: "Taipei", Hostname: "tw-008.whiskergalaxy.com", IP: net.IP{103, 4, 29, 77}},
{Region: "Taiwan", City: "Taipei", Hostname: "tw-009.whiskergalaxy.com", IP: net.IP{185, 189, 160, 12}},
{Region: "Taiwan", City: "Taipei", Hostname: "tw-010.whiskergalaxy.com", IP: net.IP{185, 189, 160, 27}},
{Region: "Taiwan", City: "Taipei", Hostname: "tw-011.whiskergalaxy.com", IP: net.IP{185, 189, 160, 32}},
{Region: "Thailand", City: "Bangkok", Hostname: "th-003.whiskergalaxy.com", IP: net.IP{27, 254, 130, 221}},
{Region: "Thailand", City: "Bangkok", Hostname: "th-005.whiskergalaxy.com", IP: net.IP{202, 129, 16, 147}},
{Region: "Thailand", City: "Bangkok", Hostname: "th-006.whiskergalaxy.com", IP: net.IP{202, 129, 16, 155}},
{Region: "Tunisia", City: "Tunis", Hostname: "tn-001.whiskergalaxy.com", IP: net.IP{41, 231, 5, 23}},
{Region: "Turkey", City: "Bursa", Hostname: "tr-001.whiskergalaxy.com", IP: net.IP{45, 123, 118, 156}},
{Region: "Turkey", City: "Istanbul", Hostname: "tr-004.whiskergalaxy.com", IP: net.IP{45, 123, 119, 11}},
{Region: "Turkey", City: "Istanbul", Hostname: "tr-006.whiskergalaxy.com", IP: net.IP{185, 125, 33, 227}},
{Region: "Turkey", City: "Istanbul", Hostname: "tr-009.whiskergalaxy.com", IP: net.IP{79, 98, 131, 43}},
{Region: "Turkey", City: "Istanbul", Hostname: "tr-011.whiskergalaxy.com", IP: net.IP{176, 53, 113, 163}},
{Region: "US Central", City: "Atlanta", Hostname: "us-central-016.whiskergalaxy.com", IP: net.IP{104, 129, 18, 3}},
{Region: "US Central", City: "Atlanta", Hostname: "us-central-020.whiskergalaxy.com", IP: net.IP{104, 129, 18, 131}},
{Region: "US Central", City: "Atlanta", Hostname: "us-central-034.whiskergalaxy.com", IP: net.IP{161, 129, 70, 195}},
{Region: "US Central", City: "Atlanta", Hostname: "us-central-046.whiskergalaxy.com", IP: net.IP{198, 12, 76, 211}},
{Region: "US Central", City: "Atlanta", Hostname: "us-central-049.whiskergalaxy.com", IP: net.IP{107, 150, 31, 3}},
{Region: "US Central", City: "Atlanta", Hostname: "us-central-050.whiskergalaxy.com", IP: net.IP{107, 150, 31, 67}},
{Region: "US Central", City: "Atlanta", Hostname: "us-central-051.whiskergalaxy.com", IP: net.IP{162, 222, 198, 67}},
{Region: "US Central", City: "Atlanta", Hostname: "us-central-054.whiskergalaxy.com", IP: net.IP{104, 223, 92, 163}},
{Region: "US Central", City: "Atlanta", Hostname: "us-central-056.whiskergalaxy.com", IP: net.IP{206, 217, 143, 131}},
{Region: "US Central", City: "Dallas", Hostname: "us-central-014.whiskergalaxy.com", IP: net.IP{69, 12, 94, 67}},
{Region: "US Central", City: "Dallas", Hostname: "us-central-029.whiskergalaxy.com", IP: net.IP{198, 55, 125, 195}},
{Region: "US Central", City: "Dallas", Hostname: "us-central-036.whiskergalaxy.com", IP: net.IP{204, 44, 112, 67}},
{Region: "US Central", City: "Dallas", Hostname: "us-central-037.whiskergalaxy.com", IP: net.IP{204, 44, 112, 131}},
{Region: "US Central", City: "Dallas", Hostname: "us-central-044.whiskergalaxy.com", IP: net.IP{206, 217, 139, 195}},
{Region: "US Central", City: "Dallas", Hostname: "us-central-045.whiskergalaxy.com", IP: net.IP{172, 241, 131, 129}},
{Region: "US Central", City: "Dallas", Hostname: "us-central-055.whiskergalaxy.com", IP: net.IP{206, 217, 139, 19}},
{Region: "US Central", City: "Dallas", Hostname: "us-central-057.whiskergalaxy.com", IP: net.IP{172, 241, 26, 78}},
{Region: "US Central", City: "Dallas", Hostname: "us-central-060.whiskergalaxy.com", IP: net.IP{198, 55, 126, 131}},
{Region: "US Central", City: "Denver", Hostname: "us-central-043.whiskergalaxy.com", IP: net.IP{199, 115, 96, 83}},
{Region: "US Central", City: "Denver", Hostname: "us-central-058.whiskergalaxy.com", IP: net.IP{198, 54, 128, 116}},
{Region: "US Central", City: "Denver", Hostname: "us-central-062.whiskergalaxy.com", IP: net.IP{174, 128, 251, 147}},
{Region: "US Central", City: "Kansas City", Hostname: "us-central-063.whiskergalaxy.com", IP: net.IP{38, 146, 5, 51}},
{Region: "US Central", City: "Salt Lake City", Hostname: "us-central-047.whiskergalaxy.com", IP: net.IP{107, 182, 234, 240}},
{Region: "US Central", City: "Salt Lake City", Hostname: "us-central-052.whiskergalaxy.com", IP: net.IP{67, 212, 238, 196}},
{Region: "US East", City: "Boston", Hostname: "us-east-039.whiskergalaxy.com", IP: net.IP{199, 217, 104, 227}},
{Region: "US East", City: "Boston", Hostname: "us-east-051.whiskergalaxy.com", IP: net.IP{199, 217, 105, 227}},
{Region: "US East", City: "Buffalo", Hostname: "us-east-045.whiskergalaxy.com", IP: net.IP{104, 168, 34, 147}},
{Region: "US East", City: "Buffalo", Hostname: "us-east-065.whiskergalaxy.com", IP: net.IP{198, 12, 64, 35}},
{Region: "US East", City: "Charlotte", Hostname: "us-east-040.whiskergalaxy.com", IP: net.IP{67, 21, 32, 145}},
{Region: "US East", City: "Chicago", Hostname: "us-east-015.whiskergalaxy.com", IP: net.IP{68, 235, 50, 227}},
{Region: "US East", City: "Chicago", Hostname: "us-east-019.whiskergalaxy.com", IP: net.IP{23, 226, 141, 195}},
{Region: "US East", City: "Chicago", Hostname: "us-east-022.whiskergalaxy.com", IP: net.IP{167, 160, 172, 3}},
{Region: "US East", City: "Chicago", Hostname: "us-east-047.whiskergalaxy.com", IP: net.IP{23, 83, 91, 170}},
{Region: "US East", City: "Chicago", Hostname: "us-east-053.whiskergalaxy.com", IP: net.IP{107, 150, 29, 131}},
{Region: "US East", City: "Chicago", Hostname: "us-east-069.whiskergalaxy.com", IP: net.IP{68, 235, 35, 172}},
{Region: "US East", City: "Chicago", Hostname: "us-east-071.whiskergalaxy.com", IP: net.IP{68, 235, 35, 12}},
{Region: "US East", City: "Chicago", Hostname: "us-east-077.whiskergalaxy.com", IP: net.IP{68, 235, 43, 204}},
{Region: "US East", City: "Cleveland", Hostname: "us-east-078.whiskergalaxy.com", IP: net.IP{38, 101, 74, 19}},
{Region: "US East", City: "Columbus", Hostname: "us-east-059.whiskergalaxy.com", IP: net.IP{67, 219, 146, 67}},
{Region: "US East", City: "Detroit", Hostname: "us-east-079.whiskergalaxy.com", IP: net.IP{104, 244, 210, 51}},
{Region: "US East", City: "Miami", Hostname: "us-east-006.whiskergalaxy.com", IP: net.IP{173, 44, 36, 67}},
{Region: "US East", City: "Miami", Hostname: "us-east-012.whiskergalaxy.com", IP: net.IP{45, 87, 214, 35}},
{Region: "US East", City: "Miami", Hostname: "us-east-028.whiskergalaxy.com", IP: net.IP{104, 223, 127, 195}},
{Region: "US East", City: "Miami", Hostname: "us-east-049.whiskergalaxy.com", IP: net.IP{23, 82, 136, 93}},
{Region: "US East", City: "Miami", Hostname: "us-east-067.whiskergalaxy.com", IP: net.IP{86, 106, 87, 83}},
{Region: "US East", City: "New Jersey", Hostname: "us-east-020.whiskergalaxy.com", IP: net.IP{162, 222, 195, 67}},
{Region: "US East", City: "New Jersey", Hostname: "us-east-054.whiskergalaxy.com", IP: net.IP{167, 160, 167, 195}},
{Region: "US East", City: "New York", Hostname: "us-east-013.whiskergalaxy.com", IP: net.IP{185, 232, 22, 195}},
{Region: "US East", City: "New York", Hostname: "us-east-046.whiskergalaxy.com", IP: net.IP{206, 217, 129, 227}},
{Region: "US East", City: "New York", Hostname: "us-east-050.whiskergalaxy.com", IP: net.IP{173, 208, 45, 33}},
{Region: "US East", City: "New York", Hostname: "us-east-064.whiskergalaxy.com", IP: net.IP{206, 217, 128, 3}},
{Region: "US East", City: "New York", Hostname: "us-east-068.whiskergalaxy.com", IP: net.IP{142, 234, 200, 176}},
{Region: "US East", City: "New York", Hostname: "us-east-073.whiskergalaxy.com", IP: net.IP{217, 138, 255, 163}},
{Region: "US East", City: "New York", Hostname: "us-east-074.whiskergalaxy.com", IP: net.IP{217, 138, 255, 179}},
{Region: "US East", City: "Orlando", Hostname: "us-east-052.whiskergalaxy.com", IP: net.IP{198, 147, 22, 225}},
{Region: "US East", City: "Philadelphia", Hostname: "us-east-060.whiskergalaxy.com", IP: net.IP{76, 72, 175, 99}},
{Region: "US East", City: "Philadelphia", Hostname: "us-east-061.whiskergalaxy.com", IP: net.IP{156, 96, 59, 102}},
{Region: "US East", City: "Washington DC", Hostname: "us-east-048.whiskergalaxy.com", IP: net.IP{23, 82, 8, 143}},
{Region: "US East", City: "Washington DC", Hostname: "us-east-055.whiskergalaxy.com", IP: net.IP{23, 105, 170, 139}},
{Region: "US East", City: "Washington DC", Hostname: "us-east-057.whiskergalaxy.com", IP: net.IP{23, 105, 170, 130}},
{Region: "US East", City: "Washington DC", Hostname: "us-east-058.whiskergalaxy.com", IP: net.IP{23, 105, 170, 151}},
{Region: "US West", City: "Bend", Hostname: "us-west-038.whiskergalaxy.com", IP: net.IP{104, 152, 222, 33}},
{Region: "US West", City: "Las Vegas", Hostname: "us-west-018.whiskergalaxy.com", IP: net.IP{82, 102, 30, 67}},
{Region: "US West", City: "Las Vegas", Hostname: "us-west-030.whiskergalaxy.com", IP: net.IP{37, 120, 147, 163}},
{Region: "US West", City: "Los Angeles", Hostname: "us-west-004.whiskergalaxy.com", IP: net.IP{185, 236, 200, 35}},
{Region: "US West", City: "Los Angeles", Hostname: "us-west-015.whiskergalaxy.com", IP: net.IP{216, 45, 53, 131}},
{Region: "US West", City: "Los Angeles", Hostname: "us-west-027.whiskergalaxy.com", IP: net.IP{212, 103, 49, 67}},
{Region: "US West", City: "Los Angeles", Hostname: "us-west-040.whiskergalaxy.com", IP: net.IP{89, 187, 185, 34}},
{Region: "US West", City: "Los Angeles", Hostname: "us-west-044.whiskergalaxy.com", IP: net.IP{192, 3, 20, 51}},
{Region: "US West", City: "Los Angeles", Hostname: "us-west-047.whiskergalaxy.com", IP: net.IP{172, 241, 214, 202}},
{Region: "US West", City: "Los Angeles", Hostname: "us-west-055.whiskergalaxy.com", IP: net.IP{104, 129, 3, 67}},
{Region: "US West", City: "Los Angeles", Hostname: "us-west-059.whiskergalaxy.com", IP: net.IP{104, 129, 3, 163}},
{Region: "US West", City: "Los Angeles", Hostname: "us-west-060.whiskergalaxy.com", IP: net.IP{217, 138, 217, 51}},
{Region: "US West", City: "Los Angeles", Hostname: "us-west-063.whiskergalaxy.com", IP: net.IP{198, 23, 242, 147}},
{Region: "US West", City: "Los Angeles", Hostname: "us-west-065.whiskergalaxy.com", IP: net.IP{217, 138, 217, 211}},
{Region: "US West", City: "Los Angeles", Hostname: "us-west-066.whiskergalaxy.com", IP: net.IP{89, 187, 187, 98}},
{Region: "US West", City: "Phoenix", Hostname: "us-west-046.whiskergalaxy.com", IP: net.IP{23, 83, 130, 166}},
{Region: "US West", City: "Phoenix", Hostname: "us-west-061.whiskergalaxy.com", IP: net.IP{23, 83, 131, 187}},
{Region: "US West", City: "San Francisco", Hostname: "us-west-048.whiskergalaxy.com", IP: net.IP{172, 241, 250, 131}},
{Region: "US West", City: "San Francisco", Hostname: "us-west-053.whiskergalaxy.com", IP: net.IP{209, 58, 129, 121}},
{Region: "US West", City: "San Francisco", Hostname: "us-west-054.whiskergalaxy.com", IP: net.IP{172, 255, 125, 141}},
{Region: "US West", City: "San Jose", Hostname: "us-west-052.whiskergalaxy.com", IP: net.IP{66, 115, 176, 3}},
{Region: "US West", City: "Santa Clara", Hostname: "us-west-050.whiskergalaxy.com", IP: net.IP{167, 88, 60, 227}},
{Region: "US West", City: "Santa Clara", Hostname: "us-west-051.whiskergalaxy.com", IP: net.IP{167, 88, 60, 243}},
{Region: "US West", City: "Seattle", Hostname: "us-west-043.whiskergalaxy.com", IP: net.IP{23, 94, 74, 99}},
{Region: "US West", City: "Seattle", Hostname: "us-west-045.whiskergalaxy.com", IP: net.IP{64, 120, 2, 174}},
{Region: "US West", City: "Seattle", Hostname: "us-west-056.whiskergalaxy.com", IP: net.IP{104, 129, 56, 67}},
{Region: "US West", City: "Seattle", Hostname: "us-west-057.whiskergalaxy.com", IP: net.IP{104, 129, 56, 131}},
{Region: "US West", City: "Seattle", Hostname: "us-west-062.whiskergalaxy.com", IP: net.IP{198, 12, 116, 195}},
{Region: "Ukraine", City: "Kyiv", Hostname: "ua-006.whiskergalaxy.com", IP: net.IP{45, 141, 156, 11}},
{Region: "Ukraine", City: "Kyiv", Hostname: "ua-007.whiskergalaxy.com", IP: net.IP{45, 141, 156, 50}},
{Region: "United Arab Emirates", City: "Dubai", Hostname: "ae-001.whiskergalaxy.com", IP: net.IP{45, 9, 249, 43}},
{Region: "United Kingdom", City: "Edinburgh", Hostname: "uk-026.whiskergalaxy.com", IP: net.IP{193, 36, 118, 243}},
{Region: "United Kingdom", City: "London", Hostname: "uk-007.whiskergalaxy.com", IP: net.IP{185, 212, 168, 133}},
{Region: "United Kingdom", City: "London", Hostname: "uk-013.whiskergalaxy.com", IP: net.IP{89, 238, 150, 229}},
{Region: "United Kingdom", City: "London", Hostname: "uk-014.whiskergalaxy.com", IP: net.IP{2, 58, 29, 145}},
{Region: "United Kingdom", City: "London", Hostname: "uk-015.whiskergalaxy.com", IP: net.IP{2, 58, 29, 17}},
{Region: "United Kingdom", City: "London", Hostname: "uk-017.whiskergalaxy.com", IP: net.IP{84, 17, 50, 130}},
{Region: "United Kingdom", City: "London", Hostname: "uk-021.whiskergalaxy.com", IP: net.IP{212, 102, 63, 32}},
{Region: "United Kingdom", City: "London", Hostname: "uk-022.whiskergalaxy.com", IP: net.IP{212, 102, 63, 62}},
{Region: "United Kingdom", City: "London", Hostname: "uk-024.whiskergalaxy.com", IP: net.IP{217, 138, 254, 51}},
{Region: "United Kingdom", City: "Manchester", Hostname: "uk-008.whiskergalaxy.com", IP: net.IP{81, 92, 207, 69}},
{Region: "United Kingdom", City: "Manchester", Hostname: "uk-010.whiskergalaxy.com", IP: net.IP{89, 238, 135, 133}},
{Region: "United Kingdom", City: "Manchester", Hostname: "uk-025.whiskergalaxy.com", IP: net.IP{89, 44, 201, 99}},
{Region: "Vietnam", City: "Hanoi", Hostname: "vn-001.whiskergalaxy.com", IP: net.IP{103, 9, 76, 197}},
{Region: "Vietnam", City: "Hanoi", Hostname: "vn-002.whiskergalaxy.com", IP: net.IP{103, 9, 79, 186}},
{Region: "Vietnam", City: "Hanoi", Hostname: "vn-003.whiskergalaxy.com", IP: net.IP{103, 9, 79, 219}},
{Region: "WINDFLIX CA", City: "Toronto", Hostname: "wf-ca-003.whiskergalaxy.com", IP: net.IP{104, 218, 60, 111}},
{Region: "WINDFLIX CA", City: "Toronto", Hostname: "wf-ca-004.whiskergalaxy.com", IP: net.IP{104, 254, 92, 99}},
{Region: "WINDFLIX JP", City: "Tokyo", Hostname: "wf-jp-002.whiskergalaxy.com", IP: net.IP{5, 181, 235, 67}},
{Region: "WINDFLIX UK", City: "London", Hostname: "wf-uk-001.whiskergalaxy.com", IP: net.IP{45, 9, 248, 3}},
{Region: "WINDFLIX UK", City: "London", Hostname: "wf-uk-006.whiskergalaxy.com", IP: net.IP{81, 92, 200, 85}},
{Region: "WINDFLIX UK", City: "London", Hostname: "wf-uk-007.whiskergalaxy.com", IP: net.IP{89, 47, 62, 83}},
{Region: "WINDFLIX US", City: "New York", Hostname: "wf-us-010.whiskergalaxy.com", IP: net.IP{38, 132, 122, 195}},
{Region: "WINDFLIX US", City: "New York", Hostname: "wf-us-011.whiskergalaxy.com", IP: net.IP{38, 132, 122, 131}},
{Region: "WINDFLIX US", City: "New York", Hostname: "wf-us-012.whiskergalaxy.com", IP: net.IP{185, 232, 22, 131}},
{Region: "WINDFLIX US", City: "New York", Hostname: "wf-us-013.whiskergalaxy.com", IP: net.IP{217, 138, 206, 211}},
{Region: "WINDFLIX US", City: "New York", Hostname: "wf-us-014.whiskergalaxy.com", IP: net.IP{77, 81, 136, 99}},
{Region: "WINDFLIX US", City: "New York", Hostname: "wf-us-015.whiskergalaxy.com", IP: net.IP{38, 132, 101, 211}},
}
}

View File

@@ -66,7 +66,7 @@ func generateUnboundConf(ctx context.Context, settings settings.DNS,
// Network
"do-ip4": "yes",
"do-ip6": doIPv6,
"interface": "127.0.0.1",
"interface": "0.0.0.0",
"port": "53",
// Other
"username": "\"nonrootuser\"",

View File

@@ -54,7 +54,7 @@ server:
harden-referral-path: yes
hide-identity: yes
hide-version: yes
interface: 127.0.0.1
interface: 0.0.0.0
key-cache-size: 16m
key-cache-slabs: 4
msg-cache-size: 4m

View File

@@ -94,6 +94,16 @@ func (c *configurator) enable(ctx context.Context) (err error) {
return fmt.Errorf("cannot enable firewall: %w", err)
}
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, c.localSubnet, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
for _, subnet := range c.outboundSubnets {
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subnet, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err)
}
}
// Allows packets from any IP address to go through eth0 / local network
// to reach Gluetun.
if err := c.acceptInputToSubnet(ctx, c.defaultInterface, c.localSubnet, remove); err != nil {

View File

@@ -18,10 +18,11 @@ type Configurator interface {
SetEnabled(ctx context.Context, enabled bool) (err error)
SetVPNConnection(ctx context.Context, connection models.OpenVPNConnection) (err error)
SetAllowedPort(ctx context.Context, port uint16, intf string) (err error)
SetOutboundSubnets(ctx context.Context, subnets []net.IPNet) (err error)
RemoveAllowedPort(ctx context.Context, port uint16) (err error)
SetDebug()
// SetNetworkInformation is meant to be called only once
SetNetworkInformation(defaultInterface string, defaultGateway net.IP, localSubnet net.IPNet)
SetNetworkInformation(defaultInterface string, defaultGateway net.IP, localSubnet net.IPNet, localIP net.IP)
}
type configurator struct { //nolint:maligned
@@ -34,11 +35,13 @@ type configurator struct { //nolint:maligned
defaultInterface string
defaultGateway net.IP
localSubnet net.IPNet
localIP net.IP
networkInfoMutex sync.Mutex
// State
enabled bool
vpnConnection models.OpenVPNConnection
outboundSubnets []net.IPNet
allowedInputPorts map[uint16]string // port to interface mapping
stateMutex sync.Mutex
}
@@ -58,10 +61,12 @@ func (c *configurator) SetDebug() {
c.debug = true
}
func (c *configurator) SetNetworkInformation(defaultInterface string, defaultGateway net.IP, localSubnet net.IPNet) {
func (c *configurator) SetNetworkInformation(
defaultInterface string, defaultGateway net.IP, localSubnet net.IPNet, localIP net.IP) {
c.networkInfoMutex.Lock()
defer c.networkInfoMutex.Unlock()
c.defaultInterface = defaultInterface
c.defaultGateway = defaultGateway
c.localSubnet = localSubnet
c.localIP = localIP
}

View File

@@ -124,6 +124,19 @@ func (c *configurator) acceptOutputTrafficToVPN(ctx context.Context,
appendOrDelete(remove), connection.IP, defaultInterface, connection.Protocol, connection.Protocol, connection.Port))
}
// Thanks to @npawelek.
func (c *configurator) acceptOutputFromIPToSubnet(ctx context.Context,
intf string, sourceIP net.IP, destinationSubnet net.IPNet, remove bool) error {
interfaceFlag := "-o " + intf
if intf == "*" { // all interfaces
interfaceFlag = ""
}
return c.runIptablesInstruction(ctx, fmt.Sprintf(
"%s OUTPUT %s -s %s -d %s -j ACCEPT",
appendOrDelete(remove), interfaceFlag, sourceIP.String(), destinationSubnet.String(),
))
}
// Used for port forwarding, with intf set to tun.
func (c *configurator) acceptInputToPort(ctx context.Context, intf string, port uint16, remove bool) error {
interfaceFlag := "-i " + intf

View File

@@ -0,0 +1,56 @@
package firewall
import (
"context"
"fmt"
"net"
)
func (c *configurator) SetOutboundSubnets(ctx context.Context, subnets []net.IPNet) (err error) {
c.stateMutex.Lock()
defer c.stateMutex.Unlock()
if !c.enabled {
c.logger.Info("firewall disabled, only updating allowed subnets internal list")
c.outboundSubnets = make([]net.IPNet, len(subnets))
copy(c.outboundSubnets, subnets)
return nil
}
c.logger.Info("setting allowed subnets through firewall...")
subnetsToAdd := findSubnetsToAdd(c.outboundSubnets, subnets)
subnetsToRemove := findSubnetsToRemove(c.outboundSubnets, subnets)
if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 {
return nil
}
c.removeOutboundSubnets(ctx, subnetsToRemove)
if err := c.addOutboundSubnets(ctx, subnetsToAdd); err != nil {
return fmt.Errorf("cannot set allowed subnets through firewall: %w", err)
}
return nil
}
func (c *configurator) removeOutboundSubnets(ctx context.Context, subnets []net.IPNet) {
const remove = true
for _, subnet := range subnets {
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subnet, remove); err != nil {
c.logger.Error("cannot remove outdated outbound subnet through firewall: %s", err)
continue
}
c.outboundSubnets = removeSubnetFromSubnets(c.outboundSubnets, subnet)
}
}
func (c *configurator) addOutboundSubnets(ctx context.Context, subnets []net.IPNet) error {
const remove = false
for _, subnet := range subnets {
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subnet, remove); err != nil {
return fmt.Errorf("cannot add allowed subnet through firewall: %w", err)
}
c.outboundSubnets = append(c.outboundSubnets, subnet)
}
return nil
}

View File

@@ -0,0 +1,53 @@
package firewall
import (
"net"
)
func findSubnetsToAdd(oldSubnets, newSubnets []net.IPNet) (subnetsToAdd []net.IPNet) {
for _, newSubnet := range newSubnets {
found := false
for _, oldSubnet := range oldSubnets {
if subnetsAreEqual(oldSubnet, newSubnet) {
found = true
break
}
}
if !found {
subnetsToAdd = append(subnetsToAdd, newSubnet)
}
}
return subnetsToAdd
}
func findSubnetsToRemove(oldSubnets, newSubnets []net.IPNet) (subnetsToRemove []net.IPNet) {
for _, oldSubnet := range oldSubnets {
found := false
for _, newSubnet := range newSubnets {
if subnetsAreEqual(oldSubnet, newSubnet) {
found = true
break
}
}
if !found {
subnetsToRemove = append(subnetsToRemove, oldSubnet)
}
}
return subnetsToRemove
}
func subnetsAreEqual(a, b net.IPNet) bool {
return a.IP.Equal(b.IP) && a.Mask.String() == b.Mask.String()
}
func removeSubnetFromSubnets(subnets []net.IPNet, subnet net.IPNet) []net.IPNet {
L := len(subnets)
for i := range subnets {
if subnetsAreEqual(subnet, subnets[i]) {
subnets[i] = subnets[L-1]
subnets = subnets[:L-1]
break
}
}
return subnets
}

View File

@@ -0,0 +1,42 @@
package healthcheck
import (
"context"
"fmt"
"io/ioutil"
"net/http"
)
type Checker interface {
Check(ctx context.Context, url string) error
}
type checker struct {
httpClient *http.Client
}
func NewChecker(httpClient *http.Client) Checker {
return &checker{
httpClient: httpClient,
}
}
func (h *checker) Check(ctx context.Context, url string) error {
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
response, err := h.httpClient.Do(request)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode == http.StatusOK {
return nil
}
b, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
return fmt.Errorf("%s: %s", response.Status, string(b))
}

View File

@@ -0,0 +1,34 @@
package healthcheck
import (
"net"
"net/http"
"github.com/qdm12/golibs/logging"
)
type handler struct {
logger logging.Logger
resolver *net.Resolver
}
func newHandler(logger logging.Logger, resolver *net.Resolver) http.Handler {
return &handler{
logger: logger,
resolver: resolver,
}
}
func (h *handler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
if request.Method != http.MethodGet {
http.Error(responseWriter, "method not supported for healthcheck", http.StatusBadRequest)
return
}
err := healthCheck(request.Context(), h.resolver)
if err != nil {
h.logger.Error(err)
http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
return
}
responseWriter.WriteHeader(http.StatusOK)
}

View File

@@ -0,0 +1,21 @@
package healthcheck
import (
"context"
"fmt"
"net"
)
func healthCheck(ctx context.Context, resolver *net.Resolver) (err error) {
// TODO use mullvad API if current provider is Mullvad
const domainToResolve = "github.com"
ips, err := resolver.LookupIP(ctx, "ip", domainToResolve)
switch {
case err != nil:
return fmt.Errorf("cannot resolve github.com: %s", err)
case len(ips) == 0:
return fmt.Errorf("resolved no IP addresses for %s", domainToResolve)
default:
return nil
}
}

View File

@@ -0,0 +1,54 @@
package healthcheck
import (
"context"
"errors"
"net"
"net/http"
"sync"
"time"
"github.com/qdm12/golibs/logging"
)
type Server interface {
Run(ctx context.Context, wg *sync.WaitGroup)
}
type server struct {
address string
logger logging.Logger
handler http.Handler
}
func NewServer(address string, logger logging.Logger) Server {
return &server{
address: address,
logger: logger.WithPrefix("healthcheck: "),
handler: newHandler(logger, &net.Resolver{}),
}
}
func (s *server) Run(ctx context.Context, wg *sync.WaitGroup) {
server := http.Server{
Addr: s.address,
Handler: s.handler,
}
go func() {
defer wg.Done()
<-ctx.Done()
s.logger.Warn("context canceled: shutting down server")
defer s.logger.Warn("server shut down")
const shutdownGraceDuration = 2 * time.Second
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownGraceDuration)
defer cancel()
if err := server.Shutdown(shutdownCtx); err != nil {
s.logger.Error("failed shutting down: %s", err)
}
}()
s.logger.Info("listening on %s", s.address)
err := server.ListenAndServe()
if err != nil && !errors.Is(ctx.Err(), context.Canceled) {
s.logger.Error(err)
}
}

View File

@@ -0,0 +1,34 @@
package httpproxy
import (
"encoding/base64"
"net/http"
"strings"
)
func isAuthorized(responseWriter http.ResponseWriter, request *http.Request,
username, password string) (authorized bool) {
basicAuth := request.Header.Get("Proxy-Authorization")
if len(basicAuth) == 0 {
responseWriter.Header().Set("Proxy-Authenticate", `Basic realm="Access to Gluetun over HTTP"`)
responseWriter.WriteHeader(http.StatusProxyAuthRequired)
return false
}
b64UsernamePassword := strings.TrimPrefix(basicAuth, "Basic ")
b, err := base64.StdEncoding.DecodeString(b64UsernamePassword)
if err != nil {
responseWriter.WriteHeader(http.StatusUnauthorized)
return false
}
usernamePassword := strings.Split(string(b), ":")
const expectedFields = 2
if len(usernamePassword) != expectedFields {
responseWriter.WriteHeader(http.StatusBadRequest)
return false
}
if username != usernamePassword[0] && password != usernamePassword[1] {
responseWriter.WriteHeader(http.StatusUnauthorized)
return false
}
return true
}

View File

@@ -0,0 +1,62 @@
package httpproxy
import (
"context"
"net/http"
"sync"
"time"
"github.com/qdm12/golibs/logging"
)
func newHandler(ctx context.Context, wg *sync.WaitGroup,
client *http.Client, logger logging.Logger,
stealth, verbose bool, username, password string) http.Handler {
const relayTimeout = 10 * time.Second
return &handler{
ctx: ctx,
wg: wg,
client: client,
logger: logger,
relayTimeout: relayTimeout,
verbose: verbose,
stealth: stealth,
username: username,
password: password,
}
}
type handler struct {
ctx context.Context
wg *sync.WaitGroup
client *http.Client
logger logging.Logger
relayTimeout time.Duration
verbose, stealth bool
username, password string
}
func (h *handler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
if len(h.username) > 0 && !isAuthorized(responseWriter, request, h.username, h.password) {
h.logger.Info("%s unauthorized", request.RemoteAddr)
return
}
switch request.Method {
case http.MethodConnect:
h.handleHTTPS(responseWriter, request)
default:
h.handleHTTP(responseWriter, request)
}
}
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
var hopHeaders = [...]string{ //nolint:gochecknoglobals
"Connection",
"Keep-Alive",
"Proxy-Authenticate",
"Proxy-Authorization",
"Te", // canonicalized version of "TE"
"Trailers",
"Transfer-Encoding",
"Upgrade",
}

View File

@@ -0,0 +1,74 @@
package httpproxy
import (
"context"
"fmt"
"io"
"net"
"net/http"
"strings"
)
func (h *handler) handleHTTP(responseWriter http.ResponseWriter, request *http.Request) {
switch request.URL.Scheme {
case "http", "https":
default:
h.logger.Warn("Unsupported scheme %q", request.URL.Scheme)
http.Error(responseWriter, "unsupported scheme", http.StatusBadRequest)
return
}
ctx, cancel := context.WithTimeout(h.ctx, h.relayTimeout)
defer cancel()
request = request.WithContext(ctx)
request.RequestURI = ""
for _, key := range hopHeaders {
request.Header.Del(key)
}
if !h.stealth {
setForwardedHeaders(request)
}
response, err := h.client.Do(request)
if err != nil {
http.Error(responseWriter, "server error", http.StatusInternalServerError)
h.logger.Warn("cannot request %s for client %q: %s",
request.URL, request.RemoteAddr, err)
return
}
defer response.Body.Close()
if h.verbose {
h.logger.Info("%s %s %s %s", request.RemoteAddr, response.Status, request.Method, request.URL)
}
for _, key := range hopHeaders {
response.Header.Del(key)
}
targetHeaderPtr := responseWriter.Header()
for key, values := range response.Header {
for _, value := range values {
targetHeaderPtr.Add(key, value)
}
}
responseWriter.WriteHeader(response.StatusCode)
if _, err := io.Copy(responseWriter, response.Body); err != nil {
h.logger.Error("%s %s: body copy error: %s", request.RemoteAddr, request.URL, err)
}
}
func setForwardedHeaders(request *http.Request) {
clientIP, _, err := net.SplitHostPort(request.RemoteAddr)
if err != nil {
return
}
// keep existing proxy headers
if prior, ok := request.Header["X-Forwarded-For"]; ok {
clientIP = fmt.Sprintf("%s,%s", strings.Join(prior, ", "), clientIP)
}
request.Header.Set("X-Forwarded-For", clientIP)
}

View File

@@ -0,0 +1,64 @@
package httpproxy
import (
"context"
"io"
"net"
"net/http"
"sync"
)
func (h *handler) handleHTTPS(responseWriter http.ResponseWriter, request *http.Request) {
dialer := net.Dialer{Timeout: h.relayTimeout}
destinationConn, err := dialer.DialContext(h.ctx, "tcp", request.Host)
if err != nil {
http.Error(responseWriter, err.Error(), http.StatusServiceUnavailable)
return
}
responseWriter.WriteHeader(http.StatusOK)
hijacker, ok := responseWriter.(http.Hijacker)
if !ok {
http.Error(responseWriter, "Hijacking not supported", http.StatusInternalServerError)
return
}
clientConnection, _, err := hijacker.Hijack()
if err != nil {
h.logger.Warn(err)
http.Error(responseWriter, err.Error(), http.StatusServiceUnavailable)
if err := destinationConn.Close(); err != nil {
h.logger.Error("closing destination connection: %s", err)
}
return
}
if h.verbose {
h.logger.Info("%s <-> %s", request.RemoteAddr, request.Host)
}
h.wg.Add(1)
ctx, cancel := context.WithCancel(h.ctx)
const transferGoroutines = 2
wg := &sync.WaitGroup{}
wg.Add(transferGoroutines)
go func() { // trigger cleanup when done
wg.Wait()
cancel()
}()
go func() { // cleanup
<-ctx.Done()
destinationConn.Close()
clientConnection.Close()
h.wg.Done()
}()
go transfer(destinationConn, clientConnection, wg)
go transfer(clientConnection, destinationConn, wg)
}
func transfer(destination io.WriteCloser, source io.ReadCloser, wg *sync.WaitGroup) {
_, _ = io.Copy(destination, source)
_ = source.Close()
_ = destination.Close()
wg.Done()
}

139
internal/httpproxy/loop.go Normal file
View File

@@ -0,0 +1,139 @@
package httpproxy
import (
"context"
"fmt"
"net/http"
"sync"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging"
)
type Looper interface {
Run(ctx context.Context, wg *sync.WaitGroup)
Restart()
Start()
Stop()
GetSettings() (settings settings.HTTPProxy)
SetSettings(settings settings.HTTPProxy)
}
type looper struct {
client *http.Client
settings settings.HTTPProxy
settingsMutex sync.RWMutex
logger logging.Logger
restart chan struct{}
start chan struct{}
stop chan struct{}
}
func NewLooper(client *http.Client, logger logging.Logger,
settings settings.HTTPProxy) Looper {
return &looper{
client: client,
settings: settings,
logger: logger.WithPrefix("http proxy: "),
restart: make(chan struct{}),
start: make(chan struct{}),
stop: make(chan struct{}),
}
}
func (l *looper) GetSettings() (settings settings.HTTPProxy) {
l.settingsMutex.RLock()
defer l.settingsMutex.RUnlock()
return l.settings
}
func (l *looper) SetSettings(settings settings.HTTPProxy) {
l.settingsMutex.Lock()
defer l.settingsMutex.Unlock()
l.settings = settings
}
func (l *looper) isEnabled() bool {
l.settingsMutex.RLock()
defer l.settingsMutex.RUnlock()
return l.settings.Enabled
}
func (l *looper) setEnabled(enabled bool) {
l.settingsMutex.Lock()
defer l.settingsMutex.Unlock()
l.settings.Enabled = enabled
}
func (l *looper) Restart() { l.restart <- struct{}{} }
func (l *looper) Start() { l.start <- struct{}{} }
func (l *looper) Stop() { l.stop <- struct{}{} }
func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done()
waitForStart := true
for waitForStart {
select {
case <-l.stop:
l.logger.Info("not started yet")
case <-l.start:
waitForStart = false
case <-l.restart:
waitForStart = false
case <-ctx.Done():
return
}
}
defer l.logger.Warn("loop exited")
for ctx.Err() == nil {
for !l.isEnabled() {
// wait for a signal to re-enable
select {
case <-l.stop:
l.logger.Info("already disabled")
case <-l.restart:
l.setEnabled(true)
case <-l.start:
l.setEnabled(true)
case <-ctx.Done():
return
}
}
settings := l.GetSettings()
address := fmt.Sprintf("0.0.0.0:%d", settings.Port)
server := New(ctx, address, l.logger, l.client, settings.Stealth, settings.Log, settings.User, settings.Password)
runCtx, runCancel := context.WithCancel(context.Background())
runWg := &sync.WaitGroup{}
runWg.Add(1)
go server.Run(runCtx, runWg)
stayHere := true
for stayHere {
select {
case <-ctx.Done():
l.logger.Warn("context canceled: exiting loop")
runCancel()
runWg.Wait()
return
case <-l.restart: // triggered restart
l.logger.Info("restarting")
runCancel()
runWg.Wait()
stayHere = false
case <-l.start:
l.logger.Info("already started")
case <-l.stop:
l.logger.Info("stopping")
runCancel()
runWg.Wait()
l.setEnabled(false)
stayHere = false
}
}
runCancel() // repetition for linter only
}
}

View File

@@ -0,0 +1,55 @@
package httpproxy
import (
"context"
"net/http"
"sync"
"time"
"github.com/qdm12/golibs/logging"
)
type Server interface {
Run(ctx context.Context, wg *sync.WaitGroup)
}
type server struct {
address string
handler http.Handler
logger logging.Logger
internalWG *sync.WaitGroup
}
func New(ctx context.Context, address string,
logger logging.Logger, client *http.Client,
stealth, verbose bool, username, password string) Server {
wg := &sync.WaitGroup{}
return &server{
address: address,
handler: newHandler(ctx, wg, client, logger, stealth, verbose, username, password),
logger: logger,
internalWG: wg,
}
}
func (s *server) Run(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done()
server := http.Server{Addr: s.address, Handler: s.handler}
go func() {
<-ctx.Done()
s.logger.Warn("context canceled: exiting loop")
defer s.logger.Warn("loop exited")
const shutdownGraceDuration = 2 * time.Second
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownGraceDuration)
defer cancel()
if err := server.Shutdown(shutdownCtx); err != nil {
s.logger.Error("failed shutting down: %s", err)
}
}()
s.logger.Info("listening on %s", s.address)
err := server.ListenAndServe()
if err != nil && ctx.Err() != context.Canceled {
s.logger.Error(err)
}
s.internalWG.Wait()
}

View File

@@ -12,17 +12,9 @@ import (
//nolint:lll
var regularExpressions = struct { //nolint:gochecknoglobals
unboundPrefix *regexp.Regexp
shadowsocksPrefix *regexp.Regexp
shadowsocksErrorPrefix *regexp.Regexp
tinyproxyLoglevel *regexp.Regexp
tinyproxyPrefix *regexp.Regexp
unboundPrefix *regexp.Regexp
}{
unboundPrefix: regexp.MustCompile(`unbound: \[[0-9]{10}\] unbound\[[0-9]+:0\] `),
shadowsocksPrefix: regexp.MustCompile(`shadowsocks:[ ]+2[0-9]{3}\-[0-1][0-9]\-[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9] `),
shadowsocksErrorPrefix: regexp.MustCompile(`shadowsocks error:[ ]+2[0-9]{3}\-[0-1][0-9]\-[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9] `),
tinyproxyLoglevel: regexp.MustCompile(`INFO|CONNECT|NOTICE|WARNING|ERROR|CRITICAL`),
tinyproxyPrefix: regexp.MustCompile(`tinyproxy: .+[ ]+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9] \[[0-9]+\]: `),
unboundPrefix: regexp.MustCompile(`unbound: \[[0-9]{10}\] unbound\[[0-9]+:0\] `),
}
func PostProcessLine(s string) (filtered string, level logging.Level) {
@@ -52,7 +44,7 @@ func PostProcessLine(s string) (filtered string, level logging.Level) {
case s == "openvpn: Initialization Sequence Completed":
return color.HiGreenString(s), logging.InfoLevel
case s == "openvpn: AUTH: Received control message: AUTH_FAILED":
filtered = s + "\n\n (IF YOU ARE USING PIA V4 servers, MAYBE CHECK OUT https://github.com/qdm12/gluetun/issues/265)\n" //nolint:lll
filtered = s + "\n\n (IF YOU ARE USING PIA servers, MAYBE CHECK OUT https://github.com/qdm12/gluetun/issues/265)\n" //nolint:lll
level = logging.ErrorLevel
default:
filtered = s
@@ -82,44 +74,6 @@ func PostProcessLine(s string) (filtered string, level logging.Level) {
filtered = fmt.Sprintf("unbound: %s", filtered)
filtered = constants.ColorUnbound().Sprintf(filtered)
return filtered, level
case strings.HasPrefix(s, "shadowsocks: "):
prefix := regularExpressions.shadowsocksPrefix.FindString(s)
filtered = s[len(prefix):]
switch {
case strings.HasPrefix(filtered, "INFO: "):
level = logging.InfoLevel
filtered = strings.TrimPrefix(filtered, "INFO: ")
default:
level = logging.WarnLevel
}
filtered = fmt.Sprintf("shadowsocks: %s", filtered)
filtered = constants.ColorShadowsocks().Sprintf(filtered)
return filtered, level
case strings.HasPrefix(s, "shadowsocks error: "):
if strings.Contains(s, "ERROR: unable to resolve") { // caused by DNS blocking
return "", logging.ErrorLevel
}
prefix := regularExpressions.shadowsocksErrorPrefix.FindString(s)
filtered = s[len(prefix):]
filtered = strings.TrimPrefix(filtered, "ERROR: ")
filtered = fmt.Sprintf("shadowsocks: %s", filtered)
filtered = constants.ColorShadowsocksError().Sprintf(filtered)
return filtered, logging.ErrorLevel
case strings.HasPrefix(s, "tinyproxy: "):
logLevel := regularExpressions.tinyproxyLoglevel.FindString(s)
prefix := regularExpressions.tinyproxyPrefix.FindString(s)
filtered = fmt.Sprintf("tinyproxy: %s", s[len(prefix):])
filtered = constants.ColorTinyproxy().Sprintf(filtered)
switch logLevel {
case "INFO", "CONNECT", "NOTICE":
return filtered, logging.InfoLevel
case "WARNING":
return filtered, logging.WarnLevel
case "ERROR", "CRITICAL":
return filtered, logging.ErrorLevel
default:
return filtered, logging.ErrorLevel
}
}
return s, logging.InfoLevel
}

View File

@@ -36,50 +36,6 @@ func Test_PostProcessLine(t *testing.T) {
"unbound: [1594595249] unbound[75:0] BLA: init module 0: validator",
"unbound: BLA: init module 0: validator",
logging.ErrorLevel},
"shadowsocks stdout info": {
"shadowsocks: 2020-07-12 23:07:25 INFO: UDP relay enabled",
"shadowsocks: UDP relay enabled",
logging.InfoLevel},
"shadowsocks stdout other": {
"shadowsocks: 2020-07-12 23:07:25 BLABLA: UDP relay enabled",
"shadowsocks: BLABLA: UDP relay enabled",
logging.WarnLevel},
"shadowsocks stderr": {
"shadowsocks error: 2020-07-12 23:07:25 Some error",
"shadowsocks: Some error",
logging.ErrorLevel},
"shadowsocks stderr unable to resolve muted": {
"shadowsocks error: 2020-07-12 23:07:25 ERROR: unable to resolve",
"",
logging.ErrorLevel},
"tinyproxy info": {
"tinyproxy: INFO Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.InfoLevel},
"tinyproxy connect": {
"tinyproxy: CONNECT Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.InfoLevel},
"tinyproxy notice": {
"tinyproxy: NOTICE Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.InfoLevel},
"tinyproxy warning": {
"tinyproxy: WARNING Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.WarnLevel},
"tinyproxy error": {
"tinyproxy: ERROR Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.ErrorLevel},
"tinyproxy critical": {
"tinyproxy: CRITICAL Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.ErrorLevel},
"tinyproxy unknown": {
"tinyproxy: BLABLA Jul 12 23:07:25 [32]: Reloading config file",
"tinyproxy: Reloading config file",
logging.ErrorLevel},
"openvpn unknown": {
"openvpn: message",
"openvpn: message",
@@ -106,7 +62,7 @@ func Test_PostProcessLine(t *testing.T) {
logging.InfoLevel},
"openvpn auth failed": {
"openvpn: AUTH: Received control message: AUTH_FAILED",
"openvpn: AUTH: Received control message: AUTH_FAILED\n\n (IF YOU ARE USING PIA V4 servers, MAYBE CHECK OUT https://github.com/qdm12/gluetun/issues/265)\n", //nolint:lll
"openvpn: AUTH: Received control message: AUTH_FAILED\n\n (IF YOU ARE USING PIA servers, MAYBE CHECK OUT https://github.com/qdm12/gluetun/issues/265)\n", //nolint:lll
logging.ErrorLevel},
}
for name, tc := range tests {

View File

@@ -7,13 +7,15 @@ import (
"github.com/kyokomi/emoji"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
)
// Splash returns the welcome spash message.
func Splash(version, commit, buildDate string) string {
func Splash(buildInfo models.BuildInformation) string {
lines := title()
lines = append(lines, "")
lines = append(lines, fmt.Sprintf("Running version %s built on %s (commit %s)", version, buildDate, commit))
lines = append(lines, fmt.Sprintf("Running version %s built on %s (commit %s)",
buildInfo.Version, buildInfo.BuildDate, buildInfo.Commit))
lines = append(lines, "")
lines = append(lines, announcement()...)
lines = append(lines, "")
@@ -27,7 +29,7 @@ func title() []string {
"================ Gluetun ================",
"=========================================",
"==== A mix of OpenVPN, DNS over TLS, ====",
"======= Shadowsocks and Tinyproxy =======",
"======= Shadowsocks and HTTP proxy ======",
"========= all glued up with Go ==========",
"=========================================",
"=========== For tunneling to ============",

View File

@@ -16,8 +16,6 @@ type (
URL string
// Filepath is a local filesytem file path.
Filepath string
// TinyProxyLogLevel is the log level for TinyProxy.
TinyProxyLogLevel string
// VPNProvider is the name of the VPN provider to be used.
VPNProvider string
// NetworkProtocol contains the network protocol to be used to communicate with the VPN servers.

7
internal/models/build.go Normal file
View File

@@ -0,0 +1,7 @@
package models
type BuildInformation struct {
Version string `json:"version"`
Commit string `json:"commit"`
BuildDate string `json:"buildDate"`
}

View File

@@ -6,8 +6,10 @@ type OpenVPNConnection struct {
IP net.IP
Port uint16
Protocol NetworkProtocol
Hostname string // Privado for tls verification
}
func (o *OpenVPNConnection) Equal(other OpenVPNConnection) bool {
return o.IP.Equal(other.IP) && o.Port == other.Port && o.Protocol == other.Protocol
return o.IP.Equal(other.IP) && o.Port == other.Port && o.Protocol == other.Protocol &&
o.Hostname == other.Hostname
}

View File

@@ -25,9 +25,9 @@ type ServerSelection struct {
// Cyberghost
Group string `json:"group"`
// Mullvad, PureVPN
Countries []string `json:"countries"`
Cities []string `json:"cities"`
Countries []string `json:"countries"` // Mullvad, PureVPN
Cities []string `json:"cities"` // Mullvad, PureVPN, Windscribe
Hostnames []string `json:"hostnames"` // Windscribe, Privado
// Mullvad
ISPs []string `json:"isps"`
@@ -44,9 +44,10 @@ type ServerSelection struct {
}
type ExtraConfigOptions struct {
ClientKey string `json:"-"` // Cyberghost
EncryptionPreset string `json:"encryptionPreset"` // PIA
OpenVPNIPv6 bool `json:"openvpnIPv6"` // Mullvad
ClientCertificate string `json:"-"` // Cyberghost
ClientKey string `json:"-"` // Cyberghost
EncryptionPreset string `json:"encryptionPreset"` // PIA
OpenVPNIPv6 bool `json:"openvpnIPv6"` // Mullvad
}
// PortForwarding contains settings for port forwarding.
@@ -111,7 +112,8 @@ func (p *ProviderSettings) String() string {
)
case "cyberghost":
settingsList = append(settingsList,
"ClientKey: [redacted]",
"Client key: [redacted]",
"Client certificate: [redacted]",
"Group: "+p.ServerSelection.Group,
"Regions: "+commaJoin(p.ServerSelection.Regions),
)
@@ -130,6 +132,11 @@ func (p *ProviderSettings) String() string {
"Countries: "+commaJoin(p.ServerSelection.Countries),
"Cities: "+commaJoin(p.ServerSelection.Cities),
)
case "privado":
settingsList = append(settingsList,
"Cities: "+commaJoin(p.ServerSelection.Cities),
"Server numbers: "+commaJoin(numbers),
)
default:
settingsList = append(settingsList,
"<Missing String method, please implement me!>",

View File

@@ -28,15 +28,6 @@ func (p *PIAServer) String() string {
p.Region, p.PortForward, p.OpenvpnUDP.String(), p.OpenvpnTCP.String())
}
type PIAOldServer struct {
IPs []net.IP `json:"ips"`
Region string `json:"region"`
}
func (p *PIAOldServer) String() string {
return fmt.Sprintf("{Region: %q, IPs: %s}", p.Region, goStringifyIPs(p.IPs))
}
type MullvadServer struct {
IPs []net.IP `json:"ips"`
IPsV6 []net.IP `json:"ipsv6"`
@@ -52,12 +43,15 @@ func (s *MullvadServer) String() string {
}
type WindscribeServer struct {
Region string `json:"region"`
IPs []net.IP `json:"ips"`
Region string `json:"region"`
City string `json:"city"`
Hostname string `json:"hostname"`
IP net.IP `json:"ip"`
}
func (s *WindscribeServer) String() string {
return fmt.Sprintf("{Region: %q, IPs: %s}", s.Region, goStringifyIPs(s.IPs))
return fmt.Sprintf("{Region: %q, City: %q, Hostname: %q, IP: %s}",
s.Region, s.City, s.Hostname, goStringifyIP(s.IP))
}
type SurfsharkServer struct {
@@ -113,6 +107,16 @@ func (s *PurevpnServer) String() string {
s.Region, s.Country, s.City, goStringifyIPs(s.IPs))
}
type PrivadoServer struct {
IP net.IP `json:"ip"`
Hostname string `json:"hostname"`
}
func (s *PrivadoServer) String() string {
return fmt.Sprintf("{Hostname: %q, IP: %s}",
s.Hostname, goStringifyIP(s.IP))
}
func goStringifyIP(ip net.IP) string {
s := fmt.Sprintf("%#v", ip)
s = strings.TrimSuffix(strings.TrimPrefix(s, "net.IP{"), "}")

View File

@@ -7,31 +7,6 @@ import (
"github.com/stretchr/testify/assert"
)
func Test_PIAOldServer_String(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
server PIAOldServer
s string
}{
"no ips": {
server: PIAOldServer{Region: "a b"},
s: `{Region: "a b", IPs: []net.IP{}}`,
},
"with ips": {
server: PIAOldServer{Region: "a b", IPs: []net.IP{{1, 1, 1, 1}, {2, 2, 2, 2}}},
s: `{Region: "a b", IPs: []net.IP{{1, 1, 1, 1}, {2, 2, 2, 2}}}`,
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
s := testCase.server.String()
assert.Equal(t, testCase.s, s)
})
}
}
func Test_MullvadServer_String(t *testing.T) {
t.Parallel()
testCases := map[string]struct {

View File

@@ -5,8 +5,8 @@ type AllServers struct {
Cyberghost CyberghostServers `json:"cyberghost"`
Mullvad MullvadServers `json:"mullvad"`
Nordvpn NordvpnServers `json:"nordvpn"`
PiaOld PiaOldServers `json:"piaOld"`
Pia PiaServers `json:"pia"`
Privado PrivadoServers `json:"privado"`
Purevpn PurevpnServers `json:"purevpn"`
Surfshark SurfsharkServers `json:"surfshark"`
Vyprvpn VyprvpnServers `json:"vyprvpn"`
@@ -28,20 +28,20 @@ type NordvpnServers struct {
Timestamp int64 `json:"timestamp"`
Servers []NordvpnServer `json:"servers"`
}
type PiaOldServers struct {
Version uint16 `json:"version"`
Timestamp int64 `json:"timestamp"`
Servers []PIAOldServer `json:"servers"`
}
type PiaServers struct {
Version uint16 `json:"version"`
Timestamp int64 `json:"timestamp"`
Servers []PIAServer `json:"servers"`
}
type PrivadoServers struct {
Version uint16 `json:"version"`
Timestamp int64 `json:"timestamp"`
Servers []PrivadoServer `json:"servers"`
}
type PurevpnServers struct {
Version uint16 `json:"version"`
Timestamp int64 `json:"timestamp"`
Servers []PurevpnServer `json:"purevpn"`
Servers []PurevpnServer `json:"servers"`
}
type SurfsharkServers struct {
Version uint16 `json:"version"`

View File

@@ -1,6 +1,8 @@
package params
import (
"encoding/pem"
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/constants"
@@ -22,7 +24,7 @@ func (p *reader) GetCyberghostRegions() (regions []string, err error) {
}
// GetCyberghostClientKey obtains the one line client key to use for openvpn from the
// environment variable CLIENT_KEY.
// environment variable CLIENT_KEY or from the file at /gluetun/client.key.
func (p *reader) GetCyberghostClientKey() (clientKey string, err error) {
clientKey, err = p.envParams.GetEnv("CLIENT_KEY", libparams.CaseSensitiveValue())
if err != nil {
@@ -30,12 +32,45 @@ func (p *reader) GetCyberghostClientKey() (clientKey string, err error) {
} else if len(clientKey) > 0 {
return clientKey, nil
}
content, err := p.fileManager.ReadFile("/files/client.key")
content, err := p.fileManager.ReadFile(string(constants.ClientKey))
if err != nil {
return "", err
}
s := string(content)
return extractClientKey(content)
}
func extractClientKey(b []byte) (key string, err error) {
pemBlock, _ := pem.Decode(b)
if pemBlock == nil {
return "", fmt.Errorf("cannot decode PEM block from client key")
}
parsedBytes := pem.EncodeToMemory(pemBlock)
s := string(parsedBytes)
s = strings.ReplaceAll(s, "\n", "")
s = strings.ReplaceAll(s, "\r", "")
s = strings.TrimPrefix(s, "-----BEGIN PRIVATE KEY-----")
s = strings.TrimSuffix(s, "-----END PRIVATE KEY-----")
return s, nil
}
// GetCyberghostClientCertificate obtains the client certificate to use for openvpn from the
// file at /gluetun/client.crt.
func (p *reader) GetCyberghostClientCertificate() (clientCertificate string, err error) {
content, err := p.fileManager.ReadFile(string(constants.ClientCertificate))
if err != nil {
return "", err
}
return extractClientCertificate(content)
}
func extractClientCertificate(b []byte) (certificate string, err error) {
pemBlock, _ := pem.Decode(b)
if pemBlock == nil {
return "", fmt.Errorf("cannot decode PEM block from client certificate")
}
parsedBytes := pem.EncodeToMemory(pemBlock)
s := string(parsedBytes)
s = strings.ReplaceAll(s, "\n", "")
s = strings.TrimPrefix(s, "-----BEGIN CERTIFICATE-----")
s = strings.TrimSuffix(s, "-----END CERTIFICATE-----")
return s, nil
}

View File

@@ -0,0 +1,175 @@
package params
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_extractClientKey(t *testing.T) {
t.Parallel()
const validPEM = `
-----BEGIN PRIVATE KEY-----
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
zZjrL52saevO25cigVl+hxcnY8DTpbk=
-----END PRIVATE KEY-----
`
const validKeyString = "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" //nolint:lll
testCases := map[string]struct {
b []byte
key string
err error
}{
"no input": {
err: fmt.Errorf("cannot decode PEM block from client key"),
},
"bad input": {
b: []byte{1, 2, 3},
err: fmt.Errorf("cannot decode PEM block from client key"),
},
"valid key": {
b: []byte(validPEM),
key: validKeyString,
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
key, err := extractClientKey(testCase.b)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.key, key)
})
}
}
func Test_extractClientCertificate(t *testing.T) {
t.Parallel()
const validPEM = `
-----BEGIN CERTIFICATE-----
MIIGrDCCBJSgAwIBAgIEAdTnfTANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJS
TzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4x
GzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5m
b0BjeWJlcmdob3N0LnJvMB4XDTIwMDcwNDE1MjkzNloXDTMwMDcwMjE1MjkzNlow
fTELMAkGA1UEBhMCUk8xEjAQBgNVBAcMCUJ1Y2hhcmVzdDEYMBYGA1UECgwPQ3li
ZXJHaG9zdCBTLkEuMR0wGwYDVQQDDBRjLmoua2xhdmVyQGdtYWlsLmNvbTEhMB8G
CSqGSIb3DQEJARYSaW5mb0BjeWJlcmdob3N0LnJvMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEAobp2NlGUHMNBe08YEOnVG3QJjF3ZaXbRhE/II9rmtgJT
NZtDohGChvFlNRsExKzVrKxHCeuJkVffwzQ6fYk4/M1RdYLJUh0UVw3e4WdApw8E
7TJZxDYm4SHQNXUvt1Rt5TjslcXxIpDZgrMSc/kHROYEL9tdgdzPZErUJehXyJPh
EzIrzmAJh501x7WwKPz9ctSVlItyavqEWFF2vyUa6X9DYmD9mQTz5c+VXNO5DkXm
PFBIaEVDnvFtcjGJ56yEvFnWVukL+OUX7ezowrIOFOcp9udjgpeiHq+XvsQ6ER0D
Jt25MiEId3NjkxtZ8BitDftTcLN/kt81hWKT7adMVc3kpIZ80cxrwRCttMd7sHAz
KI9u7pMxv10eUOsIEY87ewBe3l6KvEnjA+9uIjim6gLLebDIaEH50Ee9PzNJ8fqQ
2u54Ab4bt00/H1sUnJ6Ss/+WsQDOK1BsPRKKcnHZntOlHrs2Tu5+txKNU2cOapI8
SjVULUNKrRXASbpfWnLUfri/HO742bJb/TjkOJcOxta3hTPFAhaRWBusVlB41XVH
euH5DAhugYXeSNK6/6Ul8YvKUNH/7QbxuGIGXfth19Xl4QLI1umyEjZopSlt3tOi
O2V1soVNSQCCfxXVoCTMESMLjhkjWdmBDhdy2GTW7S4YoJfqVKiS18rYkN7I4ZMC
AwEAAaOCATQwggEwMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMDQGCWCGSAGG+EIB
DQQnFiVDeWJlckdob3N0IEdlbmVyYXRlZCBVc2VyIENlcnRpZmljYXRlMBEGCWCG
SAGG+EIBAQQEAwIHgDAdBgNVHQ4EFgQULwUtU5s6pL2NN9gPeEnKX0dhwiswga0G
A1UdIwSBpTCBooAU6tdK1g/He5qzjeAoM5eHt4in9iWhf6R9MHsxCzAJBgNVBAYT
AlJPMRIwEAYDVQQHEwlCdWNoYXJlc3QxGDAWBgNVBAoTD0N5YmVyR2hvc3QgUy5B
LjEbMBkGA1UEAxMSQ3liZXJHaG9zdCBSb290IENBMSEwHwYJKoZIhvcNAQkBFhJp
bmZvQGN5YmVyZ2hvc3Qucm+CCQCcVButZsQ0uzANBgkqhkiG9w0BAQsFAAOCAgEA
ystGIMYhQWaEdTqlnLCytrr8657t+PuidZMNNIaPB3wN2Fi2xKf14DTg03mqxjmP
Pb+f+PVNIOV5PdWD4jcQwOP1GEboGV0DFzlRGeAtDcvKwdee4oASJbZq1CETqDao
hQTxKEWC+UBk2F36nOaEI6Sab+Mb4cR9//PAwvzOqrXuGF5NuIOX7eFtCMQSgQq6
lRRqTQjekm0Dxigx4JA92Jo2qZRwCJ0T3IXBJGL831HCFJbDWv8PV3lsfFb/i2+v
r54uywFQVWWp18dYi97gipfuQ4zRg2Ldx5aXSmnhhKpg5ioZvtk043QofF12YORh
obElqavRbvvhZvlCouvcuoq9QKi7IPe5SJZkZ1X7ezMesCwBzwFpt6vRUAcslsNF
bcYS1iSENlY/PTcDqBhbKuc9yAhq+/aUgaY/8VF5RWVzSRZufbf3BPwOkE4K0Uyb
aobO/YX0JOkCacAD+4tdR6YSXNIMMRAOCBQvxbxFXaHzhwhzBAjdsC56FrJKwXvQ
rRLU3tF4P0zFMeNTay8uTtUXugDK7EnklLESuYdpUJ8bUMlAUhJBi6UFI9/icMud
xXvLRvhnBW9EtKib5JnVFUovcEUt+3EJbyst05nkL4YPjQS4TC9DHdo5SyRAy1Tp
iOCYTbretAFZRhh6ycUN5hBeN8GMQxiMreMtDV4PEIQ=
-----END CERTIFICATE-----
`
const validCertificateString = "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" //nolint:lll
testCases := map[string]struct {
b []byte
certificate string
err error
}{
"no input": {
err: fmt.Errorf("cannot decode PEM block from client certificate"),
},
"bad input": {
b: []byte{1, 2, 3},
err: fmt.Errorf("cannot decode PEM block from client certificate"),
},
"valid key": {
b: []byte(validPEM),
certificate: validCertificateString,
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
certificate, err := extractClientCertificate(testCase.b)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.certificate, certificate)
})
}
}

View File

@@ -107,7 +107,7 @@ func (r *reader) GetDNSUnblockedHostnames() (hostnames []string, err error) {
// GetDNSOverTLSCaching obtains if Unbound caching should be enable or not
// from the environment variable DOT_CACHING.
func (r *reader) GetDNSOverTLSCaching() (caching bool, err error) {
return r.envParams.GetOnOff("DOT_CACHING")
return r.envParams.GetOnOff("DOT_CACHING", libparams.Default("on"))
}
// GetDNSOverTLSPrivateAddresses obtains if Unbound caching should be enable or not

View File

@@ -0,0 +1,78 @@
package params
import (
"strings"
libparams "github.com/qdm12/golibs/params"
)
// GetHTTPProxy obtains if the HTTP proxy is on from the environment variable
// HTTPPROXY, and using PROXY and TINYPROXY as retro-compatibility names.
func (r *reader) GetHTTPProxy() (enabled bool, err error) {
retroKeysOption := libparams.RetroKeys(
[]string{"TINYPROXY", "PROXY"},
r.onRetroActive,
)
return r.envParams.GetOnOff("HTTPPROXY", retroKeysOption, libparams.Default("off"))
}
// GetHTTPProxyLog obtains the if http proxy requests should be logged from
// the environment variable HTTPPROXY_LOG, and using PROXY_LOG_LEVEL and
// TINYPROXY_LOG as retro-compatibility names.
func (r *reader) GetHTTPProxyLog() (log bool, err error) {
s, _ := r.envParams.GetEnv("HTTPPROXY_LOG")
if len(s) == 0 {
s, _ = r.envParams.GetEnv("PROXY_LOG_LEVEL")
if len(s) == 0 {
s, _ = r.envParams.GetEnv("TINYPROXY_LOG")
if len(s) == 0 {
return false, nil // default log disabled
}
}
switch strings.ToLower(s) {
case "info", "connect", "notice":
return true, nil
default:
return false, nil
}
}
return r.envParams.GetOnOff("HTTPPROXY_LOG", libparams.Default("off"))
}
// GetHTTPProxyPort obtains the HTTP proxy listening port from the environment variable
// HTTPPROXY_PORT, and using PROXY_PORT and TINYPROXY_PORT as retro-compatibility names.
func (r *reader) GetHTTPProxyPort() (port uint16, err error) {
retroKeysOption := libparams.RetroKeys(
[]string{"TINYPROXY_PORT", "PROXY_PORT"},
r.onRetroActive,
)
return r.envParams.GetPort("HTTPPROXY_PORT", retroKeysOption, libparams.Default("8888"))
}
// GetHTTPProxyUser obtains the HTTP proxy server user from the environment variable
// HTTPPROXY_USER, and using TINYPROXY_USER and PROXY_USER as retro-compatibility names.
func (r *reader) GetHTTPProxyUser() (user string, err error) {
retroKeysOption := libparams.RetroKeys(
[]string{"TINYPROXY_USER", "PROXY_USER"},
r.onRetroActive,
)
return r.envParams.GetEnv("HTTPPROXY_USER",
retroKeysOption, libparams.CaseSensitiveValue(), libparams.Unset())
}
// GetHTTPProxyPassword obtains the HTTP proxy server password from the environment variable
// HTTPPROXY_PASSWORD, and using TINYPROXY_PASSWORD and PROXY_PASSWORD as retro-compatibility names.
func (r *reader) GetHTTPProxyPassword() (password string, err error) {
retroKeysOption := libparams.RetroKeys(
[]string{"TINYPROXY_PASSWORD", "PROXY_PASSWORD"},
r.onRetroActive,
)
return r.envParams.GetEnv("HTTPPROXY_PASSWORD",
retroKeysOption, libparams.CaseSensitiveValue(), libparams.Unset())
}
// GetHTTPProxyStealth obtains the HTTP proxy server stealth mode
// from the environment variable HTTPPROXY_STEALTH.
func (r *reader) GetHTTPProxyStealth() (stealth bool, err error) {
return r.envParams.GetOnOff("HTTPPROXY_STEALTH", libparams.Default("off"))
}

View File

@@ -43,6 +43,7 @@ type Reader interface {
GetFirewall() (enabled bool, err error)
GetVPNInputPorts() (ports []uint16, err error)
GetInputPorts() (ports []uint16, err error)
GetOutboundSubnets() (outboundSubnets []net.IPNet, err error)
GetFirewallDebug() (debug bool, err error)
// VPN getters
@@ -61,7 +62,6 @@ type Reader interface {
GetPortForwardingStatusFilepath() (filepath models.Filepath, err error)
GetPIAEncryptionPreset() (preset string, err error)
GetPIARegions() (regions []string, err error)
GetPIAOldRegions() (regions []string, err error)
// Mullvad getters
GetMullvadCountries() (countries []string, err error)
@@ -72,6 +72,8 @@ type Reader interface {
// Windscribe getters
GetWindscribeRegions() (countries []string, err error)
GetWindscribeCities() (cities []string, err error)
GetWindscribeHostnames() (hostnames []string, err error)
GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error)
// Surfshark getters
@@ -81,6 +83,7 @@ type Reader interface {
GetCyberghostGroup() (group string, err error)
GetCyberghostRegions() (regions []string, err error)
GetCyberghostClientKey() (clientKey string, err error)
GetCyberghostClientCertificate() (clientCertificate string, err error)
// Vyprvpn getters
GetVyprvpnRegions() (regions []string, err error)
@@ -89,6 +92,9 @@ type Reader interface {
GetNordvpnRegions() (regions []string, err error)
GetNordvpnNumbers() (numbers []uint16, err error)
// Privado getters
GetPrivadoHostnames() (hostnames []string, err error)
// PureVPN getters
GetPurevpnRegions() (regions []string, err error)
GetPurevpnCountries() (countries []string, err error)
@@ -101,12 +107,13 @@ type Reader interface {
GetShadowSocksPassword() (password string, err error)
GetShadowSocksMethod() (method string, err error)
// Tinyproxy getters
GetTinyProxy() (activated bool, err error)
GetTinyProxyLog() (models.TinyProxyLogLevel, error)
GetTinyProxyPort() (port uint16, err error)
GetTinyProxyUser() (user string, err error)
GetTinyProxyPassword() (password string, err error)
// HTTP proxy getters
GetHTTPProxy() (activated bool, err error)
GetHTTPProxyLog() (log bool, err error)
GetHTTPProxyPort() (port uint16, err error)
GetHTTPProxyUser() (user string, err error)
GetHTTPProxyPassword() (password string, err error)
GetHTTPProxyStealth() (stealth bool, err error)
// Public IP getters
GetPublicIPPeriod() (period time.Duration, err error)
@@ -145,9 +152,9 @@ func (r *reader) GetVPNSP() (vpnServiceProvider models.VPNProvider, err error) {
s, err := r.envParams.GetValueIfInside(
"VPNSP",
[]string{
"pia", "private internet access", "private internet access old",
"pia", "private internet access",
"mullvad", "windscribe", "surfshark", "cyberghost",
"vyprvpn", "nordvpn", "purevpn",
"vyprvpn", "nordvpn", "purevpn", "privado",
}, libparams.Default("private internet access"))
if s == "pia" {
s = "private internet access"
@@ -158,3 +165,10 @@ func (r *reader) GetVPNSP() (vpnServiceProvider models.VPNProvider, err error) {
func (r *reader) GetVersionInformation() (enabled bool, err error) {
return r.envParams.GetOnOff("VERSION_INFORMATION", libparams.Default("on"))
}
func (r *reader) onRetroActive(oldKey, newKey string) {
r.logger.Warn(
"You are using the old environment variable %s, please consider changing it to %s",
oldKey, newKey,
)
}

View File

@@ -63,9 +63,3 @@ func (r *reader) GetPIAEncryptionPreset() (preset string, err error) {
func (r *reader) GetPIARegions() (regions []string, err error) {
return r.envParams.GetCSVInPossibilities("REGION", constants.PIAGeoChoices())
}
// GetPIAOldRegions obtains the regions for the PIA servers from the
// environment variable REGION.
func (r *reader) GetPIAOldRegions() (regions []string, err error) {
return r.envParams.GetCSVInPossibilities("REGION", constants.PIAOldGeoChoices())
}

View File

@@ -0,0 +1,11 @@
package params
import (
"github.com/qdm12/gluetun/internal/constants"
)
// GetPrivadoHostnames obtains the hostnames for the Privado server from the
// environment variable HOSTNAME.
func (r *reader) GetPrivadoHostnames() (hosts []string, err error) {
return r.envParams.GetCSVInPossibilities("HOSTNAME", constants.PrivadoHostnameChoices())
}

View File

@@ -0,0 +1,37 @@
package params
import (
"fmt"
"net"
"strings"
libparams "github.com/qdm12/golibs/params"
)
// GetOutboundSubnets obtains the CIDR subnets from the comma separated list of the
// environment variable FIREWALL_OUTBOUND_SUBNETS.
func (r *reader) GetOutboundSubnets() (outboundSubnets []net.IPNet, err error) {
const key = "FIREWALL_OUTBOUND_SUBNETS"
retroOption := libparams.RetroKeys(
[]string{"EXTRA_SUBNETS"},
r.onRetroActive,
)
s, err := r.envParams.GetEnv(key, retroOption)
if err != nil {
return nil, err
} else if s == "" {
return nil, nil
}
subnets := strings.Split(s, ",")
for _, subnet := range subnets {
_, cidr, err := net.ParseCIDR(subnet)
if err != nil {
return nil, fmt.Errorf("cannot parse outbound subnet %q from environment variable with key %s: %w", subnet, key, err)
} else if cidr == nil {
return nil, fmt.Errorf("cannot parse outbound subnet %q from environment variable with key %s: subnet is nil",
subnet, key)
}
outboundSubnets = append(outboundSubnets, *cidr)
}
return outboundSubnets, nil
}

View File

@@ -1,120 +0,0 @@
package params
import (
"strconv"
"github.com/qdm12/gluetun/internal/models"
libparams "github.com/qdm12/golibs/params"
)
// GetTinyProxy obtains if TinyProxy is on from the environment variable
// TINYPROXY, and using PROXY as a retro-compatibility name.
func (r *reader) GetTinyProxy() (activated bool, err error) {
// Retro-compatibility
s, err := r.envParams.GetEnv("PROXY")
if err != nil {
return false, err
} else if len(s) != 0 {
r.logger.Warn("You are using the old environment variable PROXY, please consider changing it to TINYPROXY")
return r.envParams.GetOnOff("PROXY", libparams.Compulsory())
}
return r.envParams.GetOnOff("TINYPROXY", libparams.Default("off"))
}
// GetTinyProxyLog obtains the TinyProxy log level from the environment variable
// TINYPROXY_LOG, and using PROXY_LOG_LEVEL as a retro-compatibility name.
func (r *reader) GetTinyProxyLog() (models.TinyProxyLogLevel, error) {
// Retro-compatibility
s, err := r.envParams.GetEnv("PROXY_LOG_LEVEL")
if err != nil {
return models.TinyProxyLogLevel(s), err
} else if len(s) != 0 {
r.logger.Warn("You are using the old environment variable PROXY_LOG_LEVEL, please consider changing it to TINYPROXY_LOG") //nolint:lll
s, err = r.envParams.GetValueIfInside("PROXY_LOG_LEVEL",
[]string{"Info", "Connect", "Notice", "Warning", "Error", "Critical"},
libparams.Compulsory())
return models.TinyProxyLogLevel(s), err
}
s, err = r.envParams.GetValueIfInside("TINYPROXY_LOG",
[]string{"Info", "Connect", "Notice", "Warning", "Error", "Critical"},
libparams.Default("Connect"))
return models.TinyProxyLogLevel(s), err
}
// GetTinyProxyPort obtains the TinyProxy listening port from the environment variable
// TINYPROXY_PORT, and using PROXY_PORT as a retro-compatibility name.
func (r *reader) GetTinyProxyPort() (port uint16, err error) {
// Retro-compatibility
portStr, err := r.envParams.GetEnv("PROXY_PORT")
switch {
case err != nil:
return 0, err
case len(portStr) != 0:
r.logger.Warn("You are using the old environment variable PROXY_PORT, please consider changing it to TINYPROXY_PORT")
default:
portStr, err = r.envParams.GetEnv("TINYPROXY_PORT", libparams.Default("8888"))
if err != nil {
return 0, err
}
}
if err := r.verifier.VerifyPort(portStr); err != nil {
return 0, err
}
portUint64, err := strconv.ParseUint(portStr, 10, 16)
return uint16(portUint64), err
}
// GetTinyProxyUser obtains the TinyProxy server user from the environment variable
// TINYPROXY_USER, and using PROXY_USER as a retro-compatibility name.
func (r *reader) GetTinyProxyUser() (user string, err error) {
defer func() {
unsetErr := r.unsetEnv("PROXY_USER")
if err == nil {
err = unsetErr
}
}()
defer func() {
unsetErr := r.unsetEnv("TINYPROXY_USER")
if err == nil {
err = unsetErr
}
}()
// Retro-compatibility
user, err = r.envParams.GetEnv("PROXY_USER", libparams.CaseSensitiveValue())
if err != nil {
return user, err
}
if len(user) != 0 {
r.logger.Warn("You are using the old environment variable PROXY_USER, please consider changing it to TINYPROXY_USER")
return user, nil
}
return r.envParams.GetEnv("TINYPROXY_USER", libparams.CaseSensitiveValue())
}
// GetTinyProxyPassword obtains the TinyProxy server password from the environment variable
// TINYPROXY_PASSWORD, and using PROXY_PASSWORD as a retro-compatibility name.
func (r *reader) GetTinyProxyPassword() (password string, err error) {
defer func() {
unsetErr := r.unsetEnv("PROXY_PASSWORD")
if err == nil {
err = unsetErr
}
}()
defer func() {
unsetErr := r.unsetEnv("TINYPROXY_PASSWORD")
if err == nil {
err = unsetErr
}
}()
// Retro-compatibility
password, err = r.envParams.GetEnv("PROXY_PASSWORD", libparams.CaseSensitiveValue())
if err != nil {
return password, err
}
if len(password) != 0 {
r.logger.Warn("You are using the old environment variable PROXY_PASSWORD, please consider changing it to TINYPROXY_PASSWORD") //nolint:lll
return password, nil
}
return r.envParams.GetEnv("TINYPROXY_PASSWORD", libparams.CaseSensitiveValue())
}

View File

@@ -14,7 +14,19 @@ func (r *reader) GetWindscribeRegions() (regions []string, err error) {
return r.envParams.GetCSVInPossibilities("REGION", constants.WindscribeRegionChoices())
}
// GetMullvadPort obtains the port to reach the Mullvad server on from the
// GetWindscribeCities obtains the cities for the Windscribe servers from the
// environment variable CITY.
func (r *reader) GetWindscribeCities() (cities []string, err error) {
return r.envParams.GetCSVInPossibilities("CITY", constants.WindscribeCityChoices())
}
// GetWindscribeHostnames obtains the hostnames for the Windscribe servers from the
// environment variable HOSTNAME.
func (r *reader) GetWindscribeHostnames() (hostnames []string, err error) {
return r.envParams.GetCSVInPossibilities("HOSTNAME", constants.WindscribeHostnameChoices())
}
// GetWindscribePort obtains the port to reach the Windscribe server on from the
// environment variable PORT.
//nolint:gomnd
func (r *reader) GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error) {

View File

@@ -2,4 +2,5 @@ package provider
const (
aes256cbc = "aes-256-cbc"
sha256 = "sha256"
)

View File

@@ -30,7 +30,7 @@ func newCyberghost(servers []models.CyberghostServer, timeNow timeNowFunc) *cybe
func (c *cyberghost) filterServers(regions []string, group string) (servers []models.CyberghostServer) {
for _, server := range c.servers {
switch {
case len(group) > 0 && group != server.Group,
case len(group) > 0 && !strings.EqualFold(group, server.Group),
filterByPossibilities(server.Region, regions):
default:
servers = append(servers, server)
@@ -68,7 +68,7 @@ func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, verbosity,
cipher = aes256cbc
}
if len(auth) == 0 {
auth = "SHA256"
auth = sha256
}
lines = []string{
"client",
@@ -91,7 +91,6 @@ func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, verbosity,
"mute-replay-warnings",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"remote-random",
"suppress-timestamps",
// Modified variables
@@ -118,7 +117,7 @@ func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, verbosity,
lines = append(lines, []string{
"<cert>",
"-----BEGIN CERTIFICATE-----",
constants.CyberghostClientCertificate,
extras.ClientCertificate,
"-----END CERTIFICATE-----",
"</cert>",
}...)

View File

@@ -0,0 +1,82 @@
package provider
import (
"testing"
"github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert"
)
func Test_cyberghost_filterServers(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
servers []models.CyberghostServer
regions []string
group string
filteredServers []models.CyberghostServer
}{
"no servers": {},
"servers without filter": {
servers: []models.CyberghostServer{
{Region: "a", Group: "1"},
{Region: "b", Group: "1"},
{Region: "c", Group: "2"},
{Region: "d", Group: "2"},
},
filteredServers: []models.CyberghostServer{
{Region: "a", Group: "1"},
{Region: "b", Group: "1"},
{Region: "c", Group: "2"},
{Region: "d", Group: "2"},
},
},
"servers with regions filter": {
servers: []models.CyberghostServer{
{Region: "a", Group: "1"},
{Region: "b", Group: "1"},
{Region: "c", Group: "2"},
{Region: "d", Group: "2"},
},
regions: []string{"a", "c"},
filteredServers: []models.CyberghostServer{
{Region: "a", Group: "1"},
{Region: "c", Group: "2"},
},
},
"servers with group filter": {
servers: []models.CyberghostServer{
{Region: "a", Group: "1"},
{Region: "b", Group: "1"},
{Region: "c", Group: "2"},
{Region: "d", Group: "2"},
},
group: "1",
filteredServers: []models.CyberghostServer{
{Region: "a", Group: "1"},
{Region: "b", Group: "1"},
},
},
"servers with regions and group filter": {
servers: []models.CyberghostServer{
{Region: "a", Group: "1"},
{Region: "b", Group: "1"},
{Region: "c", Group: "2"},
{Region: "d", Group: "2"},
},
regions: []string{"a", "c"},
group: "1",
filteredServers: []models.CyberghostServer{
{Region: "a", Group: "1"},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
c := &cyberghost{servers: testCase.servers}
filteredServers := c.filterServers(testCase.regions, testCase.group)
assert.Equal(t, testCase.filteredServers, filteredServers)
})
}
}

View File

@@ -98,7 +98,6 @@ func (m *mullvad) BuildConf(connection models.OpenVPNConnection,
"auth-nocache",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"remote-random",
"suppress-timestamps",
// Modified variables

View File

@@ -110,7 +110,6 @@ func (n *nordvpn) BuildConf(connection models.OpenVPNConnection, verbosity, uid,
"mute-replay-warnings",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"remote-random",
"suppress-timestamps",
// Modified variables

View File

@@ -1,82 +0,0 @@
package provider
import (
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
)
func buildPIAConf(connection models.OpenVPNConnection, verbosity int, root bool, cipher, auth string,
extras models.ExtraConfigOptions) (lines []string) {
var X509CRL, certificate string
var defaultCipher, defaultAuth string
if extras.EncryptionPreset == constants.PIAEncryptionPresetNormal {
defaultCipher = "aes-128-cbc"
defaultAuth = "sha1"
X509CRL = constants.PiaX509CRLNormal
certificate = constants.PIACertificateNormal
} else { // strong encryption
defaultCipher = aes256cbc
defaultAuth = "sha256"
X509CRL = constants.PiaX509CRLStrong
certificate = constants.PIACertificateStrong
}
if len(cipher) == 0 {
cipher = defaultCipher
}
if len(auth) == 0 {
auth = defaultAuth
}
lines = []string{
"client",
"dev tun",
"nobind",
"persist-key",
"remote-cert-tls server",
// PIA specific
"ping 300", // Ping every 5 minutes to prevent a timeout error
"reneg-sec 0",
"compress", // allow PIA server to choose the compression to use
// Added constant values
"auth-nocache",
"mute-replay-warnings",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"remote-random",
"suppress-timestamps",
// Modified variables
fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP, connection.Port),
fmt.Sprintf("cipher %s", cipher),
fmt.Sprintf("auth %s", auth),
}
if strings.HasSuffix(cipher, "-gcm") {
lines = append(lines, "ncp-disable")
}
if !root {
lines = append(lines, "user nonrootuser")
}
lines = append(lines, []string{
"<crl-verify>",
"-----BEGIN X509 CRL-----",
X509CRL,
"-----END X509 CRL-----",
"</crl-verify>",
}...)
lines = append(lines, []string{
"<ca>",
"-----BEGIN CERTIFICATE-----",
certificate,
"-----END CERTIFICATE-----",
"</ca>",
"",
}...)
return lines
}

View File

@@ -1,152 +0,0 @@
package provider
import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"net"
"net/http"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
"golang.org/x/net/context/ctxhttp"
)
type piaV3 struct {
servers []models.PIAOldServer
randSource rand.Source
}
func newPrivateInternetAccessV3(servers []models.PIAOldServer, timeNow timeNowFunc) *piaV3 {
return &piaV3{
servers: servers,
randSource: rand.NewSource(timeNow().UnixNano()),
}
}
func (p *piaV3) GetOpenVPNConnection(selection models.ServerSelection) (
connection models.OpenVPNConnection, err error) {
var port uint16
switch selection.Protocol {
case constants.TCP:
switch selection.EncryptionPreset {
case constants.PIAEncryptionPresetNormal:
port = 502
case constants.PIAEncryptionPresetStrong:
port = 501
}
case constants.UDP:
switch selection.EncryptionPreset {
case constants.PIAEncryptionPresetNormal:
port = 1198
case constants.PIAEncryptionPresetStrong:
port = 1197
}
}
if port == 0 {
return connection, fmt.Errorf(
"combination of protocol %q and encryption %q does not yield any port number",
selection.Protocol, selection.EncryptionPreset)
}
if selection.TargetIP != nil {
return models.OpenVPNConnection{IP: selection.TargetIP, Port: port, Protocol: selection.Protocol}, nil
}
servers := filterPIAOldServers(p.servers, selection.Regions)
if len(servers) == 0 {
return connection, fmt.Errorf("no server found for regions %s", commaJoin(selection.Regions))
}
var connections []models.OpenVPNConnection
for _, server := range servers {
for _, IP := range server.IPs {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol})
}
}
return pickRandomConnection(connections, p.randSource), nil
}
func (p *piaV3) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int,
root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
return buildPIAConf(connection, verbosity, root, cipher, auth, extras)
}
func (p *piaV3) PortForward(ctx context.Context, client *http.Client,
fileManager files.FileManager, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator,
syncState func(port uint16) (pfFilepath models.Filepath)) {
const uuidLength = 32
b := make([]byte, uuidLength)
n, err := rand.New(p.randSource).Read(b) //nolint:gosec
if err != nil {
pfLogger.Error(err)
return
} else if n != uuidLength {
pfLogger.Error("only read %d bytes instead of %d", n, uuidLength)
return
}
clientID := hex.EncodeToString(b)
url := fmt.Sprintf("%s/?client_id=%s", constants.PIAPortForwardURL, clientID)
response, err := ctxhttp.Get(ctx, client, url)
if err != nil {
pfLogger.Error(err)
return
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
pfLogger.Error("%s for %s; does your PIA server support port forwarding?", response.Status, url)
return
}
b, err = ioutil.ReadAll(response.Body)
if err != nil {
pfLogger.Error(err)
return
} else if len(b) == 0 {
pfLogger.Error("port forwarding is already activated on this connection, has expired, or you are not connected to a PIA region that supports port forwarding") //nolint:lll
return
}
body := struct {
Port uint16 `json:"port"`
}{}
if err := json.Unmarshal(b, &body); err != nil {
pfLogger.Error("port forwarding response: %s", err)
return
}
port := body.Port
filepath := syncState(port)
pfLogger.Info("Writing port to %s", filepath)
if err := fileManager.WriteToFile(
string(filepath), []byte(fmt.Sprintf("%d", port)),
files.Permissions(constants.AllReadWritePermissions),
); err != nil {
pfLogger.Error(err)
}
if err := fw.SetAllowedPort(ctx, port, string(constants.TUN)); err != nil {
pfLogger.Error(err)
}
<-ctx.Done()
if err := fw.RemoveAllowedPort(ctx, port); err != nil {
pfLogger.Error(err)
}
}
func filterPIAOldServers(servers []models.PIAOldServer, regions []string) (filtered []models.PIAOldServer) {
for _, server := range servers {
switch {
case filterByPossibilities(server.Region, regions):
default:
filtered = append(filtered, server)
}
}
return filtered
}

View File

@@ -21,10 +21,9 @@ import (
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
"golang.org/x/net/context/ctxhttp"
)
type piaV4 struct {
type pia struct {
servers []models.PIAServer
timeNow timeNowFunc
randSource rand.Source
@@ -32,15 +31,15 @@ type piaV4 struct {
activeProtocol models.NetworkProtocol
}
func newPrivateInternetAccessV4(servers []models.PIAServer, timeNow timeNowFunc) *piaV4 {
return &piaV4{
func newPrivateInternetAccess(servers []models.PIAServer, timeNow timeNowFunc) *pia {
return &pia{
servers: servers,
timeNow: timeNow,
randSource: rand.NewSource(timeNow().UnixNano()),
}
}
func (p *piaV4) GetOpenVPNConnection(selection models.ServerSelection) (
func (p *pia) GetOpenVPNConnection(selection models.ServerSelection) (
connection models.OpenVPNConnection, err error) {
var port uint16
switch selection.Protocol {
@@ -110,13 +109,80 @@ func (p *piaV4) GetOpenVPNConnection(selection models.ServerSelection) (
return connection, nil
}
func (p *piaV4) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, root bool,
func (p *pia) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, root bool,
cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
return buildPIAConf(connection, verbosity, root, cipher, auth, extras)
var X509CRL, certificate string
var defaultCipher, defaultAuth string
if extras.EncryptionPreset == constants.PIAEncryptionPresetNormal {
defaultCipher = "aes-128-cbc"
defaultAuth = "sha1"
X509CRL = constants.PiaX509CRLNormal
certificate = constants.PIACertificateNormal
} else { // strong encryption
defaultCipher = aes256cbc
defaultAuth = "sha256"
X509CRL = constants.PiaX509CRLStrong
certificate = constants.PIACertificateStrong
}
if len(cipher) == 0 {
cipher = defaultCipher
}
if len(auth) == 0 {
auth = defaultAuth
}
lines = []string{
"client",
"dev tun",
"nobind",
"persist-key",
"remote-cert-tls server",
// PIA specific
"ping 300", // Ping every 5 minutes to prevent a timeout error
"reneg-sec 0",
"compress", // allow PIA server to choose the compression to use
// Added constant values
"auth-nocache",
"mute-replay-warnings",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"suppress-timestamps",
// Modified variables
fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP, connection.Port),
fmt.Sprintf("cipher %s", cipher),
fmt.Sprintf("auth %s", auth),
}
if strings.HasSuffix(cipher, "-gcm") {
lines = append(lines, "ncp-disable")
}
if !root {
lines = append(lines, "user nonrootuser")
}
lines = append(lines, []string{
"<crl-verify>",
"-----BEGIN X509 CRL-----",
X509CRL,
"-----END X509 CRL-----",
"</crl-verify>",
}...)
lines = append(lines, []string{
"<ca>",
"-----BEGIN CERTIFICATE-----",
certificate,
"-----END CERTIFICATE-----",
"</ca>",
"",
}...)
return lines
}
//nolint:gocognit
func (p *piaV4) PortForward(ctx context.Context, client *http.Client,
func (p *pia) PortForward(ctx context.Context, client *http.Client,
fileManager files.FileManager, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator,
syncState func(port uint16) (pfFilepath models.Filepath)) {
if !p.activeServer.PortForward {
@@ -131,7 +197,7 @@ func (p *piaV4) PortForward(ctx context.Context, client *http.Client,
if p.activeProtocol == constants.TCP {
commonName = p.activeServer.OpenvpnTCP.CN
}
client, err := newPIAv4HTTPClient(commonName)
client, err := newPIAHTTPClient(commonName)
if err != nil {
pfLogger.Error("aborting because: %s", err)
return
@@ -261,7 +327,7 @@ func filterPIAServers(servers []models.PIAServer, regions []string) (filtered []
return filtered
}
func newPIAv4HTTPClient(serverName string) (client *http.Client, err error) {
func newPIAHTTPClient(serverName string) (client *http.Client, err error) {
certificateBytes, err := base64.StdEncoding.DecodeString(constants.PIACertificateStrong)
if err != nil {
return nil, fmt.Errorf("cannot decode PIA root certificate: %w", err)
@@ -293,7 +359,7 @@ func newPIAv4HTTPClient(serverName string) (client *http.Client, err error) {
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
const httpTimeout = 5 * time.Second
const httpTimeout = 30 * time.Second
client = &http.Client{Transport: &transport, Timeout: httpTimeout}
return client, nil
}
@@ -447,7 +513,11 @@ func fetchPIAPortForwardData(ctx context.Context, client *http.Client, gateway n
Path: "/getSignature",
RawQuery: queryParams.Encode(),
}
response, err := ctxhttp.Get(ctx, client, url.String())
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url.String(), nil)
if err != nil {
return 0, "", expiration, fmt.Errorf("cannot obtain signature: %w", err)
}
response, err := client.Do(request)
if err != nil {
return 0, "", expiration, fmt.Errorf("cannot obtain signature: %w", err)
}
@@ -489,7 +559,11 @@ func bindPIAPort(ctx context.Context, client *http.Client, gateway net.IP, data
RawQuery: queryParams.Encode(),
}
response, err := ctxhttp.Get(ctx, client, url.String())
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url.String(), nil)
if err != nil {
return fmt.Errorf("cannot bind port: %w", err)
}
response, err := client.Do(request)
if err != nil {
return fmt.Errorf("cannot bind port: %w", err)
}

View File

@@ -0,0 +1,123 @@
package provider
import (
"context"
"fmt"
"math/rand"
"net"
"net/http"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
)
type privado struct {
servers []models.PrivadoServer
randSource rand.Source
}
func newPrivado(servers []models.PrivadoServer, timeNow timeNowFunc) *privado {
return &privado{
servers: servers,
randSource: rand.NewSource(timeNow().UnixNano()),
}
}
func (s *privado) filterServers(hostnames []string) (servers []models.PrivadoServer) {
for _, server := range s.servers {
switch {
case filterByPossibilities(server.Hostname, hostnames):
default:
servers = append(servers, server)
}
}
return servers
}
func (s *privado) GetOpenVPNConnection(selection models.ServerSelection) (
connection models.OpenVPNConnection, err error) {
var port uint16 = 1194
switch selection.Protocol {
case constants.UDP:
default:
return connection, fmt.Errorf("protocol %q is not supported by Privado", selection.Protocol)
}
if selection.TargetIP != nil {
return models.OpenVPNConnection{IP: selection.TargetIP, Port: port, Protocol: selection.Protocol}, nil
}
servers := s.filterServers(selection.Hostnames)
if len(servers) == 0 {
return connection, fmt.Errorf("no server found for cities %s and server numbers %v",
commaJoin(selection.Cities), selection.Numbers)
}
connections := make([]models.OpenVPNConnection, len(servers))
for i := range servers {
connection := models.OpenVPNConnection{
IP: servers[i].IP,
Port: port,
Protocol: selection.Protocol,
Hostname: servers[i].Hostname,
}
connections = append(connections, connection)
}
return pickRandomConnection(connections, s.randSource), nil
}
func (s *privado) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, root bool,
cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(cipher) == 0 {
cipher = aes256cbc
}
if len(auth) == 0 {
auth = sha256
}
lines = []string{
"client",
"dev tun",
"nobind",
"persist-key",
// Privado specific
"tls-cipher TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-DSS-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA",
fmt.Sprintf("verify-x509-name %s name", connection.Hostname),
// Added constant values
"auth-nocache",
"mute-replay-warnings",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"suppress-timestamps",
// Modified variables
fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP, connection.Port),
fmt.Sprintf("cipher %s", cipher),
fmt.Sprintf("auth %s", auth),
}
if !root {
lines = append(lines, "user nonrootuser")
}
lines = append(lines, []string{
"<ca>",
"-----BEGIN CERTIFICATE-----",
constants.PrivadoCertificate,
"-----END CERTIFICATE-----",
"</ca>",
}...)
return lines
}
func (s *privado) PortForward(ctx context.Context, client *http.Client,
fileManager files.FileManager, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator,
syncState func(port uint16) (pfFilepath models.Filepath)) {
panic("port forwarding is not supported for privado")
}

View File

@@ -25,9 +25,7 @@ type Provider interface {
func New(provider models.VPNProvider, allServers models.AllServers, timeNow timeNowFunc) Provider {
switch provider {
case constants.PrivateInternetAccess:
return newPrivateInternetAccessV4(allServers.Pia.Servers, timeNow)
case constants.PrivateInternetAccessOld:
return newPrivateInternetAccessV3(allServers.PiaOld.Servers, timeNow)
return newPrivateInternetAccess(allServers.Pia.Servers, timeNow)
case constants.Mullvad:
return newMullvad(allServers.Mullvad.Servers, timeNow)
case constants.Windscribe:
@@ -42,6 +40,8 @@ func New(provider models.VPNProvider, allServers models.AllServers, timeNow time
return newNordvpn(allServers.Nordvpn.Servers, timeNow)
case constants.Purevpn:
return newPurevpn(allServers.Purevpn.Servers, timeNow)
case constants.Privado:
return newPrivado(allServers.Privado.Servers, timeNow)
default:
return nil // should never occur
}

View File

@@ -98,7 +98,6 @@ func (p *purevpn) BuildConf(connection models.OpenVPNConnection, verbosity, uid,
"mute-replay-warnings",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"remote-random",
"suppress-timestamps",
// Modified variables

View File

@@ -105,7 +105,6 @@ func (s *surfshark) BuildConf(connection models.OpenVPNConnection, verbosity, ui
"mute-replay-warnings",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"remote-random",
"suppress-timestamps",
// Modified variables

View File

@@ -95,7 +95,6 @@ func (v *vyprvpn) BuildConf(connection models.OpenVPNConnection, verbosity, uid,
"mute-replay-warnings",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"remote-random",
"suppress-timestamps",
// Modified variables

View File

@@ -27,11 +27,13 @@ func newWindscribe(servers []models.WindscribeServer, timeNow timeNowFunc) *wind
}
}
func (w *windscribe) filterServers(regions []string) (servers []models.WindscribeServer) {
func (w *windscribe) filterServers(regions, cities, hostnames []string) (servers []models.WindscribeServer) {
for _, server := range w.servers {
switch {
case
filterByPossibilities(server.Region, regions):
filterByPossibilities(server.Region, regions),
filterByPossibilities(server.City, cities),
filterByPossibilities(server.Hostname, hostnames):
default:
servers = append(servers, server)
}
@@ -57,16 +59,14 @@ func (w *windscribe) GetOpenVPNConnection(selection models.ServerSelection) (con
return models.OpenVPNConnection{IP: selection.TargetIP, Port: port, Protocol: selection.Protocol}, nil
}
servers := w.filterServers(selection.Regions)
servers := w.filterServers(selection.Regions, selection.Cities, selection.Hostnames)
if len(servers) == 0 {
return connection, fmt.Errorf("no server found for region %s", commaJoin(selection.Regions))
}
var connections []models.OpenVPNConnection
connections := make([]models.OpenVPNConnection, len(servers))
for _, server := range servers {
for _, IP := range server.IPs {
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol})
}
connections = append(connections, models.OpenVPNConnection{IP: server.IP, Port: port, Protocol: selection.Protocol})
}
return pickRandomConnection(connections, w.randSource), nil
@@ -97,7 +97,6 @@ func (w *windscribe) BuildConf(connection models.OpenVPNConnection, verbosity, u
"mute-replay-warnings",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"remote-random",
"suppress-timestamps",
// Modified variables

View File

@@ -16,7 +16,7 @@ const (
)
func (r *routing) Setup() (err error) {
defaultIP, err := r.defaultIP()
defaultIP, err := r.DefaultIP()
if err != nil {
return fmt.Errorf("%s: %w", ErrSetup, err)
}
@@ -36,14 +36,23 @@ func (r *routing) Setup() (err error) {
if err := r.addIPRule(defaultIP, table, priority); err != nil {
return fmt.Errorf("%s: %w", ErrSetup, err)
}
if err := r.addRouteVia(net.IPNet{}, defaultGateway, defaultInterfaceName, table); err != nil {
defaultDestination := net.IPNet{IP: net.IPv4(0, 0, 0, 0), Mask: net.IPv4Mask(0, 0, 0, 0)}
if err := r.addRouteVia(defaultDestination, defaultGateway, defaultInterfaceName, table); err != nil {
return fmt.Errorf("%s: %w", ErrSetup, err)
}
r.stateMutex.RLock()
outboundSubnets := r.outboundSubnets
r.stateMutex.RUnlock()
if err := r.setOutboundRoutes(outboundSubnets, defaultInterfaceName, defaultGateway); err != nil {
return fmt.Errorf("%s: %w", ErrSetup, err)
}
return nil
}
func (r *routing) TearDown() error {
defaultIP, err := r.defaultIP()
defaultIP, err := r.DefaultIP()
if err != nil {
return fmt.Errorf("%s: %w", ErrTeardown, err)
}
@@ -52,11 +61,17 @@ func (r *routing) TearDown() error {
return fmt.Errorf("%s: %w", ErrTeardown, err)
}
if err := r.deleteRouteVia(net.IPNet{}, defaultGateway, defaultInterfaceName, table); err != nil {
defaultNet := net.IPNet{IP: net.IPv4(0, 0, 0, 0), Mask: net.IPv4Mask(0, 0, 0, 0)}
if err := r.deleteRouteVia(defaultNet, defaultGateway, defaultInterfaceName, table); err != nil {
return fmt.Errorf("%s: %w", ErrTeardown, err)
}
if err := r.deleteIPRule(defaultIP, table, priority); err != nil {
return fmt.Errorf("%s: %w", ErrTeardown, err)
}
if err := r.setOutboundRoutes(nil, defaultInterfaceName, defaultGateway); err != nil {
return fmt.Errorf("%s: %w", ErrSetup, err)
}
return nil
}

View File

@@ -10,7 +10,9 @@ import (
func (r *routing) addRouteVia(destination net.IPNet, gateway net.IP, iface string, table int) error {
destinationStr := destination.String()
r.logger.Info("adding route for %s", destinationStr)
if r.verbose {
r.logger.Info("adding route for %s", destinationStr)
}
if r.debug {
fmt.Printf("ip route replace %s via %s dev %s table %d\n", destinationStr, gateway, iface, table)
}
@@ -33,7 +35,9 @@ func (r *routing) addRouteVia(destination net.IPNet, gateway net.IP, iface strin
func (r *routing) deleteRouteVia(destination net.IPNet, gateway net.IP, iface string, table int) (err error) {
destinationStr := destination.String()
r.logger.Info("deleting route for %s", destinationStr)
if r.verbose {
r.logger.Info("deleting route for %s", destinationStr)
}
if r.debug {
fmt.Printf("ip route delete %s via %s dev %s table %d\n", destinationStr, gateway, iface, table)
}

View File

@@ -0,0 +1,58 @@
package routing
import (
"fmt"
"net"
)
func (r *routing) SetOutboundRoutes(outboundSubnets []net.IPNet) error {
defaultInterface, defaultGateway, err := r.DefaultRoute()
if err != nil {
return fmt.Errorf("cannot set oubtound subnets in routing: %w", err)
}
return r.setOutboundRoutes(outboundSubnets, defaultInterface, defaultGateway)
}
func (r *routing) setOutboundRoutes(outboundSubnets []net.IPNet,
defaultInterfaceName string, defaultGateway net.IP) error {
r.stateMutex.Lock()
defer r.stateMutex.Unlock()
subnetsToRemove := findSubnetsToRemove(r.outboundSubnets, outboundSubnets)
subnetsToAdd := findSubnetsToAdd(r.outboundSubnets, outboundSubnets)
if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 {
return nil
}
r.removeOutboundSubnets(subnetsToRemove, defaultInterfaceName, defaultGateway)
if err := r.addOutboundSubnets(subnetsToAdd, defaultInterfaceName, defaultGateway); err != nil {
return fmt.Errorf("cannot set outbound subnets in routing: %w", err)
}
return nil
}
func (r *routing) removeOutboundSubnets(subnets []net.IPNet,
defaultInterfaceName string, defaultGateway net.IP) {
for _, subnet := range subnets {
const table = 0
if err := r.deleteRouteVia(subnet, defaultGateway, defaultInterfaceName, table); err != nil {
r.logger.Error("cannot remove outdated outbound subnet from routing: %s", err)
continue
}
r.outboundSubnets = removeSubnetFromSubnets(r.outboundSubnets, subnet)
}
}
func (r *routing) addOutboundSubnets(subnets []net.IPNet,
defaultInterfaceName string, defaultGateway net.IP) error {
for _, subnet := range subnets {
const table = 0
if err := r.addRouteVia(subnet, defaultGateway, defaultInterfaceName, table); err != nil {
return fmt.Errorf("cannot add outbound subnet %s to routing: %w", subnet, err)
}
r.outboundSubnets = append(r.outboundSubnets, subnet)
}
return nil
}

View File

@@ -24,14 +24,16 @@ func (r *routing) DefaultRoute() (defaultInterface string, defaultGateway net.IP
}
attributes := link.Attrs()
defaultInterface = attributes.Name
r.logger.Info("default route found: interface %s, gateway %s", defaultInterface, defaultGateway.String())
if r.verbose {
r.logger.Info("default route found: interface %s, gateway %s", defaultInterface, defaultGateway.String())
}
return defaultInterface, defaultGateway, nil
}
}
return "", nil, fmt.Errorf("cannot find default route in %d routes", len(routes))
}
func (r *routing) defaultIP() (ip net.IP, err error) {
func (r *routing) DefaultIP() (ip net.IP, err error) {
routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL)
if err != nil {
return nil, fmt.Errorf("cannot get default IP address: %w", err)
@@ -77,7 +79,9 @@ func (r *routing) LocalSubnet() (defaultSubnet net.IPNet, err error) {
continue
}
defaultSubnet = *route.Dst
r.logger.Info("local subnet found: %s", defaultSubnet.String())
if r.verbose {
r.logger.Info("local subnet found: %s", defaultSubnet.String())
}
return defaultSubnet, nil
}
@@ -124,7 +128,7 @@ func (r *routing) VPNDestinationIP() (ip net.IP, err error) {
for _, route := range routes {
if route.LinkIndex == defaultLinkIndex &&
route.Dst != nil &&
!ipIsPrivate(route.Dst.IP) &&
!IPIsPrivate(route.Dst.IP) &&
bytes.Equal(route.Dst.Mask, net.IPMask{255, 255, 255, 255}) {
return route.Dst.IP, nil
}
@@ -152,7 +156,7 @@ func (r *routing) VPNLocalGatewayIP() (ip net.IP, err error) {
return nil, fmt.Errorf("cannot find VPN local gateway IP address from ip routes")
}
func ipIsPrivate(ip net.IP) bool {
func IPIsPrivate(ip net.IP) bool {
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
return true
}

View File

@@ -2,32 +2,49 @@ package routing
import (
"net"
"sync"
"github.com/qdm12/golibs/logging"
)
type Routing interface {
// Mutations
Setup() (err error)
TearDown() error
SetOutboundRoutes(outboundSubnets []net.IPNet) error
// Read only
DefaultRoute() (defaultInterface string, defaultGateway net.IP, err error)
LocalSubnet() (defaultSubnet net.IPNet, err error)
DefaultIP() (defaultIP net.IP, err error)
VPNDestinationIP() (ip net.IP, err error)
VPNLocalGatewayIP() (ip net.IP, err error)
// Internal state
SetVerbose(verbose bool)
SetDebug()
}
type routing struct {
logger logging.Logger
debug bool
logger logging.Logger
verbose bool
debug bool
outboundSubnets []net.IPNet
stateMutex sync.RWMutex
}
// NewConfigurator creates a new Configurator instance.
func NewRouting(logger logging.Logger) Routing {
return &routing{
logger: logger.WithPrefix("routing: "),
logger: logger.WithPrefix("routing: "),
verbose: true,
}
}
func (c *routing) SetVerbose(verbose bool) {
c.verbose = verbose
}
func (c *routing) SetDebug() {
c.debug = true
}

View File

@@ -0,0 +1,53 @@
package routing
import (
"net"
)
func findSubnetsToAdd(oldSubnets, newSubnets []net.IPNet) (subnetsToAdd []net.IPNet) {
for _, newSubnet := range newSubnets {
found := false
for _, oldSubnet := range oldSubnets {
if subnetsAreEqual(oldSubnet, newSubnet) {
found = true
break
}
}
if !found {
subnetsToAdd = append(subnetsToAdd, newSubnet)
}
}
return subnetsToAdd
}
func findSubnetsToRemove(oldSubnets, newSubnets []net.IPNet) (subnetsToRemove []net.IPNet) {
for _, oldSubnet := range oldSubnets {
found := false
for _, newSubnet := range newSubnets {
if subnetsAreEqual(oldSubnet, newSubnet) {
found = true
break
}
}
if !found {
subnetsToRemove = append(subnetsToRemove, oldSubnet)
}
}
return subnetsToRemove
}
func subnetsAreEqual(a, b net.IPNet) bool {
return a.IP.Equal(b.IP) && a.Mask.String() == b.Mask.String()
}
func removeSubnetFromSubnets(subnets []net.IPNet, subnet net.IPNet) []net.IPNet {
L := len(subnets)
for i := range subnets {
if subnetsAreEqual(subnet, subnets[i]) {
subnets[i] = subnets[L-1]
subnets = subnets[:L-1]
break
}
}
return subnets
}

View File

@@ -0,0 +1,70 @@
package server
import (
"fmt"
"net/http"
"github.com/qdm12/gluetun/internal/dns"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/openvpn"
"github.com/qdm12/gluetun/internal/updater"
"github.com/qdm12/golibs/logging"
)
func newHandler(logger logging.Logger, logging bool,
buildInfo models.BuildInformation,
openvpnLooper openvpn.Looper,
unboundLooper dns.Looper,
updaterLooper updater.Looper,
) http.Handler {
return &handler{
logger: logger,
logging: logging,
buildInfo: buildInfo,
openvpnLooper: openvpnLooper,
unboundLooper: unboundLooper,
updaterLooper: updaterLooper,
}
}
type handler struct {
logger logging.Logger
logging bool
buildInfo models.BuildInformation
openvpnLooper openvpn.Looper
unboundLooper dns.Looper
updaterLooper updater.Looper
}
func (h *handler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
if h.logging {
h.logger.Info("HTTP %s %s", request.Method, request.RequestURI)
}
switch request.Method {
case http.MethodGet:
switch request.RequestURI {
case "/version":
h.getVersion(responseWriter)
responseWriter.WriteHeader(http.StatusOK)
case "/openvpn/actions/restart":
h.openvpnLooper.Restart()
responseWriter.WriteHeader(http.StatusOK)
case "/unbound/actions/restart":
h.unboundLooper.Restart()
responseWriter.WriteHeader(http.StatusOK)
case "/openvpn/portforwarded":
h.getPortForwarded(responseWriter)
case "/openvpn/settings":
h.getOpenvpnSettings(responseWriter)
case "/updater/restart":
h.updaterLooper.Restart()
responseWriter.WriteHeader(http.StatusOK)
default:
errString := fmt.Sprintf("Nothing here for %s %s", request.Method, request.RequestURI)
http.Error(responseWriter, errString, http.StatusBadRequest)
}
default:
errString := fmt.Sprintf("Nothing here for %s %s", request.Method, request.RequestURI)
http.Error(responseWriter, errString, http.StatusBadRequest)
}
}

View File

@@ -1,27 +0,0 @@
package server
import (
"fmt"
"net/http"
)
func (s *server) handleHealth(w http.ResponseWriter) {
// TODO option to disable
// TODO use mullvad API if current provider is Mullvad
ips, err := s.lookupIP("github.com")
var errorMessage string
switch {
case err != nil:
errorMessage = fmt.Sprintf("cannot resolve github.com (%s)", err)
case len(ips) == 0:
errorMessage = "resolved no IP addresses for github.com"
default: // success
w.WriteHeader(http.StatusOK)
return
}
s.logger.Warn(errorMessage)
w.WriteHeader(http.StatusInternalServerError)
if _, err := w.Write([]byte(errorMessage)); err != nil {
s.logger.Warn(err)
}
}

View File

@@ -5,32 +5,32 @@ import (
"net/http"
)
func (s *server) handleGetPortForwarded(w http.ResponseWriter) {
port := s.openvpnLooper.GetPortForwarded()
func (h *handler) getPortForwarded(w http.ResponseWriter) {
port := h.openvpnLooper.GetPortForwarded()
data, err := json.Marshal(struct {
Port uint16 `json:"port"`
}{port})
if err != nil {
s.logger.Warn(err)
h.logger.Warn(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if _, err := w.Write(data); err != nil {
s.logger.Warn(err)
h.logger.Warn(err)
w.WriteHeader(http.StatusInternalServerError)
}
}
func (s *server) handleGetOpenvpnSettings(w http.ResponseWriter) {
settings := s.openvpnLooper.GetSettings()
func (h *handler) getOpenvpnSettings(w http.ResponseWriter) {
settings := h.openvpnLooper.GetSettings()
data, err := json.Marshal(settings)
if err != nil {
s.logger.Warn(err)
h.logger.Warn(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if _, err := w.Write(data); err != nil {
s.logger.Warn(err)
h.logger.Warn(err)
w.WriteHeader(http.StatusInternalServerError)
}
}

View File

@@ -2,13 +2,12 @@ package server
import (
"context"
"fmt"
"net"
"net/http"
"sync"
"time"
"github.com/qdm12/gluetun/internal/dns"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/openvpn"
"github.com/qdm12/gluetun/internal/updater"
"github.com/qdm12/golibs/logging"
@@ -19,30 +18,24 @@ type Server interface {
}
type server struct {
address string
logging bool
logger logging.Logger
openvpnLooper openvpn.Looper
unboundLooper dns.Looper
updaterLooper updater.Looper
lookupIP func(host string) ([]net.IP, error)
address string
logger logging.Logger
handler http.Handler
}
func New(address string, logging bool, logger logging.Logger,
func New(address string, logging bool, logger logging.Logger, buildInfo models.BuildInformation,
openvpnLooper openvpn.Looper, unboundLooper dns.Looper, updaterLooper updater.Looper) Server {
serverLogger := logger.WithPrefix("http server: ")
handler := newHandler(serverLogger, logging, buildInfo, openvpnLooper, unboundLooper, updaterLooper)
return &server{
address: address,
logging: logging,
logger: logger.WithPrefix("http server: "),
openvpnLooper: openvpnLooper,
unboundLooper: unboundLooper,
updaterLooper: updaterLooper,
lookupIP: net.LookupIP,
address: address,
logger: serverLogger,
handler: handler,
}
}
func (s *server) Run(ctx context.Context, wg *sync.WaitGroup) {
server := http.Server{Addr: s.address, Handler: s.makeHandler()}
server := http.Server{Addr: s.address, Handler: s.handler}
go func() {
defer wg.Done()
<-ctx.Done()
@@ -61,43 +54,3 @@ func (s *server) Run(ctx context.Context, wg *sync.WaitGroup) {
s.logger.Error(err)
}
}
func (s *server) makeHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if s.logging && (r.Method != http.MethodGet || r.RequestURI != "/health") {
s.logger.Info("HTTP %s %s", r.Method, r.RequestURI)
}
switch r.Method {
case http.MethodGet:
switch r.RequestURI {
case "/openvpn/actions/restart":
s.openvpnLooper.Restart()
w.WriteHeader(http.StatusOK)
case "/unbound/actions/restart":
s.unboundLooper.Restart()
w.WriteHeader(http.StatusOK)
case "/openvpn/portforwarded":
s.handleGetPortForwarded(w)
case "/openvpn/settings":
s.handleGetOpenvpnSettings(w)
case "/health":
s.handleHealth(w)
case "/updater/restart":
s.updaterLooper.Restart()
w.WriteHeader(http.StatusOK)
default:
routeDoesNotExist(s.logger, w, r)
}
default:
routeDoesNotExist(s.logger, w, r)
}
}
}
func routeDoesNotExist(logger logging.Logger, w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
_, err := w.Write([]byte(fmt.Sprintf("Nothing here for %s %s", r.Method, r.RequestURI)))
if err != nil {
logger.Error(err)
}
}

View File

@@ -0,0 +1,19 @@
package server
import (
"encoding/json"
"net/http"
)
func (h *handler) getVersion(w http.ResponseWriter) {
data, err := json.Marshal(h.buildInfo)
if err != nil {
h.logger.Warn(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if _, err := w.Write(data); err != nil {
h.logger.Warn(err)
w.WriteHeader(http.StatusInternalServerError)
}
}

View File

@@ -2,6 +2,7 @@ package settings
import (
"fmt"
"net"
"strings"
"github.com/qdm12/gluetun/internal/params"
@@ -9,10 +10,11 @@ import (
// Firewall contains settings to customize the firewall operation.
type Firewall struct {
VPNInputPorts []uint16
InputPorts []uint16
Enabled bool
Debug bool
VPNInputPorts []uint16
InputPorts []uint16
OutboundSubnets []net.IPNet
Enabled bool
Debug bool
}
func (f *Firewall) String() string {
@@ -27,11 +29,16 @@ func (f *Firewall) String() string {
for i, port := range f.InputPorts {
inputPorts[i] = fmt.Sprintf("%d", port)
}
outboundSubnets := make([]string, len(f.OutboundSubnets))
for i := range f.OutboundSubnets {
outboundSubnets[i] = f.OutboundSubnets[i].String()
}
settingsList := []string{
"Firewall settings:",
"VPN input ports: " + strings.Join(vpnInputPorts, ", "),
"Input ports: " + strings.Join(inputPorts, ", "),
"Outbound subnets: " + strings.Join(outboundSubnets, ", "),
}
if f.Debug {
settingsList = append(settingsList, "Debug: on")
@@ -49,6 +56,10 @@ func GetFirewallSettings(paramsReader params.Reader) (settings Firewall, err err
if err != nil {
return settings, err
}
settings.OutboundSubnets, err = paramsReader.GetOutboundSubnets()
if err != nil {
return settings, err
}
settings.Enabled, err = paramsReader.GetFirewall()
if err != nil {
return settings, err

View File

@@ -0,0 +1,71 @@
package settings
import (
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/params"
)
// HTTPProxy contains settings to configure the HTTP proxy.
type HTTPProxy struct { //nolint:maligned
Enabled bool
Port uint16
User string
Password string
Stealth bool
Log bool
}
func (h *HTTPProxy) String() string {
if !h.Enabled {
return "HTTP Proxy settings: disabled"
}
auth, log, stealth := disabled, disabled, disabled
if h.User != "" {
auth = enabled
}
if h.Log {
log = enabled
}
if h.Stealth {
stealth = enabled
}
settingsList := []string{
"HTTP proxy settings:",
fmt.Sprintf("Port: %d", h.Port),
"Authentication: " + auth,
"Stealth: " + stealth,
"Log: " + log,
}
return strings.Join(settingsList, "\n |--")
}
// GetHTTPProxySettings obtains HTTPProxy settings from environment variables using the params package.
func GetHTTPProxySettings(paramsReader params.Reader) (settings HTTPProxy, err error) {
settings.Enabled, err = paramsReader.GetHTTPProxy()
if err != nil || !settings.Enabled {
return settings, err
}
settings.Port, err = paramsReader.GetHTTPProxyPort()
if err != nil {
return settings, err
}
settings.User, err = paramsReader.GetHTTPProxyUser()
if err != nil {
return settings, err
}
settings.Password, err = paramsReader.GetHTTPProxyPassword()
if err != nil {
return settings, err
}
settings.Stealth, err = paramsReader.GetHTTPProxyStealth()
if err != nil {
return settings, err
}
settings.Log, err = paramsReader.GetHTTPProxyLog()
if err != nil {
return settings, err
}
return settings, nil
}

View File

@@ -54,8 +54,6 @@ func GetOpenVPNSettings(paramsReader params.Reader, vpnProvider models.VPNProvid
switch vpnProvider {
case constants.PrivateInternetAccess:
settings.Provider, err = GetPIASettings(paramsReader)
case constants.PrivateInternetAccessOld:
settings.Provider, err = GetPIAOldSettings(paramsReader)
case constants.Mullvad:
settings.Provider, err = GetMullvadSettings(paramsReader)
case constants.Windscribe:
@@ -70,6 +68,8 @@ func GetOpenVPNSettings(paramsReader params.Reader, vpnProvider models.VPNProvid
settings.Provider, err = GetNordvpnSettings(paramsReader)
case constants.Purevpn:
settings.Provider, err = GetPurevpnSettings(paramsReader)
case constants.Privado:
settings.Provider, err = GetPrivadoSettings(paramsReader)
default:
err = fmt.Errorf("VPN service provider %q is not valid", vpnProvider)
}

View File

@@ -20,7 +20,7 @@ func Test_OpenVPN_JSON(t *testing.T) {
data, err := json.Marshal(in)
require.NoError(t, err)
//nolint:lll
assert.Equal(t, `{"user":"","verbosity":0,"runAsRoot":true,"cipher":"","auth":"","provider":{"name":"name","serverSelection":{"networkProtocol":"","regions":null,"group":"","countries":null,"cities":null,"isps":null,"owned":false,"customPort":0,"numbers":null,"encryptionPreset":""},"extraConfig":{"encryptionPreset":"","openvpnIPv6":false},"portForwarding":{"enabled":false,"filepath":""}}}`, string(data))
assert.Equal(t, `{"user":"","verbosity":0,"runAsRoot":true,"cipher":"","auth":"","provider":{"name":"name","serverSelection":{"networkProtocol":"","regions":null,"group":"","countries":null,"cities":null,"hostnames":null,"isps":null,"owned":false,"customPort":0,"numbers":null,"encryptionPreset":""},"extraConfig":{"encryptionPreset":"","openvpnIPv6":false},"portForwarding":{"enabled":false,"filepath":""}}}`, string(data))
var out OpenVPN
err = json.Unmarshal(data, &out)
require.NoError(t, err)

View File

@@ -10,17 +10,7 @@ import (
// GetPIASettings obtains PIA settings from environment variables using the params package.
func GetPIASettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
return getPIASettings(paramsReader, constants.PrivateInternetAccess)
}
// GetPIAOldSettings obtains PIA settings for the older PIA servers (pre summer 2020)
// from environment variables using the params package.
func GetPIAOldSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
return getPIASettings(paramsReader, constants.PrivateInternetAccessOld)
}
func getPIASettings(paramsReader params.Reader, name models.VPNProvider) (settings models.ProviderSettings, err error) {
settings.Name = name
settings.Name = constants.PrivateInternetAccess
settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil {
return settings, err
@@ -118,6 +108,14 @@ func GetWindscribeSettings(paramsReader params.Reader) (settings models.Provider
if err != nil {
return settings, err
}
settings.ServerSelection.Cities, err = paramsReader.GetWindscribeCities()
if err != nil {
return settings, err
}
settings.ServerSelection.Hostnames, err = paramsReader.GetWindscribeHostnames()
if err != nil {
return settings, err
}
settings.ServerSelection.CustomPort, err = paramsReader.GetWindscribePort(settings.ServerSelection.Protocol)
if err != nil {
return settings, err
@@ -158,6 +156,10 @@ func GetCyberghostSettings(paramsReader params.Reader) (settings models.Provider
if err != nil {
return settings, err
}
settings.ExtraConfigOptions.ClientCertificate, err = paramsReader.GetCyberghostClientCertificate()
if err != nil {
return settings, err
}
settings.ServerSelection.Group, err = paramsReader.GetCyberghostGroup()
if err != nil {
return settings, err
@@ -234,3 +236,21 @@ func GetPurevpnSettings(paramsReader params.Reader) (settings models.ProviderSet
}
return settings, nil
}
// GetPrivadoSettings obtains Privado settings from environment variables using the params package.
func GetPrivadoSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
settings.Name = constants.Privado
settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil {
return settings, err
}
settings.ServerSelection.TargetIP, err = paramsReader.GetTargetIP()
if err != nil {
return settings, err
}
settings.ServerSelection.Hostnames, err = paramsReader.GetPrivadoHostnames()
if err != nil {
return settings, err
}
return settings, nil
}

View File

@@ -21,7 +21,7 @@ type Settings struct {
System System
DNS DNS
Firewall Firewall
TinyProxy TinyProxy
HTTPProxy HTTPProxy
ShadowSocks ShadowSocks
PublicIPPeriod time.Duration
UpdaterPeriod time.Duration
@@ -44,7 +44,7 @@ func (s *Settings) String() string {
s.System.String(),
s.DNS.String(),
s.Firewall.String(),
s.TinyProxy.String(),
s.HTTPProxy.String(),
s.ShadowSocks.String(),
s.ControlServer.String(),
"Public IP check period: " + s.PublicIPPeriod.String(), // TODO print disabled if 0
@@ -73,7 +73,7 @@ func GetAllSettings(paramsReader params.Reader) (settings Settings, err error) {
if err != nil {
return settings, err
}
settings.TinyProxy, err = GetTinyProxySettings(paramsReader)
settings.HTTPProxy, err = GetHTTPProxySettings(paramsReader)
if err != nil {
return settings, err
}

View File

@@ -1,59 +0,0 @@
package settings
import (
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/params"
)
// TinyProxy contains settings to configure TinyProxy.
type TinyProxy struct {
User string
Password string
LogLevel models.TinyProxyLogLevel
Port uint16
Enabled bool
}
func (t *TinyProxy) String() string {
if !t.Enabled {
return "TinyProxy settings: disabled"
}
auth := disabled
if t.User != "" {
auth = enabled
}
settingsList := []string{
fmt.Sprintf("Port: %d", t.Port),
"Authentication: " + auth,
"Log level: " + string(t.LogLevel),
}
return "TinyProxy settings:\n" + strings.Join(settingsList, "\n |--")
}
// GetTinyProxySettings obtains TinyProxy settings from environment variables using the params package.
func GetTinyProxySettings(paramsReader params.Reader) (settings TinyProxy, err error) {
settings.Enabled, err = paramsReader.GetTinyProxy()
if err != nil || !settings.Enabled {
return settings, err
}
settings.User, err = paramsReader.GetTinyProxyUser()
if err != nil {
return settings, err
}
settings.Password, err = paramsReader.GetTinyProxyPassword()
if err != nil {
return settings, err
}
settings.Port, err = paramsReader.GetTinyProxyPort()
if err != nil {
return settings, err
}
settings.LogLevel, err = paramsReader.GetTinyProxyLog()
if err != nil {
return settings, err
}
return settings, nil
}

View File

@@ -6,7 +6,6 @@ import (
"sync"
"time"
"github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/logging"
shadowsockslib "github.com/qdm12/ss-server/pkg"
@@ -22,7 +21,6 @@ type Looper interface {
}
type looper struct {
firewallConf firewall.Configurator
settings settings.ShadowSocks
settingsMutex sync.RWMutex
logger logging.Logger
@@ -46,10 +44,8 @@ func (l *looper) logAndWait(ctx context.Context, err error) {
}
}
func NewLooper(firewallConf firewall.Configurator, settings settings.ShadowSocks,
logger logging.Logger, defaultInterface string) Looper {
func NewLooper(settings settings.ShadowSocks, logger logging.Logger, defaultInterface string) Looper {
return &looper{
firewallConf: firewallConf,
settings: settings,
logger: logger.WithPrefix("shadowsocks: "),
defaultInterface: defaultInterface,
@@ -106,7 +102,6 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
l.setEnabled(true)
var previousPort uint16
for ctx.Err() == nil {
for !l.isEnabled() {
// wait for a signal to re-enable
@@ -129,18 +124,6 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
continue
}
if previousPort > 0 {
if err := l.firewallConf.RemoveAllowedPort(ctx, previousPort); err != nil {
l.logger.Error(err)
continue
}
}
if err := l.firewallConf.SetAllowedPort(ctx, settings.Port, l.defaultInterface); err != nil {
l.logger.Error(err)
continue
}
previousPort = settings.Port
shadowsocksCtx, shadowsocksCancel := context.WithCancel(context.Background())
waitError := make(chan error)

View File

@@ -47,12 +47,19 @@ func (s *storage) mergeServers(hardcoded, persistent models.AllServers) (merged
merged.Pia = persistent.Pia
}
}
merged.PiaOld = hardcoded.PiaOld
if persistent.PiaOld.Timestamp > hardcoded.PiaOld.Timestamp {
s.logger.Info("Using Private Internet Access older servers from file (%s more recent)",
getUnixTimeDifference(persistent.PiaOld.Timestamp, hardcoded.PiaOld.Timestamp))
merged.PiaOld = persistent.PiaOld
merged.Privado = hardcoded.Privado
versionDiff := int(persistent.Privado.Version) - int(hardcoded.Privado.Version)
switch {
case versionDiff > 0:
s.logger.Info("Using Privado servers from file (%d version(s) more recent)", versionDiff)
merged.Privado = persistent.Privado
case persistent.Privado.Timestamp > hardcoded.Privado.Timestamp:
s.logger.Info("Using Privado servers from file (%s more recent)",
getUnixTimeDifference(persistent.Privado.Timestamp, hardcoded.Privado.Timestamp))
merged.Privado = persistent.Privado
}
merged.Purevpn = hardcoded.Purevpn
if persistent.Purevpn.Timestamp > hardcoded.Purevpn.Timestamp {
s.logger.Info("Using Purevpn servers from file (%s more recent)",
@@ -73,9 +80,14 @@ func (s *storage) mergeServers(hardcoded, persistent models.AllServers) (merged
}
merged.Windscribe = hardcoded.Windscribe
if persistent.Windscribe.Timestamp > hardcoded.Windscribe.Timestamp {
s.logger.Info("Using Windscribe servers from file (%s more recent)",
getUnixTimeDifference(persistent.Windscribe.Timestamp, hardcoded.Windscribe.Timestamp))
merged.Windscribe = persistent.Windscribe
if hardcoded.Windscribe.Version == 2 && persistent.Windscribe.Version == 1 {
s.logger.Info("Windscribe servers from file discarded because they are one version behind")
merged.Windscribe = hardcoded.Windscribe
} else {
s.logger.Info("Using Windscribe servers from file (%s more recent)",
getUnixTimeDifference(persistent.Windscribe.Timestamp, hardcoded.Windscribe.Timestamp))
merged.Windscribe = persistent.Windscribe
}
}
return merged
}

View File

@@ -18,7 +18,7 @@ func countServers(allServers models.AllServers) int {
len(allServers.Mullvad.Servers) +
len(allServers.Nordvpn.Servers) +
len(allServers.Pia.Servers) +
len(allServers.PiaOld.Servers) +
len(allServers.Privado.Servers) +
len(allServers.Purevpn.Servers) +
len(allServers.Surfshark.Servers) +
len(allServers.Vyprvpn.Servers) +

View File

@@ -1,28 +0,0 @@
package tinyproxy
import (
"context"
"fmt"
"io"
"strings"
)
func (c *configurator) Start(ctx context.Context) (stdout io.ReadCloser, waitFn func() error, err error) {
c.logger.Info("starting tinyproxy server")
stdout, _, waitFn, err = c.commander.Start(ctx, "tinyproxy", "-d")
return stdout, waitFn, err
}
// Version obtains the version of the installed Tinyproxy server.
func (c *configurator) Version(ctx context.Context) (string, error) {
output, err := c.commander.Run(ctx, "tinyproxy", "-v")
if err != nil {
return "", err
}
words := strings.Fields(output)
const minWords = 2
if len(words) < minWords {
return "", fmt.Errorf("tinyproxy -v: output is too short: %q", output)
}
return words[1], nil
}

View File

@@ -1,50 +0,0 @@
package tinyproxy
import (
"fmt"
"sort"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/files"
)
func (c *configurator) MakeConf(logLevel models.TinyProxyLogLevel,
port uint16, user, password string, uid, gid int) error {
c.logger.Info("generating tinyproxy configuration file")
lines := generateConf(logLevel, port, user, password, uid, gid)
return c.fileManager.WriteLinesToFile(string(constants.TinyProxyConf),
lines,
files.Ownership(uid, gid),
files.Permissions(constants.UserReadPermission))
}
func generateConf(logLevel models.TinyProxyLogLevel, port uint16, user, password string, uid, gid int) (
lines []string) {
confMapping := map[string]string{
"User": fmt.Sprintf("%d", uid),
"Group": fmt.Sprintf("%d", gid),
"Port": fmt.Sprintf("%d", port),
"Timeout": "600",
"DefaultErrorFile": "\"/usr/share/tinyproxy/default.html\"",
"MaxClients": "100",
"MinSpareServers": "5",
"MaxSpareServers": "20",
"StartServers": "10",
"MaxRequestsPerChild": "0",
"DisableViaHeader": "Yes",
"LogLevel": string(logLevel),
// "StatFile": "\"/usr/share/tinyproxy/stats.html\"",
}
if len(user) > 0 {
confMapping["BasicAuth"] = fmt.Sprintf("%s %s", user, password)
}
for k, v := range confMapping {
line := fmt.Sprintf("%s %s", k, v)
lines = append(lines, line)
}
sort.Slice(lines, func(i, j int) bool {
return lines[i] < lines[j]
})
return lines
}

View File

@@ -1,68 +0,0 @@
package tinyproxy
import (
"testing"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert"
)
func Test_generateConf(t *testing.T) {
t.Parallel()
tests := map[string]struct {
logLevel models.TinyProxyLogLevel
port uint16
user string
password string
lines []string
}{
"No credentials": {
logLevel: constants.TinyProxyInfoLevel,
port: 2000,
lines: []string{
"DefaultErrorFile \"/usr/share/tinyproxy/default.html\"",
"DisableViaHeader Yes",
"Group 1001",
"LogLevel Info",
"MaxClients 100",
"MaxRequestsPerChild 0",
"MaxSpareServers 20",
"MinSpareServers 5",
"Port 2000",
"StartServers 10",
"Timeout 600",
"User 1000",
},
},
"With credentials": {
logLevel: constants.TinyProxyErrorLevel,
port: 2000,
user: "abc",
password: "def",
lines: []string{
"BasicAuth abc def",
"DefaultErrorFile \"/usr/share/tinyproxy/default.html\"",
"DisableViaHeader Yes",
"Group 1001",
"LogLevel Error",
"MaxClients 100",
"MaxRequestsPerChild 0",
"MaxSpareServers 20",
"MinSpareServers 5",
"Port 2000",
"StartServers 10",
"Timeout 600",
"User 1000",
},
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := generateConf(tc.logLevel, tc.port, tc.user, tc.password, 1000, 1001)
assert.Equal(t, tc.lines, lines)
})
}
}

Some files were not shown because too many files have changed in this diff Show More