Compare commits

...

10 Commits

Author SHA1 Message Date
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
46 changed files with 1183 additions and 842 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 **What VPN provider are you using**:
- [x] No
2. What VPN service provider are you using? **What are you using to run your container?**: Docker Compose
- [x] PIA **What is the version of the program** (See the line at the top of your logs)
- [ ] Mullvad
- [ ] Windscribe
- [ ] Surfshark
- [ ] Cyberghost
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 ...*careful to remove i.e. token information with PIA port forwarding*
- [x] Docker Compose
- [ ] Kubernetes
- [ ] Docker stack
- [ ] Docker swarm
- [ ] Podman
- [ ] Other:
5. Extra information
Logs:
```log ```log
``` PASTE YOUR LOGS
IN THERE
Configuration file:
```yml
``` ```
Host OS:

View File

@@ -1,14 +1,17 @@
--- ---
name: Feature request name: Feature request
about: Suggest a feature to add to this project about: Suggest a feature to add to this project
title: 'Feature request: ...' title: 'Feature request: FILL THIS TEXT!'
labels: ":bulb: feature request" labels: ":bulb: feature request"
assignees: qdm12 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 **What VPN provider are you using**:
- [x] No
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 Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
- [ ] Windscribe ```
- [ ] Surfshark
- [ ] Cyberghost
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? ...*careful to remove i.e. token information with PIA port forwarding*
- [ ] Docker run
- [x] Docker Compose
- [ ] Kubernetes
- [ ] Docker stack
- [ ] Docker swarm
- [ ] Podman
- [ ] Other:
5. Extra information
Logs:
```log ```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 ```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" - name: "Bug :bug:"
color: "69cde9"
description: ""
- name: ":bug: bug"
color: "b60205" color: "b60205"
description: "" description: ""
- name: ":game_die: dependencies" - name: "Feature request :bulb:"
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"
color: "0e8a16" color: "0e8a16"
description: "" description: ""
- name: ":mega: feedback" - name: "Help wanted :pray:"
color: "03a9f4"
description: ""
- name: ":rocket: future maybe"
color: "fef2c0"
description: ""
- name: ":hatching_chick: good first issue"
color: "7057ff"
description: ""
- name: ":pray: help wanted"
color: "4caf50" color: "4caf50"
description: "" description: ""
- name: ":hand: hold" - name: "Documentation :memo:"
color: "24292f" color: "c5def5"
description: "" description: ""
- name: ":no_entry_sign: invalid" - name: "Needs more info :thinking:"
color: "e6e6e6"
description: ""
- name: ":interrobang: maybe bug"
color: "ff5722"
description: ""
- name: ":thinking: needs more info"
color: "795548" color: "795548"
description: "" description: ""
- name: ":question: question"
color: "3f51b5" # VPN providers
- name: ":cloud: Cyberghost"
color: "cfe8d4"
description: "" description: ""
- name: ":coffin: wontfix" - name: ":cloud: Mullvad"
color: "ffffff" 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: "" description: ""

View File

@@ -58,7 +58,10 @@ ENV VPNSP=pia \
PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \ PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
# Mullvad and PureVPN only # Mullvad and PureVPN only
COUNTRY= \ COUNTRY= \
# Mullvad, PureVPN, Windscribe only
CITY= \ CITY= \
# Windscribe only
HOSTNAME= \
# Mullvad only # Mullvad only
ISP= \ ISP= \
OWNED=no \ OWNED=no \

View File

@@ -1,7 +1,7 @@
# Gluetun VPN client # Gluetun VPN client
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access, *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 an HTTP proxy* 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* **ANNOUNCEMENT**: *Github Wiki reworked*
@@ -28,7 +28,7 @@ Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN and PureVPN VPN serv
## Features ## Features
- Based on Alpine 3.12 for a small Docker image of 52MB - 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 - Supports Openvpn only for now
- DNS over TLS baked in with service provider(s) of your choice - 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 - DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
@@ -96,7 +96,7 @@ docker run --rm --network=container:gluetun alpine:3.12 wget -qO- https://ipinfo
| Variable | Default | Choices | Description | | 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 | | `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 | | `PROTOCOL` | `udp` | `udp` or `tcp` | Network protocol to use |
| `OPENVPN_VERBOSITY` | `1` | `0` to `6` | Openvpn verbosity level | | `OPENVPN_VERBOSITY` | `1` | `0` to `6` | Openvpn verbosity level |
@@ -134,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` 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 | | Variable | Default | Choices | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 🏁 `USER` | | | Your username | | 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password | | 🏁 `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 | | `PORT` | | One from the [this list of ports](https://windscribe.com/getconfig/openvpn) | Custom VPN port to use |
- Surfshark - Surfshark
@@ -200,6 +202,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 | | `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 | | `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 ### DNS over TLS
None of the following values are required. None of the following values are required.

View File

@@ -38,13 +38,17 @@ import (
) )
//nolint:gochecknoglobals //nolint:gochecknoglobals
var buildInfo = models.BuildInformation{ var (
Version: "unknown", buildInfo models.BuildInformation
Commit: "unknown", version = "unknown"
BuildDate: "an unknown date", commit = "unknown"
} buildDate = "an unknown date"
)
func main() { func main() {
buildInfo.Version = version
buildInfo.Commit = commit
buildInfo.BuildDate = buildDate
ctx := context.Background() ctx := context.Background()
os.Exit(_main(ctx, os.Args)) os.Exit(_main(ctx, os.Args))
} }
@@ -411,24 +415,13 @@ func routeReadyEvents(ctx context.Context, wg *sync.WaitGroup, tunnelReadyCh, dn
logger.Info("VPN routing IP address: %s", vpnDestination) logger.Info("VPN routing IP address: %s", vpnDestination)
} }
if portForwardingEnabled { if portForwardingEnabled {
// TODO make instantaneous once v3 go out of service // vpnGateway required only for PIA v4
const waitDuration = 5 * time.Second vpnGateway, err := routing.VPNLocalGatewayIP()
timer := time.NewTimer(waitDuration) if err != nil {
select { logger.Error(err)
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)
} }
logger.Info("VPN gateway IP address: %s", vpnGateway)
startPortForward(vpnGateway)
} }
case <-dnsReadyCh: case <-dnsReadyCh:
publicIPLooper.Restart() // TODO do not restart if disabled publicIPLooper.Restart() // TODO do not restart if disabled

View File

@@ -93,7 +93,7 @@ func Update(args []string) error {
flagSet.BoolVar(&options.Mullvad, "mullvad", false, "Update Mullvad servers") flagSet.BoolVar(&options.Mullvad, "mullvad", false, "Update Mullvad servers")
flagSet.BoolVar(&options.Nordvpn, "nordvpn", false, "Update Nordvpn 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.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.Purevpn, "purevpn", false, "Update Purevpn servers")
flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers") flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers")
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers") flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")

View File

@@ -125,88 +125,3 @@ func PIAServers() []models.PIAServer {
{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: "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}}}},
} }
} }
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

@@ -26,16 +26,16 @@ func GetAllServers() (allServers models.AllServers) {
Timestamp: 1602531173, Timestamp: 1602531173,
Servers: PIAServers(), Servers: PIAServers(),
}, },
PiaOld: models.PiaOldServers{
Version: 1,
Timestamp: 1602523433,
Servers: PIAOldServers(),
},
Purevpn: models.PurevpnServers{ Purevpn: models.PurevpnServers{
Version: 1, Version: 1,
Timestamp: 1599323261, Timestamp: 1599323261,
Servers: PurevpnServers(), Servers: PurevpnServers(),
}, },
Privado: models.PrivadoServers{
Version: 2,
Timestamp: 1604963273,
Servers: PrivadoServers(),
},
Surfshark: models.SurfsharkServers{ Surfshark: models.SurfsharkServers{
Version: 1, Version: 1,
Timestamp: 1599957644, Timestamp: 1599957644,
@@ -47,8 +47,8 @@ func GetAllServers() (allServers models.AllServers) {
Servers: VyprvpnServers(), Servers: VyprvpnServers(),
}, },
Windscribe: models.WindscribeServers{ Windscribe: models.WindscribeServers{
Version: 1, Version: 2,
Timestamp: 1599323261, Timestamp: 1604019438,
Servers: WindscribeServers(), Servers: WindscribeServers(),
}, },
} }

View File

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

View File

@@ -2,9 +2,9 @@ package constants
const ( const (
// Announcement is a message announcement. // 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 is the expiration date of the announcement in format yyyy-mm-dd.
AnnouncementExpiration = "2020-11-15" AnnouncementExpiration = "2020-11-25"
) )
const ( const (

View File

@@ -7,8 +7,6 @@ import (
const ( const (
// PrivateInternetAccess is a VPN provider. // PrivateInternetAccess is a VPN provider.
PrivateInternetAccess models.VPNProvider = "private internet access" 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 is a VPN provider.
Mullvad models.VPNProvider = "mullvad" Mullvad models.VPNProvider = "mullvad"
// Windscribe is a VPN provider. // Windscribe is a VPN provider.
@@ -23,6 +21,8 @@ const (
Nordvpn models.VPNProvider = "nordvpn" Nordvpn models.VPNProvider = "nordvpn"
// PureVPN is a VPN provider. // PureVPN is a VPN provider.
Purevpn models.VPNProvider = "purevpn" Purevpn models.VPNProvider = "purevpn"
// Privado is a VPN provider.
Privado models.VPNProvider = "privado"
) )
const ( const (

View File

@@ -21,79 +21,319 @@ func WindscribeRegionChoices() (choices []string) {
return choices 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 //nolint:lll
func WindscribeServers() []models.WindscribeServer { func WindscribeServers() []models.WindscribeServer {
return []models.WindscribeServer{ return []models.WindscribeServer{
{Region: "Albania", IPs: []net.IP{{31, 171, 152, 179}}}, {Region: "Albania", City: "Tirana", Hostname: "al-002.whiskergalaxy.com", IP: 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: "Argentina", City: "Buenos Aires", Hostname: "ar-001.whiskergalaxy.com", IP: net.IP{200, 85, 152, 110}},
{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: "Argentina", City: "Buenos Aires", Hostname: "ar-003.whiskergalaxy.com", IP: net.IP{167, 250, 6, 121}},
{Region: "Austria", IPs: []net.IP{{89, 187, 168, 66}, {217, 64, 127, 11}}}, {Region: "Argentina", City: "Buenos Aires", Hostname: "ar-004.whiskergalaxy.com", IP: net.IP{190, 105, 236, 50}},
{Region: "Azerbaijan", IPs: []net.IP{{85, 132, 61, 123}}}, {Region: "Argentina", City: "Buenos Aires", Hostname: "ar-005.whiskergalaxy.com", IP: net.IP{190, 105, 236, 32}},
{Region: "Belgium", IPs: []net.IP{{185, 232, 21, 131}, {194, 187, 251, 147}}}, {Region: "Argentina", City: "Buenos Aires", Hostname: "ar-006.whiskergalaxy.com", IP: net.IP{190, 105, 236, 19}},
{Region: "Bosnia", IPs: []net.IP{{185, 99, 3, 24}}}, {Region: "Australia", City: "Adelaide ", Hostname: "au-011.whiskergalaxy.com", IP: net.IP{103, 108, 92, 83}},
{Region: "Brazil", IPs: []net.IP{{177, 54, 144, 68}, {177, 67, 80, 59}, {189, 1, 172, 12}}}, {Region: "Australia", City: "Adelaide", Hostname: "au-008.whiskergalaxy.com", IP: net.IP{116, 90, 72, 243}},
{Region: "Bulgaria", IPs: []net.IP{{185, 94, 192, 35}}}, {Region: "Australia", City: "Brisbane", Hostname: "au-007.whiskergalaxy.com", IP: net.IP{103, 62, 50, 208}},
{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: "Australia", City: "Brisbane", Hostname: "au-014.whiskergalaxy.com", IP: net.IP{43, 245, 160, 35}},
{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: "Australia", City: "Canberra", Hostname: "au-010.whiskergalaxy.com", IP: net.IP{116, 206, 229, 131}},
{Region: "Colombia", IPs: []net.IP{{138, 121, 203, 203}, {138, 186, 141, 155}}}, {Region: "Australia", City: "Melbourne ", Hostname: "au-005.whiskergalaxy.com", IP: net.IP{45, 121, 209, 160}},
{Region: "Croatia", IPs: []net.IP{{85, 10, 56, 252}}}, {Region: "Australia", City: "Melbourne ", Hostname: "au-013.whiskergalaxy.com", IP: net.IP{116, 206, 228, 67}},
{Region: "Cyprus", IPs: []net.IP{{157, 97, 132, 43}}}, {Region: "Australia", City: "Perth", Hostname: "au-004.whiskergalaxy.com", IP: net.IP{45, 121, 208, 160}},
{Region: "Czech republic", IPs: []net.IP{{185, 156, 174, 11}, {185, 246, 210, 2}}}, {Region: "Australia", City: "Perth", Hostname: "au-012.whiskergalaxy.com", IP: net.IP{103, 77, 234, 211}},
{Region: "Denmark", IPs: []net.IP{{134, 90, 149, 147}, {185, 206, 224, 195}}}, {Region: "Australia", City: "Sydney", Hostname: "au-009.whiskergalaxy.com", IP: net.IP{103, 77, 233, 67}},
{Region: "Estonia", IPs: []net.IP{{46, 22, 211, 251}, {196, 196, 216, 131}}}, {Region: "Australia", City: "Sydney", Hostname: "au-015.whiskergalaxy.com", IP: net.IP{103, 1, 213, 211}},
{Region: "Fake antarctica", IPs: []net.IP{{23, 154, 160, 212}, {23, 154, 160, 222}}}, {Region: "Austria", City: "Vienna", Hostname: "at-001.whiskergalaxy.com", IP: net.IP{217, 64, 127, 11}},
{Region: "Finland", IPs: []net.IP{{185, 112, 82, 227}, {194, 34, 133, 82}}}, {Region: "Austria", City: "Vienna", Hostname: "at-002.whiskergalaxy.com", IP: net.IP{89, 187, 168, 66}},
{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: "Belgium", City: "Brussels", Hostname: "be-001.whiskergalaxy.com", IP: net.IP{194, 187, 251, 147}},
{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: "Belgium", City: "Brussels", Hostname: "be-002.whiskergalaxy.com", IP: net.IP{185, 232, 21, 131}},
{Region: "Greece", IPs: []net.IP{{78, 108, 38, 155}, {185, 226, 64, 111}, {188, 123, 126, 146}}}, {Region: "Brazil", City: "Sao Paulo", Hostname: "br-004.whiskergalaxy.com", IP: net.IP{177, 67, 80, 59}},
{Region: "Guinea-Bissau", IPs: []net.IP{{149, 56, 10, 82}}}, {Region: "Brazil", City: "Sao Paulo", Hostname: "br-005.whiskergalaxy.com", IP: net.IP{177, 54, 157, 178}},
{Region: "Hong kong", IPs: []net.IP{{84, 17, 57, 114}, {103, 10, 197, 99}}}, {Region: "Brazil", City: "Sao Paulo", Hostname: "br-006.whiskergalaxy.com", IP: net.IP{177, 54, 148, 247}},
{Region: "Hungary", IPs: []net.IP{{185, 104, 187, 43}}}, {Region: "Bulgaria", City: "Sofia", Hostname: "bg-001.whiskergalaxy.com", IP: net.IP{185, 94, 192, 35}},
{Region: "Iceland", IPs: []net.IP{{82, 221, 139, 38}, {185, 165, 170, 2}}}, {Region: "Canada East", City: "Halifax", Hostname: "ca-029.whiskergalaxy.com", IP: net.IP{199, 204, 208, 158}},
{Region: "India", IPs: []net.IP{{103, 205, 140, 227}, {169, 38, 68, 188}, {169, 38, 72, 12}, {169, 38, 72, 14}}}, {Region: "Canada East", City: "Montreal", Hostname: "ca-004.whiskergalaxy.com", IP: net.IP{66, 70, 148, 80}},
{Region: "Indonesia", IPs: []net.IP{{45, 127, 134, 91}}}, {Region: "Canada East", City: "Montreal", Hostname: "ca-027.whiskergalaxy.com", IP: net.IP{144, 168, 163, 160}},
{Region: "Ireland", IPs: []net.IP{{185, 24, 232, 146}, {185, 104, 219, 2}}}, {Region: "Canada East", City: "Montreal", Hostname: "ca-028.whiskergalaxy.com", IP: net.IP{144, 168, 163, 193}},
{Region: "Israel", IPs: []net.IP{{160, 116, 0, 27}, {185, 191, 205, 139}}}, {Region: "Canada East", City: "Montreal", Hostname: "ca-032.whiskergalaxy.com", IP: net.IP{104, 227, 235, 129}},
{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: "Canada East", City: "Montreal", Hostname: "ca-033.whiskergalaxy.com", IP: net.IP{198, 8, 85, 195}},
{Region: "Japan", IPs: []net.IP{{89, 187, 161, 114}, {193, 148, 16, 243}}}, {Region: "Canada East", City: "Montreal", Hostname: "ca-034.whiskergalaxy.com", IP: net.IP{198, 8, 85, 210}},
{Region: "Latvia", IPs: []net.IP{{85, 254, 72, 23}, {89, 111, 33, 220}}}, {Region: "Canada East", City: "Toronto", Hostname: "ca-002.whiskergalaxy.com", IP: net.IP{104, 254, 92, 11}},
{Region: "Lithuania", IPs: []net.IP{{85, 206, 163, 225}}}, {Region: "Canada East", City: "Toronto", Hostname: "ca-009.whiskergalaxy.com", IP: net.IP{104, 254, 92, 91}},
{Region: "Macedonia", IPs: []net.IP{{185, 225, 28, 51}}}, {Region: "Canada East", City: "Toronto", Hostname: "ca-017.whiskergalaxy.com", IP: net.IP{184, 75, 212, 91}},
{Region: "Madagascar", IPs: []net.IP{{104, 20, 26, 217}, {104, 20, 27, 217}, {172, 67, 17, 175}}}, {Region: "Canada East", City: "Toronto", Hostname: "ca-025.whiskergalaxy.com", IP: net.IP{192, 190, 19, 65}},
{Region: "Malaysia", IPs: []net.IP{{103, 106, 250, 31}, {103, 212, 69, 232}}}, {Region: "Canada East", City: "Toronto", Hostname: "ca-026.whiskergalaxy.com", IP: net.IP{192, 190, 19, 97}},
{Region: "Mexico", IPs: []net.IP{{143, 255, 57, 67}, {190, 103, 179, 211}, {190, 103, 179, 217}, {201, 131, 125, 107}}}, {Region: "Canada East", City: "Toronto", Hostname: "ca-030.whiskergalaxy.com", IP: net.IP{23, 154, 160, 177}},
{Region: "Moldova", IPs: []net.IP{{178, 175, 144, 123}}}, {Region: "Canada West", City: "Vancouver", Hostname: "ca-west-005.whiskergalaxy.com", IP: net.IP{162, 221, 207, 95}},
{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: "Canada West", City: "Vancouver", Hostname: "ca-west-011.whiskergalaxy.com", IP: net.IP{104, 218, 61, 1}},
{Region: "New zealand", IPs: []net.IP{{103, 62, 49, 113}}}, {Region: "Canada West", City: "Vancouver", Hostname: "ca-west-012.whiskergalaxy.com", IP: net.IP{104, 218, 61, 33}},
{Region: "Norway", IPs: []net.IP{{37, 120, 203, 67}, {185, 206, 225, 131}}}, {Region: "Canada West", City: "Vancouver", Hostname: "ca-west-016.whiskergalaxy.com", IP: net.IP{208, 78, 41, 1}},
{Region: "Panama", IPs: []net.IP{{138, 186, 142, 203}}}, {Region: "Canada West", City: "Vancouver", Hostname: "ca-west-017.whiskergalaxy.com", IP: net.IP{208, 78, 41, 131}},
{Region: "Peru", IPs: []net.IP{{190, 120, 229, 139}}}, {Region: "Canada West", City: "Vancouver", Hostname: "ca-west-019.whiskergalaxy.com", IP: net.IP{208, 78, 41, 163}},
{Region: "Philippines", IPs: []net.IP{{103, 103, 0, 118}, {141, 98, 215, 211}}}, {Region: "Colombia", City: "Bogota", Hostname: "co-001.whiskergalaxy.com", IP: net.IP{138, 121, 203, 203}},
{Region: "Poland", IPs: []net.IP{{5, 133, 11, 116}, {84, 17, 55, 98}, {185, 244, 214, 35}}}, {Region: "Colombia", City: "Bogota", Hostname: "co-002.whiskergalaxy.com", IP: net.IP{138, 186, 141, 155}},
{Region: "Portugal", IPs: []net.IP{{94, 46, 13, 215}, {185, 15, 21, 66}}}, {Region: "Croatia", City: "Zagreb", Hostname: "hr-002.whiskergalaxy.com", IP: net.IP{85, 10, 56, 129}},
{Region: "Romania", IPs: []net.IP{{89, 46, 103, 147}, {91, 207, 102, 147}}}, {Region: "Cyprus", City: "Nicosia", Hostname: "cy-001.whiskergalaxy.com", IP: net.IP{157, 97, 132, 43}},
{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: "Czech Republic", City: "Prague ", Hostname: "cz-002.whiskergalaxy.com", IP: net.IP{185, 246, 210, 2}},
{Region: "Serbia", IPs: []net.IP{{141, 98, 103, 19}}}, {Region: "Czech Republic", City: "Prague", Hostname: "cz-001.whiskergalaxy.com", IP: net.IP{185, 156, 174, 11}},
{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: "Denmark", City: "Copenhagen", Hostname: "dk-001.whiskergalaxy.com", IP: net.IP{185, 206, 224, 195}},
{Region: "Slovakia", IPs: []net.IP{{185, 245, 85, 3}}}, {Region: "Denmark", City: "Copenhagen", Hostname: "dk-002.whiskergalaxy.com", IP: net.IP{134, 90, 149, 147}},
{Region: "South Africa", IPs: []net.IP{{129, 232, 167, 211}, {165, 73, 248, 91}, {197, 242, 157, 235}}}, {Region: "Estonia", City: "Tallinn", Hostname: "ee-001.whiskergalaxy.com", IP: net.IP{46, 22, 211, 251}},
{Region: "South Korea", IPs: []net.IP{{27, 255, 92, 52}, {103, 212, 223, 3}, {218, 232, 76, 179}}}, {Region: "Estonia", City: "Tallinn", Hostname: "ee-002.whiskergalaxy.com", IP: net.IP{196, 196, 216, 131}},
{Region: "Spain", IPs: []net.IP{{37, 120, 142, 227}, {89, 238, 178, 43}, {185, 253, 99, 131}, {217, 138, 218, 99}}}, {Region: "Fake Antarctica", City: "Troll", Hostname: "aq-001.whiskergalaxy.com", IP: net.IP{23, 154, 160, 212}},
{Region: "Sweden", IPs: []net.IP{{31, 13, 191, 67}, {79, 142, 76, 198}, {195, 181, 166, 129}}}, {Region: "Fake Antarctica", City: "Troll", Hostname: "aq-002.whiskergalaxy.com", IP: net.IP{23, 154, 160, 222}},
{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: "Finland", City: "Helsinki", Hostname: "fi-002.whiskergalaxy.com", IP: net.IP{185, 112, 82, 227}},
{Region: "Taiwan", IPs: []net.IP{{103, 4, 29, 77}, {185, 189, 160, 12}, {185, 189, 160, 27}, {185, 189, 160, 32}}}, {Region: "Finland", City: "Helsinki", Hostname: "fi-003.whiskergalaxy.com", IP: net.IP{194, 34, 133, 82}},
{Region: "Thailand", IPs: []net.IP{{27, 254, 130, 221}, {202, 129, 16, 147}, {202, 129, 16, 155}}}, {Region: "Finland", City: "Helsinki", Hostname: "fi-004.whiskergalaxy.com", IP: net.IP{196, 244, 192, 51}},
{Region: "Tunisia", IPs: []net.IP{{41, 231, 5, 23}}}, {Region: "France", City: "Paris", Hostname: "fr-004.whiskergalaxy.com", IP: net.IP{185, 156, 173, 187}},
{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: "France", City: "Paris", Hostname: "fr-005.whiskergalaxy.com", IP: net.IP{82, 102, 18, 35}},
{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: "France", City: "Paris", Hostname: "fr-008.whiskergalaxy.com", IP: net.IP{84, 17, 42, 34}},
{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: "France", City: "Paris", Hostname: "fr-009.whiskergalaxy.com", IP: net.IP{84, 17, 42, 2}},
{Region: "Ukraine", IPs: []net.IP{{45, 141, 156, 11}, {45, 141, 156, 50}}}, {Region: "France", City: "Paris", Hostname: "fr-011.whiskergalaxy.com", IP: net.IP{45, 89, 174, 35}},
{Region: "United Arab Emirates", IPs: []net.IP{{45, 9, 249, 43}}}, {Region: "Germany", City: "Frankfurt", Hostname: "de-003.whiskergalaxy.com", IP: net.IP{89, 249, 65, 19}},
{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: "Germany", City: "Frankfurt", Hostname: "de-006.whiskergalaxy.com", IP: net.IP{185, 130, 184, 195}},
{Region: "Vietnam", IPs: []net.IP{{103, 9, 76, 197}, {103, 9, 79, 186}, {103, 9, 79, 219}}}, {Region: "Germany", City: "Frankfurt", Hostname: "de-009.whiskergalaxy.com", IP: net.IP{195, 181, 170, 66}},
{Region: "Windflix CA", IPs: []net.IP{{104, 218, 60, 111}, {104, 254, 92, 99}}}, {Region: "Germany", City: "Frankfurt", Hostname: "de-010.whiskergalaxy.com", IP: net.IP{195, 181, 175, 98}},
{Region: "Windflix JP", IPs: []net.IP{{5, 181, 235, 67}}}, {Region: "Germany", City: "Frankfurt", Hostname: "de-011.whiskergalaxy.com", IP: net.IP{217, 138, 194, 115}},
{Region: "Windflix UK", IPs: []net.IP{{45, 9, 248, 3}, {81, 92, 200, 85}, {89, 47, 62, 83}}}, {Region: "Germany", City: "Frankfurt", Hostname: "de-012.whiskergalaxy.com", IP: net.IP{45, 87, 212, 51}},
{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: "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

@@ -94,6 +94,10 @@ func (c *configurator) enable(ctx context.Context) (err error) {
return fmt.Errorf("cannot enable firewall: %w", err) 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 { for _, subnet := range c.outboundSubnets {
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subnet, remove); err != nil { if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subnet, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)

View File

@@ -6,8 +6,10 @@ type OpenVPNConnection struct {
IP net.IP IP net.IP
Port uint16 Port uint16
Protocol NetworkProtocol Protocol NetworkProtocol
Hostname string // Privado for tls verification
} }
func (o *OpenVPNConnection) Equal(other OpenVPNConnection) bool { 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 // Cyberghost
Group string `json:"group"` Group string `json:"group"`
// Mullvad, PureVPN Countries []string `json:"countries"` // Mullvad, PureVPN
Countries []string `json:"countries"` Cities []string `json:"cities"` // Mullvad, PureVPN, Windscribe
Cities []string `json:"cities"` Hostnames []string `json:"hostnames"` // Windscribe, Privado
// Mullvad // Mullvad
ISPs []string `json:"isps"` ISPs []string `json:"isps"`
@@ -130,6 +130,11 @@ func (p *ProviderSettings) String() string {
"Countries: "+commaJoin(p.ServerSelection.Countries), "Countries: "+commaJoin(p.ServerSelection.Countries),
"Cities: "+commaJoin(p.ServerSelection.Cities), "Cities: "+commaJoin(p.ServerSelection.Cities),
) )
case "privado":
settingsList = append(settingsList,
"Cities: "+commaJoin(p.ServerSelection.Cities),
"Server numbers: "+commaJoin(numbers),
)
default: default:
settingsList = append(settingsList, settingsList = append(settingsList,
"<Missing String method, please implement me!>", "<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()) 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 { type MullvadServer struct {
IPs []net.IP `json:"ips"` IPs []net.IP `json:"ips"`
IPsV6 []net.IP `json:"ipsv6"` IPsV6 []net.IP `json:"ipsv6"`
@@ -52,12 +43,15 @@ func (s *MullvadServer) String() string {
} }
type WindscribeServer struct { type WindscribeServer struct {
Region string `json:"region"` Region string `json:"region"`
IPs []net.IP `json:"ips"` City string `json:"city"`
Hostname string `json:"hostname"`
IP net.IP `json:"ip"`
} }
func (s *WindscribeServer) String() string { 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 { type SurfsharkServer struct {
@@ -113,6 +107,16 @@ func (s *PurevpnServer) String() string {
s.Region, s.Country, s.City, goStringifyIPs(s.IPs)) 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 { func goStringifyIP(ip net.IP) string {
s := fmt.Sprintf("%#v", ip) s := fmt.Sprintf("%#v", ip)
s = strings.TrimSuffix(strings.TrimPrefix(s, "net.IP{"), "}") s = strings.TrimSuffix(strings.TrimPrefix(s, "net.IP{"), "}")

View File

@@ -7,31 +7,6 @@ import (
"github.com/stretchr/testify/assert" "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) { func Test_MullvadServer_String(t *testing.T) {
t.Parallel() t.Parallel()
testCases := map[string]struct { testCases := map[string]struct {

View File

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

View File

@@ -62,7 +62,6 @@ type Reader interface {
GetPortForwardingStatusFilepath() (filepath models.Filepath, err error) GetPortForwardingStatusFilepath() (filepath models.Filepath, err error)
GetPIAEncryptionPreset() (preset string, err error) GetPIAEncryptionPreset() (preset string, err error)
GetPIARegions() (regions []string, err error) GetPIARegions() (regions []string, err error)
GetPIAOldRegions() (regions []string, err error)
// Mullvad getters // Mullvad getters
GetMullvadCountries() (countries []string, err error) GetMullvadCountries() (countries []string, err error)
@@ -73,6 +72,8 @@ type Reader interface {
// Windscribe getters // Windscribe getters
GetWindscribeRegions() (countries []string, err error) GetWindscribeRegions() (countries []string, err error)
GetWindscribeCities() (cities []string, err error)
GetWindscribeHostnames() (hostnames []string, err error)
GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error) GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error)
// Surfshark getters // Surfshark getters
@@ -90,6 +91,9 @@ type Reader interface {
GetNordvpnRegions() (regions []string, err error) GetNordvpnRegions() (regions []string, err error)
GetNordvpnNumbers() (numbers []uint16, err error) GetNordvpnNumbers() (numbers []uint16, err error)
// Privado getters
GetPrivadoHostnames() (hostnames []string, err error)
// PureVPN getters // PureVPN getters
GetPurevpnRegions() (regions []string, err error) GetPurevpnRegions() (regions []string, err error)
GetPurevpnCountries() (countries []string, err error) GetPurevpnCountries() (countries []string, err error)
@@ -147,9 +151,9 @@ func (r *reader) GetVPNSP() (vpnServiceProvider models.VPNProvider, err error) {
s, err := r.envParams.GetValueIfInside( s, err := r.envParams.GetValueIfInside(
"VPNSP", "VPNSP",
[]string{ []string{
"pia", "private internet access", "private internet access old", "pia", "private internet access",
"mullvad", "windscribe", "surfshark", "cyberghost", "mullvad", "windscribe", "surfshark", "cyberghost",
"vyprvpn", "nordvpn", "purevpn", "vyprvpn", "nordvpn", "purevpn", "privado",
}, libparams.Default("private internet access")) }, libparams.Default("private internet access"))
if s == "pia" { if s == "pia" {
s = "private internet access" s = "private internet access"

View File

@@ -63,9 +63,3 @@ func (r *reader) GetPIAEncryptionPreset() (preset string, err error) {
func (r *reader) GetPIARegions() (regions []string, err error) { func (r *reader) GetPIARegions() (regions []string, err error) {
return r.envParams.GetCSVInPossibilities("REGION", constants.PIAGeoChoices()) 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

@@ -14,7 +14,19 @@ func (r *reader) GetWindscribeRegions() (regions []string, err error) {
return r.envParams.GetCSVInPossibilities("REGION", constants.WindscribeRegionChoices()) 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. // environment variable PORT.
//nolint:gomnd //nolint:gomnd
func (r *reader) GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error) { func (r *reader) GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error) {

View File

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

View File

@@ -68,7 +68,7 @@ func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, verbosity,
cipher = aes256cbc cipher = aes256cbc
} }
if len(auth) == 0 { if len(auth) == 0 {
auth = "SHA256" auth = sha256
} }
lines = []string{ lines = []string{
"client", "client",

View File

@@ -1,156 +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"
)
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)
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
pfLogger.Error(err)
return
}
response, err := client.Do(request)
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

@@ -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

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

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 { for _, server := range w.servers {
switch { switch {
case case
filterByPossibilities(server.Region, regions): filterByPossibilities(server.Region, regions),
filterByPossibilities(server.City, cities),
filterByPossibilities(server.Hostname, hostnames):
default: default:
servers = append(servers, server) 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 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 { if len(servers) == 0 {
return connection, fmt.Errorf("no server found for region %s", commaJoin(selection.Regions)) 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 _, server := range servers {
for _, IP := range server.IPs { connections = append(connections, models.OpenVPNConnection{IP: server.IP, Port: port, Protocol: selection.Protocol})
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol})
}
} }
return pickRandomConnection(connections, w.randSource), nil return pickRandomConnection(connections, w.randSource), nil

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

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

View File

@@ -2,7 +2,6 @@ package server
import ( import (
"context" "context"
"fmt"
"net/http" "net/http"
"sync" "sync"
"time" "time"
@@ -19,30 +18,24 @@ type Server interface {
} }
type server struct { type server struct {
address string address string
logging bool logger logging.Logger
logger logging.Logger handler http.Handler
buildInfo models.BuildInformation
openvpnLooper openvpn.Looper
unboundLooper dns.Looper
updaterLooper updater.Looper
} }
func New(address string, logging bool, logger logging.Logger, buildInfo models.BuildInformation, func New(address string, logging bool, logger logging.Logger, buildInfo models.BuildInformation,
openvpnLooper openvpn.Looper, unboundLooper dns.Looper, updaterLooper updater.Looper) Server { 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{ return &server{
address: address, address: address,
logging: logging, logger: serverLogger,
logger: logger.WithPrefix("http server: "), handler: handler,
buildInfo: buildInfo,
openvpnLooper: openvpnLooper,
unboundLooper: unboundLooper,
updaterLooper: updaterLooper,
} }
} }
func (s *server) Run(ctx context.Context, wg *sync.WaitGroup) { 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() { go func() {
defer wg.Done() defer wg.Done()
<-ctx.Done() <-ctx.Done()
@@ -61,42 +54,3 @@ func (s *server) Run(ctx context.Context, wg *sync.WaitGroup) {
s.logger.Error(err) s.logger.Error(err)
} }
} }
func (s *server) makeHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
s.logger.Info("HTTP %s %s", r.Method, r.RequestURI)
switch r.Method {
case http.MethodGet:
switch r.RequestURI {
case "/version":
s.handleGetVersion(w)
w.WriteHeader(http.StatusOK)
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 "/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

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

View File

@@ -54,8 +54,6 @@ func GetOpenVPNSettings(paramsReader params.Reader, vpnProvider models.VPNProvid
switch vpnProvider { switch vpnProvider {
case constants.PrivateInternetAccess: case constants.PrivateInternetAccess:
settings.Provider, err = GetPIASettings(paramsReader) settings.Provider, err = GetPIASettings(paramsReader)
case constants.PrivateInternetAccessOld:
settings.Provider, err = GetPIAOldSettings(paramsReader)
case constants.Mullvad: case constants.Mullvad:
settings.Provider, err = GetMullvadSettings(paramsReader) settings.Provider, err = GetMullvadSettings(paramsReader)
case constants.Windscribe: case constants.Windscribe:
@@ -70,6 +68,8 @@ func GetOpenVPNSettings(paramsReader params.Reader, vpnProvider models.VPNProvid
settings.Provider, err = GetNordvpnSettings(paramsReader) settings.Provider, err = GetNordvpnSettings(paramsReader)
case constants.Purevpn: case constants.Purevpn:
settings.Provider, err = GetPurevpnSettings(paramsReader) settings.Provider, err = GetPurevpnSettings(paramsReader)
case constants.Privado:
settings.Provider, err = GetPrivadoSettings(paramsReader)
default: default:
err = fmt.Errorf("VPN service provider %q is not valid", vpnProvider) 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) data, err := json.Marshal(in)
require.NoError(t, err) require.NoError(t, err)
//nolint:lll //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 var out OpenVPN
err = json.Unmarshal(data, &out) err = json.Unmarshal(data, &out)
require.NoError(t, err) require.NoError(t, err)

View File

@@ -10,17 +10,7 @@ import (
// GetPIASettings obtains PIA settings from environment variables using the params package. // GetPIASettings obtains PIA settings from environment variables using the params package.
func GetPIASettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) { func GetPIASettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
return getPIASettings(paramsReader, constants.PrivateInternetAccess) settings.Name = 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.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol() settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
if err != nil { if err != nil {
return settings, err return settings, err
@@ -118,6 +108,14 @@ func GetWindscribeSettings(paramsReader params.Reader) (settings models.Provider
if err != nil { if err != nil {
return settings, err 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) settings.ServerSelection.CustomPort, err = paramsReader.GetWindscribePort(settings.ServerSelection.Protocol)
if err != nil { if err != nil {
return settings, err return settings, err
@@ -234,3 +232,21 @@ func GetPurevpnSettings(paramsReader params.Reader) (settings models.ProviderSet
} }
return settings, nil 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

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

View File

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

View File

@@ -101,6 +101,20 @@ func getCyberghostGroups() map[string]string {
} }
} }
func mergeCountryCodes(base, extend map[string]string) (merged map[string]string) {
merged = make(map[string]string, len(base))
for countryCode, region := range base {
merged[countryCode] = region
}
for countryCode := range base {
delete(extend, countryCode)
}
for countryCode, region := range extend {
merged[countryCode] = region
}
return merged
}
func getCyberghostSubdomainToRegion() map[string]string { //nolint:dupl func getCyberghostSubdomainToRegion() map[string]string { //nolint:dupl
return map[string]string{ return map[string]string{
"af": "Afghanistan", "af": "Afghanistan",

View File

@@ -5,7 +5,7 @@ type Options struct {
Mullvad bool Mullvad bool
Nordvpn bool Nordvpn bool
PIA bool PIA bool
PIAold bool Privado bool
Purevpn bool Purevpn bool
Surfshark bool Surfshark bool
Vyprvpn bool Vyprvpn bool
@@ -21,7 +21,6 @@ func NewOptions(dnsAddress string) Options {
Mullvad: true, Mullvad: true,
Nordvpn: true, Nordvpn: true,
PIA: true, PIA: true,
PIAold: true,
Purevpn: true, Purevpn: true,
Surfshark: true, Surfshark: true,
Vyprvpn: true, Vyprvpn: true,

View File

@@ -1,101 +0,0 @@
package updater
import (
"context"
"fmt"
"net"
"sort"
"strings"
"sync"
"github.com/qdm12/gluetun/internal/models"
)
func (u *updater) updatePIAOld(ctx context.Context) (err error) {
const zipURL = "https://www.privateinternetaccess.com/openvpn/openvpn.zip"
contents, err := fetchAndExtractFiles(ctx, u.client, zipURL)
if err != nil {
return err
}
const maxGoroutines = 10
guard := make(chan struct{}, maxGoroutines)
errors := make(chan error)
serversCh := make(chan models.PIAOldServer)
servers := make([]models.PIAOldServer, 0, len(contents))
ctx, cancel := context.WithCancel(ctx)
wg := &sync.WaitGroup{}
defer func() {
cancel()
wg.Wait()
defer close(guard)
defer close(errors)
defer close(serversCh)
}()
for fileName, content := range contents {
remoteLines := extractRemoteLinesFromOpenvpn(content)
if len(remoteLines) == 0 {
return fmt.Errorf("cannot find any remote lines in %s", fileName)
}
hosts := extractHostnamesFromRemoteLines(remoteLines)
if len(hosts) == 0 {
return fmt.Errorf("cannot find any hosts in %s", fileName)
}
region := strings.TrimSuffix(fileName, ".ovpn")
wg.Add(1)
go resolvePIAv3Hostname(ctx, wg, region, hosts, u.lookupIP, errors, serversCh, guard)
}
for range contents {
select {
case err := <-errors:
return err
case server := <-serversCh:
servers = append(servers, server)
}
}
sort.Slice(servers, func(i, j int) bool {
return servers[i].Region < servers[j].Region
})
if u.options.Stdout {
u.println(stringifyPIAOldServers(servers))
}
u.servers.PiaOld.Timestamp = u.timeNow().Unix()
u.servers.PiaOld.Servers = servers
return nil
}
func resolvePIAv3Hostname(ctx context.Context, wg *sync.WaitGroup,
region string, hosts []string, lookupIP lookupIPFunc,
errors chan<- error, serversCh chan<- models.PIAOldServer, guard chan struct{}) {
guard <- struct{}{}
defer func() {
<-guard
wg.Done()
}()
var IPs []net.IP //nolint:prealloc
// usually one single host in this case
// so no need to run in goroutines the for loop below
for _, host := range hosts {
const repetition = 5
newIPs, err := resolveRepeat(ctx, lookupIP, host, repetition)
if err != nil {
errors <- err
return
}
IPs = append(IPs, newIPs...)
}
serversCh <- models.PIAOldServer{
Region: region,
IPs: uniqueSortedIPs(IPs),
}
}
func stringifyPIAOldServers(servers []models.PIAOldServer) (s string) {
s = "func PIAOldServers() []models.PIAOldServer {\n"
s += " return []models.PIAOldServer{\n"
for _, server := range servers {
s += " " + server.String() + ",\n"
}
s += " }\n"
s += "}"
return s
}

View File

@@ -0,0 +1,94 @@
package updater
import (
"context"
"fmt"
"net"
"sort"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/network"
)
func (u *updater) updatePrivado(ctx context.Context) (err error) {
servers, warnings, err := findPrivadoServersFromZip(ctx, u.client, u.lookupIP)
if u.options.CLI {
for _, warning := range warnings {
u.logger.Warn("Privado: %s", warning)
}
}
if err != nil {
return fmt.Errorf("cannot update Privado servers: %w", err)
}
if u.options.Stdout {
u.println(stringifyPrivadoServers(servers))
}
u.servers.Privado.Timestamp = u.timeNow().Unix()
u.servers.Privado.Servers = servers
return nil
}
func findPrivadoServersFromZip(ctx context.Context, client network.Client, lookupIP lookupIPFunc) (
servers []models.PrivadoServer, warnings []string, err error) {
const zipURL = "https://privado.io/apps/ovpn_configs.zip"
contents, err := fetchAndExtractFiles(ctx, client, zipURL)
if err != nil {
return nil, nil, err
}
for fileName, content := range contents {
if err := ctx.Err(); err != nil {
return nil, warnings, err
}
remoteLines := extractRemoteLinesFromOpenvpn(content)
if len(remoteLines) == 0 {
return nil, warnings, fmt.Errorf("cannot find any remote lines in %s", fileName)
}
hostnames := extractHostnamesFromRemoteLines(remoteLines)
if len(hostnames) == 0 {
return nil, warnings, fmt.Errorf("cannot find any hosts in %s", fileName)
} else if len(hostnames) > 1 {
warning := fmt.Sprintf("more than one host in %q, only taking first one %q into account", fileName, hostnames[0])
warnings = append(warnings, warning)
}
hostname := hostnames[0]
if net.ParseIP(hostname) != nil {
warning := fmt.Sprintf("ignoring IP address host %q in %s", hostname, fileName)
warnings = append(warnings, warning)
continue
}
const repetition = 1
IPs, err := resolveRepeat(ctx, lookupIP, hostname, repetition)
switch {
case err != nil:
return nil, warnings, err
case len(IPs) == 0:
warning := fmt.Sprintf("no IP address found for host %q", hostname)
warnings = append(warnings, warning)
continue
case len(IPs) > 1:
warning := fmt.Sprintf("more than one IP address found for host %q", hostname)
warnings = append(warnings, warning)
}
server := models.PrivadoServer{
Hostname: hostname,
IP: IPs[0],
}
servers = append(servers, server)
}
sort.Slice(servers, func(i, j int) bool {
return servers[i].Hostname < servers[j].Hostname
})
return servers, warnings, nil
}
func stringifyPrivadoServers(servers []models.PrivadoServer) (s string) {
s = "func PrivadoServers() []models.PrivadoServer {\n"
s += " return []models.PrivadoServer{\n"
for _, server := range servers {
s += " " + server.String() + ",\n"
}
s += " }\n"
s += "}"
return s
}

View File

@@ -90,14 +90,14 @@ func (u *updater) UpdateServers(ctx context.Context) (allServers models.AllServe
} }
} }
if u.options.PIAold { if u.options.Privado {
u.logger.Info("updating Private Internet Access old (v3) servers...") u.logger.Info("updating Privado servers...")
if err := u.updatePIAOld(ctx); err != nil { if err := u.updatePrivado(ctx); err != nil {
if ctxErr := ctx.Err(); ctxErr != nil {
return allServers, ctxErr
}
u.logger.Error(err) u.logger.Error(err)
} }
if ctx.Err() != nil {
return allServers, ctx.Err()
}
} }
if u.options.Purevpn { if u.options.Purevpn {

View File

@@ -2,14 +2,19 @@ package updater
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"net"
"net/http"
"sort" "sort"
"time"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/network"
) )
func (u *updater) updateWindscribe(ctx context.Context) (err error) { func (u *updater) updateWindscribe(ctx context.Context) (err error) {
servers, err := findWindscribeServers(ctx, u.lookupIP) servers, err := findWindscribeServers(ctx, u.client)
if err != nil { if err != nil {
return fmt.Errorf("cannot update Windscribe servers: %w", err) return fmt.Errorf("cannot update Windscribe servers: %w", err)
} }
@@ -21,46 +26,53 @@ func (u *updater) updateWindscribe(ctx context.Context) (err error) {
return nil return nil
} }
func findWindscribeServers(ctx context.Context, lookupIP lookupIPFunc) (servers []models.WindscribeServer, err error) { func findWindscribeServers(ctx context.Context, client network.Client) (servers []models.WindscribeServer, err error) {
allCountryCodes := getCountryCodes() const baseURL = "https://assets.windscribe.com/serverlist/mob-v2/1/"
windscribeCountryCodes := getWindscribeSubdomainToRegion() cacheBreaker := time.Now().Unix()
possibleCountryCodes := mergeCountryCodes(windscribeCountryCodes, allCountryCodes) url := fmt.Sprintf("%s%d", baseURL, cacheBreaker)
const domain = "windscribe.com" content, status, err := client.Get(ctx, url)
for countryCode, region := range possibleCountryCodes { if err != nil {
if err := ctx.Err(); err != nil { return nil, err
return nil, err } else if status != http.StatusOK {
return nil, fmt.Errorf(http.StatusText(status))
}
var jsonData struct {
Data []struct {
Region string `json:"name"`
Groups []struct {
City string `json:"city"`
Nodes []struct {
Hostname string `json:"hostname"`
OpenvpnIP net.IP `json:"ip2"`
} `json:"nodes"`
} `json:"groups"`
} `json:"data"`
}
if err := json.Unmarshal(content, &jsonData); err != nil {
return nil, err
}
for _, regionBlock := range jsonData.Data {
region := regionBlock.Region
for _, group := range regionBlock.Groups {
city := group.City
for _, node := range group.Nodes {
server := models.WindscribeServer{
Region: region,
City: city,
Hostname: node.Hostname,
IP: node.OpenvpnIP,
}
servers = append(servers, server)
}
} }
host := countryCode + "." + domain
const repetitions = 5
ips, err := resolveRepeat(ctx, lookupIP, host, repetitions)
if err != nil || len(ips) == 0 {
continue
}
servers = append(servers, models.WindscribeServer{
Region: region,
IPs: ips,
})
} }
sort.Slice(servers, func(i, j int) bool { sort.Slice(servers, func(i, j int) bool {
return servers[i].Region < servers[j].Region return servers[i].Region+servers[i].City+servers[i].Hostname <
servers[j].Region+servers[j].City+servers[j].Hostname
}) })
return servers, nil return servers, nil
} }
func mergeCountryCodes(base, extend map[string]string) (merged map[string]string) {
merged = make(map[string]string, len(base))
for countryCode, region := range base {
merged[countryCode] = region
}
for countryCode := range base {
delete(extend, countryCode)
}
for countryCode, region := range extend {
merged[countryCode] = region
}
return merged
}
func stringifyWindscribeServers(servers []models.WindscribeServer) (s string) { func stringifyWindscribeServers(servers []models.WindscribeServer) (s string) {
s = "func WindscribeServers() []models.WindscribeServer {\n" s = "func WindscribeServers() []models.WindscribeServer {\n"
s += " return []models.WindscribeServer{\n" s += " return []models.WindscribeServer{\n"
@@ -71,77 +83,3 @@ func stringifyWindscribeServers(servers []models.WindscribeServer) (s string) {
s += "}" s += "}"
return s return s
} }
func getWindscribeSubdomainToRegion() map[string]string {
return map[string]string{
"al": "Albania",
"ar": "Argentina",
"au": "Australia",
"at": "Austria",
"az": "Azerbaijan",
"be": "Belgium",
"ba": "Bosnia",
"br": "Brazil",
"bg": "Bulgaria",
"ca": "Canada East",
"ca-west": "Canada West",
"co": "Colombia",
"hr": "Croatia",
"cy": "Cyprus",
"cz": "Czech republic",
"dk": "Denmark",
"ee": "Estonia",
"aq": "Fake antarctica",
"fi": "Finland",
"fr": "France",
"ge": "Georgia",
"de": "Germany",
"gr": "Greece",
"hk": "Hong kong",
"hu": "Hungary",
"is": "Iceland",
"in": "India",
"id": "Indonesia",
"ie": "Ireland",
"il": "Israel",
"it": "Italy",
"jp": "Japan",
"lv": "Latvia",
"lt": "Lithuania",
"mk": "Macedonia",
"my": "Malaysia",
"mx": "Mexico",
"md": "Moldova",
"nl": "Netherlands",
"nz": "New zealand",
"no": "Norway",
"ph": "Philippines",
"pl": "Poland",
"pt": "Portugal",
"ro": "Romania",
"ru": "Russia",
"rs": "Serbia",
"sg": "Singapore",
"sk": "Slovakia",
"si": "Slovenia",
"za": "South Africa",
"kr": "South Korea",
"es": "Spain",
"se": "Sweden",
"ch": "Switzerland",
"th": "Thailand",
"tn": "Tunisia",
"tr": "Turkey",
"ua": "Ukraine",
"ae": "United Arab Emirates",
"uk": "United Kingdom",
"us-central": "US Central",
"us-east": "US East",
"us-west": "US West",
"vn": "Vietnam",
"wf-ca": "Windflix CA",
"wf-jp": "Windflix JP",
"wf-uk": "Windflix UK",
"wf-us": "Windflix US",
}
}