Compare commits

..

3 Commits

Author SHA1 Message Date
Quentin McGaw
89bd10fc33 Fix DNS_KEEP_NAMESERVER behavior 2021-01-03 16:38:46 +00:00
Quentin McGaw
1f52df9747 DNS ready signaling fixed 2021-01-02 23:55:53 +00:00
Quentin McGaw
f04fd845bb Bug fix: DNS setup failure loop behavior 2021-01-02 23:55:29 +00:00
511 changed files with 17775 additions and 128635 deletions

View File

@@ -19,7 +19,6 @@ It works on Linux, Windows and OSX.
1. Open the command palette in Visual Studio Code (CTRL+SHIFT+P). 1. Open the command palette in Visual Studio Code (CTRL+SHIFT+P).
1. Select `Remote-Containers: Open Folder in Container...` and choose the project directory. 1. Select `Remote-Containers: Open Folder in Container...` and choose the project directory.
1. For Docker running on Windows HyperV, if you want to use SSH keys, bind mount them at `/tmp/.ssh` by changing the `volumes` section in the [docker-compose.yml](docker-compose.yml).
## Customization ## Customization

View File

@@ -8,7 +8,7 @@
"vscode" "vscode"
], ],
"shutdownAction": "stopCompose", "shutdownAction": "stopCompose",
"postCreateCommand": "source ~/.windows.sh && go mod download && go mod tidy", "postCreateCommand": "go mod download",
"workspaceFolder": "/workspace", "workspaceFolder": "/workspace",
"extensions": [ "extensions": [
"golang.go", "golang.go",
@@ -31,34 +31,25 @@
"remote.extensionKind": { "remote.extensionKind": {
"ms-azuretools.vscode-docker": "workspace" "ms-azuretools.vscode-docker": "workspace"
}, },
"editor.codeActionsOnSaveTimeout": 3000,
"go.useLanguageServer": true, "go.useLanguageServer": true,
"[go]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
},
// Optional: Disable snippets, as they conflict with completion ranking.
"editor.snippetSuggestions": "none"
},
"[go.mod]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
},
},
"gopls": {
"usePlaceholders": false,
"staticcheck": true
},
"go.autocompleteUnimportedPackages": true, "go.autocompleteUnimportedPackages": true,
"go.gotoSymbol.includeImports": true, "go.gotoSymbol.includeImports": true,
"go.gotoSymbol.includeGoroot": true, "go.gotoSymbol.includeGoroot": true,
"gopls": {
"completeUnimported": true,
"deepCompletion": true,
"usePlaceholders": false
},
"go.lintTool": "golangci-lint", "go.lintTool": "golangci-lint",
"go.buildOnSave": "workspace", "go.buildOnSave": "workspace",
"go.lintOnSave": "workspace", "go.lintOnSave": "workspace",
"go.vetOnSave": "workspace", "go.vetOnSave": "workspace",
"editor.formatOnSave": true, "editor.formatOnSave": true,
"[go]": {
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
"go.toolsEnvVars": { "go.toolsEnvVars": {
"GOFLAGS": "-tags=", "GOFLAGS": "-tags=",
// "CGO_ENABLED": 1 // for the race detector // "CGO_ENABLED": 1 // for the race detector
@@ -66,9 +57,7 @@
"gopls.env": { "gopls.env": {
"GOFLAGS": "-tags=" "GOFLAGS": "-tags="
}, },
"go.testEnvVars": { "go.testEnvVars": {},
"": ""
},
"go.testFlags": [ "go.testFlags": [
"-v", "-v",
// "-race" // "-race"

View File

@@ -8,17 +8,12 @@ services:
- ../:/workspace - ../:/workspace
# Docker socket to access Docker server # Docker socket to access Docker server
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
# Docker configuration # SSH directory
- ~/.docker:/root/.docker:z - ~/.ssh:/home/vscode/.ssh
# SSH directory for Linux, OSX and WSL - ~/.ssh:/root/.ssh
- ~/.ssh:/root/.ssh:z
# For Windows without WSL, a copy will be made
# from /tmp/.ssh to ~/.ssh to fix permissions
#- ~/.ssh:/tmp/.ssh:ro
# Shell history persistence
- ~/.zsh_history:/root/.zsh_history:z
# Git config # Git config
- ~/.gitconfig:/root/.gitconfig:z - ~/.gitconfig:/home/districter/.gitconfig
- ~/.gitconfig:/root/.gitconfig
environment: environment:
- TZ= - TZ=
cap_add: cap_add:

View File

@@ -1,9 +1,11 @@
.devcontainer .devcontainer
.git .git
.github .github
.vscode
cmd
!cmd/gluetun
doc doc
docker-compose.yml docker-compose.yml
Dockerfile
LICENSE LICENSE
README.md README.md
title.svg title.svg

View File

@@ -7,12 +7,23 @@ Contributions are [released](https://help.github.com/articles/github-terms-of-se
1. [Fork](https://github.com/qdm12/gluetun/fork) and clone the repository 1. [Fork](https://github.com/qdm12/gluetun/fork) and clone the repository
1. Create a new branch `git checkout -b my-branch-name` 1. Create a new branch `git checkout -b my-branch-name`
1. Modify the code 1. Modify the code
1. Ensure the docker build succeeds `docker build .` (you might need `export DOCKER_BUILDKIT=1`) 1. Ensure the docker build succeeds `docker build .`
1. Commit your modifications 1. Commit your modifications
1. Push to your fork and [submit a pull request](https://github.com/qdm12/gluetun/compare) 1. Push to your fork and [submit a pull request](https://github.com/qdm12/gluetun/compare)
## Resources ## Resources
- [Gluetun guide on development](https://github.com/qdm12/gluetun/wiki/Development)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
## Contributors
Thanks for all the contributions, whether small or not so small!
- [@JeordyR](https://github.com/JeordyR) for testing the Mullvad version and opening a [PR with a few fixes](https://github.com/qdm12/gluetun/pull/84/files) 👍
- [@rorph](https://github.com/rorph) for a [PR to pick a random region for PIA](https://github.com/qdm12/gluetun/pull/70) and a [PR to make the container work with kubernetes](https://github.com/qdm12/gluetun/pull/69)
- [@JesterEE](https://github.com/JesterEE) for a [PR to fix silly line endings in block lists back then](https://github.com/qdm12/gluetun/pull/55) 📎
- [@elmerfdz](https://github.com/elmerfdz) for a [PR to add timezone information to have correct log timestampts](https://github.com/qdm12/gluetun/pull/51) 🕙
- [@Juggels](https://github.com/Juggels) for a [PR to write the PIA forwarded port to a file](https://github.com/qdm12/gluetun/pull/43)
- [@gdlx](https://github.com/gdlx) for a [PR to fix and improve PIA port forwarding script](https://github.com/qdm12/gluetun/pull/32)
- [@janaz](https://github.com/janaz) for keeping an eye on [updating things in the Dockerfile](https://github.com/qdm12/gluetun/pull/8)

View File

@@ -7,15 +7,9 @@ assignees: qdm12
--- ---
<!---
⚠️ Answer the following or I'll insta-close your issue
-->
**Is this urgent?**: No
**Host OS** (approximate answer is fine too): Ubuntu 18 **Host OS** (approximate answer is fine too): Ubuntu 18
**CPU arch** or **device name**: amd64 **Is this urgent?**: No
**What VPN provider are you using**: **What VPN provider are you using**:
@@ -31,7 +25,9 @@ Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
That feature doesn't work That feature doesn't work
**Share your logs... (careful to remove in example tokens)** **Share your logs...**
...*careful to remove i.e. token information with PIA port forwarding*
```log ```log
@@ -39,7 +35,3 @@ PASTE YOUR LOGS
IN THERE IN THERE
``` ```
<!---
💡 You can highlight your code with https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlight
-->

View File

@@ -7,21 +7,9 @@ assignees:
--- ---
<!---
⚠️ If this about a Docker configuration problem or another service:
Start a discussion at https://github.com/qdm12/gluetun/discussions/new
OR I WILL INSTA-CLOSE YOUR ISSUE.
-->
<!---
⚠️ Answer the following or I'll insta-close your issue
-->
**Is this urgent?**: No
**Host OS** (approximate answer is fine too): Ubuntu 18 **Host OS** (approximate answer is fine too): Ubuntu 18
**CPU arch** or **device name**: amd64 **Is this urgent?**: No
**What VPN provider are you using**: **What VPN provider are you using**:
@@ -35,7 +23,9 @@ Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
That feature doesn't work That feature doesn't work
**Share your logs... (careful to remove in example tokens)** **Share your logs...**
...*careful to remove i.e. token information with PIA port forwarding*
```log ```log
@@ -46,10 +36,6 @@ IN THERE
**What are you using to run your container?**: Docker Compose **What are you using to run your container?**: Docker Compose
<!---
💡 You can highlight your code with https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlight
-->
Please also share your configuration file: Please also share your configuration file:
```yml ```yml

View File

@@ -1,17 +0,0 @@
---
name: Support a VPN provider
about: Suggest a VPN provider to be supported
title: 'VPN provider support: NAME OF THE PROVIDER'
labels: ":bulb: New provider"
---
One of the following is required:
- Publicly accessible URL to a zip file containing the Openvpn configuration files
- Publicly accessible URL to a structured (JSON etc.) list of servers **and attach** an example Openvpn configuration file for both TCP and UDP
- Publicly accessible URL to the list of servers **and attach** an example Openvpn configuration file for both TCP and UDP
If the list of servers requires to login **or** is hidden behind an interactive configurator,
you can only use a custom Openvpn configuration file.
[The Wiki](https://github.com/qdm12/gluetun/wiki/Openvpn-file) describes how to do so.

View File

@@ -1,15 +0,0 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: docker
directory: /
schedule:
interval: "daily"
- package-ecosystem: gomod
directory: /
schedule:
interval: "daily"

23
.github/labels.yml vendored
View File

@@ -18,18 +18,6 @@
- name: ":cloud: Cyberghost" - name: ":cloud: Cyberghost"
color: "cfe8d4" color: "cfe8d4"
description: "" description: ""
- name: ":cloud: HideMyAss"
color: "cfe8d4"
description: ""
- name: ":cloud: IPVanish"
color: "cfe8d4"
description: ""
- name: ":cloud: IVPN"
color: "cfe8d4"
description: ""
- name: ":cloud: FastestVPN"
color: "cfe8d4"
description: ""
- name: ":cloud: Mullvad" - name: ":cloud: Mullvad"
color: "cfe8d4" color: "cfe8d4"
description: "" description: ""
@@ -42,23 +30,12 @@
- name: ":cloud: Privado" - name: ":cloud: Privado"
color: "cfe8d4" color: "cfe8d4"
description: "" description: ""
- name: ":cloud: PrivateVPN"
color: "cfe8d4"
description: ""
- name: ":cloud: ProtonVPN"
color: "cfe8d4"
- name: ":cloud: PureVPN" - name: ":cloud: PureVPN"
color: "cfe8d4" color: "cfe8d4"
description: "" description: ""
- name: ":cloud: Surfshark" - name: ":cloud: Surfshark"
color: "cfe8d4" color: "cfe8d4"
description: "" description: ""
- name: ":cloud: Torguard"
color: "cfe8d4"
description: ""
- name: ":cloud: VPNUnlimited"
color: "cfe8d4"
description: ""
- name: ":cloud: Vyprvpn" - name: ":cloud: Vyprvpn"
color: "cfe8d4" color: "cfe8d4"
description: "" description: ""

21
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Docker build
on:
pull_request:
branches: [master]
paths:
- .github/workflows/build.yml
- cmd/**
- internal/**
- .dockerignore
- .golangci.yml
- Dockerfile
- go.mod
- go.sum
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build image
run: docker build .

39
.github/workflows/buildx-branch.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Buildx branch
on:
push:
branches:
- "*"
- "*/*"
- "!master"
paths:
- .github/workflows/buildx-branch.yml
- cmd/**
- internal/**
- .dockerignore
- .golangci.yml
- Dockerfile
- go.mod
- go.sum
jobs:
buildx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Buildx setup
uses: crazy-max/ghaction-docker-buildx@v3
- name: Dockerhub login
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
- name: Run Buildx
run: |
docker buildx build \
--progress plain \
--platform=linux/amd64 \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg COMMIT=`git rev-parse --short HEAD` \
--build-arg VERSION=${GITHUB_REF##*/} \
-t qmcgaw/private-internet-access:${GITHUB_REF##*/} \
-t qmcgaw/gluetun:${GITHUB_REF##*/} \
--push \
.
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/private-internet-access/tQFy7AxtSUNANPe6aoVChYdsI_I=
continue-on-error: true

36
.github/workflows/buildx-latest.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Buildx latest
on:
push:
branches: [master]
paths:
- .github/workflows/buildx-latest.yml
- cmd/**
- internal/**
- .dockerignore
- .golangci.yml
- Dockerfile
- go.mod
- go.sum
jobs:
buildx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Buildx setup
uses: crazy-max/ghaction-docker-buildx@v3
- name: Dockerhub login
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
- name: Run Buildx
run: |
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6 \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg COMMIT=`git rev-parse --short HEAD` \
--build-arg VERSION=latest \
-t qmcgaw/private-internet-access:latest \
-t qmcgaw/gluetun:latest \
--push \
.
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/private-internet-access/tQFy7AxtSUNANPe6aoVChYdsI_I=
continue-on-error: true

36
.github/workflows/buildx-release.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Buildx release
on:
release:
types: [published]
paths:
- .github/workflows/buildx-release.yml
- cmd/**
- internal/**
- .dockerignore
- .golangci.yml
- Dockerfile
- go.mod
- go.sum
jobs:
buildx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Buildx setup
uses: crazy-max/ghaction-docker-buildx@v3
- name: Dockerhub login
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
- name: Run Buildx
run: |
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6 \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg COMMIT=`git rev-parse --short HEAD` \
--build-arg VERSION=${GITHUB_REF##*/} \
-t qmcgaw/private-internet-access:${GITHUB_REF##*/} \
-t qmcgaw/gluetun:${GITHUB_REF##*/} \
--push \
.
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/private-internet-access/tQFy7AxtSUNANPe6aoVChYdsI_I=
continue-on-error: true

View File

@@ -1,99 +0,0 @@
name: CI
on:
push:
paths:
- .github/workflows/ci.yml
- cmd/**
- internal/**
- pkg/**
- .dockerignore
- .golangci.yml
- Dockerfile
- go.mod
- go.sum
jobs:
verify:
runs-on: ubuntu-latest
env:
DOCKER_BUILDKIT: "1"
steps:
- uses: actions/checkout@v2.3.4
- name: Linting
run: docker build --target lint .
- name: Go mod tidy check
run: docker build --target tidy .
- name: Build test image
run: docker build --target test -t test-container .
- name: Run tests in test container
run: |
touch coverage.txt
docker run --rm \
-v "$(pwd)/coverage.txt:/tmp/gobuild/coverage.txt" \
test-container
- name: Code security analysis
uses: snyk/actions/golang@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
- name: Build final image
run: docker build -t final-image .
- name: Image security analysis
uses: snyk/actions/docker@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
image: final-image
publish:
needs: [verify]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.4
- uses: docker/setup-qemu-action@v1
- uses: docker/setup-buildx-action@v1
- uses: docker/login-action@v1
with:
username: qmcgaw
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Set variables
id: vars
env:
EVENT_NAME: ${{ github.event_name }}
run: |
BRANCH=${GITHUB_REF#refs/heads/}
TAG=${GITHUB_REF#refs/tags/}
echo ::set-output name=commit::$(git rev-parse --short HEAD)
echo ::set-output name=created::$(date -u +%Y-%m-%dT%H:%M:%SZ)
if [ "$TAG" != "$GITHUB_REF" ]; then
echo ::set-output name=version::$TAG
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
elif [ "$BRANCH" = "master" ]; then
echo ::set-output name=version::latest
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
else
echo ::set-output name=version::$BRANCH
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
fi
- name: Build and push final image
uses: docker/build-push-action@v2.6.1
with:
platforms: ${{ steps.vars.outputs.platforms }}
build-args: |
CREATED=${{ steps.vars.outputs.created }}
COMMIT=${{ steps.vars.outputs.commit }}
VERSION=${{ steps.vars.outputs.version }}
tags: |
qmcgaw/gluetun:${{ steps.vars.outputs.version }}
qmcgaw/private-internet-access:${{ steps.vars.outputs.version }}
push: true

View File

@@ -10,9 +10,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2.3.4 uses: actions/checkout@v2
- name: Docker Hub Description - name: Docker Hub Description
uses: peter-evans/dockerhub-description@v2 uses: peter-evans/dockerhub-description@v2.4.1
with: with:
username: qmcgaw username: qmcgaw
password: ${{ secrets.DOCKERHUB_PASSWORD }} password: ${{ secrets.DOCKERHUB_PASSWORD }}

View File

@@ -9,7 +9,10 @@ jobs:
labeler: labeler:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2.3.4 - name: Checkout
- uses: crazy-max/ghaction-github-labeler@v3 uses: actions/checkout@v2
with: - name: Labeler
yaml-file: .github/labels.yml if: success()
uses: crazy-max/ghaction-github-labeler@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -8,8 +8,9 @@ jobs:
misspell: misspell:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2.3.4 - uses: actions/checkout@v2
- uses: reviewdog/action-misspell@v1 - uses: reviewdog/action-misspell@master
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }}
locale: "US" locale: "US"
level: error level: error

1
.gitignore vendored
View File

@@ -1 +0,0 @@
scratch.txt

View File

@@ -10,25 +10,10 @@ issues:
linters: linters:
- dupl - dupl
- maligned - maligned
- goerr113 - path: internal/unix/constants\.go
- path: internal/server/
linters: linters:
- dupl - golint
- path: internal/configuration/ text: don't use ALL_CAPS in Go names; use CamelCase
linters:
- dupl
- path: internal/constants/
linters:
- dupl
- text: "exported: exported var Err*"
linters:
- revive
- text: "mnd: Magic number: 0644*"
linters:
- gomnd
- text: "mnd: Magic number: 0400*"
linters:
- gomnd
linters: linters:
disable-all: true disable-all: true
enable: enable:
@@ -48,32 +33,29 @@ linters:
- gocritic - gocritic
- gocyclo - gocyclo
- godot - godot
- goerr113
- goheader - goheader
- goimports - goimports
- golint
- gomnd - gomnd
- goprintffuncname - goprintffuncname
- gosec - gosec
- gosimple - gosimple
- govet - govet
- importas
- ineffassign - ineffassign
- interfacer
- lll - lll
- maligned
- misspell - misspell
- nakedret - nakedret
- nestif - nestif
- nilerr
- noctx - noctx
- nolintlint - nolintlint
- prealloc - prealloc
- predeclared
- revive
- rowserrcheck - rowserrcheck
- scopelint
- sqlclosecheck - sqlclosecheck
- staticcheck - staticcheck
- structcheck - structcheck
- thelper
- tparallel
- typecheck - typecheck
- unconvert - unconvert
- unparam - unparam

9
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"recommendations": [
"shardulm94.trailing-spaces",
"ms-azuretools.vscode-docker",
"davidanson.vscode-markdownlint",
"IBM.output-colorizer",
"golang.go"
]
}

91
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,91 @@
{
// General settings
"files.eol": "\n",
// Docker
"remote.extensionKind": {
"ms-azuretools.vscode-docker": "workspace"
},
// Golang general settings
"go.useLanguageServer": true,
"go.autocompleteUnimportedPackages": true,
"go.gotoSymbol.includeImports": true,
"go.gotoSymbol.includeGoroot": true,
"gopls": {
"completeUnimported": true,
"deepCompletion": true,
"usePlaceholders": false
},
"go.lintTool": "golangci-lint",
"go.lintFlags": [
"--fast",
"--enable",
"rowserrcheck",
"--enable",
"bodyclose",
"--enable",
"dogsled",
"--enable",
"dupl",
"--enable",
"gochecknoglobals",
"--enable",
"gochecknoinits",
"--enable",
"gocognit",
"--enable",
"goconst",
"--enable",
"gocritic",
"--enable",
"gocyclo",
"--enable",
"goimports",
"--enable",
"golint",
"--enable",
"gosec",
"--enable",
"interfacer",
"--enable",
"maligned",
"--enable",
"misspell",
"--enable",
"nakedret",
"--enable",
"prealloc",
"--enable",
"scopelint",
"--enable",
"unconvert",
"--enable",
"unparam",
"--enable",
"whitespace"
],
// Golang on save
"go.buildOnSave": "workspace",
"go.lintOnSave": "workspace",
"go.vetOnSave": "workspace",
"editor.formatOnSave": true,
"[go]": {
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
// Golang testing
"go.toolsEnvVars": {
"GOFLAGS": "-tags="
},
"gopls.env": {
"GOFLAGS": "-tags="
},
"go.testEnvVars": {},
"go.testFlags": [
"-v",
// "-race"
],
"go.testTimeout": "600s",
"go.coverOnSingleTestFile": true,
"go.coverOnSingleTest": true
}

View File

@@ -1,64 +1,35 @@
ARG ALPINE_VERSION=3.14 ARG ALPINE_VERSION=3.12
ARG GO_ALPINE_VERSION=3.13 ARG GO_VERSION=1.15
ARG GO_VERSION=1.16
ARG XCPUTRANSLATE_VERSION=v0.6.0
ARG GOLANGCI_LINT_VERSION=v1.41.1
ARG BUILDPLATFORM=linux/amd64
FROM --platform=${BUILDPLATFORM} qmcgaw/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder
FROM --platform=${BUILDPLATFORM} qmcgaw/binpot:golangci-lint-${GOLANGCI_LINT_VERSION} AS golangci-lint RUN apk --update add git
FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION}-alpine${GO_ALPINE_VERSION} AS base
COPY --from=xcputranslate /xcputranslate /usr/local/bin/xcputranslate
RUN apk --update add git g++
ENV CGO_ENABLED=0 ENV CGO_ENABLED=0
COPY --from=golangci-lint /bin /go/bin/golangci-lint ARG GOLANGCI_LINT_VERSION=v1.34.1
RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s ${GOLANGCI_LINT_VERSION}
WORKDIR /tmp/gobuild WORKDIR /tmp/gobuild
COPY .golangci.yml .
COPY go.mod go.sum ./ COPY go.mod go.sum ./
RUN go mod download RUN go mod download
COPY cmd/ ./cmd/
COPY internal/ ./internal/
FROM --platform=${BUILDPLATFORM} base AS test
# Note on the go race detector:
# - we set CGO_ENABLED=1 to have it enabled
# - we installed g++ to support the race detector
ENV CGO_ENABLED=1
ENTRYPOINT go test -race -coverpkg=./... -coverprofile=coverage.txt -covermode=atomic ./...
FROM --platform=${BUILDPLATFORM} base AS lint
COPY .golangci.yml ./
RUN golangci-lint run --timeout=10m
FROM --platform=${BUILDPLATFORM} base AS tidy
RUN git init && \
git config user.email ci@localhost && \
git config user.name ci && \
git add -A && git commit -m ci && \
sed -i '/\/\/ indirect/d' go.mod && \
go mod tidy && \
git diff --exit-code -- go.mod
FROM --platform=${BUILDPLATFORM} base AS build
ARG TARGETPLATFORM
ARG VERSION=unknown ARG VERSION=unknown
ARG CREATED="an unknown date" ARG BUILD_DATE="an unknown date"
ARG COMMIT=unknown ARG COMMIT=unknown
RUN GOARCH="$(xcputranslate translate -field arch -targetplatform ${TARGETPLATFORM})" \ COPY cmd/gluetun/main.go .
GOARM="$(xcputranslate translate -field arm -targetplatform ${TARGETPLATFORM})" \ COPY internal/ ./internal/
go build -trimpath -ldflags="-s -w \ RUN go test ./...
RUN golangci-lint run --timeout=10m
RUN go build -trimpath -ldflags="-s -w \
-X 'main.version=$VERSION' \ -X 'main.version=$VERSION' \
-X 'main.created=$CREATED' \ -X 'main.buildDate=$BUILD_DATE' \
-X 'main.commit=$COMMIT' \ -X 'main.commit=$COMMIT' \
" -o entrypoint cmd/gluetun/main.go " -o entrypoint main.go
FROM alpine:${ALPINE_VERSION} FROM alpine:${ALPINE_VERSION}
ARG VERSION=unknown ARG VERSION=unknown
ARG CREATED="an unknown date" ARG BUILD_DATE="an unknown date"
ARG COMMIT=unknown ARG COMMIT=unknown
LABEL \ LABEL \
org.opencontainers.image.authors="quentin.mcgaw@gmail.com" \ org.opencontainers.image.authors="quentin.mcgaw@gmail.com" \
org.opencontainers.image.created=$CREATED \ org.opencontainers.image.created=$BUILD_DATE \
org.opencontainers.image.version=$VERSION \ org.opencontainers.image.version=$VERSION \
org.opencontainers.image.revision=$COMMIT \ org.opencontainers.image.revision=$COMMIT \
org.opencontainers.image.url="https://github.com/qdm12/gluetun" \ org.opencontainers.image.url="https://github.com/qdm12/gluetun" \
@@ -69,55 +40,48 @@ LABEL \
ENV VPNSP=pia \ ENV VPNSP=pia \
VERSION_INFORMATION=on \ VERSION_INFORMATION=on \
PROTOCOL=udp \ PROTOCOL=udp \
OPENVPN_VERSION=2.5 \
OPENVPN_VERBOSITY=1 \ OPENVPN_VERBOSITY=1 \
OPENVPN_FLAGS= \ OPENVPN_ROOT=no \
OPENVPN_ROOT=yes \
OPENVPN_TARGET_IP= \ OPENVPN_TARGET_IP= \
OPENVPN_IPV6=off \ OPENVPN_IPV6=off \
OPENVPN_CUSTOM_CONFIG= \
TZ= \ TZ= \
PUID= \ PUID= \
PGID= \ PGID= \
PUBLICIP_FILE="/tmp/gluetun/ip" \ PUBLICIP_FILE="/tmp/gluetun/ip" \
# VPN provider settings # PIA, Windscribe, Surfshark, Cyberghost, Vyprvpn, NordVPN, PureVPN only
OPENVPN_USER= \ OPENVPN_USER= \
OPENVPN_PASSWORD= \ OPENVPN_PASSWORD= \
USER_SECRETFILE=/run/secrets/openvpn_user \ USER_SECRETFILE=/run/secrets/openvpn_user \
PASSWORD_SECRETFILE=/run/secrets/openvpn_password \ PASSWORD_SECRETFILE=/run/secrets/openvpn_password \
REGION= \ REGION= \
COUNTRY= \ # PIA only
CITY= \
PORT= \
SERVER_HOSTNAME= \
# Mullvad only:
ISP= \
OWNED=no \
# Private Internet Access only:
PIA_ENCRYPTION=strong \ PIA_ENCRYPTION=strong \
PORT_FORWARDING=off \ PORT_FORWARDING=off \
PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \ PORT_FORWARDING_STATUS_FILE="/tmp/gluetun/forwarded_port" \
# Cyberghost only: # Mullvad and PureVPN only
COUNTRY= \
# Mullvad, PureVPN, Windscribe only
CITY= \
# Windscribe only
SERVER_HOSTNAME= \
# Mullvad only
ISP= \
OWNED=no \
# Mullvad and Windscribe only
PORT= \
# Cyberghost only
CYBERGHOST_GROUP="Premium UDP Europe" \ CYBERGHOST_GROUP="Premium UDP Europe" \
OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt \ OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt \
OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \ OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \
# Nordvpn only: # NordVPN only
SERVER_NUMBER= \ SERVER_NUMBER= \
# NordVPN and ProtonVPN only:
SERVER_NAME= \
# ProtonVPN only:
FREE_ONLY= \
# Openvpn # Openvpn
OPENVPN_CIPHER= \ OPENVPN_CIPHER= \
OPENVPN_AUTH= \ OPENVPN_AUTH= \
# Health
HEALTH_OPENVPN_DURATION_INITIAL=6s \
HEALTH_OPENVPN_DURATION_ADDITION=5s \
HEALTH_SERVER_ADDRESS=127.0.0.1:9999 \
# DNS over TLS # DNS over TLS
DOT=on \ DOT=on \
DOT_PROVIDERS=cloudflare \ DOT_PROVIDERS=cloudflare \
DOT_PRIVATE_ADDRESS=127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1/128,fc00::/7,fe80::/10,::ffff:7f00:1/104,::ffff:a00:0/104,::ffff:a9fe:0/112,::ffff:ac10:0/108,::ffff:c0a8:0/112 \ DOT_PRIVATE_ADDRESS=127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1/128,fc00::/7,fe80::/10,::ffff:0:0/96 \
DOT_VERBOSITY=1 \ DOT_VERBOSITY=1 \
DOT_VERBOSITY_DETAILS=0 \ DOT_VERBOSITY_DETAILS=0 \
DOT_VALIDATION_LOGLEVEL=0 \ DOT_VALIDATION_LOGLEVEL=0 \
@@ -155,14 +119,11 @@ ENV VPNSP=pia \
ENTRYPOINT ["/entrypoint"] ENTRYPOINT ["/entrypoint"]
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /entrypoint healthcheck HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /entrypoint healthcheck
ARG TARGETPLATFORM RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
RUN apk add --no-cache --update -l apk-tools && \ rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* && \
apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.12/main" openvpn==2.4.11-r0 && \
mv /usr/sbin/openvpn /usr/sbin/openvpn2.4 && \
apk del openvpn && \
apk add --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* /etc/openvpn/*.sh /usr/lib/openvpn/plugins/openvpn-plugin-down-root.so && \
deluser openvpn && \ deluser openvpn && \
deluser unbound && \ deluser unbound && \
mkdir /gluetun mkdir /gluetun
COPY --from=build /tmp/gobuild/entrypoint /entrypoint # TODO remove once SAN is added to PIA servers certificates, see https://github.com/pia-foss/manual-connections/issues/10
ENV GODEBUG=x509ignoreCN=0
COPY --from=builder /tmp/gobuild/entrypoint /entrypoint

102
README.md
View File

@@ -1,42 +1,26 @@
# Gluetun VPN client # Gluetun VPN client
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, FastestVPN, *Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access,
HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN, Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN and Privado VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN and Windscribe VPN servers
using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
**ANNOUNCEMENT**: **ANNOUNCEMENT**: *New Docker image name `qmcgaw/gluetun`*
![Title image](https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg) <img height="250" src="https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg?sanitize=true">
[![Build status](https://github.com/qdm12/gluetun/actions/workflows/ci.yml/badge.svg)](https://github.com/qdm12/gluetun/actions/workflows/ci.yml) [![Size](https://img.shields.io/docker/image-size/qmcgaw/gluetun?sort=semver&label=Last%20released%20image)](https://hub.docker.com/r/qmcgaw/gluetun/tags?page=1&ordering=last_updated)
[![Size](https://img.shields.io/docker/image-size/qmcgaw/gluetun/latest?label=Latest%20image)](https://hub.docker.com/r/qmcgaw/gluetun/tags)
[![Docker pulls qmcgaw/gluetun](https://img.shields.io/docker/pulls/qmcgaw/gluetun.svg)](https://hub.docker.com/r/qmcgaw/gluetun) [![Docker Pulls](https://img.shields.io/docker/pulls/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/private-internet-access)
[![Docker pulls qmcgaw/private-internet-access](https://img.shields.io/docker/pulls/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/gluetun) [![Docker Pulls](https://img.shields.io/docker/pulls/qmcgaw/gluetun.svg)](https://hub.docker.com/r/qmcgaw/gluetun)
[![Docker stars qmcgaw/gluetun](https://img.shields.io/docker/stars/qmcgaw/gluetun.svg)](https://hub.docker.com/r/qmcgaw/gluetun)
[![Docker stars qmcgaw/private-internet-access](https://img.shields.io/docker/stars/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/gluetun)
![Last release](https://img.shields.io/github/release/qdm12/gluetun?label=Last%20release) ![Last release](https://img.shields.io/github/release/qdm12/gluetun?label=Last%20release)
![Last Docker tag](https://img.shields.io/docker/v/qmcgaw/gluetun?sort=semver&label=Last%20Docker%20tag) ![Last Docker tag](https://img.shields.io/docker/v/qmcgaw/gluetun?sort=semver&label=Last%20Docker%20tag)
[![Last release size](https://img.shields.io/docker/image-size/qmcgaw/gluetun?sort=semver&label=Last%20released%20image)](https://hub.docker.com/r/qmcgaw/gluetun/tags?page=1&ordering=last_updated) ![GitHub Release Date](https://img.shields.io/github/release-date/qdm12/gluetun?label=Last%20release%20date)
![GitHub last release date](https://img.shields.io/github/release-date/qdm12/gluetun?label=Last%20release%20date)
![Commits since release](https://img.shields.io/github/commits-since/qdm12/gluetun/latest?sort=semver) ![Commits since release](https://img.shields.io/github/commits-since/qdm12/gluetun/latest?sort=semver)
[![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/commits)
[![Latest size](https://img.shields.io/docker/image-size/qmcgaw/gluetun/latest?label=Latest%20image)](https://hub.docker.com/r/qmcgaw/gluetun/tags)
[![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/commits/master)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/graphs/contributors)
[![GitHub closed PRs](https://img.shields.io/github/issues-pr-closed/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/pulls?q=is%3Apr+is%3Aclosed)
[![GitHub issues](https://img.shields.io/github/issues/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues)
[![GitHub closed issues](https://img.shields.io/github/issues-closed/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues?q=is%3Aissue+is%3Aclosed)
[![Lines of code](https://img.shields.io/tokei/lines/github/qdm12/gluetun)](https://github.com/qdm12/gluetun) [![Lines of code](https://img.shields.io/tokei/lines/github/qdm12/gluetun)](https://github.com/qdm12/gluetun)
![Code size](https://img.shields.io/github/languages/code-size/qdm12/gluetun)
![GitHub repo size](https://img.shields.io/github/repo-size/qdm12/gluetun)
![Go version](https://img.shields.io/github/go-mod/go-version/qdm12/gluetun)
![Visitors count](https://visitor-badge.laobi.icu/badge?page_id=gluetun.readme)
## Quick links ## Quick links
@@ -49,14 +33,11 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
- Sponsor me on [github.com/sponsors/qdm12](https://github.com/sponsors/qdm12) - Sponsor me on [github.com/sponsors/qdm12](https://github.com/sponsors/qdm12)
- Donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw) - Donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
- Drop me [an email](mailto:quentin.mcgaw@gmail.com) - Drop me [an email](mailto:quentin.mcgaw@gmail.com)
- Video:
[![Video Gif](https://i.imgur.com/CetWunc.gif)](https://youtu.be/0F6I03LQcI4)
## Features ## Features
- Based on Alpine 3.13 for a small Docker image of 54MB - Based on Alpine 3.12 for a small Docker image of 52MB
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** 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
@@ -66,7 +47,7 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP) - Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
- [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun) - [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun) - [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even ppc64le 🎆 - Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7 🎆
- VPN server side port forwarding for Private Internet Access and Vyprvpn - VPN server side port forwarding for Private Internet Access and Vyprvpn
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers - Possibility of split horizon DNS by selecting multiple DNS over TLS providers
- Subprograms all drop root privileges once launched - Subprograms all drop root privileges once launched
@@ -75,17 +56,8 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
## Setup ## Setup
1. Ensure your `tun` kernel module is setup: 1. On some devices you may need to setup your tunnel kernel module on your host with `insmod /lib/modules/tun.ko` or `modprobe tun`
- [Synology users Wiki page](https://github.com/qdm12/gluetun/wiki/Synology-setup)
```sh
sudo modprobe tun
# or, if you don't have modprobe, with
sudo insmod /lib/modules/tun.ko
```
1. Extra steps:
- [For Synology users](https://github.com/qdm12/gluetun/wiki/Synology-setup)
- [For 32 bit Operating systems (**Rasberry Pis**)](https://github.com/qdm12/gluetun/wiki/32-bit-setup)
1. Launch the container with: 1. Launch the container with:
```bash ```bash
@@ -99,6 +71,8 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
or use [docker-compose.yml](https://github.com/qdm12/gluetun/blob/master/docker-compose.yml) with: or use [docker-compose.yml](https://github.com/qdm12/gluetun/blob/master/docker-compose.yml) with:
```bash ```bash
echo "your openvpn username" > openvpn_user
echo "your openvpn password" > openvpn_password
docker-compose up -d docker-compose up -d
``` ```
@@ -108,13 +82,51 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
The following points are all optional but should give you insights on all the possibilities with this container. The following points are all optional but should give you insights on all the possibilities with this container.
- Use [Docker secrets](https://github.com/qdm12/gluetun/wiki/Docker-secrets) to read your credentials instead of environment variables
- [Test your setup](https://github.com/qdm12/gluetun/wiki/Test-your-setup) - [Test your setup](https://github.com/qdm12/gluetun/wiki/Test-your-setup)
- [How to connect other containers and devices to Gluetun](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun) - [How to connect other containers and devices to Gluetun](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
- [VPN server side port forwarding](https://github.com/qdm12/gluetun/wiki/Port-forwarding) - [VPN server side port forwarding](https://github.com/qdm12/gluetun/wiki/Port-forwarding)
- [HTTP control server](https://github.com/qdm12/gluetun/wiki/HTTP-Control-server) to automate things, restart Openvpn etc. - [HTTP control server](https://github.com/qdm12/gluetun/wiki/HTTP-Control-server) to automate things, restart Openvpn etc.
- Update the image with `docker pull qmcgaw/gluetun:latest`. See this [Wiki document](https://github.com/qdm12/gluetun/wiki/Docker-image-tags) for Docker tags available. - Update the image with `docker pull qmcgaw/gluetun:latest`. See this [Wiki document](https://github.com/qdm12/gluetun/wiki/Docker-image-tags) for Docker tags available.
- Use [Docker secrets](https://github.com/qdm12/gluetun/wiki/Docker-secrets) to read your credentials instead of environment variables
## Development
- 💻 [Contribute with code](https://github.com/qdm12/gluetun/wiki/Development) ([existing contributors 👍](https://github.com/qdm12/gluetun/blob/master/.github/CONTRIBUTING.md#Contributors))
- [List of issues and feature requests](https://github.com/qdm12/gluetun/issues)
- [Kanban board](https://github.com/qdm12/gluetun/projects/1)
## License ## License
[![MIT](https://img.shields.io/github/license/qdm12/gluetun)](https://github.com/qdm12/gluetun/master/LICENSE) [![MIT](https://img.shields.io/github/license/qdm12/gluetun)](https://github.com/qdm12/gluetun/master/LICENSE)
## Support
- Sponsor me on [Github](https://github.com/sponsors/qdm12) or donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
[![https://github.com/sponsors/qdm12](https://raw.githubusercontent.com/qdm12/gluetun/master/doc/sponsors.jpg)](https://github.com/sponsors/qdm12)
[![https://www.paypal.me/qmcgaw](https://raw.githubusercontent.com/qdm12/gluetun/master/doc/paypal.jpg)](https://www.paypal.me/qmcgaw)
- Contribute to the issues and discussions on Github
- Many thanks to @Frepke, @Ralph521, G. Mendez, M. Otmar Weber, J. Perez, A. Cooper and **others** for supporting me financially 🥇👍
## Metadata
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/commits)
[![GitHub closed PRs](https://img.shields.io/github/issues-pr-closed/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/pulls?q=is%3Apr+is%3Aclosed)
[![GitHub issues](https://img.shields.io/github/issues/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues)
[![GitHub closed issues](https://img.shields.io/github/issues-closed/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues?q=is%3Aissue+is%3Aclosed)
![Visitors count](https://visitor-badge.laobi.icu/badge?page_id=gluetun.readme)
![GitHub stars](https://img.shields.io/github/stars/qdm12/gluetun?style=social)
![GitHub watchers](https://img.shields.io/github/watchers/qdm12/gluetun?style=social)
![Contributors](https://img.shields.io/github/contributors/qdm12/gluetun)
![GitHub forks](https://img.shields.io/github/forks/qdm12/gluetun?style=social)
![Code size](https://img.shields.io/github/languages/code-size/qdm12/gluetun)
![GitHub repo size](https://img.shields.io/github/repo-size/qdm12/gluetun)
[![dockeri.co](https://dockeri.co/image/qmcgaw/gluetun)](https://hub.docker.com/r/qmcgaw/gluetun)
![Docker Layers for latest](https://img.shields.io/microbadger/layers/qmcgaw/gluetun/latest?label=Docker%20image%20layers)
![Go version](https://img.shields.io/github/go-mod/go-version/qdm12/gluetun)

View File

@@ -2,31 +2,31 @@ package main
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
"os" nativeos "os"
"os/signal" "os/signal"
"strconv"
"strings" "strings"
"sync"
"syscall" "syscall"
"time" "time"
"github.com/qdm12/dns/pkg/unbound"
"github.com/qdm12/gluetun/internal/alpine" "github.com/qdm12/gluetun/internal/alpine"
"github.com/qdm12/gluetun/internal/cli" "github.com/qdm12/gluetun/internal/cli"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/dns" "github.com/qdm12/gluetun/internal/dns"
"github.com/qdm12/gluetun/internal/firewall" "github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/healthcheck" "github.com/qdm12/gluetun/internal/healthcheck"
"github.com/qdm12/gluetun/internal/httpproxy" "github.com/qdm12/gluetun/internal/httpproxy"
gluetunLogging "github.com/qdm12/gluetun/internal/logging"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/openvpn" "github.com/qdm12/gluetun/internal/openvpn"
"github.com/qdm12/gluetun/internal/params"
"github.com/qdm12/gluetun/internal/publicip" "github.com/qdm12/gluetun/internal/publicip"
"github.com/qdm12/gluetun/internal/routing" "github.com/qdm12/gluetun/internal/routing"
"github.com/qdm12/gluetun/internal/server" "github.com/qdm12/gluetun/internal/server"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/gluetun/internal/shadowsocks" "github.com/qdm12/gluetun/internal/shadowsocks"
"github.com/qdm12/gluetun/internal/storage" "github.com/qdm12/gluetun/internal/storage"
"github.com/qdm12/gluetun/internal/unix" "github.com/qdm12/gluetun/internal/unix"
@@ -34,170 +34,101 @@ import (
versionpkg "github.com/qdm12/gluetun/internal/version" versionpkg "github.com/qdm12/gluetun/internal/version"
"github.com/qdm12/golibs/command" "github.com/qdm12/golibs/command"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/os"
"github.com/qdm12/goshutdown" "github.com/qdm12/golibs/os/user"
"github.com/qdm12/gosplash"
"github.com/qdm12/updated/pkg/dnscrypto"
) )
//nolint:gochecknoglobals //nolint:gochecknoglobals
var ( var (
version = "unknown" version = "unknown"
commit = "unknown" commit = "unknown"
created = "an unknown date" buildDate = "an unknown date"
)
var (
errSetupRouting = errors.New("cannot setup routing")
errCreateUser = errors.New("cannot create user")
) )
func main() { func main() {
buildInfo := models.BuildInformation{ buildInfo := models.BuildInformation{
Version: version, Version: version,
Commit: commit, Commit: commit,
Created: created, BuildDate: buildDate,
} }
ctx := context.Background() ctx := context.Background()
ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) args := nativeos.Args
ctx, cancel := context.WithCancel(ctx) os := os.New()
osUser := user.New()
logger := logging.NewParent(logging.Settings{
Level: logging.LevelInfo,
})
args := os.Args
unix := unix.New() unix := unix.New()
cli := cli.New() cli := cli.New()
env := params.NewEnv() nativeos.Exit(_main(ctx, buildInfo, args, os, osUser, unix, cli))
cmder := command.NewCmder()
errorCh := make(chan error)
go func() {
errorCh <- _main(ctx, buildInfo, args, logger, env, unix, cmder, cli)
}()
select {
case <-ctx.Done():
stop()
logger.Warn("Caught OS signal, shutting down")
case err := <-errorCh:
stop()
close(errorCh)
if err == nil { // expected exit such as healthcheck
os.Exit(0)
} }
logger.Error(err.Error())
cancel()
}
const shutdownGracePeriod = 5 * time.Second
timer := time.NewTimer(shutdownGracePeriod)
select {
case <-errorCh:
if !timer.Stop() {
<-timer.C
}
logger.Info("Shutdown successful")
case <-timer.C:
logger.Warn("Shutdown timed out")
}
os.Exit(1)
}
var (
errCommandUnknown = errors.New("command is unknown")
)
//nolint:gocognit,gocyclo //nolint:gocognit,gocyclo
func _main(ctx context.Context, buildInfo models.BuildInformation, func _main(background context.Context, buildInfo models.BuildInformation,
args []string, logger logging.ParentLogger, env params.Env, args []string, os os.OS, osUser user.OSUser, unix unix.Unix,
unix unix.Unix, cmder command.RunStarter, cli cli.CLIer) error { cli cli.CLI) int {
if len(args) > 1 { // cli operation if len(args) > 1 { // cli operation
var err error
switch args[1] { switch args[1] {
case "healthcheck": case "healthcheck":
return cli.HealthCheck(ctx, env, logger) err = cli.HealthCheck(background)
case "clientkey": case "clientkey":
return cli.ClientKey(args[2:]) err = cli.ClientKey(args[2:], os.OpenFile)
case "openvpnconfig": case "openvpnconfig":
return cli.OpenvpnConfig(logger) err = cli.OpenvpnConfig(os)
case "update": case "update":
return cli.Update(ctx, args[2:], logger) err = cli.Update(args[2:], os)
default: default:
return fmt.Errorf("%w: %s", errCommandUnknown, args[1]) err = fmt.Errorf("command %q is unknown", args[1])
} }
if err != nil {
fmt.Println(err)
return 1
} }
return 0
}
ctx, cancel := context.WithCancel(background)
defer cancel()
logger := createLogger()
const clientTimeout = 15 * time.Second const clientTimeout = 15 * time.Second
httpClient := &http.Client{Timeout: clientTimeout} httpClient := &http.Client{Timeout: clientTimeout}
// Create configurators // Create configurators
alpineConf := alpine.New() alpineConf := alpine.NewConfigurator(os.OpenFile, osUser)
ovpnConf := openvpn.NewConfigurator( ovpnConf := openvpn.NewConfigurator(logger, os, unix)
logger.NewChild(logging.Settings{Prefix: "openvpn configurator: "}), dnsConf := dns.NewConfigurator(logger, httpClient, os.OpenFile)
unix, cmder) routingConf := routing.NewRouting(logger)
dnsCrypto := dnscrypto.New(httpClient, "", "") firewallConf := firewall.NewConfigurator(logger, routingConf, os.OpenFile)
const cacertsPath = "/etc/ssl/certs/ca-certificates.crt" streamMerger := command.NewStreamMerger()
dnsConf := unbound.NewConfigurator(nil, cmder, dnsCrypto,
"/etc/unbound", "/usr/sbin/unbound", cacertsPath)
announcementExp, err := time.Parse(time.RFC3339, "2021-07-22T00:00:00Z") paramsReader := params.NewReader(logger, os)
if err != nil { fmt.Println(gluetunLogging.Splash(buildInfo))
return err
}
splashSettings := gosplash.Settings{
User: "qdm12",
Repository: "gluetun",
Emails: []string{"quentin.mcgaw@gmail.com"},
Version: buildInfo.Version,
Commit: buildInfo.Commit,
BuildDate: buildInfo.Created,
Announcement: "",
AnnounceExp: announcementExp,
// Sponsor information
PaypalUser: "qmcgaw",
GithubSponsor: "qdm12",
}
for _, line := range gosplash.MakeLines(splashSettings) {
fmt.Println(line)
}
err = printVersions(ctx, logger, []printVersionElement{ printVersions(ctx, logger, map[string]func(ctx context.Context) (string, error){
{name: "Alpine", getVersion: alpineConf.Version}, "OpenVPN": ovpnConf.Version,
{name: "OpenVPN 2.4", getVersion: ovpnConf.Version24}, "Unbound": dnsConf.Version,
{name: "OpenVPN 2.5", getVersion: ovpnConf.Version25}, "IPtables": firewallConf.Version,
{name: "Unbound", getVersion: dnsConf.Version},
{name: "IPtables", getVersion: func(ctx context.Context) (version string, err error) {
return firewall.Version(ctx, cmder)
}},
}) })
if err != nil {
return err
}
var allSettings configuration.Settings allSettings, err := settings.GetAllSettings(paramsReader)
err = allSettings.Read(env,
logger.NewChild(logging.Settings{Prefix: "configuration: "}))
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
logger.Info(allSettings.String()) logger.Info(allSettings.String())
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil { if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
return err logger.Error(err)
return 1
} }
if err := os.MkdirAll("/gluetun", 0644); err != nil { if err := os.MkdirAll("/gluetun", 0644); err != nil {
return err logger.Error(err)
return 1
} }
// TODO run this in a loop or in openvpn to reload from file without restarting // TODO run this in a loop or in openvpn to reload from file without restarting
storage := storage.New( storage := storage.New(logger, os, constants.ServersData)
logger.NewChild(logging.Settings{Prefix: "storage: "}),
constants.ServersData)
allServers, err := storage.SyncServers(constants.GetAllServers()) allServers, err := storage.SyncServers(constants.GetAllServers())
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
// Should never change // Should never change
@@ -206,312 +137,313 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
const defaultUsername = "nonrootuser" const defaultUsername = "nonrootuser"
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid) nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
if err != nil { if err != nil {
return fmt.Errorf("%w: %s", errCreateUser, err) logger.Error(err)
return 1
} }
if nonRootUsername != defaultUsername { if nonRootUsername != defaultUsername {
logger.Info("using existing username " + nonRootUsername + " corresponding to user id " + fmt.Sprint(puid)) logger.Info("using existing username %s corresponding to user id %d", nonRootUsername, puid)
} }
// set it for Unbound
// TODO remove this when migrating to qdm12/dns v2
allSettings.DNS.Unbound.Username = nonRootUsername
if err := os.Chown("/etc/unbound", puid, pgid); err != nil { if err := os.Chown("/etc/unbound", puid, pgid); err != nil {
return err logger.Error(err)
return 1
} }
firewallLogLevel := logging.LevelInfo
if allSettings.Firewall.Debug { if allSettings.Firewall.Debug {
firewallLogLevel = logging.LevelDebug firewallConf.SetDebug()
routingConf.SetDebug()
} }
routingLogger := logger.NewChild(logging.Settings{
Prefix: "routing: ",
Level: firewallLogLevel,
})
routingConf := routing.NewRouting(routingLogger)
defaultInterface, defaultGateway, err := routingConf.DefaultRoute() defaultInterface, defaultGateway, err := routingConf.DefaultRoute()
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
localNetworks, err := routingConf.LocalNetworks() localSubnet, err := routingConf.LocalSubnet()
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
defaultIP, err := routingConf.DefaultIP() defaultIP, err := routingConf.DefaultIP()
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
firewallLogger := logger.NewChild(logging.Settings{ firewallConf.SetNetworkInformation(defaultInterface, defaultGateway, localSubnet, defaultIP)
Prefix: "firewall: ",
Level: firewallLogLevel,
})
firewallConf := firewall.NewConfig(firewallLogger, cmder,
defaultInterface, defaultGateway, localNetworks, defaultIP)
if err := routingConf.Setup(); err != nil { if err := routingConf.Setup(); err != nil {
if strings.Contains(err.Error(), "operation not permitted") { logger.Error(err)
logger.Warn("💡 Tip: Are you passing NET_ADMIN capability to gluetun?") return 1
}
return fmt.Errorf("%w: %s", errSetupRouting, err)
} }
defer func() { defer func() {
logger.Info("routing cleanup...") routingConf.SetVerbose(false)
if err := routingConf.TearDown(); err != nil { if err := routingConf.TearDown(); err != nil {
logger.Error("cannot teardown routing: " + err.Error()) logger.Error(err)
} }
}() }()
if err := firewallConf.SetOutboundSubnets(ctx, allSettings.Firewall.OutboundSubnets); err != nil { if err := firewallConf.SetOutboundSubnets(ctx, allSettings.Firewall.OutboundSubnets); err != nil {
return err logger.Error(err)
return 1
} }
if err := routingConf.SetOutboundRoutes(allSettings.Firewall.OutboundSubnets); err != nil { if err := routingConf.SetOutboundRoutes(allSettings.Firewall.OutboundSubnets); err != nil {
return err logger.Error(err)
return 1
} }
if err := ovpnConf.CheckTUN(); err != nil { if err := ovpnConf.CheckTUN(); err != nil {
logger.Warn(err.Error()) logger.Warn(err)
err = ovpnConf.CreateTUN() err = ovpnConf.CreateTUN()
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
} }
tunnelReadyCh := make(chan struct{}) tunnelReadyCh, dnsReadyCh := make(chan struct{}), make(chan struct{})
signalTunnelReady := func() { tunnelReadyCh <- struct{}{} }
signalDNSReady := func() { dnsReadyCh <- struct{}{} }
defer close(tunnelReadyCh) defer close(tunnelReadyCh)
defer close(dnsReadyCh)
if allSettings.Firewall.Enabled { if allSettings.Firewall.Enabled {
err := firewallConf.SetEnabled(ctx, true) // disabled by default err := firewallConf.SetEnabled(ctx, true) // disabled by default
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
} }
for _, vpnPort := range allSettings.Firewall.VPNInputPorts { for _, vpnPort := range allSettings.Firewall.VPNInputPorts {
err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN)) err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN))
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
} }
for _, port := range allSettings.Firewall.InputPorts { for _, port := range allSettings.Firewall.InputPorts {
err = firewallConf.SetAllowedPort(ctx, port, defaultInterface) err = firewallConf.SetAllowedPort(ctx, port, defaultInterface)
if err != nil { if err != nil {
return err logger.Error(err)
return 1
} }
} // TODO move inside firewall? } // TODO move inside firewall?
// Shutdown settings wg := &sync.WaitGroup{}
const defaultShutdownTimeout = 400 * time.Millisecond
defaultShutdownOnSuccess := func(goRoutineName string) {
logger.Info(goRoutineName + ": terminated ✔️")
}
defaultShutdownOnFailure := func(goRoutineName string, err error) {
logger.Warn(goRoutineName + ": " + err.Error() + " ⚠️")
}
defaultGoRoutineSettings := goshutdown.GoRoutineSettings{Timeout: defaultShutdownTimeout}
defaultGroupSettings := goshutdown.GroupSettings{
Timeout: defaultShutdownTimeout,
OnSuccess: defaultShutdownOnSuccess,
}
controlGroupHandler := goshutdown.NewGroupHandler("control", defaultGroupSettings) go collectStreamLines(ctx, streamMerger, logger, signalTunnelReady)
tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupSettings)
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupSettings)
openvpnLooper := openvpn.NewLoop(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers, openvpnLooper := openvpn.NewLooper(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers,
ovpnConf, firewallConf, logger, httpClient, tunnelReadyCh) ovpnConf, firewallConf, routingConf, logger, httpClient, os.OpenFile, streamMerger, cancel)
openvpnHandler, openvpnCtx, openvpnDone := goshutdown.NewGoRoutineHandler( wg.Add(1)
"openvpn", goshutdown.GoRoutineSettings{Timeout: time.Second})
// wait for restartOpenvpn // wait for restartOpenvpn
go openvpnLooper.Run(openvpnCtx, openvpnDone) go openvpnLooper.Run(ctx, wg)
updaterLooper := updater.NewLooper(allSettings.Updater, updaterLooper := updater.NewLooper(allSettings.Updater,
allServers, storage, openvpnLooper.SetServers, httpClient, allServers, storage, openvpnLooper.SetServers, httpClient, logger)
logger.NewChild(logging.Settings{Prefix: "updater: "})) wg.Add(1)
updaterHandler, updaterCtx, updaterDone := goshutdown.NewGoRoutineHandler(
"updater", defaultGoRoutineSettings)
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker // wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
go updaterLooper.Run(updaterCtx, updaterDone) go updaterLooper.Run(ctx, wg)
tickersGroupHandler.Add(updaterHandler)
unboundLogger := logger.NewChild(logging.Settings{Prefix: "dns over tls: "}) unboundLooper := dns.NewLooper(dnsConf, allSettings.DNS, logger, streamMerger, nonRootUsername, puid, pgid)
unboundLooper := dns.NewLoop(dnsConf, allSettings.DNS, httpClient, wg.Add(1)
unboundLogger)
dnsHandler, dnsCtx, dnsDone := goshutdown.NewGoRoutineHandler(
"unbound", defaultGoRoutineSettings)
// wait for unboundLooper.Restart or its ticker launched with RunRestartTicker // wait for unboundLooper.Restart or its ticker launched with RunRestartTicker
go unboundLooper.Run(dnsCtx, dnsDone) go unboundLooper.Run(ctx, wg, signalDNSReady)
otherGroupHandler.Add(dnsHandler)
publicIPLooper := publicip.NewLoop(httpClient, publicIPLooper := publicip.NewLooper(
logger.NewChild(logging.Settings{Prefix: "ip getter: "}), httpClient, logger, allSettings.PublicIP, puid, pgid, os)
allSettings.PublicIP, puid, pgid) wg.Add(1)
pubIPHandler, pubIPCtx, pubIPDone := goshutdown.NewGoRoutineHandler( go publicIPLooper.Run(ctx, wg)
"public IP", defaultGoRoutineSettings) wg.Add(1)
go publicIPLooper.Run(pubIPCtx, pubIPDone) go publicIPLooper.RunRestartTicker(ctx, wg)
otherGroupHandler.Add(pubIPHandler)
pubIPTickerHandler, pubIPTickerCtx, pubIPTickerDone := goshutdown.NewGoRoutineHandler( httpProxyLooper := httpproxy.NewLooper(logger, allSettings.HTTPProxy)
"public IP", defaultGoRoutineSettings) wg.Add(1)
go publicIPLooper.RunRestartTicker(pubIPTickerCtx, pubIPTickerDone) go httpProxyLooper.Run(ctx, wg)
tickersGroupHandler.Add(pubIPTickerHandler)
httpProxyLooper := httpproxy.NewLoop( shadowsocksLooper := shadowsocks.NewLooper(allSettings.ShadowSocks, logger)
logger.NewChild(logging.Settings{Prefix: "http proxy: "}), wg.Add(1)
allSettings.HTTPProxy) go shadowsocksLooper.Run(ctx, wg)
httpProxyHandler, httpProxyCtx, httpProxyDone := goshutdown.NewGoRoutineHandler(
"http proxy", defaultGoRoutineSettings)
go httpProxyLooper.Run(httpProxyCtx, httpProxyDone)
otherGroupHandler.Add(httpProxyHandler)
shadowsocksLooper := shadowsocks.NewLooper(allSettings.ShadowSocks, wg.Add(1)
logger.NewChild(logging.Settings{Prefix: "shadowsocks: "})) go routeReadyEvents(ctx, wg, buildInfo, tunnelReadyCh, dnsReadyCh,
shadowsocksHandler, shadowsocksCtx, shadowsocksDone := goshutdown.NewGoRoutineHandler(
"shadowsocks proxy", defaultGoRoutineSettings)
go shadowsocksLooper.Run(shadowsocksCtx, shadowsocksDone)
otherGroupHandler.Add(shadowsocksHandler)
eventsRoutingHandler, eventsRoutingCtx, eventsRoutingDone := goshutdown.NewGoRoutineHandler(
"events routing", defaultGoRoutineSettings)
go routeReadyEvents(eventsRoutingCtx, eventsRoutingDone, buildInfo, tunnelReadyCh,
unboundLooper, updaterLooper, publicIPLooper, routingConf, logger, httpClient, unboundLooper, updaterLooper, publicIPLooper, routingConf, logger, httpClient,
allSettings.VersionInformation, allSettings.OpenVPN.Provider.PortForwarding.Enabled, openvpnLooper.PortForward, allSettings.VersionInformation, allSettings.OpenVPN.Provider.PortForwarding.Enabled, openvpnLooper.PortForward,
) )
controlGroupHandler.Add(eventsRoutingHandler) controlServerAddress := fmt.Sprintf("0.0.0.0:%d", allSettings.ControlServer.Port)
controlServerAddress := ":" + strconv.Itoa(int(allSettings.ControlServer.Port))
controlServerLogging := allSettings.ControlServer.Log controlServerLogging := allSettings.ControlServer.Log
httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler( httpServer := server.New(controlServerAddress, controlServerLogging,
"http server", defaultGoRoutineSettings) logger, buildInfo, openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
httpServer := server.New(httpServerCtx, controlServerAddress, controlServerLogging, wg.Add(1)
logger.NewChild(logging.Settings{Prefix: "http server: "}), go httpServer.Run(ctx, wg)
buildInfo, openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
go httpServer.Run(httpServerCtx, httpServerDone)
controlGroupHandler.Add(httpServerHandler)
healthLogger := logger.NewChild(logging.Settings{Prefix: "healthcheck: "}) healthcheckServer := healthcheck.NewServer(
healthcheckServer := healthcheck.NewServer(allSettings.Health, healthLogger, openvpnLooper) constants.HealthcheckAddress, logger)
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler( wg.Add(1)
"HTTP health server", defaultGoRoutineSettings) go healthcheckServer.Run(ctx, wg)
go healthcheckServer.Run(healthServerCtx, healthServerDone)
const orderShutdownTimeout = 3 * time.Second
orderSettings := goshutdown.OrderSettings{
Timeout: orderShutdownTimeout,
OnFailure: defaultShutdownOnFailure,
OnSuccess: defaultShutdownOnSuccess,
}
orderHandler := goshutdown.NewOrder("gluetun", orderSettings)
orderHandler.Append(controlGroupHandler, tickersGroupHandler, healthServerHandler,
openvpnHandler, otherGroupHandler)
// Start openvpn for the first time in a blocking call // Start openvpn for the first time in a blocking call
// until openvpn is launched // until openvpn is launched
_, _ = openvpnLooper.ApplyStatus(ctx, constants.Running) // TODO option to disable with variable _, _ = openvpnLooper.SetStatus(constants.Running) // TODO option to disable with variable
<-ctx.Done()
signalsCh := make(chan nativeos.Signal, 1)
signal.Notify(signalsCh,
syscall.SIGINT,
syscall.SIGTERM,
nativeos.Interrupt,
)
shutdownErrorsCount := 0
select {
case signal := <-signalsCh:
logger.Warn("Caught OS signal %s, shutting down", signal)
cancel()
case <-ctx.Done():
logger.Warn("context canceled, shutting down")
}
if allSettings.OpenVPN.Provider.PortForwarding.Enabled { if allSettings.OpenVPN.Provider.PortForwarding.Enabled {
logger.Info("Clearing forwarded port status file " + allSettings.OpenVPN.Provider.PortForwarding.Filepath) logger.Info("Clearing forwarded port status file %s", allSettings.OpenVPN.Provider.PortForwarding.Filepath)
if err := os.Remove(allSettings.OpenVPN.Provider.PortForwarding.Filepath); err != nil { if err := os.Remove(string(allSettings.OpenVPN.Provider.PortForwarding.Filepath)); err != nil {
logger.Error(err.Error()) logger.Error(err)
shutdownErrorsCount++
} }
} }
const shutdownGracePeriod = 5 * time.Second
waiting, waited := context.WithTimeout(context.Background(), shutdownGracePeriod)
go func() {
defer waited()
wg.Wait()
}()
<-waiting.Done()
if waiting.Err() == context.DeadlineExceeded {
if shutdownErrorsCount > 0 {
logger.Warn("Shutdown had %d errors", shutdownErrorsCount)
}
logger.Warn("Shutdown timed out")
return 1
}
if shutdownErrorsCount > 0 {
logger.Warn("Shutdown had %d errors")
return 1
}
logger.Info("Shutdown successful")
return 0
}
return orderHandler.Shutdown(context.Background()) func createLogger() logging.Logger {
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel)
if err != nil {
panic(err)
} }
return logger
type printVersionElement struct {
name string
getVersion func(ctx context.Context) (version string, err error)
} }
func printVersions(ctx context.Context, logger logging.Logger, func printVersions(ctx context.Context, logger logging.Logger,
elements []printVersionElement) (err error) { versionFunctions map[string]func(ctx context.Context) (string, error)) {
const timeout = 5 * time.Second const timeout = 5 * time.Second
ctx, cancel := context.WithTimeout(ctx, timeout) ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel() defer cancel()
for name, f := range versionFunctions {
for _, element := range elements { version, err := f(ctx)
version, err := element.getVersion(ctx)
if err != nil { if err != nil {
return err logger.Error(err)
} else {
logger.Info("%s version: %s", name, version)
}
} }
logger.Info(element.name + " version: " + version)
} }
return nil //nolint:lll
func collectStreamLines(ctx context.Context, streamMerger command.StreamMerger,
logger logging.Logger, signalTunnelReady func()) {
// Blocking line merging paramsReader for openvpn and unbound
logger.Info("Launching standard output merger")
streamMerger.CollectLines(ctx, func(line string) {
line, level := gluetunLogging.PostProcessLine(line)
if line == "" {
return
}
switch level {
case logging.DebugLevel:
logger.Debug(line)
case logging.InfoLevel:
logger.Info(line)
case logging.WarnLevel:
logger.Warn(line)
case logging.ErrorLevel:
logger.Error(line)
}
switch {
case strings.Contains(line, "Initialization Sequence Completed"):
signalTunnelReady()
case strings.Contains(line, "TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)"):
logger.Warn("This means that either...")
logger.Warn("1. The VPN server IP address you are trying to connect to is no longer valid, see https://github.com/qdm12/gluetun/wiki/Update-servers-information")
logger.Warn("2. The VPN server crashed, try changing region")
logger.Warn("3. Your Internet connection is not working, ensure it works")
logger.Warn("Feel free to create an issue at https://github.com/qdm12/gluetun/issues/new/choose")
}
}, func(err error) {
logger.Warn(err)
})
} }
func routeReadyEvents(ctx context.Context, done chan<- struct{}, buildInfo models.BuildInformation, func routeReadyEvents(ctx context.Context, wg *sync.WaitGroup, buildInfo models.BuildInformation,
tunnelReadyCh <-chan struct{}, tunnelReadyCh, dnsReadyCh <-chan struct{},
unboundLooper dns.Looper, updaterLooper updater.Looper, publicIPLooper publicip.Looper, unboundLooper dns.Looper, updaterLooper updater.Looper, publicIPLooper publicip.Looper,
routing routing.VPNGetter, logger logging.Logger, httpClient *http.Client, routing routing.Routing, logger logging.Logger, httpClient *http.Client,
versionInformation, portForwardingEnabled bool, startPortForward func(vpnGateway net.IP)) { versionInformation, portForwardingEnabled bool, startPortForward func(vpnGateway net.IP)) {
defer close(done) defer wg.Done()
tickerWg := &sync.WaitGroup{}
// for linters only // for linters only
var restartTickerContext context.Context var restartTickerContext context.Context
var restartTickerCancel context.CancelFunc = func() {} var restartTickerCancel context.CancelFunc = func() {}
unboundTickerDone := make(chan struct{})
close(unboundTickerDone)
updaterTickerDone := make(chan struct{})
close(updaterTickerDone)
first := true
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
restartTickerCancel() // for linters only restartTickerCancel() // for linters only
<-unboundTickerDone tickerWg.Wait()
<-updaterTickerDone
return return
case <-tunnelReadyCh: // blocks until openvpn is connected case <-tunnelReadyCh: // blocks until openvpn is connected
if unboundLooper.GetSettings().Enabled {
_, _ = unboundLooper.SetStatus(constants.Running)
}
restartTickerCancel() // stop previous restart tickers
tickerWg.Wait()
restartTickerContext, restartTickerCancel = context.WithCancel(ctx)
tickerWg.Add(2) //nolint:gomnd
go unboundLooper.RunRestartTicker(restartTickerContext, tickerWg)
go updaterLooper.RunRestartTicker(restartTickerContext, tickerWg)
vpnDestination, err := routing.VPNDestinationIP() vpnDestination, err := routing.VPNDestinationIP()
if err != nil { if err != nil {
logger.Warn(err.Error()) logger.Warn(err)
} else { } else {
logger.Info("VPN routing IP address: " + vpnDestination.String()) logger.Info("VPN routing IP address: %s", vpnDestination)
} }
if unboundLooper.GetSettings().Enabled {
_, _ = unboundLooper.ApplyStatus(ctx, constants.Running)
}
restartTickerCancel() // stop previous restart tickers
<-unboundTickerDone
<-updaterTickerDone
restartTickerContext, restartTickerCancel = context.WithCancel(ctx)
// Runs the Public IP getter job once
_, _ = publicIPLooper.ApplyStatus(ctx, constants.Running)
if versionInformation && first {
first = false
message, err := versionpkg.GetMessage(ctx, buildInfo, httpClient)
if err != nil {
logger.Error("cannot get version information: " + err.Error())
} else {
logger.Info(message)
}
}
unboundTickerDone = make(chan struct{})
updaterTickerDone = make(chan struct{})
go unboundLooper.RunRestartTicker(restartTickerContext, unboundTickerDone)
go updaterLooper.RunRestartTicker(restartTickerContext, updaterTickerDone)
if portForwardingEnabled { if portForwardingEnabled {
// vpnGateway required only for PIA // vpnGateway required only for PIA
vpnGateway, err := routing.VPNLocalGatewayIP() vpnGateway, err := routing.VPNLocalGatewayIP()
if err != nil { if err != nil {
logger.Error("cannot get VPN local gateway IP: " + err.Error()) logger.Error(err)
} }
logger.Info("VPN gateway IP address: " + vpnGateway.String()) logger.Info("VPN gateway IP address: %s", vpnGateway)
startPortForward(vpnGateway) startPortForward(vpnGateway)
} }
case <-dnsReadyCh:
// Runs the Public IP getter job once
_, _ = publicIPLooper.SetStatus(constants.Running)
if !versionInformation {
break
}
message, err := versionpkg.GetMessage(ctx, buildInfo, httpClient)
if err != nil {
logger.Error(err)
break
}
logger.Info(message)
} }
} }
} }

BIN
doc/paypal.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
doc/sponsors.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
doc/windscribe.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -14,11 +14,18 @@ services:
# command: # command:
volumes: volumes:
- /yourpath:/gluetun - /yourpath:/gluetun
secrets:
- openvpn_user
- openvpn_password
environment: environment:
# More variables are available, see the readme table # More variables are available, see the readme table
- OPENVPN_USER=
- OPENVPN_PASSWORD=
- VPNSP=private internet access - VPNSP=private internet access
# Timezone for accurate logs times # Timezone for accurate logs times
- TZ= - TZ=
restart: always restart: always
secrets:
openvpn_user:
file: ./openvpn_user
openvpn_password:
file: ./openvpn_password

20
go.mod
View File

@@ -1,18 +1,14 @@
module github.com/qdm12/gluetun module github.com/qdm12/gluetun
go 1.16 go 1.15
require ( require (
github.com/fatih/color v1.12.0 github.com/fatih/color v1.10.0
github.com/golang/mock v1.6.0 github.com/golang/mock v1.4.4
github.com/qdm12/dns v1.11.0 github.com/kyokomi/emoji v2.2.4+incompatible
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2 github.com/qdm12/golibs v0.0.0-20210102015428-6e1d159e61a3
github.com/qdm12/goshutdown v0.1.0 github.com/qdm12/ss-server v0.1.0
github.com/qdm12/gosplash v0.1.0 github.com/stretchr/testify v1.6.1
github.com/qdm12/ss-server v0.2.0
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
github.com/stretchr/testify v1.7.0
github.com/vishvananda/netlink v1.1.0 github.com/vishvananda/netlink v1.1.0
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c golang.org/x/sys v0.0.0-20201223074533-0d417f636930
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722
) )

178
go.sum
View File

@@ -1,169 +1,149 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb h1:D4uzjWwKYQ5XnAvUbuvHW93esHg7F8N/OYeBBcJoTr0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0 h1:8JV+dzJJiK46XqGLqqLav8ZfEiJECp8jlOFhpiCdZ+0=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.17.2 h1:azEQ8Fnx0jmtFF2fxsnmd6I0x6rsweUF63qqSO1NmKk=
github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonreference v0.17.0 h1:yJW3HCkTHg7NOA+gZ83IPHzUSnUzGXhGmsdiCcMexbA=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/loads v0.17.0 h1:H22nMs3GDQk4SwAaFQ+jLNw+0xoFeCueawhZlv8MBYs=
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
github.com/go-openapi/runtime v0.17.2 h1:/ZK67ikFhQAMFFH/aPu2MaGH7QjP4wHBvHYOVIzDAw0=
github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q= github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q=
github.com/go-openapi/spec v0.17.0 h1:XNvrt8FlSVP8T1WuhbAFF6QDhJc0zsoWzX4wXARhhpE=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/strfmt v0.17.0 h1:1isAxYf//QDTnVzbLAMrUK++0k1EjeLJU/gTOR0o3Mc=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/validate v0.17.0 h1:pqoViQz3YLOGIhAmD0N4Lt6pa/3Gnj3ymKqQwq8iS6U=
github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gotify/go-api-client/v2 v2.0.4 h1:0w8skCr8aLBDKaQDg31LKKHUGF7rt7zdRpR+6cqIAlE=
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU= github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kyokomi/emoji v2.2.4+incompatible h1:np0woGKwx9LiHAQmwZx79Oc0rHpNw3o+3evou4BEPv4=
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA= github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs=
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY= github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/qdm12/dns v1.11.0 h1:jpcD5DZXXQSQe5a263PL09ghukiIdptvXFOZvyKEm6Q= github.com/qdm12/golibs v0.0.0-20210102015428-6e1d159e61a3 h1:tnkjZkYZuAFNga7Wd/j3z3gPJLkv0OXow4q/YTkRdmE=
github.com/qdm12/dns v1.11.0/go.mod h1:FmQsNOUcrrZ4UFzWAiED56AKXeNgaX3ySbmPwEfNjjE= github.com/qdm12/golibs v0.0.0-20210102015428-6e1d159e61a3/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
github.com/qdm12/golibs v0.0.0-20210603202746-e5494e9c2ebb/go.mod h1:15RBzkun0i8XB7ADIoLJWp9ITRgsz3LroEI2FiOXLRg= github.com/qdm12/ss-server v0.1.0 h1:WV9MkHCDEWRwe4WpnYFeR/zcZAxYoTbfntLDnw9AQ50=
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2 h1:FMeOhe/bGloI0T5Wb6QB7/rfOqgFeI//UF/N/f7PUCI= github.com/qdm12/ss-server v0.1.0/go.mod h1:ABVUkxubboL3vqBkOwDV9glX1/x7SnYrckBe5d+M/zw=
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
github.com/qdm12/goshutdown v0.1.0 h1:lmwnygdXtnr2pa6VqfR/bm8077/BnBef1+7CP96B7Sw=
github.com/qdm12/goshutdown v0.1.0/go.mod h1:/LP3MWLqI+wGH/ijfaUG+RHzBbKXIiVKnrg5vXOCf6Q=
github.com/qdm12/gosplash v0.1.0 h1:Sfl+zIjFZFP7b0iqf2l5UkmEY97XBnaKkH3FNY6Gf7g=
github.com/qdm12/gosplash v0.1.0/go.mod h1:+A3fWW4/rUeDXhY3ieBzwghKdnIPFJgD8K3qQkenJlw=
github.com/qdm12/ss-server v0.2.0 h1:+togLzeeLAJ68MD1JqOWvYi9rl9t/fx1Qh7wKzZhY1g=
github.com/qdm12/ss-server v0.2.0/go.mod h1:+1bWO1EfWNvsGM5Cuep6vneChK2OHniqtAsED9Fh1y0=
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e h1:4q+uFLawkaQRq3yARYLsjJPZd2wYwxn4g6G/5v0xW1g=
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e/go.mod h1:UvJRGkZ9XL3/D7e7JiTTVLm1F3Cymd3/gFpD6frEpBo=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
inet.af/netaddr v0.0.0-20210511181906-37180328850c/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722 h1:Qws2rZnQudC58cIagVucPQDLmMi3kAXgxscsgD0v6DU= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@@ -1,29 +1,22 @@
// Package alpine defines a configurator to interact with the Alpine operating system.
package alpine package alpine
import ( import (
"os/user" "github.com/qdm12/golibs/os"
"github.com/qdm12/golibs/os/user"
) )
var _ Alpiner = (*Alpine)(nil) type Configurator interface {
CreateUser(username string, uid int) (createdUsername string, err error)
type Alpiner interface {
UserCreater
VersionGetter
} }
type Alpine struct { type configurator struct {
alpineReleasePath string openFile os.OpenFileFunc
passwdPath string osUser user.OSUser
lookupID func(uid string) (*user.User, error)
lookup func(username string) (*user.User, error)
} }
func New() *Alpine { func NewConfigurator(openFile os.OpenFileFunc, osUser user.OSUser) Configurator {
return &Alpine{ return &configurator{
alpineReleasePath: "/etc/alpine-release", openFile: openFile,
passwdPath: "/etc/passwd", osUser: osUser,
lookupID: user.LookupId,
lookup: user.Lookup,
} }
} }

View File

@@ -1,51 +1,35 @@
package alpine package alpine
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"os/user" "os/user"
"strconv"
) )
var (
ErrUserAlreadyExists = errors.New("user already exists")
)
type UserCreater interface {
CreateUser(username string, uid int) (createdUsername string, err error)
}
// CreateUser creates a user in Alpine with the given UID. // CreateUser creates a user in Alpine with the given UID.
func (a *Alpine) CreateUser(username string, uid int) (createdUsername string, err error) { func (c *configurator) CreateUser(username string, uid int) (createdUsername string, err error) {
UIDStr := strconv.Itoa(uid) UIDStr := fmt.Sprintf("%d", uid)
u, err := a.lookupID(UIDStr) u, err := c.osUser.LookupID(UIDStr)
_, unknownUID := err.(user.UnknownUserIdError) _, unknownUID := err.(user.UnknownUserIdError)
if err != nil && !unknownUID { if err != nil && !unknownUID {
return "", err return "", fmt.Errorf("cannot create user: %w", err)
} } else if u != nil {
if u != nil {
if u.Username == username { if u.Username == username {
return "", nil return "", nil
} }
return u.Username, nil return u.Username, nil
} }
u, err = c.osUser.Lookup(username)
u, err = a.lookup(username)
_, unknownUsername := err.(user.UnknownUserError) _, unknownUsername := err.(user.UnknownUserError)
if err != nil && !unknownUsername { if err != nil && !unknownUsername {
return "", err return "", fmt.Errorf("cannot create user: %w", err)
} else if u != nil {
return "", fmt.Errorf("cannot create user: user with name %s already exists for ID %s instead of %d",
username, u.Uid, uid)
} }
file, err := c.openFile("/etc/passwd", os.O_APPEND|os.O_WRONLY, 0644)
if u != nil {
return "", fmt.Errorf("%w: with name %s for ID %s instead of %d",
ErrUserAlreadyExists, username, u.Uid, uid)
}
file, err := os.OpenFile(a.passwdPath, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil { if err != nil {
return "", err return "", fmt.Errorf("cannot create user: %w", err)
} }
s := fmt.Sprintf("%s:x:%d:::/dev/null:/sbin/nologin\n", username, uid) s := fmt.Sprintf("%s:x:%d:::/dev/null:/sbin/nologin\n", username, uid)
_, err = file.WriteString(s) _, err = file.WriteString(s)
@@ -53,6 +37,5 @@ func (a *Alpine) CreateUser(username string, uid int) (createdUsername string, e
_ = file.Close() _ = file.Close()
return "", err return "", err
} }
return username, file.Close() return username, file.Close()
} }

View File

@@ -1,31 +0,0 @@
package alpine
import (
"context"
"io"
"os"
"strings"
)
type VersionGetter interface {
Version(ctx context.Context) (version string, err error)
}
func (a *Alpine) Version(ctx context.Context) (version string, err error) {
file, err := os.OpenFile(a.alpineReleasePath, os.O_RDONLY, 0)
if err != nil {
return "", err
}
b, err := io.ReadAll(file)
if err != nil {
return "", err
}
if err := file.Close(); err != nil {
return "", err
}
version = strings.ReplaceAll(string(b), "\n", "")
return version, nil
}

View File

@@ -1,7 +0,0 @@
package cli
import "context"
func (c *CLI) CI(context context.Context) error {
return nil
}

View File

@@ -1,21 +1,20 @@
// Package cli defines an interface CLI to run command line operations.
package cli package cli
var _ CLIer = (*CLI)(nil) import (
"context"
type CLIer interface { "github.com/qdm12/golibs/os"
ClientKeyFormatter )
HealthChecker
OpenvpnConfigMaker type CLI interface {
Updater ClientKey(args []string, openFile os.OpenFileFunc) error
HealthCheck(ctx context.Context) error
OpenvpnConfig(os os.OS) error
Update(args []string, os os.OS) error
} }
type CLI struct { type cli struct{}
repoServersPath string
}
func New() *CLI { func New() CLI {
return &CLI{ return &cli{}
repoServersPath: "./internal/constants/servers.json",
}
} }

View File

@@ -3,28 +3,24 @@ package cli
import ( import (
"flag" "flag"
"fmt" "fmt"
"io" "io/ioutil"
"os"
"strings" "strings"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/os"
) )
type ClientKeyFormatter interface { func (c *cli) ClientKey(args []string, openFile os.OpenFileFunc) error {
ClientKey(args []string) error
}
func (c *CLI) ClientKey(args []string) error {
flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError) flagSet := flag.NewFlagSet("clientkey", flag.ExitOnError)
filepath := flagSet.String("path", constants.ClientKey, "file path to the client.key file") filepath := flagSet.String("path", string(constants.ClientKey), "file path to the client.key file")
if err := flagSet.Parse(args); err != nil { if err := flagSet.Parse(args); err != nil {
return err return err
} }
file, err := os.OpenFile(*filepath, os.O_RDONLY, 0) file, err := openFile(*filepath, os.O_RDONLY, 0)
if err != nil { if err != nil {
return err return err
} }
data, err := io.ReadAll(file) data, err := ioutil.ReadAll(file)
if err != nil { if err != nil {
_ = file.Close() _ = file.Close()
return err return err

View File

@@ -2,39 +2,19 @@ package cli
import ( import (
"context" "context"
"net"
"net/http" "net/http"
"time" "time"
"github.com/qdm12/gluetun/internal/configuration" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/healthcheck" "github.com/qdm12/gluetun/internal/healthcheck"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
) )
type HealthChecker interface { func (c *cli) HealthCheck(ctx context.Context) error {
HealthCheck(ctx context.Context, env params.Env, logger logging.Logger) error
}
func (c *CLI) HealthCheck(ctx context.Context, env params.Env,
logger logging.Logger) error {
// Extract the health server port from the configuration.
config := configuration.Health{}
err := config.Read(env, logger)
if err != nil {
return err
}
_, port, err := net.SplitHostPort(config.ServerAddress)
if err != nil {
return err
}
const timeout = 10 * time.Second const timeout = 10 * time.Second
httpClient := &http.Client{Timeout: timeout} httpClient := &http.Client{Timeout: timeout}
client := healthcheck.NewClient(httpClient) healthchecker := healthcheck.NewChecker(httpClient)
ctx, cancel := context.WithTimeout(ctx, timeout) ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel() defer cancel()
const url = "http://" + constants.HealthcheckAddress
url := "http://127.0.0.1:" + port return healthchecker.Check(ctx, url)
return client.Check(ctx, url)
} }

View File

@@ -5,25 +5,26 @@ import (
"strings" "strings"
"time" "time"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/params"
"github.com/qdm12/gluetun/internal/provider" "github.com/qdm12/gluetun/internal/provider"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/gluetun/internal/storage" "github.com/qdm12/gluetun/internal/storage"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/os"
) )
type OpenvpnConfigMaker interface { func (c *cli) OpenvpnConfig(os os.OS) error {
OpenvpnConfig(logger logging.Logger) error logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel)
}
func (c *CLI) OpenvpnConfig(logger logging.Logger) error {
var allSettings configuration.Settings
err := allSettings.Read(params.NewEnv(), logger)
if err != nil { if err != nil {
return err return err
} }
allServers, err := storage.New(logger, constants.ServersData). paramsReader := params.NewReader(logger, os)
allSettings, err := settings.GetAllSettings(paramsReader)
if err != nil {
return err
}
allServers, err := storage.New(logger, os, constants.ServersData).
SyncServers(constants.GetAllServers()) SyncServers(constants.GetAllServers())
if err != nil { if err != nil {
return err return err
@@ -33,7 +34,15 @@ func (c *CLI) OpenvpnConfig(logger logging.Logger) error {
if err != nil { if err != nil {
return err return err
} }
lines := providerConf.BuildConf(connection, "nonroortuser", allSettings.OpenVPN) lines := providerConf.BuildConf(
connection,
allSettings.OpenVPN.Verbosity,
"nonroortuser",
allSettings.OpenVPN.Root,
allSettings.OpenVPN.Cipher,
allSettings.OpenVPN.Auth,
allSettings.OpenVPN.Provider.ExtraConfigOptions,
)
fmt.Println(strings.Join(lines, "\n")) fmt.Println(strings.Join(lines, "\n"))
return nil return nil
} }

View File

@@ -2,110 +2,63 @@ package cli
import ( import (
"context" "context"
"encoding/json"
"errors"
"flag" "flag"
"fmt" "fmt"
"net/http" "net/http"
"os"
"time" "time"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/gluetun/internal/storage" "github.com/qdm12/gluetun/internal/storage"
"github.com/qdm12/gluetun/internal/updater" "github.com/qdm12/gluetun/internal/updater"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os"
) )
var ( func (c *cli) Update(args []string, os os.OS) error {
ErrModeUnspecified = errors.New("at least one of -enduser or -maintainers must be specified") options := settings.Updater{CLI: true}
ErrSyncServers = errors.New("cannot sync hardcoded and persisted servers") var flushToFile bool
ErrUpdateServerInformation = errors.New("cannot update server information")
ErrWriteToFile = errors.New("cannot write updated information to file")
)
type Updater interface {
Update(ctx context.Context, args []string, logger logging.Logger) error
}
func (c *CLI) Update(ctx context.Context, args []string, logger logging.Logger) error {
options := configuration.Updater{CLI: true}
var endUserMode, maintainerMode, updateAll bool
flagSet := flag.NewFlagSet("update", flag.ExitOnError) flagSet := flag.NewFlagSet("update", flag.ExitOnError)
flagSet.BoolVar(&endUserMode, "enduser", false, "Write results to /gluetun/servers.json (for end users)") flagSet.BoolVar(&flushToFile, "file", false, "Write results to /gluetun/servers.json (for end users)")
flagSet.BoolVar(&maintainerMode, "maintainer", false, flagSet.BoolVar(&options.Stdout, "stdout", false, "Write results to console to modify the program (for maintainers)")
"Write results to ./internal/constants/servers.json to modify the program (for maintainers)") flagSet.StringVar(&options.DNSAddress, "dns", "1.1.1.1", "DNS resolver address to use")
flagSet.StringVar(&options.DNSAddress, "dns", "8.8.8.8", "DNS resolver address to use")
flagSet.BoolVar(&updateAll, "all", false, "Update servers for all VPN providers")
flagSet.BoolVar(&options.Cyberghost, "cyberghost", false, "Update Cyberghost servers") flagSet.BoolVar(&options.Cyberghost, "cyberghost", false, "Update Cyberghost servers")
flagSet.BoolVar(&options.Fastestvpn, "fastestvpn", false, "Update FastestVPN servers")
flagSet.BoolVar(&options.HideMyAss, "hidemyass", false, "Update HideMyAss servers")
flagSet.BoolVar(&options.Ipvanish, "ipvanish", false, "Update IpVanish servers")
flagSet.BoolVar(&options.Ivpn, "ivpn", false, "Update IVPN servers")
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.Privado, "privado", false, "Update Privado servers") flagSet.BoolVar(&options.Privado, "privado", false, "Update Privado servers")
flagSet.BoolVar(&options.Privatevpn, "privatevpn", false, "Update Private VPN servers")
flagSet.BoolVar(&options.Protonvpn, "protonvpn", false, "Update Protonvpn 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.Torguard, "torguard", false, "Update Torguard servers")
flagSet.BoolVar(&options.VPNUnlimited, "vpnunlimited", false, "Update VPN Unlimited servers")
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers") flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")
flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers") flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers")
if err := flagSet.Parse(args); err != nil { if err := flagSet.Parse(args); err != nil {
return err return err
} }
if !endUserMode && !maintainerMode { logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel)
return ErrModeUnspecified if err != nil {
return err
} }
if !flushToFile && !options.Stdout {
if updateAll { return fmt.Errorf("at least one of -file or -stdout must be specified")
options.EnableAll()
} }
ctx := context.Background()
const clientTimeout = 10 * time.Second const clientTimeout = 10 * time.Second
httpClient := &http.Client{Timeout: clientTimeout} httpClient := &http.Client{Timeout: clientTimeout}
storage := storage.New(logger, constants.ServersData) storage := storage.New(logger, os, constants.ServersData)
currentServers, err := storage.SyncServers(constants.GetAllServers()) currentServers, err := storage.SyncServers(constants.GetAllServers())
if err != nil { if err != nil {
return fmt.Errorf("%w: %s", ErrSyncServers, err) return fmt.Errorf("cannot update servers: %w", err)
} }
updater := updater.New(options, httpClient, currentServers, logger) updater := updater.New(options, httpClient, currentServers, logger)
allServers, err := updater.UpdateServers(ctx) allServers, err := updater.UpdateServers(ctx)
if err != nil { if err != nil {
return fmt.Errorf("%w: %s", ErrUpdateServerInformation, err) return err
} }
if flushToFile {
if endUserMode {
if err := storage.FlushToFile(allServers); err != nil { if err := storage.FlushToFile(allServers); err != nil {
return fmt.Errorf("%w: %s", ErrWriteToFile, err) return fmt.Errorf("cannot update servers: %w", err)
}
}
if maintainerMode {
if err := writeToEmbeddedJSON(c.repoServersPath, allServers); err != nil {
return fmt.Errorf("%w: %s", ErrWriteToFile, err)
} }
} }
return nil return nil
} }
func writeToEmbeddedJSON(repoServersPath string,
allServers models.AllServers) error {
const perms = 0600
f, err := os.OpenFile(repoServersPath,
os.O_TRUNC|os.O_WRONLY|os.O_CREATE, perms)
if err != nil {
return err
}
defer f.Close()
encoder := json.NewEncoder(f)
encoder.SetIndent("", " ")
return encoder.Encode(allServers)
}

View File

@@ -1,3 +0,0 @@
// Package configuration reads initial settings from environment variables
// and secret files.
package configuration

View File

@@ -1,6 +0,0 @@
package configuration
const (
lastIndent = "|--"
indent = " "
)

View File

@@ -1,72 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) cyberghostLines() (lines []string) {
lines = append(lines, lastIndent+"Server group: "+settings.ServerSelection.Group)
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ExtraConfigOptions.ClientKey != "" {
lines = append(lines, lastIndent+"Client key is set")
}
if settings.ExtraConfigOptions.ClientCertificate != "" {
lines = append(lines, lastIndent+"Client certificate is set")
}
return lines
}
func (settings *Provider) readCyberghost(r reader) (err error) {
settings.Name = constants.Cyberghost
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ExtraConfigOptions.ClientKey, err = readClientKey(r)
if err != nil {
return err
}
settings.ExtraConfigOptions.ClientCertificate, err = readClientCertificate(r)
if err != nil {
return err
}
settings.ServerSelection.Group, err = r.env.Inside("CYBERGHOST_GROUP",
constants.CyberghostGroupChoices(), params.Default("Premium UDP Europe"))
if err != nil {
return fmt.Errorf("environment variable CYBERGHOST_GROUP: %w", err)
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.CyberghostRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.CyberghostHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -1,117 +0,0 @@
package configuration
import (
"errors"
"fmt"
"net"
"strings"
"time"
"github.com/qdm12/dns/pkg/blacklist"
"github.com/qdm12/dns/pkg/unbound"
"github.com/qdm12/golibs/params"
)
// DNS contains settings to configure Unbound for DNS over TLS operation.
type DNS struct { //nolint:maligned
Enabled bool
PlaintextAddress net.IP
KeepNameserver bool
UpdatePeriod time.Duration
Unbound unbound.Settings
BlacklistBuild blacklist.BuilderSettings
}
func (settings *DNS) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *DNS) lines() (lines []string) {
lines = append(lines, lastIndent+"DNS:")
if settings.PlaintextAddress != nil {
lines = append(lines, indent+lastIndent+"Plaintext address: "+settings.PlaintextAddress.String())
}
if settings.KeepNameserver {
lines = append(lines, indent+lastIndent+"Keep nameserver (disabled blocking): yes")
}
if !settings.Enabled {
return lines
}
lines = append(lines, indent+lastIndent+"DNS over TLS:")
lines = append(lines, indent+indent+lastIndent+"Unbound:")
for _, line := range settings.Unbound.Lines() {
lines = append(lines, indent+indent+indent+line)
}
lines = append(lines, indent+indent+lastIndent+"Blacklist:")
for _, line := range settings.BlacklistBuild.Lines(indent, lastIndent) {
lines = append(lines, indent+indent+indent+line)
}
if settings.UpdatePeriod > 0 {
lines = append(lines, indent+indent+lastIndent+"Update: every "+settings.UpdatePeriod.String())
}
return lines
}
var (
ErrUnboundSettings = errors.New("failed getting Unbound settings")
ErrBlacklistSettings = errors.New("failed getting DNS blacklist settings")
)
func (settings *DNS) read(r reader) (err error) {
settings.Enabled, err = r.env.OnOff("DOT", params.Default("on"))
if err != nil {
return fmt.Errorf("environment variable DOT: %w", err)
}
// Plain DNS settings
if err := settings.readDNSPlaintext(r.env); err != nil {
return err
}
settings.KeepNameserver, err = r.env.OnOff("DNS_KEEP_NAMESERVER", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable DNS_KEEP_NAMESERVER: %w", err)
}
// DNS over TLS external settings
if err := settings.readBlacklistBuilding(r); err != nil {
return fmt.Errorf("%w: %s", ErrBlacklistSettings, err)
}
settings.UpdatePeriod, err = r.env.Duration("DNS_UPDATE_PERIOD", params.Default("24h"))
if err != nil {
return fmt.Errorf("environment variable DNS_UPDATE_PERIOD: %w", err)
}
// Unbound settings
if err := settings.readUnbound(r); err != nil {
return fmt.Errorf("%w: %s", ErrUnboundSettings, err)
}
return nil
}
var (
ErrDNSAddressNotAnIP = errors.New("DNS plaintext address is not an IP address")
)
func (settings *DNS) readDNSPlaintext(env params.Env) error {
s, err := env.Get("DNS_PLAINTEXT_ADDRESS", params.Default("1.1.1.1"))
if err != nil {
return fmt.Errorf("environment variable DNS_PLAINTEXT_ADDRESS: %w", err)
}
settings.PlaintextAddress = net.ParseIP(s)
if settings.PlaintextAddress == nil {
return fmt.Errorf("%w: %s", ErrDNSAddressNotAnIP, s)
}
return nil
}

View File

@@ -1,76 +0,0 @@
package configuration
import (
"net"
"testing"
"time"
"github.com/qdm12/dns/pkg/blacklist"
"github.com/qdm12/dns/pkg/provider"
"github.com/qdm12/dns/pkg/unbound"
"github.com/stretchr/testify/assert"
)
func Test_DNS_Lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings DNS
lines []string
}{
"disabled DOT": {
settings: DNS{
PlaintextAddress: net.IP{1, 1, 1, 1},
},
lines: []string{
"|--DNS:",
" |--Plaintext address: 1.1.1.1",
},
},
"enabled DOT": {
settings: DNS{
Enabled: true,
KeepNameserver: true,
Unbound: unbound.Settings{
Providers: []provider.Provider{
provider.Cloudflare(),
},
},
BlacklistBuild: blacklist.BuilderSettings{
BlockMalicious: true,
BlockAds: true,
BlockSurveillance: true,
},
UpdatePeriod: time.Hour,
},
lines: []string{
"|--DNS:",
" |--Keep nameserver (disabled blocking): yes",
" |--DNS over TLS:",
" |--Unbound:",
" |--DNS over TLS providers:",
" |--Cloudflare",
" |--Listening port: 0",
" |--Access control:",
" |--Allowed:",
" |--Caching: disabled",
" |--IPv4 resolution: disabled",
" |--IPv6 resolution: disabled",
" |--Verbosity level: 0/5",
" |--Verbosity details level: 0/4",
" |--Validation log level: 0/2",
" |--Username: ",
" |--Blacklist:",
" |--Blocked categories: malicious, surveillance, ads",
" |--Update: every 1h0m0s",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}

View File

@@ -1,87 +0,0 @@
package configuration
import (
"errors"
"fmt"
"github.com/qdm12/golibs/params"
"inet.af/netaddr"
)
func (settings *DNS) readBlacklistBuilding(r reader) (err error) {
settings.BlacklistBuild.BlockMalicious, err = r.env.OnOff("BLOCK_MALICIOUS", params.Default("on"))
if err != nil {
return fmt.Errorf("environment variable BLOCK_MALICIOUS: %w", err)
}
settings.BlacklistBuild.BlockSurveillance, err = r.env.OnOff("BLOCK_SURVEILLANCE", params.Default("on"),
params.RetroKeys([]string{"BLOCK_NSA"}, r.onRetroActive))
if err != nil {
return fmt.Errorf("environment variable BLOCK_SURVEILLANCE (or BLOCK_NSA): %w", err)
}
settings.BlacklistBuild.BlockAds, err = r.env.OnOff("BLOCK_ADS", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable BLOCK_ADS: %w", err)
}
if err := settings.readPrivateAddresses(r.env); err != nil {
return err
}
return settings.readBlacklistUnblockedHostnames(r)
}
var (
ErrInvalidPrivateAddress = errors.New("private address is not a valid IP or CIDR range")
)
func (settings *DNS) readPrivateAddresses(env params.Env) (err error) {
privateAddresses, err := env.CSV("DOT_PRIVATE_ADDRESS")
if err != nil {
return fmt.Errorf("environment variable DOT_PRIVATE_ADDRESS: %w", err)
} else if len(privateAddresses) == 0 {
return nil
}
ips := make([]netaddr.IP, 0, len(privateAddresses))
ipPrefixes := make([]netaddr.IPPrefix, 0, len(privateAddresses))
for _, address := range privateAddresses {
ip, err := netaddr.ParseIP(address)
if err == nil {
ips = append(ips, ip)
continue
}
ipPrefix, err := netaddr.ParseIPPrefix(address)
if err == nil {
ipPrefixes = append(ipPrefixes, ipPrefix)
continue
}
return fmt.Errorf("%w: %s", ErrInvalidPrivateAddress, address)
}
settings.BlacklistBuild.AddBlockedIPs = append(settings.BlacklistBuild.AddBlockedIPs, ips...)
settings.BlacklistBuild.AddBlockedIPPrefixes = append(settings.BlacklistBuild.AddBlockedIPPrefixes, ipPrefixes...)
return nil
}
func (settings *DNS) readBlacklistUnblockedHostnames(r reader) (err error) {
hostnames, err := r.env.CSV("UNBLOCK")
if err != nil {
return fmt.Errorf("environment variable UNBLOCK: %w", err)
} else if len(hostnames) == 0 {
return nil
}
for _, hostname := range hostnames {
if !r.regex.MatchHostname(hostname) {
return fmt.Errorf("%w: %s", ErrInvalidHostname, hostname)
}
}
settings.BlacklistBuild.AllowedHosts = append(settings.BlacklistBuild.AllowedHosts, hostnames...)
return nil
}

View File

@@ -1,45 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) fastestvpnLines() (lines []string) {
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
return lines
}
func (settings *Provider) readFastestvpn(r reader) (err error) {
settings.Name = constants.Fastestvpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.FastestvpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.FastestvpnCountriesChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
return nil
}

View File

@@ -1,99 +0,0 @@
package configuration
import (
"fmt"
"net"
"strings"
"github.com/qdm12/golibs/params"
)
// Firewall contains settings to customize the firewall operation.
type Firewall struct {
VPNInputPorts []uint16
InputPorts []uint16
OutboundSubnets []net.IPNet
Enabled bool
Debug bool
}
func (settings *Firewall) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *Firewall) lines() (lines []string) {
if !settings.Enabled {
lines = append(lines, lastIndent+"Firewall: disabled ⚠️")
return lines
}
lines = append(lines, lastIndent+"Firewall:")
if settings.Debug {
lines = append(lines, indent+lastIndent+"Debug: on")
}
if len(settings.VPNInputPorts) > 0 {
lines = append(lines, indent+lastIndent+"VPN input ports: "+
strings.Join(uint16sToStrings(settings.VPNInputPorts), ", "))
}
if len(settings.InputPorts) > 0 {
lines = append(lines, indent+lastIndent+"Input ports: "+
strings.Join(uint16sToStrings(settings.InputPorts), ", "))
}
if len(settings.OutboundSubnets) > 0 {
lines = append(lines, indent+lastIndent+"Outbound subnets: "+
strings.Join(ipNetsToStrings(settings.OutboundSubnets), ", "))
}
return lines
}
func (settings *Firewall) read(r reader) (err error) {
settings.Enabled, err = r.env.OnOff("FIREWALL", params.Default("on"))
if err != nil {
return fmt.Errorf("environment variable FIREWALL: %w", err)
}
settings.Debug, err = r.env.OnOff("FIREWALL_DEBUG", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable FIREWALL_DEBUG: %w", err)
}
if err := settings.readVPNInputPorts(r.env); err != nil {
return err
}
if err := settings.readInputPorts(r.env); err != nil {
return err
}
return settings.readOutboundSubnets(r)
}
func (settings *Firewall) readVPNInputPorts(env params.Env) (err error) {
settings.VPNInputPorts, err = readCSVPorts(env, "FIREWALL_VPN_INPUT_PORTS")
if err != nil {
return fmt.Errorf("environment variable FIREWALL_VPN_INPUT_PORTS: %w", err)
}
return nil
}
func (settings *Firewall) readInputPorts(env params.Env) (err error) {
settings.InputPorts, err = readCSVPorts(env, "FIREWALL_INPUT_PORTS")
if err != nil {
return fmt.Errorf("environment variable FIREWALL_INPUT_PORTS: %w", err)
}
return nil
}
func (settings *Firewall) readOutboundSubnets(r reader) (err error) {
retroOption := params.RetroKeys([]string{"EXTRA_SUBNETS"}, r.onRetroActive)
settings.OutboundSubnets, err = readCSVIPNets(r.env, "FIREWALL_OUTBOUND_SUBNETS", retroOption)
if err != nil {
return fmt.Errorf("environment variable FIREWALL_OUTBOUND_SUBNETS: %w", err)
}
return nil
}

View File

@@ -1,62 +0,0 @@
package configuration
import (
"fmt"
"strings"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
)
// Health contains settings for the healthcheck and health server.
type Health struct {
ServerAddress string
OpenVPN HealthyWait
}
func (settings *Health) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *Health) lines() (lines []string) {
lines = append(lines, lastIndent+"Health:")
lines = append(lines, indent+lastIndent+"Server address: "+settings.ServerAddress)
lines = append(lines, indent+lastIndent+"OpenVPN:")
for _, line := range settings.OpenVPN.lines() {
lines = append(lines, indent+indent+line)
}
return lines
}
// Read is to be used for the healthcheck query mode.
func (settings *Health) Read(env params.Env, logger logging.Logger) (err error) {
reader := newReader(env, logger)
return settings.read(reader)
}
func (settings *Health) read(r reader) (err error) {
var warning string
settings.ServerAddress, warning, err = r.env.ListeningAddress(
"HEALTH_SERVER_ADDRESS", params.Default("127.0.0.1:9999"))
if warning != "" {
r.logger.Warn("environment variable HEALTH_SERVER_ADDRESS: " + warning)
}
if err != nil {
return fmt.Errorf("environment variable HEALTH_SERVER_ADDRESS: %w", err)
}
settings.OpenVPN.Initial, err = r.env.Duration("HEALTH_OPENVPN_DURATION_INITIAL", params.Default("6s"))
if err != nil {
return fmt.Errorf("environment variable HEALTH_OPENVPN_DURATION_INITIAL: %w", err)
}
settings.OpenVPN.Addition, err = r.env.Duration("HEALTH_OPENVPN_DURATION_ADDITION", params.Default("5s"))
if err != nil {
return fmt.Errorf("environment variable HEALTH_OPENVPN_DURATION_ADDITION: %w", err)
}
return nil
}

View File

@@ -1,182 +0,0 @@
package configuration
import (
"errors"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/qdm12/golibs/logging/mock_logging"
"github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Health_String(t *testing.T) {
t.Parallel()
var health Health
const expected = "|--Health:\n |--Server address: \n |--OpenVPN:\n |--Initial duration: 0s"
s := health.String()
assert.Equal(t, expected, s)
}
func Test_Health_lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Health
lines []string
}{
"empty": {
lines: []string{
"|--Health:",
" |--Server address: ",
" |--OpenVPN:",
" |--Initial duration: 0s",
},
},
"filled settings": {
settings: Health{
ServerAddress: "address:9999",
OpenVPN: HealthyWait{
Initial: time.Second,
Addition: time.Minute,
},
},
lines: []string{
"|--Health:",
" |--Server address: address:9999",
" |--OpenVPN:",
" |--Initial duration: 1s",
" |--Addition duration: 1m0s",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}
func Test_Health_read(t *testing.T) {
t.Parallel()
errDummy := errors.New("dummy")
testCases := map[string]struct {
openvpnInitialDuration time.Duration
openvpnInitialErr error
openvpnAdditionDuration time.Duration
openvpnAdditionErr error
serverAddress string
serverAddressWarning string
serverAddressErr error
expected Health
err error
}{
"success": {
openvpnInitialDuration: time.Second,
openvpnAdditionDuration: time.Minute,
serverAddress: "127.0.0.1:9999",
expected: Health{
ServerAddress: "127.0.0.1:9999",
OpenVPN: HealthyWait{
Initial: time.Second,
Addition: time.Minute,
},
},
},
"listening address error": {
openvpnInitialDuration: time.Second,
openvpnAdditionDuration: time.Minute,
serverAddress: "127.0.0.1:9999",
serverAddressWarning: "warning",
serverAddressErr: errDummy,
expected: Health{
ServerAddress: "127.0.0.1:9999",
},
err: errors.New("environment variable HEALTH_SERVER_ADDRESS: dummy"),
},
"initial error": {
openvpnInitialDuration: time.Second,
openvpnInitialErr: errDummy,
openvpnAdditionDuration: time.Minute,
expected: Health{
OpenVPN: HealthyWait{
Initial: time.Second,
},
},
err: errors.New("environment variable HEALTH_OPENVPN_DURATION_INITIAL: dummy"),
},
"addition error": {
openvpnInitialDuration: time.Second,
openvpnAdditionDuration: time.Minute,
openvpnAdditionErr: errDummy,
expected: Health{
OpenVPN: HealthyWait{
Initial: time.Second,
Addition: time.Minute,
},
},
err: errors.New("environment variable HEALTH_OPENVPN_DURATION_ADDITION: dummy"),
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl)
logger := mock_logging.NewMockLogger(ctrl)
env.EXPECT().ListeningAddress("HEALTH_SERVER_ADDRESS", gomock.Any()).
Return(testCase.serverAddress, testCase.serverAddressWarning,
testCase.serverAddressErr)
if testCase.serverAddressWarning != "" {
logger.EXPECT().Warn("environment variable HEALTH_SERVER_ADDRESS: " + testCase.serverAddressWarning)
}
if testCase.serverAddressErr == nil {
env.EXPECT().
Duration("HEALTH_OPENVPN_DURATION_INITIAL", gomock.Any()).
Return(testCase.openvpnInitialDuration, testCase.openvpnInitialErr)
if testCase.openvpnInitialErr == nil {
env.EXPECT().
Duration("HEALTH_OPENVPN_DURATION_ADDITION", gomock.Any()).
Return(testCase.openvpnAdditionDuration, testCase.openvpnAdditionErr)
}
}
r := reader{
env: env,
logger: logger,
}
var health Health
err := health.read(r)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.expected, health)
})
}
}

View File

@@ -1,55 +0,0 @@
package configuration
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func Test_HealthyWait_String(t *testing.T) {
t.Parallel()
var healthyWait HealthyWait
const expected = "|--Initial duration: 0s"
s := healthyWait.String()
assert.Equal(t, expected, s)
}
func Test_HealthyWait_lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings HealthyWait
lines []string
}{
"empty": {
lines: []string{
"|--Initial duration: 0s",
},
},
"filled settings": {
settings: HealthyWait{
Initial: time.Second,
Addition: time.Minute,
},
lines: []string{
"|--Initial duration: 1s",
"|--Addition duration: 1m0s",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}

View File

@@ -1,30 +0,0 @@
package configuration
import (
"strings"
"time"
)
type HealthyWait struct {
// Initial is the initial duration to wait for the program
// to be healthy before taking action.
Initial time.Duration
// Addition is the duration to add to the Initial duration
// after Initial has expired to wait longer for the program
// to be healthy.
Addition time.Duration
}
func (settings *HealthyWait) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *HealthyWait) lines() (lines []string) {
lines = append(lines, lastIndent+"Initial duration: "+settings.Initial.String())
if settings.Addition > 0 {
lines = append(lines, lastIndent+"Addition duration: "+settings.Addition.String())
}
return lines
}

View File

@@ -1,63 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) hideMyAssLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readHideMyAss(r reader) (err error) {
settings.Name = constants.HideMyAss
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.HideMyAssCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.HideMyAssCountryChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.HideMyAssCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.HideMyAssHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -1,106 +0,0 @@
package configuration
import (
"fmt"
"strconv"
"strings"
"github.com/qdm12/golibs/params"
)
// HTTPProxy contains settings to configure the HTTP proxy.
type HTTPProxy struct {
User string
Password string
Port uint16
Enabled bool
Stealth bool
Log bool
}
func (settings *HTTPProxy) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *HTTPProxy) lines() (lines []string) {
if !settings.Enabled {
return nil
}
lines = append(lines, lastIndent+"HTTP proxy:")
lines = append(lines, indent+lastIndent+"Port: "+strconv.Itoa(int(settings.Port)))
if settings.User != "" {
lines = append(lines, indent+lastIndent+"Authentication: enabled")
}
if settings.Log {
lines = append(lines, indent+lastIndent+"Log: enabled")
}
if settings.Stealth {
lines = append(lines, indent+lastIndent+"Stealth: enabled")
}
return lines
}
func (settings *HTTPProxy) read(r reader) (err error) {
settings.Enabled, err = r.env.OnOff("HTTPPROXY", params.Default("off"),
params.RetroKeys([]string{"TINYPROXY", "PROXY"}, r.onRetroActive))
if err != nil {
return fmt.Errorf("environment variable HTTPPROXY (or TINYPROXY, PROXY): %w", err)
}
settings.User, err = r.getFromEnvOrSecretFile("HTTPPROXY_USER", false, // compulsory
[]string{"TINYPROXY_USER", "PROXY_USER"})
if err != nil {
return fmt.Errorf("environment variable HTTPPROXY_USER (or TINYPROXY_USER, PROXY_USER): %w", err)
}
settings.Password, err = r.getFromEnvOrSecretFile("HTTPPROXY_PASSWORD", false,
[]string{"TINYPROXY_PASSWORD", "PROXY_PASSWORD"})
if err != nil {
return fmt.Errorf("environment variable HTTPPROXY_PASSWORD (or TINYPROXY_PASSWORD, PROXY_PASSWORD): %w", err)
}
settings.Stealth, err = r.env.OnOff("HTTPPROXY_STEALTH", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable HTTPPROXY_STEALTH: %w", err)
}
if err := settings.readLog(r); err != nil {
return err
}
var warning string
settings.Port, warning, err = r.env.ListeningPort("HTTPPROXY_PORT", params.Default("8888"),
params.RetroKeys([]string{"TINYPROXY_PORT", "PROXY_PORT"}, r.onRetroActive))
if len(warning) > 0 {
r.logger.Warn(warning)
}
if err != nil {
return fmt.Errorf("environment variable HTTPPROXY_PORT (or TINYPROXY_PORT, PROXY_PORT): %w", err)
}
return nil
}
func (settings *HTTPProxy) readLog(r reader) error {
s, err := r.env.Get("HTTPPROXY_LOG",
params.RetroKeys([]string{"PROXY_LOG_LEVEL", "TINYPROXY_LOG"}, r.onRetroActive))
if err != nil {
return fmt.Errorf("environment variable HTTPPROXY_LOG (or TINYPROXY_LOG, PROXY_LOG_LEVEL): %w", err)
}
switch strings.ToLower(s) {
case "on":
settings.Log = true
// Retro compatibility
case "info", "connect", "notice":
settings.Log = true
}
return nil
}

View File

@@ -1,54 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) ipvanishLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readIpvanish(r reader) (err error) {
settings.Name = constants.Ipvanish
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IpvanishCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IpvanishCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -1,192 +0,0 @@
package configuration
import (
"errors"
"net"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Provider_ipvanishLines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Provider
lines []string
}{
"empty settings": {},
"full settings": {
settings: Provider{
ServerSelection: ServerSelection{
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
lines: []string{
"|--Countries: A, B",
"|--Cities: C, D",
"|--Hostnames: E, F",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.ipvanishLines()
assert.Equal(t, testCase.lines, lines)
})
}
}
func Test_Provider_readIpvanish(t *testing.T) {
t.Parallel()
var errDummy = errors.New("dummy test error")
type singleStringCall struct {
call bool
value string
err error
}
type sliceStringCall struct {
call bool
values []string
err error
}
testCases := map[string]struct {
protocol singleStringCall
targetIP singleStringCall
countries sliceStringCall
cities sliceStringCall
hostnames sliceStringCall
settings Provider
err error
}{
"protocol error": {
protocol: singleStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ipvanish,
},
err: errors.New("environment variable PROTOCOL: dummy test error"),
},
"target IP error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true, value: "something", err: errDummy},
settings: Provider{
Name: constants.Ipvanish,
},
err: errors.New("environment variable OPENVPN_TARGET_IP: dummy test error"),
},
"countries error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ipvanish,
},
err: errors.New("environment variable COUNTRY: dummy test error"),
},
"cities error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ipvanish,
},
err: errors.New("environment variable CITY: dummy test error"),
},
"hostnames error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ipvanish,
},
err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"),
},
"default settings": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
settings: Provider{
Name: constants.Ipvanish,
},
},
"set settings": {
protocol: singleStringCall{call: true, value: constants.TCP},
targetIP: singleStringCall{call: true, value: "1.2.3.4"},
countries: sliceStringCall{call: true, values: []string{"A", "B"}},
cities: sliceStringCall{call: true, values: []string{"C", "D"}},
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
settings: Provider{
Name: constants.Ipvanish,
ServerSelection: ServerSelection{
TCP: true,
TargetIP: net.IPv4(1, 2, 3, 4),
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl)
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
Return(testCase.protocol.value, testCase.protocol.err)
}
if testCase.targetIP.call {
env.EXPECT().Get("OPENVPN_TARGET_IP").
Return(testCase.targetIP.value, testCase.targetIP.err)
}
if testCase.countries.call {
env.EXPECT().CSVInside("COUNTRY", constants.IpvanishCountryChoices()).
Return(testCase.countries.values, testCase.countries.err)
}
if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.IpvanishCityChoices()).
Return(testCase.cities.values, testCase.cities.err)
}
if testCase.hostnames.call {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices()).
Return(testCase.hostnames.values, testCase.hostnames.err)
}
r := reader{env: env}
var settings Provider
err := settings.readIpvanish(r)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.settings, settings)
})
}
}

View File

@@ -1,54 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) ivpnLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readIvpn(r reader) (err error) {
settings.Name = constants.Ivpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IvpnCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IvpnCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -1,192 +0,0 @@
package configuration
import (
"errors"
"net"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Provider_ivpnLines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Provider
lines []string
}{
"empty settings": {},
"full settings": {
settings: Provider{
ServerSelection: ServerSelection{
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
lines: []string{
"|--Countries: A, B",
"|--Cities: C, D",
"|--Hostnames: E, F",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.ivpnLines()
assert.Equal(t, testCase.lines, lines)
})
}
}
func Test_Provider_readIvpn(t *testing.T) {
t.Parallel()
var errDummy = errors.New("dummy test error")
type singleStringCall struct {
call bool
value string
err error
}
type sliceStringCall struct {
call bool
values []string
err error
}
testCases := map[string]struct {
protocol singleStringCall
targetIP singleStringCall
countries sliceStringCall
cities sliceStringCall
hostnames sliceStringCall
settings Provider
err error
}{
"protocol error": {
protocol: singleStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable PROTOCOL: dummy test error"),
},
"target IP error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true, value: "something", err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable OPENVPN_TARGET_IP: dummy test error"),
},
"countries error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable COUNTRY: dummy test error"),
},
"cities error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable CITY: dummy test error"),
},
"hostnames error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"),
},
"default settings": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
settings: Provider{
Name: constants.Ivpn,
},
},
"set settings": {
protocol: singleStringCall{call: true, value: constants.TCP},
targetIP: singleStringCall{call: true, value: "1.2.3.4"},
countries: sliceStringCall{call: true, values: []string{"A", "B"}},
cities: sliceStringCall{call: true, values: []string{"C", "D"}},
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
settings: Provider{
Name: constants.Ivpn,
ServerSelection: ServerSelection{
TCP: true,
TargetIP: net.IPv4(1, 2, 3, 4),
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl)
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
Return(testCase.protocol.value, testCase.protocol.err)
}
if testCase.targetIP.call {
env.EXPECT().Get("OPENVPN_TARGET_IP").
Return(testCase.targetIP.value, testCase.targetIP.err)
}
if testCase.countries.call {
env.EXPECT().CSVInside("COUNTRY", constants.IvpnCountryChoices()).
Return(testCase.countries.values, testCase.countries.err)
}
if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices()).
Return(testCase.cities.values, testCase.cities.err)
}
if testCase.hostnames.call {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices()).
Return(testCase.hostnames.values, testCase.hostnames.err)
}
r := reader{env: env}
var settings Provider
err := settings.readIvpn(r)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.settings, settings)
})
}
}

View File

@@ -1,55 +0,0 @@
package configuration
import (
"encoding/pem"
"errors"
"strings"
"github.com/qdm12/gluetun/internal/constants"
)
func readClientKey(r reader) (clientKey string, err error) {
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTKEY", constants.ClientKey)
if err != nil {
return "", err
}
return extractClientKey(b)
}
var errDecodePEMBlockClientKey = errors.New("cannot decode PEM block from client key")
func extractClientKey(b []byte) (key string, err error) {
pemBlock, _ := pem.Decode(b)
if pemBlock == nil {
return "", errDecodePEMBlockClientKey
}
parsedBytes := pem.EncodeToMemory(pemBlock)
s := string(parsedBytes)
s = strings.ReplaceAll(s, "\n", "")
s = strings.TrimPrefix(s, "-----BEGIN PRIVATE KEY-----")
s = strings.TrimSuffix(s, "-----END PRIVATE KEY-----")
return s, nil
}
func readClientCertificate(r reader) (clientCertificate string, err error) {
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTCRT", constants.ClientCertificate)
if err != nil {
return "", err
}
return extractClientCertificate(b)
}
var errDecodePEMBlockClientCert = errors.New("cannot decode PEM block from client certificate")
func extractClientCertificate(b []byte) (certificate string, err error) {
pemBlock, _ := pem.Decode(b)
if pemBlock == nil {
return "", errDecodePEMBlockClientCert
}
parsedBytes := pem.EncodeToMemory(pemBlock)
s := string(parsedBytes)
s = strings.ReplaceAll(s, "\n", "")
s = strings.TrimPrefix(s, "-----BEGIN CERTIFICATE-----")
s = strings.TrimSuffix(s, "-----END CERTIFICATE-----")
return s, nil
}

View File

@@ -1,22 +0,0 @@
package configuration
import (
"net"
"strconv"
)
func uint16sToStrings(uint16s []uint16) (strings []string) {
strings = make([]string, len(uint16s))
for i := range uint16s {
strings[i] = strconv.Itoa(int(uint16s[i]))
}
return strings
}
func ipNetsToStrings(ipNets []net.IPNet) (strings []string) {
strings = make([]string, len(ipNets))
for i := range ipNets {
strings[i] = ipNets[i].String()
}
return strings
}

View File

@@ -1,89 +0,0 @@
package configuration
import (
"fmt"
"strconv"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) mullvadLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.ISPs) > 0 {
lines = append(lines, lastIndent+"ISPs: "+commaJoin(settings.ServerSelection.ISPs))
}
if settings.ServerSelection.CustomPort > 0 {
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
}
if settings.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, lastIndent+"IPv6: enabled")
}
return lines
}
func (settings *Provider) readMullvad(r reader) (err error) {
settings.Name = constants.Mullvad
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.MullvadCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.MullvadHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.MullvadISPChoices())
if err != nil {
return fmt.Errorf("environment variable ISP: %w", err)
}
settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.TCP,
[]uint16{80, 443, 1401}, []uint16{53, 1194, 1195, 1196, 1197, 1300, 1301, 1302, 1303, 1400})
if err != nil {
return err
}
settings.ServerSelection.Owned, err = r.env.YesNo("OWNED", params.Default("no"))
if err != nil {
return fmt.Errorf("environment variable OWNED: %w", err)
}
settings.ExtraConfigOptions.OpenVPNIPv6, err = r.env.OnOff("OPENVPN_IPV6", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable OPENVPN_IPV6: %w", err)
}
return nil
}

View File

@@ -1,91 +0,0 @@
package configuration
import (
"fmt"
"strconv"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) nordvpnLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.Names) > 0 {
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Hostnames))
}
if numbersUint16 := settings.ServerSelection.Numbers; len(numbersUint16) > 0 {
numbersString := make([]string, len(numbersUint16))
for i, numberUint16 := range numbersUint16 {
numbersString[i] = strconv.Itoa(int(numberUint16))
}
lines = append(lines, lastIndent+"Numbers: "+commaJoin(numbersString))
}
return lines
}
func (settings *Provider) readNordvpn(r reader) (err error) {
settings.Name = constants.Nordvpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.NordvpnRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.NordvpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.NordvpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
}
settings.ServerSelection.Numbers, err = readNordVPNServerNumbers(r.env)
if err != nil {
return err
}
return nil
}
func readNordVPNServerNumbers(env params.Env) (numbers []uint16, err error) {
const possiblePortsCount = 65537
possibilities := make([]string, possiblePortsCount)
for i := range possibilities {
possibilities[i] = fmt.Sprintf("%d", i)
}
possibilities[65536] = ""
values, err := env.CSVInside("SERVER_NUMBER", possibilities)
if err != nil {
return nil, err
}
numbers = make([]uint16, len(values))
for i := range values {
n, err := strconv.Atoi(values[i])
if err != nil {
return nil, err
}
numbers[i] = uint16(n)
}
return numbers, nil
}

View File

@@ -1,204 +0,0 @@
package configuration
import (
"errors"
"fmt"
"strconv"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
// OpenVPN contains settings to configure the OpenVPN client.
type OpenVPN struct {
User string `json:"user"`
Password string `json:"password"`
Verbosity int `json:"verbosity"`
Flags []string `json:"flags"`
MSSFix uint16 `json:"mssfix"`
Root bool `json:"run_as_root"`
Cipher string `json:"cipher"`
Auth string `json:"auth"`
Provider Provider `json:"provider"`
Config string `json:"custom_config"`
Version string `json:"version"`
}
func (settings *OpenVPN) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *OpenVPN) lines() (lines []string) {
lines = append(lines, lastIndent+"OpenVPN:")
lines = append(lines, indent+lastIndent+"Version: "+settings.Version)
lines = append(lines, indent+lastIndent+"Verbosity level: "+strconv.Itoa(settings.Verbosity))
if len(settings.Flags) > 0 {
lines = append(lines, indent+lastIndent+"Flags: "+strings.Join(settings.Flags, " "))
}
if settings.Root {
lines = append(lines, indent+lastIndent+"Run as root: enabled")
}
if len(settings.Cipher) > 0 {
lines = append(lines, indent+lastIndent+"Custom cipher: "+settings.Cipher)
}
if len(settings.Auth) > 0 {
lines = append(lines, indent+lastIndent+"Custom auth algorithm: "+settings.Auth)
}
if len(settings.Config) > 0 {
lines = append(lines, indent+lastIndent+"Custom configuration: "+settings.Config)
}
if settings.Provider.Name == "" {
lines = append(lines, indent+lastIndent+"Provider: custom configuration")
} else {
lines = append(lines, indent+lastIndent+"Provider:")
for _, line := range settings.Provider.lines() {
lines = append(lines, indent+indent+line)
}
}
return lines
}
var (
ErrInvalidVPNProvider = errors.New("invalid VPN provider")
)
func (settings *OpenVPN) read(r reader) (err error) {
vpnsp, err := r.env.Inside("VPNSP", []string{
"cyberghost", "fastestvpn", "hidemyass", "ipvanish", "ivpn", "mullvad", "nordvpn",
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"},
params.Default("private internet access"))
if err != nil {
return fmt.Errorf("environment variable VPNSP: %w", err)
}
if vpnsp == "pia" { // retro compatibility
vpnsp = "private internet access"
}
settings.Provider.Name = vpnsp
settings.Config, err = r.env.Get("OPENVPN_CUSTOM_CONFIG", params.CaseSensitiveValue())
if err != nil {
return fmt.Errorf("environment variable OPENVPN_CUSTOM_CONFIG: %w", err)
}
customConfig := settings.Config != ""
if customConfig {
settings.Provider.Name = ""
}
credentialsRequired := !customConfig && settings.Provider.Name != constants.VPNUnlimited
settings.User, err = r.getFromEnvOrSecretFile("OPENVPN_USER", credentialsRequired, []string{"USER"})
if err != nil {
return fmt.Errorf("environment variable OPENVPN_USER: %w", err)
}
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
settings.User = strings.ReplaceAll(settings.User, " ", "")
if settings.Provider.Name == constants.Mullvad {
settings.Password = "m"
} else {
settings.Password, err = r.getFromEnvOrSecretFile("OPENVPN_PASSWORD", credentialsRequired, []string{"PASSWORD"})
if err != nil {
return err
}
}
settings.Version, err = r.env.Inside("OPENVPN_VERSION",
[]string{constants.Openvpn24, constants.Openvpn25}, params.Default(constants.Openvpn25))
if err != nil {
return fmt.Errorf("environment variable OPENVPN_VERSION: %w", err)
}
settings.Verbosity, err = r.env.IntRange("OPENVPN_VERBOSITY", 0, 6, params.Default("1")) //nolint:gomnd
if err != nil {
return fmt.Errorf("environment variable OPENVPN_VERBOSITY: %w", err)
}
settings.Flags = []string{}
flagsStr, err := r.env.Get("OPENVPN_FLAGS")
if err != nil {
return fmt.Errorf("environment variable OPENVPN_FLAGS: %w", err)
}
if flagsStr != "" {
settings.Flags = strings.Fields(flagsStr)
}
settings.Root, err = r.env.YesNo("OPENVPN_ROOT", params.Default("yes"))
if err != nil {
return fmt.Errorf("environment variable OPENVPN_ROOT: %w", err)
}
settings.Cipher, err = r.env.Get("OPENVPN_CIPHER")
if err != nil {
return fmt.Errorf("environment variable OPENVPN_CIPHER: %w", err)
}
settings.Auth, err = r.env.Get("OPENVPN_AUTH")
if err != nil {
return fmt.Errorf("environment variable OPENVPN_AUTH: %w", err)
}
const maxMSSFix = 10000
mssFix, err := r.env.IntRange("OPENVPN_MSSFIX", 0, maxMSSFix, params.Default("0"))
if err != nil {
return fmt.Errorf("environment variable OPENVPN_MSSFIX: %w", err)
}
settings.MSSFix = uint16(mssFix)
return settings.readProvider(r)
}
func (settings *OpenVPN) readProvider(r reader) error {
var readProvider func(r reader) error
switch settings.Provider.Name {
case "": // custom config
readProvider = func(r reader) error { return nil }
case constants.Cyberghost:
readProvider = settings.Provider.readCyberghost
case constants.Fastestvpn:
readProvider = settings.Provider.readFastestvpn
case constants.HideMyAss:
readProvider = settings.Provider.readHideMyAss
case constants.Ipvanish:
readProvider = settings.Provider.readIpvanish
case constants.Ivpn:
readProvider = settings.Provider.readIvpn
case constants.Mullvad:
readProvider = settings.Provider.readMullvad
case constants.Nordvpn:
readProvider = settings.Provider.readNordvpn
case constants.Privado:
readProvider = settings.Provider.readPrivado
case constants.PrivateInternetAccess:
readProvider = settings.Provider.readPrivateInternetAccess
case constants.Privatevpn:
readProvider = settings.Provider.readPrivatevpn
case constants.Protonvpn:
readProvider = settings.Provider.readProtonvpn
case constants.Purevpn:
readProvider = settings.Provider.readPurevpn
case constants.Surfshark:
readProvider = settings.Provider.readSurfshark
case constants.Torguard:
readProvider = settings.Provider.readTorguard
case constants.VPNUnlimited:
readProvider = settings.Provider.readVPNUnlimited
case constants.Vyprvpn:
readProvider = settings.Provider.readVyprvpn
case constants.Windscribe:
readProvider = settings.Provider.readWindscribe
default:
return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Provider.Name)
}
return readProvider(r)
}

View File

@@ -1,65 +0,0 @@
package configuration
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_OpenVPN_JSON(t *testing.T) {
t.Parallel()
in := OpenVPN{
Root: true,
Flags: []string{},
Provider: Provider{
Name: "name",
},
}
data, err := json.MarshalIndent(in, "", " ")
require.NoError(t, err)
assert.Equal(t, `{
"user": "",
"password": "",
"verbosity": 0,
"flags": [],
"mssfix": 0,
"run_as_root": true,
"cipher": "",
"auth": "",
"provider": {
"name": "name",
"server_selection": {
"tcp": false,
"regions": null,
"group": "",
"countries": null,
"cities": null,
"hostnames": null,
"names": null,
"isps": null,
"owned": false,
"custom_port": 0,
"numbers": null,
"encryption_preset": "",
"free_only": false,
"stream_only": false
},
"extra_config": {
"encryption_preset": "",
"openvpn_ipv6": false
},
"port_forwarding": {
"enabled": false,
"filepath": ""
}
},
"custom_config": "",
"version": ""
}`, string(data))
var out OpenVPN
err = json.Unmarshal(data, &out)
require.NoError(t, err)
assert.Equal(t, in, out)
}

View File

@@ -1,63 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) privadoLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readPrivado(r reader) (err error) {
settings.Name = constants.Privado
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivadoCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PrivadoRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivadoCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -1,98 +0,0 @@
package configuration
import (
"fmt"
"strconv"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) privateinternetaccessLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.Names) > 0 {
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Names))
}
lines = append(lines, lastIndent+"Encryption preset: "+settings.ServerSelection.EncryptionPreset)
if settings.ServerSelection.CustomPort > 0 {
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
}
if settings.PortForwarding.Enabled {
lines = append(lines, lastIndent+"Port forwarding:")
for _, line := range settings.PortForwarding.lines() {
lines = append(lines, indent+line)
}
}
return lines
}
func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
settings.Name = constants.PrivateInternetAccess
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
encryptionPreset, err := r.env.Inside("PIA_ENCRYPTION",
[]string{constants.PIAEncryptionPresetNone, constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetStrong},
params.RetroKeys([]string{"ENCRYPTION"}, r.onRetroActive),
params.Default(constants.PIACertificateStrong),
)
if err != nil {
return fmt.Errorf("environment variable PIA_ENCRYPTION: %w", err)
}
settings.ServerSelection.EncryptionPreset = encryptionPreset
settings.ExtraConfigOptions.EncryptionPreset = encryptionPreset
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PIAGeoChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PIAHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_NAME", constants.PIANameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
}
settings.ServerSelection.CustomPort, err = readPortOrZero(r.env, "PORT")
if err != nil {
return fmt.Errorf("environment variable PORT: %w", err)
}
settings.PortForwarding.Enabled, err = r.env.OnOff("PORT_FORWARDING", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable PORT_FORWARDING: %w", err)
}
if settings.PortForwarding.Enabled {
settings.PortForwarding.Filepath, err = r.env.Path("PORT_FORWARDING_STATUS_FILE",
params.Default("/tmp/gluetun/forwarded_port"), params.CaseSensitiveValue())
if err != nil {
return fmt.Errorf("environment variable PORT_FORWARDING_STATUS_FILE: %w", err)
}
}
return nil
}

View File

@@ -1,54 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) privatevpnLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readPrivatevpn(r reader) (err error) {
settings.Name = constants.Privatevpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivatevpnCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivatevpnCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivatevpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -1,87 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) protonvpnLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Names) > 0 {
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Names))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ServerSelection.FreeOnly {
lines = append(lines, lastIndent+"Free only: yes")
}
return lines
}
func (settings *Provider) readProtonvpn(r reader) (err error) {
settings.Name = constants.Protonvpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.CustomPort, err = readPortOrZero(r.env, "PORT")
if err != nil {
return fmt.Errorf("environment variable PORT: %w", err)
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ProtonvpnCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.ProtonvpnRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ProtonvpnCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.ProtonvpnNameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.ProtonvpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.FreeOnly, err = r.env.YesNo("FREE_ONLY", params.Default("no"))
if err != nil {
return fmt.Errorf("environment variable FREE_ONLY: %w", err)
}
return nil
}

View File

@@ -1,129 +0,0 @@
package configuration
import (
"fmt"
"net"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
// Provider contains settings specific to a VPN provider.
type Provider struct {
Name string `json:"name"`
ServerSelection ServerSelection `json:"server_selection"`
ExtraConfigOptions ExtraConfigOptions `json:"extra_config"`
PortForwarding PortForwarding `json:"port_forwarding"`
}
func (settings *Provider) lines() (lines []string) {
lines = append(lines, lastIndent+strings.Title(settings.Name)+" settings:")
selection := settings.ServerSelection
lines = append(lines, indent+lastIndent+"Network protocol: "+protoToString(selection.TCP))
if selection.TargetIP != nil {
lines = append(lines, indent+lastIndent+"Target IP address: "+selection.TargetIP.String())
}
var providerLines []string
switch strings.ToLower(settings.Name) {
case "cyberghost":
providerLines = settings.cyberghostLines()
case "fastestvpn":
providerLines = settings.fastestvpnLines()
case "hidemyass":
providerLines = settings.hideMyAssLines()
case "ipvanish":
providerLines = settings.ipvanishLines()
case "ivpn":
providerLines = settings.ivpnLines()
case "mullvad":
providerLines = settings.mullvadLines()
case "nordvpn":
providerLines = settings.nordvpnLines()
case "privado":
providerLines = settings.privadoLines()
case "privatevpn":
providerLines = settings.privatevpnLines()
case "private internet access":
providerLines = settings.privateinternetaccessLines()
case "protonvpn":
providerLines = settings.protonvpnLines()
case "purevpn":
providerLines = settings.purevpnLines()
case "surfshark":
providerLines = settings.surfsharkLines()
case "torguard":
providerLines = settings.torguardLines()
case strings.ToLower(constants.VPNUnlimited):
providerLines = settings.vpnUnlimitedLines()
case "vyprvpn":
providerLines = settings.vyprvpnLines()
case "windscribe":
providerLines = settings.windscribeLines()
default:
panic(`Missing lines method for provider "` +
settings.Name + `"! Please create a Github issue.`)
}
for _, line := range providerLines {
lines = append(lines, indent+line)
}
return lines
}
func commaJoin(slice []string) string {
return strings.Join(slice, ", ")
}
func readProtocol(env params.Env) (tcp bool, err error) {
protocol, err := env.Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, params.Default(constants.UDP))
if err != nil {
return false, fmt.Errorf("environment variable PROTOCOL: %w", err)
}
return protocol == constants.TCP, nil
}
func protoToString(tcp bool) string {
if tcp {
return constants.TCP
}
return constants.UDP
}
func readTargetIP(env params.Env) (targetIP net.IP, err error) {
targetIP, err = readIP(env, "OPENVPN_TARGET_IP")
if err != nil {
return nil, fmt.Errorf("environment variable OPENVPN_TARGET_IP: %w", err)
}
return targetIP, nil
}
func readCustomPort(env params.Env, tcp bool,
allowedTCP, allowedUDP []uint16) (port uint16, err error) {
port, err = readPortOrZero(env, "PORT")
if err != nil {
return 0, fmt.Errorf("environment variable PORT: %w", err)
} else if port == 0 {
return 0, nil
}
if tcp {
for i := range allowedTCP {
if allowedTCP[i] == port {
return port, nil
}
}
return 0, fmt.Errorf("environment variable PORT: %w: port %d for TCP protocol", ErrInvalidPort, port)
}
for i := range allowedUDP {
if allowedUDP[i] == port {
return port, nil
}
}
return 0, fmt.Errorf("environment variable PORT: %w: port %d for UDP protocol", ErrInvalidPort, port)
}

View File

@@ -1,382 +0,0 @@
package configuration
import (
"errors"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var errDummy = errors.New("dummy")
func Test_Provider_lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Provider
lines []string
}{
"cyberghost": {
settings: Provider{
Name: constants.Cyberghost,
ServerSelection: ServerSelection{
Group: "group",
Regions: []string{"a", "El country"},
},
ExtraConfigOptions: ExtraConfigOptions{
ClientKey: "a",
ClientCertificate: "a",
},
},
lines: []string{
"|--Cyberghost settings:",
" |--Network protocol: udp",
" |--Server group: group",
" |--Regions: a, El country",
" |--Client key is set",
" |--Client certificate is set",
},
},
"fastestvpn": {
settings: Provider{
Name: constants.Fastestvpn,
ServerSelection: ServerSelection{
Hostnames: []string{"a", "b"},
Countries: []string{"c", "d"},
},
},
lines: []string{
"|--Fastestvpn settings:",
" |--Network protocol: udp",
" |--Hostnames: a, b",
" |--Countries: c, d",
},
},
"hidemyass": {
settings: Provider{
Name: constants.HideMyAss,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"},
},
},
lines: []string{
"|--Hidemyass settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Cities: c, d",
" |--Hostnames: e, f",
},
},
"ipvanish": {
settings: Provider{
Name: constants.Ipvanish,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"},
},
},
lines: []string{
"|--Ipvanish settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Cities: c, d",
" |--Hostnames: e, f",
},
},
"ivpn": {
settings: Provider{
Name: constants.Ivpn,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"},
},
},
lines: []string{
"|--Ivpn settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Cities: c, d",
" |--Hostnames: e, f",
},
},
"mullvad": {
settings: Provider{
Name: constants.Mullvad,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Cities: []string{"c", "d"},
ISPs: []string{"e", "f"},
CustomPort: 1,
},
ExtraConfigOptions: ExtraConfigOptions{
OpenVPNIPv6: true,
},
},
lines: []string{
"|--Mullvad settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Cities: c, d",
" |--ISPs: e, f",
" |--Custom port: 1",
" |--IPv6: enabled",
},
},
"nordvpn": {
settings: Provider{
Name: constants.Nordvpn,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
Numbers: []uint16{1, 2},
},
},
lines: []string{
"|--Nordvpn settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
" |--Numbers: 1, 2",
},
},
"privado": {
settings: Provider{
Name: constants.Privado,
ServerSelection: ServerSelection{
Hostnames: []string{"a", "b"},
},
},
lines: []string{
"|--Privado settings:",
" |--Network protocol: udp",
" |--Hostnames: a, b",
},
},
"privatevpn": {
settings: Provider{
Name: constants.Privatevpn,
ServerSelection: ServerSelection{
Hostnames: []string{"a", "b"},
Countries: []string{"c", "d"},
Cities: []string{"e", "f"},
},
},
lines: []string{
"|--Privatevpn settings:",
" |--Network protocol: udp",
" |--Countries: c, d",
" |--Cities: e, f",
" |--Hostnames: a, b",
},
},
"protonvpn": {
settings: Provider{
Name: constants.Protonvpn,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Regions: []string{"c", "d"},
Cities: []string{"e", "f"},
Names: []string{"g", "h"},
Hostnames: []string{"i", "j"},
},
},
lines: []string{
"|--Protonvpn settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Regions: c, d",
" |--Cities: e, f",
" |--Names: g, h",
" |--Hostnames: i, j",
},
},
"private internet access": {
settings: Provider{
Name: constants.PrivateInternetAccess,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
EncryptionPreset: constants.PIAEncryptionPresetStrong,
CustomPort: 1,
},
PortForwarding: PortForwarding{
Enabled: true,
Filepath: string("/here"),
},
},
lines: []string{
"|--Private Internet Access settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
" |--Encryption preset: strong",
" |--Custom port: 1",
" |--Port forwarding:",
" |--File path: /here",
},
},
"purevpn": {
settings: Provider{
Name: constants.Purevpn,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
Countries: []string{"c", "d"},
Cities: []string{"e", "f"},
},
},
lines: []string{
"|--Purevpn settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
" |--Countries: c, d",
" |--Cities: e, f",
},
},
"surfshark": {
settings: Provider{
Name: constants.Surfshark,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
},
},
lines: []string{
"|--Surfshark settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
},
},
"torguard": {
settings: Provider{
Name: constants.Torguard,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Cities: []string{"c", "d"},
Hostnames: []string{"e"},
},
},
lines: []string{
"|--Torguard settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Cities: c, d",
" |--Hostnames: e",
},
},
constants.VPNUnlimited: {
settings: Provider{
Name: constants.VPNUnlimited,
ServerSelection: ServerSelection{
Countries: []string{"a", "b"},
Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"},
FreeOnly: true,
StreamOnly: true,
},
ExtraConfigOptions: ExtraConfigOptions{
ClientKey: "a",
},
},
lines: []string{
"|--Vpn Unlimited settings:",
" |--Network protocol: udp",
" |--Countries: a, b",
" |--Cities: c, d",
" |--Hostnames: e, f",
" |--Free servers only",
" |--Stream servers only",
" |--Client key is set",
},
},
"vyprvpn": {
settings: Provider{
Name: constants.Vyprvpn,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
},
},
lines: []string{
"|--Vyprvpn settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
},
},
"windscribe": {
settings: Provider{
Name: constants.Windscribe,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"},
CustomPort: 1,
},
},
lines: []string{
"|--Windscribe settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
" |--Cities: c, d",
" |--Hostnames: e, f",
" |--Custom port: 1",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}
func Test_readProtocol(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
mockStr string
mockErr error
tcp bool
err error
}{
"error": {
mockErr: errDummy,
err: errors.New("environment variable PROTOCOL: dummy"),
},
"success": {
mockStr: "tcp",
tcp: true,
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl)
env.EXPECT().
Inside("PROTOCOL", []string{"tcp", "udp"}, gomock.Any()).
Return(testCase.mockStr, testCase.mockErr)
tcp, err := readProtocol(env)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.tcp, tcp)
})
}
}

View File

@@ -1,47 +0,0 @@
package configuration
import (
"fmt"
"strings"
"time"
"github.com/qdm12/golibs/params"
)
type PublicIP struct {
Period time.Duration `json:"period"`
IPFilepath string `json:"ip_filepath"`
}
func (settings *PublicIP) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *PublicIP) lines() (lines []string) {
if settings.Period == 0 {
lines = append(lines, lastIndent+"Public IP getter: disabled")
return lines
}
lines = append(lines, lastIndent+"Public IP getter:")
lines = append(lines, indent+lastIndent+"Fetch period: "+settings.Period.String())
lines = append(lines, indent+lastIndent+"IP file: "+settings.IPFilepath)
return lines
}
func (settings *PublicIP) read(r reader) (err error) {
settings.Period, err = r.env.Duration("PUBLICIP_PERIOD", params.Default("12h"))
if err != nil {
return fmt.Errorf("environment variable PUBLICIP_PERIOD: %w", err)
}
settings.IPFilepath, err = r.env.Path("PUBLICIP_FILE", params.CaseSensitiveValue(),
params.Default("/tmp/gluetun/ip"),
params.RetroKeys([]string{"IP_STATUS_FILE"}, r.onRetroActive))
if err != nil {
return fmt.Errorf("environment variable PUBLICIP_FILE (or IP_STATUS_FILE): %w", err)
}
return nil
}

View File

@@ -1,63 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) purevpnLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readPurevpn(r reader) (err error) {
settings.Name = constants.Purevpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PurevpnRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PurevpnCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PurevpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -1,122 +0,0 @@
package configuration
import (
"errors"
"fmt"
"net"
"strconv"
"strings"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
"github.com/qdm12/golibs/verification"
)
type reader struct {
env params.Env
logger logging.Logger
regex verification.Regex
}
func newReader(env params.Env, logger logging.Logger) reader {
return reader{
env: env,
logger: logger,
regex: verification.NewRegex(),
}
}
func (r *reader) onRetroActive(oldKey, newKey string) {
r.logger.Warn(
"You are using the old environment variable " + oldKey +
", please consider changing it to " + newKey)
}
var (
ErrInvalidPort = errors.New("invalid port")
)
func readCSVPorts(env params.Env, key string) (ports []uint16, err error) {
s, err := env.Get(key)
if err != nil {
return nil, err
} else if s == "" {
return nil, nil
}
portsStr := strings.Split(s, ",")
ports = make([]uint16, len(portsStr))
for i, portStr := range portsStr {
portInt, err := strconv.Atoi(portStr)
if err != nil {
return nil, fmt.Errorf("%w: %s: %s", ErrInvalidPort, portStr, err)
} else if portInt <= 0 || portInt > 65535 {
return nil, fmt.Errorf("%w: %d: must be between 1 and 65535", ErrInvalidPort, portInt)
}
ports[i] = uint16(portInt)
}
return ports, nil
}
var (
ErrInvalidIPNet = errors.New("invalid IP network")
)
func readCSVIPNets(env params.Env, key string, options ...params.OptionSetter) (
ipNets []net.IPNet, err error) {
s, err := env.Get(key, options...)
if err != nil {
return nil, err
} else if s == "" {
return nil, nil
}
ipNetsStr := strings.Split(s, ",")
ipNets = make([]net.IPNet, len(ipNetsStr))
for i, ipNetStr := range ipNetsStr {
_, ipNet, err := net.ParseCIDR(ipNetStr)
if err != nil {
return nil, fmt.Errorf("%w: %s: %s",
ErrInvalidIPNet, ipNetStr, err)
} else if ipNet == nil {
return nil, fmt.Errorf("%w: %s: subnet is nil", ErrInvalidIPNet, ipNetStr)
}
ipNets[i] = *ipNet
}
return ipNets, nil
}
var (
ErrInvalidIP = errors.New("invalid IP address")
)
func readIP(env params.Env, key string) (ip net.IP, err error) {
s, err := env.Get(key)
if s == "" {
return nil, nil
} else if err != nil {
return nil, err
}
ip = net.ParseIP(s)
if ip == nil {
return nil, fmt.Errorf("%w: %s", ErrInvalidIP, s)
}
return ip, nil
}
func readPortOrZero(env params.Env, key string) (port uint16, err error) {
s, err := env.Get(key)
if err != nil {
return 0, err
}
if s == "" || s == "0" {
return 0, nil
}
return env.Port(key)
}

View File

@@ -1,63 +0,0 @@
package configuration
import (
"net"
)
type ServerSelection struct { //nolint:maligned
// Common
TCP bool `json:"tcp"` // UDP if TCP is false
TargetIP net.IP `json:"target_ip,omitempty"`
// TODO comments
// Cyberghost, PIA, Protonvpn, Surfshark, Windscribe, Vyprvpn, NordVPN
Regions []string `json:"regions"`
// Cyberghost
Group string `json:"group"`
// Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
Countries []string `json:"countries"`
// HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited, Windscribe
Cities []string `json:"cities"`
// Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited
Hostnames []string `json:"hostnames"`
Names []string `json:"names"` // Protonvpn
// Mullvad
ISPs []string `json:"isps"`
Owned bool `json:"owned"`
// Mullvad, Windscribe, PIA
CustomPort uint16 `json:"custom_port"`
// NordVPN
Numbers []uint16 `json:"numbers"`
// PIA
EncryptionPreset string `json:"encryption_preset"`
// ProtonVPN
FreeOnly bool `json:"free_only"`
// VPNUnlimited
StreamOnly bool `json:"stream_only"`
}
type ExtraConfigOptions struct {
ClientCertificate string `json:"-"` // Cyberghost
ClientKey string `json:"-"` // Cyberghost, VPNUnlimited
EncryptionPreset string `json:"encryption_preset"` // PIA
OpenVPNIPv6 bool `json:"openvpn_ipv6"` // Mullvad
}
// PortForwarding contains settings for port forwarding.
type PortForwarding struct {
Enabled bool `json:"enabled"`
Filepath string `json:"filepath"`
}
func (p *PortForwarding) lines() (lines []string) {
return []string{
lastIndent + "File path: " + p.Filepath,
}
}

View File

@@ -1,50 +0,0 @@
package configuration
import (
"fmt"
"strconv"
"strings"
"github.com/qdm12/golibs/params"
)
// ControlServer contains settings to customize the control server operation.
type ControlServer struct {
Port uint16
Log bool
}
func (settings *ControlServer) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *ControlServer) lines() (lines []string) {
lines = append(lines, lastIndent+"HTTP control server:")
lines = append(lines, indent+lastIndent+"Listening port: "+strconv.Itoa(int(settings.Port)))
if settings.Log {
lines = append(lines, indent+lastIndent+"Logging: enabled")
}
return lines
}
func (settings *ControlServer) read(r reader) (err error) {
settings.Log, err = r.env.OnOff("HTTP_CONTROL_SERVER_LOG", params.Default("on"))
if err != nil {
return fmt.Errorf("environment variable HTTP_CONTROL_SERVER_LOG: %w", err)
}
var warning string
settings.Port, warning, err = r.env.ListeningPort(
"HTTP_CONTROL_SERVER_PORT", params.Default("8000"))
if len(warning) > 0 {
r.logger.Warn(warning)
}
if err != nil {
return fmt.Errorf("environment variable HTTP_CONTROL_SERVER_PORT: %w", err)
}
return nil
}

View File

@@ -1,117 +0,0 @@
package configuration
import (
"errors"
"fmt"
"strings"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
)
// Settings contains all settings for the program to run.
type Settings struct {
OpenVPN OpenVPN
System System
DNS DNS
Firewall Firewall
HTTPProxy HTTPProxy
ShadowSocks ShadowSocks
Updater Updater
PublicIP PublicIP
VersionInformation bool
ControlServer ControlServer
Health Health
}
func (settings *Settings) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *Settings) lines() (lines []string) {
lines = append(lines, "Settings summary below:")
lines = append(lines, settings.OpenVPN.lines()...)
lines = append(lines, settings.DNS.lines()...)
lines = append(lines, settings.Firewall.lines()...)
lines = append(lines, settings.System.lines()...)
lines = append(lines, settings.HTTPProxy.lines()...)
lines = append(lines, settings.ShadowSocks.lines()...)
lines = append(lines, settings.Health.lines()...)
lines = append(lines, settings.ControlServer.lines()...)
lines = append(lines, settings.Updater.lines()...)
lines = append(lines, settings.PublicIP.lines()...)
if settings.VersionInformation {
lines = append(lines, lastIndent+"Github version information: enabled")
}
return lines
}
var (
ErrOpenvpn = errors.New("cannot read Openvpn settings")
ErrSystem = errors.New("cannot read System settings")
ErrDNS = errors.New("cannot read DNS settings")
ErrFirewall = errors.New("cannot read firewall settings")
ErrHTTPProxy = errors.New("cannot read HTTP proxy settings")
ErrShadowsocks = errors.New("cannot read Shadowsocks settings")
ErrControlServer = errors.New("cannot read control server settings")
ErrUpdater = errors.New("cannot read Updater settings")
ErrPublicIP = errors.New("cannot read Public IP getter settings")
ErrHealth = errors.New("cannot read health settings")
)
// Read obtains all configuration options for the program and returns an error as soon
// as an error is encountered reading them.
func (settings *Settings) Read(env params.Env, logger logging.Logger) (err error) {
r := newReader(env, logger)
settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on"))
if err != nil {
return fmt.Errorf("environment variable VERSION_INFORMATION: %w", err)
}
if err := settings.OpenVPN.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrOpenvpn, err)
}
if err := settings.System.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrSystem, err)
}
if err := settings.DNS.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrDNS, err)
}
if err := settings.Firewall.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrFirewall, err)
}
if err := settings.HTTPProxy.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrHTTPProxy, err)
}
if err := settings.ShadowSocks.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrShadowsocks, err)
}
if err := settings.ControlServer.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrControlServer, err)
}
if err := settings.Updater.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrUpdater, err)
}
if ip := settings.DNS.PlaintextAddress; ip != nil {
settings.Updater.DNSAddress = ip.String()
}
if err := settings.PublicIP.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrPublicIP, err)
}
if err := settings.Health.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrHealth, err)
}
return nil
}

View File

@@ -1,61 +0,0 @@
package configuration
import (
"testing"
"github.com/qdm12/gluetun/internal/constants"
"github.com/stretchr/testify/assert"
)
func Test_Settings_lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Settings
lines []string
}{
"default settings": {
settings: Settings{
OpenVPN: OpenVPN{
Version: constants.Openvpn25,
Provider: Provider{
Name: constants.Mullvad,
},
},
},
lines: []string{
"Settings summary below:",
"|--OpenVPN:",
" |--Version: 2.5",
" |--Verbosity level: 0",
" |--Provider:",
" |--Mullvad settings:",
" |--Network protocol: udp",
"|--DNS:",
"|--Firewall: disabled ⚠️",
"|--System:",
" |--Process user ID: 0",
" |--Process group ID: 0",
" |--Timezone: NOT SET ⚠️ - it can cause time related issues",
"|--Health:",
" |--Server address: ",
" |--OpenVPN:",
" |--Initial duration: 0s",
"|--HTTP control server:",
" |--Listening port: 0",
"|--Public IP getter: disabled",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}

View File

@@ -1,75 +0,0 @@
package configuration
import (
"fmt"
"strconv"
"strings"
"github.com/qdm12/golibs/params"
)
// ShadowSocks contains settings to configure the Shadowsocks server.
type ShadowSocks struct {
Method string
Password string
Port uint16
Enabled bool
Log bool
}
func (settings *ShadowSocks) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *ShadowSocks) lines() (lines []string) {
if !settings.Enabled {
return nil
}
lines = append(lines, lastIndent+"Shadowsocks server:")
lines = append(lines, indent+lastIndent+"Listening port: "+strconv.Itoa(int(settings.Port)))
lines = append(lines, indent+lastIndent+"Method: "+settings.Method)
if settings.Log {
lines = append(lines, indent+lastIndent+"Logging: enabled")
}
return lines
}
func (settings *ShadowSocks) read(r reader) (err error) {
settings.Enabled, err = r.env.OnOff("SHADOWSOCKS", params.Default("off"))
if !settings.Enabled {
return nil
} else if err != nil {
return fmt.Errorf("environment variable SHADOWSOCKS: %w", err)
}
settings.Password, err = r.getFromEnvOrSecretFile("SHADOWSOCKS_PASSWORD", settings.Enabled, nil)
if err != nil {
return err
}
settings.Log, err = r.env.OnOff("SHADOWSOCKS_LOG", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable SHADOWSOCKS_LOG: %w", err)
}
settings.Method, err = r.env.Get("SHADOWSOCKS_METHOD", params.Default("chacha20-ietf-poly1305"))
if err != nil {
return fmt.Errorf("environment variable SHADOWSOCKS_METHOD: %w", err)
}
var warning string
settings.Port, warning, err = r.env.ListeningPort("SHADOWSOCKS_PORT", params.Default("8388"))
if len(warning) > 0 {
r.logger.Warn(warning)
}
if err != nil {
return fmt.Errorf("environment variable SHADOWSOCKS_PORT: %w", err)
}
return nil
}

View File

@@ -1,45 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) surfsharkLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readSurfshark(r reader) (err error) {
settings.Name = constants.Surfshark
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.SurfsharkRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.SurfsharkHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -1,55 +0,0 @@
package configuration
import (
"fmt"
"strconv"
"strings"
"github.com/qdm12/golibs/params"
)
// System contains settings to configure system related elements.
type System struct {
PUID int
PGID int
Timezone string
}
func (settings *System) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *System) lines() (lines []string) {
lines = append(lines, lastIndent+"System:")
lines = append(lines, indent+lastIndent+"Process user ID: "+strconv.Itoa(settings.PUID))
lines = append(lines, indent+lastIndent+"Process group ID: "+strconv.Itoa(settings.PGID))
if len(settings.Timezone) > 0 {
lines = append(lines, indent+lastIndent+"Timezone: "+settings.Timezone)
} else {
lines = append(lines, indent+lastIndent+"Timezone: NOT SET ⚠️ - it can cause time related issues")
}
return lines
}
func (settings *System) read(r reader) (err error) {
const maxID = 65535
settings.PUID, err = r.env.IntRange("PUID", 0, maxID, params.Default("1000"),
params.RetroKeys([]string{"UID"}, r.onRetroActive))
if err != nil {
return fmt.Errorf("environment variable PUID (or UID): %w", err)
}
settings.PGID, err = r.env.IntRange("PGID", 0, maxID, params.Default("1000"),
params.RetroKeys([]string{"GID"}, r.onRetroActive))
if err != nil {
return fmt.Errorf("environment variable PGID (or GID): %w", err)
}
settings.Timezone, err = r.env.Get("TZ")
if err != nil {
return fmt.Errorf("environment variable TZ: %w", err)
}
return nil
}

View File

@@ -1,54 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) torguardLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readTorguard(r reader) (err error) {
settings.Name = constants.Torguard
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.TorguardCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.TorguardHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
return nil
}

View File

@@ -1,79 +0,0 @@
package configuration
import (
"errors"
"fmt"
"strings"
"github.com/qdm12/dns/pkg/provider"
"github.com/qdm12/golibs/params"
"inet.af/netaddr"
)
func (settings *DNS) readUnbound(r reader) (err error) {
if err := settings.readUnboundProviders(r.env); err != nil {
return err
}
settings.Unbound.ListeningPort = 53
settings.Unbound.Caching, err = r.env.OnOff("DOT_CACHING", params.Default("on"))
if err != nil {
return fmt.Errorf("environment variable DOT_CACHING: %w", err)
}
settings.Unbound.IPv4 = true
settings.Unbound.IPv6, err = r.env.OnOff("DOT_IPV6", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable DOT_IPV6: %w", err)
}
verbosityLevel, err := r.env.IntRange("DOT_VERBOSITY", 0, 5, params.Default("1")) //nolint:gomnd
if err != nil {
return fmt.Errorf("environment variable DOT_VERBOSITY: %w", err)
}
settings.Unbound.VerbosityLevel = uint8(verbosityLevel)
verbosityDetailsLevel, err := r.env.IntRange("DOT_VERBOSITY_DETAILS", 0, 4, params.Default("0")) //nolint:gomnd
if err != nil {
return fmt.Errorf("environment variable DOT_VERBOSITY_DETAILS: %w", err)
}
settings.Unbound.VerbosityDetailsLevel = uint8(verbosityDetailsLevel)
validationLogLevel, err := r.env.IntRange("DOT_VALIDATION_LOGLEVEL", 0, 2, params.Default("0")) //nolint:gomnd
if err != nil {
return fmt.Errorf("environment variable DOT_VALIDATION_LOGLEVEL: %w", err)
}
settings.Unbound.ValidationLogLevel = uint8(validationLogLevel)
settings.Unbound.AccessControl.Allowed = []netaddr.IPPrefix{
netaddr.IPPrefixFrom(netaddr.IPv4(0, 0, 0, 0), 0),
netaddr.IPPrefixFrom(netaddr.IPv6Raw([16]byte{}), 0),
}
return nil
}
var (
ErrInvalidDNSOverTLSProvider = errors.New("invalid DNS over TLS provider")
)
func (settings *DNS) readUnboundProviders(env params.Env) (err error) {
s, err := env.Get("DOT_PROVIDERS", params.Default("cloudflare"))
if err != nil {
return fmt.Errorf("environment variable DOT_PROVIDERS: %w", err)
}
for _, field := range strings.Split(s, ",") {
dnsProvider, err := provider.Parse(field)
if err != nil {
return fmt.Errorf("%w: %s", ErrInvalidDNSOverTLSProvider, err)
}
settings.Unbound.Providers = append(settings.Unbound.Providers, dnsProvider)
}
return nil
}
var (
ErrInvalidHostname = errors.New("invalid hostname")
)

View File

@@ -1,80 +0,0 @@
package configuration
import (
"errors"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/dns/pkg/provider"
"github.com/qdm12/dns/pkg/unbound"
"github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_DNS_readUnboundProviders(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
envValue string
envErr error
expected DNS
err error
}{
"bad value": {
envValue: "invalid",
err: errors.New(`invalid DNS over TLS provider: cannot parse provider: "invalid"`),
},
"env error": {
envErr: errors.New("env error"),
err: errors.New("environment variable DOT_PROVIDERS: env error"),
},
"multiple valid values": {
envValue: "cloudflare,google",
expected: DNS{
Unbound: unbound.Settings{
Providers: []provider.Provider{
provider.Cloudflare(),
provider.Google(),
},
},
},
},
"one invalid value in two": {
envValue: "cloudflare,invalid",
expected: DNS{
Unbound: unbound.Settings{
Providers: []provider.Provider{
provider.Cloudflare(),
},
},
},
err: errors.New(`invalid DNS over TLS provider: cannot parse provider: "invalid"`),
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl)
env.EXPECT().Get("DOT_PROVIDERS", gomock.Any()).
Return(testCase.envValue, testCase.envErr)
var settings DNS
err := settings.readUnboundProviders(env)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.expected, settings)
})
}
}

View File

@@ -1,85 +0,0 @@
package configuration
import (
"fmt"
"strings"
"time"
"github.com/qdm12/golibs/params"
)
type Updater struct {
Period time.Duration `json:"period"`
DNSAddress string `json:"dns_address"`
Cyberghost bool `json:"cyberghost"`
Fastestvpn bool `json:"fastestvpn"`
HideMyAss bool `json:"hidemyass"`
Ipvanish bool `json:"ipvanish"`
Ivpn bool `json:"ivpn"`
Mullvad bool `json:"mullvad"`
Nordvpn bool `json:"nordvpn"`
PIA bool `json:"pia"`
Privado bool `json:"privado"`
Privatevpn bool `json:"privatevpn"`
Protonvpn bool `json:"protonvpn"`
Purevpn bool `json:"purevpn"`
Surfshark bool `json:"surfshark"`
Torguard bool `json:"torguard"`
VPNUnlimited bool `json:"vpnunlimited"`
Vyprvpn bool `json:"vyprvpn"`
Windscribe bool `json:"windscribe"`
// The two below should be used in CLI mode only
CLI bool `json:"-"`
}
func (settings *Updater) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *Updater) lines() (lines []string) {
if settings.Period == 0 {
return nil
}
lines = append(lines, lastIndent+"Updater:")
lines = append(lines, indent+lastIndent+"Period: every "+settings.Period.String())
return lines
}
func (settings *Updater) EnableAll() {
settings.Cyberghost = true
settings.HideMyAss = true
settings.Ipvanish = true
settings.Ivpn = true
settings.Mullvad = true
settings.Nordvpn = true
settings.Privado = true
settings.PIA = true
settings.Privado = true
settings.Privatevpn = true
settings.Protonvpn = true
settings.Purevpn = true
settings.Surfshark = true
settings.Torguard = true
settings.VPNUnlimited = true
settings.Vyprvpn = true
settings.Windscribe = true
}
func (settings *Updater) read(r reader) (err error) {
settings.EnableAll()
// use cloudflare in plaintext to not be blocked by DNS over TLS by default.
// If a plaintext address is set in the DNS settings, this one will be used.
// TODO use custom future encrypted DNS written in Go without blocking
// as it's too much trouble to start another parallel unbound instance for now.
settings.DNSAddress = "1.1.1.1"
settings.Period, err = r.env.Duration("UPDATER_PERIOD", params.Default("0"))
if err != nil {
return fmt.Errorf("environment variable UPDATER_PERIOD: %w", err)
}
return nil
}

View File

@@ -1,87 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) vpnUnlimitedLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ServerSelection.FreeOnly {
lines = append(lines, lastIndent+"Free servers only")
}
if settings.ServerSelection.StreamOnly {
lines = append(lines, lastIndent+"Stream servers only")
}
if settings.ExtraConfigOptions.ClientKey != "" {
lines = append(lines, lastIndent+"Client key is set")
}
return lines
}
func (settings *Provider) readVPNUnlimited(r reader) (err error) {
settings.Name = constants.VPNUnlimited
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ExtraConfigOptions.ClientKey, err = readClientKey(r)
if err != nil {
return err
}
settings.ExtraConfigOptions.ClientCertificate, err = readClientCertificate(r)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.VPNUnlimitedCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.VPNUnlimitedCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.VPNUnlimitedHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.FreeOnly, err = r.env.YesNo("FREE_ONLY", params.Default("no"))
if err != nil {
return fmt.Errorf("environment variable FREE_ONLY: %w", err)
}
settings.ServerSelection.StreamOnly, err = r.env.YesNo("STREAM_ONLY", params.Default("no"))
if err != nil {
return fmt.Errorf("environment variable STREAM_ONLY: %w", err)
}
return nil
}

View File

@@ -1,42 +0,0 @@
package configuration
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_Provider_vpnUnlimitedLines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Provider
lines []string
}{
"empty settings": {},
"full settings": {
settings: Provider{
ServerSelection: ServerSelection{
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
lines: []string{
"|--Countries: A, B",
"|--Cities: C, D",
"|--Hostnames: E, F",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.vpnUnlimitedLines()
assert.Equal(t, testCase.lines, lines)
})
}
}

View File

@@ -1,36 +0,0 @@
package configuration
import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) vyprvpnLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
return lines
}
func (settings *Provider) readVyprvpn(r reader) (err error) {
settings.Name = constants.Vyprvpn
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.VyprvpnRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
return nil
}

View File

@@ -1,66 +0,0 @@
package configuration
import (
"fmt"
"strconv"
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) windscribeLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ServerSelection.CustomPort > 0 {
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
}
return lines
}
func (settings *Provider) readWindscribe(r reader) (err error) {
settings.Name = constants.Windscribe
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.WindscribeRegionChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WindscribeCityChoices())
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.WindscribeHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}
settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.TCP,
[]uint16{21, 22, 80, 123, 143, 443, 587, 1194, 3306, 8080, 54783},
[]uint16{53, 80, 123, 443, 1194, 54783})
if err != nil {
return err
}
return nil
}

View File

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

View File

@@ -1,25 +0,0 @@
// Package constants defines constants shared throughout the program.
// It also defines constant maps and slices using functions.
package constants
import "sort"
func makeChoicesUnique(choices []string) []string {
uniqueChoices := map[string]struct{}{}
for _, choice := range choices {
uniqueChoices[choice] = struct{}{}
}
uniqueChoicesSlice := make([]string, len(uniqueChoices))
i := 0
for choice := range uniqueChoices {
uniqueChoicesSlice[i] = choice
i++
}
sort.Slice(uniqueChoicesSlice, func(i, j int) bool {
return uniqueChoicesSlice[i] < uniqueChoicesSlice[j]
})
return uniqueChoicesSlice
}

View File

@@ -1,256 +0,0 @@
package constants
func CountryCodes() map[string]string {
return map[string]string{
"af": "Afghanistan",
"ax": "Aland Islands",
"al": "Albania",
"dz": "Algeria",
"as": "American Samoa",
"ad": "Andorra",
"ao": "Angola",
"ai": "Anguilla",
"aq": "Antarctica",
"ag": "Antigua and Barbuda",
"ar": "Argentina",
"am": "Armenia",
"aw": "Aruba",
"au": "Australia",
"at": "Austria",
"az": "Azerbaijan",
"bs": "Bahamas",
"bh": "Bahrain",
"bd": "Bangladesh",
"bb": "Barbados",
"by": "Belarus",
"be": "Belgium",
"bz": "Belize",
"bj": "Benin",
"bm": "Bermuda",
"bt": "Bhutan",
"bo": "Bolivia",
"bq": "Bonaire",
"ba": "Bosnia and Herzegovina",
"bw": "Botswana",
"bv": "Bouvet Island",
"br": "Brazil",
"io": "British Indian Ocean Territory",
"vg": "British Virgin Islands",
"bn": "Brunei Darussalam",
"bg": "Bulgaria",
"bf": "Burkina Faso",
"bi": "Burundi",
"kh": "Cambodia",
"cm": "Cameroon",
"ca": "Canada",
"cv": "Cape Verde",
"ky": "Cayman Islands",
"cf": "Central African Republic",
"td": "Chad",
"cl": "Chile",
"cn": "China",
"cx": "Christmas Island",
"cc": "Cocos Islands",
"co": "Colombia",
"km": "Comoros",
"cg": "Congo",
"ck": "Cook Islands",
"cr": "Costa Rica",
"ci": "Cote d'Ivoire",
"hr": "Croatia",
"cu": "Cuba",
"cw": "Curacao",
"cy": "Cyprus",
"cz": "Czech Republic",
"cd": "Democratic Republic of the Congo",
"dk": "Denmark",
"dj": "Djibouti",
"dm": "Dominica",
"do": "Dominican Republic",
"ec": "Ecuador",
"eg": "Egypt",
"sv": "El Salvador",
"gq": "Equatorial Guinea",
"er": "Eritrea",
"ee": "Estonia",
"et": "Ethiopia",
"fk": "Falkland Islands",
"fo": "Faroe Islands",
"fj": "Fiji",
"fi": "Finland",
"fr": "France",
"gf": "French Guiana",
"pf": "French Polynesia",
"tf": "French Southern Territories",
"ga": "Gabon",
"gm": "Gambia",
"ge": "Georgia",
"de": "Germany",
"gh": "Ghana",
"gi": "Gibraltar",
"gr": "Greece",
"gl": "Greenland",
"gd": "Grenada",
"gp": "Guadeloupe",
"gu": "Guam",
"gt": "Guatemala",
"gg": "Guernsey",
"gw": "Guinea-Bissau",
"gn": "Guinea",
"gy": "Guyana",
"ht": "Haiti",
"hm": "Heard Island and McDonald Islands",
"hn": "Honduras",
"hk": "Hong Kong",
"hu": "Hungary",
"is": "Iceland",
"in": "India",
"id": "Indonesia",
"ir": "Iran",
"iq": "Iraq",
"ie": "Ireland",
"im": "Isle of Man",
"il": "Israel",
"it": "Italy",
"jm": "Jamaica",
"jp": "Japan",
"je": "Jersey",
"jo": "Jordan",
"kz": "Kazakhstan",
"ke": "Kenya",
"ki": "Kiribati",
"kr": "Korea",
"kw": "Kuwait",
"kg": "Kyrgyzstan",
"la": "Lao People's Democratic Republic",
"lv": "Latvia",
"lb": "Lebanon",
"ls": "Lesotho",
"lr": "Liberia",
"ly": "Libya",
"li": "Liechtenstein",
"lt": "Lithuania",
"lu": "Luxembourg",
"mo": "Macao",
"mk": "Macedonia",
"mg": "Madagascar",
"mw": "Malawi",
"my": "Malaysia",
"mys": "Kuala Lumpur",
"mv": "Maldives",
"ml": "Mali",
"mt": "Malta",
"mh": "Marshall Islands",
"mq": "Martinique",
"mr": "Mauritania",
"mu": "Mauritius",
"yt": "Mayotte",
"mx": "Mexico",
"fm": "Micronesia",
"md": "Moldova",
"mc": "Monaco",
"mn": "Mongolia",
"me": "Montenegro",
"ms": "Montserrat",
"ma": "Morocco",
"mz": "Mozambique",
"mm": "Myanmar",
"na": "Namibia",
"nr": "Nauru",
"np": "Nepal",
"nl": "Netherlands",
"nc": "New Caledonia",
"nz": "New Zealand",
"ni": "Nicaragua",
"ne": "Niger",
"ng": "Nigeria",
"nu": "Niue",
"nf": "Norfolk Island",
"mp": "Northern Mariana Islands",
"no": "Norway",
"om": "Oman",
"pk": "Pakistan",
"pw": "Palau",
"ps": "Palestine, State of",
"pa": "Panama",
"pg": "Papua New Guinea",
"py": "Paraguay",
"pe": "Peru",
"ph": "Philippines",
"pn": "Pitcairn",
"pl": "Poland",
"pt": "Portugal",
"pr": "Puerto Rico",
"qa": "Qatar",
"re": "Reunion",
"ro": "Romania",
"ru": "Russian Federation",
"rw": "Rwanda",
"bl": "Saint Barthelemy",
"sh": "Saint Helena",
"kn": "Saint Kitts and Nevis",
"lc": "Saint Lucia",
"mf": "Saint Martin",
"pm": "Saint Pierre and Miquelon",
"vc": "Saint Vincent and the Grenadines",
"ws": "Samoa",
"sm": "San Marino",
"st": "Sao Tome and Principe",
"sa": "Saudi Arabia",
"sn": "Senegal",
"rs": "Serbia",
"sc": "Seychelles",
"sl": "Sierra Leone",
"sg": "Singapore",
"sx": "Sint Maarten",
"sk": "Slovakia",
"si": "Slovenia",
"sb": "Solomon Islands",
"so": "Somalia",
"za": "South Africa",
"gs": "South Georgia and the South Sandwich Islands",
"ss": "South Sudan",
"es": "Spain",
"lk": "Sri Lanka",
"sd": "Sudan",
"sr": "Suriname",
"sj": "Svalbard and Jan Mayen",
"sz": "Swaziland",
"se": "Sweden",
"ch": "Switzerland",
"sy": "Syrian Arab Republic",
"tw": "Taiwan",
"tj": "Tajikistan",
"tz": "Tanzania",
"th": "Thailand",
"tl": "Timor-Leste",
"tg": "Togo",
"tk": "Tokelau",
"to": "Tonga",
"tt": "Trinidad and Tobago",
"tn": "Tunisia",
"tr": "Turkey",
"tm": "Turkmenistan",
"tc": "Turks and Caicos Islands",
"tv": "Tuvalu",
"ug": "Uganda",
"ua": "Ukraine",
"ae": "United Arab Emirates",
"gb": "United Kingdom",
"uk": "United Kingdom",
"um": "United States Minor Outlying Islands",
"us": "United States",
"uy": "Uruguay",
"vi": "US Virgin Islands",
"uz": "Uzbekistan",
"vu": "Vanuatu",
"va": "Vatican City State",
"ve": "Venezuela",
"vn": "Vietnam",
"wf": "Wallis and Futuna",
"eh": "Western Sahara",
"ye": "Yemen",
"zm": "Zambia",
"zw": "Zimbabwe",
}
}

View File

@@ -1,6 +1,9 @@
package constants package constants
import ( import (
"net"
"sort"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
) )
@@ -10,36 +13,219 @@ const (
) )
func CyberghostRegionChoices() (choices []string) { func CyberghostRegionChoices() (choices []string) {
servers := CyberghostServers() uniqueChoices := map[string]struct{}{}
choices = make([]string, len(servers)) for _, server := range CyberghostServers() {
for i := range servers { uniqueChoices[server.Region] = struct{}{}
choices[i] = servers[i].Region
} }
return makeUnique(choices) for choice := range uniqueChoices {
choices = append(choices, choice)
}
sort.Slice(choices, func(i, j int) bool {
return choices[i] < choices[j]
})
return choices
} }
func CyberghostGroupChoices() (choices []string) { func CyberghostGroupChoices() (choices []string) {
servers := CyberghostServers() uniqueChoices := map[string]struct{}{}
choices = make([]string, len(servers)) for _, server := range CyberghostServers() {
for i := range servers { uniqueChoices[server.Group] = struct{}{}
choices[i] = servers[i].Group
} }
return makeUnique(choices) for choice := range uniqueChoices {
choices = append(choices, choice)
}
sort.Slice(choices, func(i, j int) bool {
return choices[i] < choices[j]
})
return choices
} }
func CyberghostHostnameChoices() (choices []string) { //nolint:lll
servers := CyberghostServers() func CyberghostServers() []models.CyberghostServer {
choices = make([]string, len(servers)) return []models.CyberghostServer{
for i := range servers { {Region: "Albania", Group: "Premium TCP Europe", IPs: []net.IP{{31, 171, 152, 99}, {31, 171, 152, 102}, {31, 171, 152, 104}, {31, 171, 152, 106}, {31, 171, 152, 107}, {31, 171, 152, 108}, {31, 171, 152, 109}, {31, 171, 152, 133}, {31, 171, 152, 139}, {31, 171, 152, 140}}},
choices[i] = servers[i].Hostname {Region: "Albania", Group: "Premium UDP Europe", IPs: []net.IP{{31, 171, 152, 102}, {31, 171, 152, 103}, {31, 171, 152, 105}, {31, 171, 152, 106}, {31, 171, 152, 107}, {31, 171, 152, 109}, {31, 171, 152, 133}, {31, 171, 152, 136}, {31, 171, 152, 138}, {31, 171, 152, 139}}},
{Region: "Algeria", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 91, 9}, {45, 133, 91, 11}, {45, 133, 91, 12}, {45, 133, 91, 16}, {45, 133, 91, 18}, {45, 133, 91, 19}, {45, 133, 91, 21}, {45, 133, 91, 26}, {45, 133, 91, 27}, {45, 133, 91, 28}}},
{Region: "Algeria", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 91, 7}, {45, 133, 91, 9}, {45, 133, 91, 12}, {45, 133, 91, 16}, {45, 133, 91, 19}, {45, 133, 91, 20}, {45, 133, 91, 25}, {45, 133, 91, 26}, {45, 133, 91, 27}, {45, 133, 91, 29}}},
{Region: "Andorra", Group: "Premium TCP Europe", IPs: []net.IP{{45, 139, 49, 7}, {45, 139, 49, 11}, {45, 139, 49, 27}, {45, 139, 49, 151}, {45, 139, 49, 154}, {45, 139, 49, 158}, {45, 139, 49, 161}, {45, 139, 49, 162}, {45, 139, 49, 165}, {45, 139, 49, 166}}},
{Region: "Andorra", Group: "Premium UDP Europe", IPs: []net.IP{{45, 139, 49, 7}, {45, 139, 49, 21}, {45, 139, 49, 28}, {45, 139, 49, 128}, {45, 139, 49, 136}, {45, 139, 49, 146}, {45, 139, 49, 156}, {45, 139, 49, 163}, {45, 139, 49, 165}, {45, 139, 49, 171}}},
{Region: "Argentina", Group: "Premium UDP USA", IPs: []net.IP{{190, 106, 130, 19}, {190, 106, 130, 20}, {190, 106, 130, 26}, {190, 106, 130, 34}, {190, 106, 130, 36}, {190, 106, 130, 37}, {190, 106, 130, 38}, {190, 106, 130, 40}, {190, 106, 130, 42}, {190, 106, 130, 43}}},
{Region: "Argentina", Group: "Premium TCP USA", IPs: []net.IP{{190, 106, 130, 17}, {190, 106, 130, 18}, {190, 106, 130, 20}, {190, 106, 130, 37}, {190, 106, 130, 38}, {190, 106, 130, 40}, {190, 106, 130, 41}, {190, 106, 130, 42}, {190, 106, 130, 43}, {190, 106, 130, 45}}},
{Region: "Armenia", Group: "Premium UDP Europe", IPs: []net.IP{{45, 139, 50, 7}, {45, 139, 50, 9}, {45, 139, 50, 11}, {45, 139, 50, 12}, {45, 139, 50, 17}, {45, 139, 50, 22}, {45, 139, 50, 25}, {45, 139, 50, 27}, {45, 139, 50, 28}, {45, 139, 50, 29}}},
{Region: "Armenia", Group: "Premium TCP Europe", IPs: []net.IP{{45, 139, 50, 6}, {45, 139, 50, 11}, {45, 139, 50, 16}, {45, 139, 50, 17}, {45, 139, 50, 18}, {45, 139, 50, 21}, {45, 139, 50, 22}, {45, 139, 50, 24}, {45, 139, 50, 25}, {45, 139, 50, 28}}},
{Region: "Australia", Group: "Premium UDP Asia", IPs: []net.IP{{27, 50, 79, 9}, {27, 50, 79, 22}, {27, 50, 79, 23}, {27, 50, 79, 30}, {203, 26, 199, 68}, {203, 26, 199, 73}, {221, 121, 146, 40}, {221, 121, 146, 41}, {221, 121, 146, 55}, {221, 121, 146, 58}}},
{Region: "Australia", Group: "Premium TCP Asia", IPs: []net.IP{{27, 50, 79, 19}, {43, 242, 68, 3}, {43, 242, 68, 8}, {43, 242, 68, 11}, {103, 13, 101, 173}, {203, 26, 199, 66}, {221, 121, 146, 34}, {221, 121, 146, 42}, {221, 121, 146, 50}, {221, 121, 146, 52}}},
{Region: "Austria", Group: "Premium TCP Europe", IPs: []net.IP{{89, 187, 168, 147}, {89, 187, 168, 148}, {89, 187, 168, 150}, {89, 187, 168, 162}, {89, 187, 168, 165}, {89, 187, 168, 166}, {89, 187, 168, 169}, {89, 187, 168, 172}, {89, 187, 168, 174}, {89, 187, 168, 183}}},
{Region: "Austria", Group: "Premium UDP Europe", IPs: []net.IP{{89, 187, 168, 132}, {89, 187, 168, 136}, {89, 187, 168, 138}, {89, 187, 168, 146}, {89, 187, 168, 148}, {89, 187, 168, 163}, {89, 187, 168, 171}, {89, 187, 168, 172}, {89, 187, 168, 179}, {89, 187, 168, 180}}},
{Region: "Bahamas", Group: "Premium TCP USA", IPs: []net.IP{{45, 132, 143, 2}, {45, 132, 143, 4}, {45, 132, 143, 6}, {45, 132, 143, 20}, {45, 132, 143, 22}, {45, 132, 143, 23}, {45, 132, 143, 36}, {45, 132, 143, 39}, {45, 132, 143, 46}, {45, 132, 143, 48}}},
{Region: "Bahamas", Group: "Premium UDP USA", IPs: []net.IP{{45, 132, 143, 1}, {45, 132, 143, 4}, {45, 132, 143, 5}, {45, 132, 143, 13}, {45, 132, 143, 15}, {45, 132, 143, 17}, {45, 132, 143, 36}, {45, 132, 143, 41}, {45, 132, 143, 42}, {45, 132, 143, 44}}},
{Region: "Bangladesh", Group: "Premium UDP Asia", IPs: []net.IP{{45, 132, 142, 1}, {45, 132, 142, 6}, {45, 132, 142, 10}, {45, 132, 142, 11}, {45, 132, 142, 15}, {45, 132, 142, 16}, {45, 132, 142, 19}, {45, 132, 142, 26}, {45, 132, 142, 36}, {45, 132, 142, 47}}},
{Region: "Bangladesh", Group: "Premium TCP Asia", IPs: []net.IP{{45, 132, 142, 3}, {45, 132, 142, 4}, {45, 132, 142, 8}, {45, 132, 142, 16}, {45, 132, 142, 21}, {45, 132, 142, 32}, {45, 132, 142, 36}, {45, 132, 142, 37}, {45, 132, 142, 43}, {45, 132, 142, 48}}},
{Region: "Belarus", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 194, 7}, {45, 132, 194, 10}, {45, 132, 194, 20}, {45, 132, 194, 22}, {45, 132, 194, 27}, {45, 132, 194, 30}, {45, 132, 194, 38}, {45, 132, 194, 43}, {45, 132, 194, 45}, {45, 132, 194, 50}}},
{Region: "Belarus", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 194, 9}, {45, 132, 194, 14}, {45, 132, 194, 20}, {45, 132, 194, 24}, {45, 132, 194, 29}, {45, 132, 194, 32}, {45, 132, 194, 37}, {45, 132, 194, 45}, {45, 132, 194, 46}, {45, 132, 194, 48}}},
{Region: "Belgium", Group: "Premium UDP Europe", IPs: []net.IP{{5, 253, 205, 20}, {5, 253, 205, 25}, {5, 253, 205, 29}, {37, 120, 143, 168}, {185, 210, 217, 58}, {185, 232, 21, 122}, {185, 232, 21, 124}, {193, 9, 114, 211}, {193, 9, 114, 213}, {193, 9, 114, 216}}},
{Region: "Belgium", Group: "Premium TCP Europe", IPs: []net.IP{{5, 253, 205, 23}, {5, 253, 205, 27}, {37, 120, 143, 54}, {37, 120, 143, 60}, {37, 120, 143, 166}, {37, 120, 143, 173}, {185, 210, 217, 51}, {185, 210, 217, 252}, {185, 210, 217, 254}, {193, 9, 114, 227}}},
{Region: "Bosnia and Herzegovina", Group: "Premium UDP Europe", IPs: []net.IP{{185, 99, 3, 57}, {185, 99, 3, 58}, {185, 99, 3, 72}, {185, 99, 3, 73}, {185, 99, 3, 74}, {185, 99, 3, 130}, {185, 99, 3, 131}, {185, 99, 3, 134}, {185, 99, 3, 135}, {185, 99, 3, 136}}},
{Region: "Bosnia and Herzegovina", Group: "Premium TCP Europe", IPs: []net.IP{{185, 99, 3, 57}, {185, 99, 3, 58}, {185, 99, 3, 72}, {185, 99, 3, 73}, {185, 99, 3, 74}, {185, 99, 3, 130}, {185, 99, 3, 131}, {185, 99, 3, 134}, {185, 99, 3, 135}, {185, 99, 3, 136}}},
{Region: "Brazil", Group: "Premium UDP USA", IPs: []net.IP{{45, 231, 207, 71}, {45, 231, 207, 76}, {45, 231, 207, 77}, {177, 67, 81, 165}, {177, 67, 81, 172}, {181, 41, 203, 97}, {181, 41, 203, 99}, {181, 41, 203, 101}, {181, 41, 203, 106}, {181, 41, 203, 109}}},
{Region: "Brazil", Group: "Premium TCP USA", IPs: []net.IP{{45, 231, 207, 72}, {45, 231, 207, 74}, {45, 231, 207, 79}, {177, 67, 81, 166}, {177, 67, 81, 169}, {177, 67, 81, 171}, {181, 41, 203, 99}, {181, 41, 203, 106}, {181, 41, 203, 108}, {181, 41, 203, 110}}},
{Region: "Bulgaria", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 152, 100}, {37, 120, 152, 102}, {37, 120, 152, 103}, {37, 120, 152, 104}, {37, 120, 152, 105}, {37, 120, 152, 106}, {37, 120, 152, 107}, {37, 120, 152, 108}, {37, 120, 152, 109}, {37, 120, 152, 110}}},
{Region: "Bulgaria", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 152, 99}, {37, 120, 152, 100}, {37, 120, 152, 101}, {37, 120, 152, 103}, {37, 120, 152, 104}, {37, 120, 152, 105}, {37, 120, 152, 107}, {37, 120, 152, 108}, {37, 120, 152, 109}, {37, 120, 152, 110}}},
{Region: "Cambodia", Group: "Premium UDP Asia", IPs: []net.IP{{188, 215, 235, 35}, {188, 215, 235, 36}, {188, 215, 235, 37}, {188, 215, 235, 45}, {188, 215, 235, 47}, {188, 215, 235, 48}, {188, 215, 235, 49}, {188, 215, 235, 53}, {188, 215, 235, 55}, {188, 215, 235, 56}}},
{Region: "Cambodia", Group: "Premium TCP Asia", IPs: []net.IP{{188, 215, 235, 36}, {188, 215, 235, 37}, {188, 215, 235, 38}, {188, 215, 235, 40}, {188, 215, 235, 41}, {188, 215, 235, 43}, {188, 215, 235, 47}, {188, 215, 235, 49}, {188, 215, 235, 55}, {188, 215, 235, 56}}},
{Region: "Canada", Group: "Premium UDP USA", IPs: []net.IP{{37, 120, 130, 145}, {37, 120, 130, 180}, {37, 120, 130, 203}, {37, 120, 205, 27}, {37, 120, 205, 30}, {89, 47, 234, 117}, {139, 28, 218, 94}, {176, 113, 74, 199}, {176, 113, 74, 205}, {176, 113, 74, 212}}},
{Region: "Canada", Group: "Premium TCP USA", IPs: []net.IP{{37, 120, 130, 173}, {37, 120, 130, 203}, {37, 120, 130, 210}, {37, 120, 205, 8}, {89, 47, 234, 88}, {104, 245, 145, 169}, {139, 28, 218, 88}, {176, 113, 74, 70}, {176, 113, 74, 89}, {176, 113, 74, 195}}},
{Region: "Chile", Group: "Premium UDP USA", IPs: []net.IP{{190, 105, 239, 129}, {190, 105, 239, 130}, {190, 105, 239, 131}, {190, 105, 239, 132}, {190, 105, 239, 133}, {190, 105, 239, 134}, {190, 105, 239, 135}, {190, 105, 239, 136}, {190, 105, 239, 137}, {190, 105, 239, 138}}},
{Region: "Chile", Group: "Premium TCP USA", IPs: []net.IP{{190, 105, 239, 129}, {190, 105, 239, 130}, {190, 105, 239, 131}, {190, 105, 239, 132}, {190, 105, 239, 133}, {190, 105, 239, 134}, {190, 105, 239, 135}, {190, 105, 239, 136}, {190, 105, 239, 137}, {190, 105, 239, 138}}},
{Region: "China", Group: "Premium TCP Asia", IPs: []net.IP{{45, 132, 193, 2}, {45, 132, 193, 3}, {45, 132, 193, 7}, {45, 132, 193, 10}, {45, 132, 193, 13}, {45, 132, 193, 15}, {45, 132, 193, 20}, {45, 132, 193, 31}, {45, 132, 193, 41}, {45, 132, 193, 47}}},
{Region: "China", Group: "Premium UDP Asia", IPs: []net.IP{{45, 132, 193, 7}, {45, 132, 193, 14}, {45, 132, 193, 23}, {45, 132, 193, 26}, {45, 132, 193, 30}, {45, 132, 193, 36}, {45, 132, 193, 44}, {45, 132, 193, 45}, {45, 132, 193, 46}, {45, 132, 193, 48}}},
{Region: "Colombia", Group: "Premium TCP USA", IPs: []net.IP{{190, 105, 229, 19}, {190, 105, 229, 20}, {190, 105, 229, 21}, {190, 105, 229, 22}}},
{Region: "Colombia", Group: "Premium UDP USA", IPs: []net.IP{{190, 105, 229, 19}, {190, 105, 229, 20}, {190, 105, 229, 21}, {190, 105, 229, 22}}},
{Region: "Costa Rica", Group: "Premium TCP USA", IPs: []net.IP{{143, 202, 160, 67}, {143, 202, 160, 68}, {143, 202, 160, 70}, {143, 202, 160, 71}, {143, 202, 160, 72}, {143, 202, 160, 73}, {143, 202, 160, 74}, {143, 202, 160, 75}, {143, 202, 160, 77}, {143, 202, 160, 78}}},
{Region: "Costa Rica", Group: "Premium UDP USA", IPs: []net.IP{{143, 202, 160, 67}, {143, 202, 160, 68}, {143, 202, 160, 69}, {143, 202, 160, 70}, {143, 202, 160, 71}, {143, 202, 160, 72}, {143, 202, 160, 73}, {143, 202, 160, 74}, {143, 202, 160, 75}, {143, 202, 160, 76}}},
{Region: "Cyprus", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 137, 7}, {45, 132, 137, 9}, {45, 132, 137, 11}, {45, 132, 137, 13}, {45, 132, 137, 20}, {45, 132, 137, 23}, {45, 132, 137, 26}, {45, 132, 137, 27}, {45, 132, 137, 28}, {45, 132, 137, 29}}},
{Region: "Cyprus", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 137, 7}, {45, 132, 137, 10}, {45, 132, 137, 14}, {45, 132, 137, 15}, {45, 132, 137, 17}, {45, 132, 137, 18}, {45, 132, 137, 21}, {45, 132, 137, 24}, {45, 132, 137, 27}, {45, 132, 137, 29}}},
{Region: "Czech Republic", Group: "Premium UDP Europe", IPs: []net.IP{{185, 216, 35, 227}, {185, 216, 35, 232}, {185, 216, 35, 236}, {195, 181, 161, 5}, {195, 181, 161, 6}, {195, 181, 161, 7}, {195, 181, 161, 10}, {195, 181, 161, 12}, {195, 181, 161, 15}, {195, 181, 161, 19}}},
{Region: "Czech Republic", Group: "Premium TCP Europe", IPs: []net.IP{{185, 216, 35, 230}, {185, 216, 35, 232}, {185, 216, 35, 236}, {195, 181, 161, 4}, {195, 181, 161, 8}, {195, 181, 161, 15}, {195, 181, 161, 18}, {195, 181, 161, 20}, {195, 181, 161, 24}, {195, 181, 161, 25}}},
{Region: "Denmark", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 145, 85}, {37, 120, 145, 90}, {37, 120, 194, 37}, {37, 120, 194, 41}, {95, 174, 65, 164}, {95, 174, 65, 174}, {185, 206, 224, 231}, {185, 206, 224, 235}, {185, 206, 224, 236}, {185, 206, 224, 238}}},
{Region: "Denmark", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 145, 86}, {37, 120, 145, 88}, {37, 120, 194, 40}, {37, 120, 194, 42}, {37, 120, 194, 60}, {37, 120, 194, 62}, {95, 174, 65, 170}, {185, 206, 224, 231}, {185, 206, 224, 248}, {185, 206, 224, 250}}},
{Region: "Egypt", Group: "Premium TCP Europe", IPs: []net.IP{{188, 214, 122, 40}, {188, 214, 122, 44}, {188, 214, 122, 46}, {188, 214, 122, 47}, {188, 214, 122, 48}, {188, 214, 122, 51}, {188, 214, 122, 52}, {188, 214, 122, 56}, {188, 214, 122, 59}, {188, 214, 122, 60}}},
{Region: "Egypt", Group: "Premium UDP Europe", IPs: []net.IP{{188, 214, 122, 40}, {188, 214, 122, 41}, {188, 214, 122, 43}, {188, 214, 122, 46}, {188, 214, 122, 48}, {188, 214, 122, 51}, {188, 214, 122, 52}, {188, 214, 122, 55}, {188, 214, 122, 57}, {188, 214, 122, 60}}},
{Region: "Estonia", Group: "Premium UDP Europe", IPs: []net.IP{{77, 247, 111, 3}, {77, 247, 111, 4}, {77, 247, 111, 6}, {77, 247, 111, 9}, {77, 247, 111, 11}, {77, 247, 111, 51}, {77, 247, 111, 53}, {77, 247, 111, 57}, {77, 247, 111, 58}, {77, 247, 111, 62}}},
{Region: "Estonia", Group: "Premium TCP Europe", IPs: []net.IP{{77, 247, 111, 4}, {77, 247, 111, 7}, {77, 247, 111, 9}, {77, 247, 111, 10}, {77, 247, 111, 11}, {77, 247, 111, 52}, {77, 247, 111, 53}, {77, 247, 111, 55}, {77, 247, 111, 56}, {77, 247, 111, 57}}},
{Region: "Finland", Group: "Premium UDP Europe", IPs: []net.IP{{188, 126, 89, 102}, {188, 126, 89, 103}, {188, 126, 89, 104}, {188, 126, 89, 105}, {188, 126, 89, 112}, {188, 126, 89, 116}, {188, 126, 89, 124}, {188, 126, 89, 131}, {188, 126, 89, 137}, {188, 126, 89, 138}}},
{Region: "Finland", Group: "Premium TCP Europe", IPs: []net.IP{{188, 126, 89, 99}, {188, 126, 89, 106}, {188, 126, 89, 113}, {188, 126, 89, 121}, {188, 126, 89, 125}, {188, 126, 89, 131}, {188, 126, 89, 132}, {188, 126, 89, 142}, {188, 126, 89, 146}, {188, 126, 89, 155}}},
{Region: "France", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 60, 9}, {84, 17, 60, 23}, {84, 17, 60, 28}, {84, 17, 60, 95}, {84, 17, 61, 110}, {84, 17, 61, 162}, {84, 17, 61, 171}, {84, 17, 61, 187}, {151, 106, 12, 248}, {194, 59, 249, 150}}},
{Region: "France", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 60, 53}, {84, 17, 60, 59}, {84, 17, 60, 99}, {84, 17, 60, 150}, {84, 17, 61, 53}, {84, 17, 61, 104}, {84, 17, 61, 158}, {84, 17, 61, 203}, {151, 106, 8, 45}, {151, 106, 12, 243}}},
{Region: "Georgia", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 138, 7}, {45, 132, 138, 11}, {45, 132, 138, 12}, {45, 132, 138, 13}, {45, 132, 138, 16}, {45, 132, 138, 19}, {45, 132, 138, 20}, {45, 132, 138, 21}, {45, 132, 138, 28}, {45, 132, 138, 29}}},
{Region: "Georgia", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 138, 6}, {45, 132, 138, 7}, {45, 132, 138, 8}, {45, 132, 138, 10}, {45, 132, 138, 14}, {45, 132, 138, 15}, {45, 132, 138, 17}, {45, 132, 138, 19}, {45, 132, 138, 22}, {45, 132, 138, 29}}},
{Region: "Germany", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 217, 40}, {84, 17, 48, 203}, {84, 17, 49, 94}, {84, 17, 49, 102}, {84, 17, 49, 188}, {154, 28, 188, 30}, {154, 28, 188, 99}, {154, 28, 188, 117}, {154, 28, 188, 131}, {193, 176, 86, 218}}},
{Region: "Germany", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 48, 13}, {84, 17, 48, 66}, {84, 17, 48, 72}, {84, 17, 48, 193}, {84, 17, 49, 129}, {154, 28, 188, 45}, {154, 28, 188, 58}, {154, 28, 188, 109}, {193, 176, 86, 215}, {212, 103, 50, 69}}},
{Region: "Greece", Group: "Premium UDP Europe", IPs: []net.IP{{154, 57, 3, 130}, {154, 57, 3, 131}, {154, 57, 3, 133}, {154, 57, 3, 134}, {154, 57, 3, 137}, {154, 57, 3, 138}, {188, 123, 126, 170}, {188, 123, 126, 174}, {188, 123, 126, 175}, {188, 123, 126, 176}}},
{Region: "Greece", Group: "Premium TCP Europe", IPs: []net.IP{{154, 57, 3, 130}, {154, 57, 3, 131}, {154, 57, 3, 133}, {154, 57, 3, 135}, {154, 57, 3, 136}, {154, 57, 3, 140}, {154, 57, 3, 141}, {188, 123, 126, 168}, {188, 123, 126, 174}, {188, 123, 126, 176}}},
{Region: "Greenland", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 209, 6}, {45, 131, 209, 8}, {45, 131, 209, 9}, {45, 131, 209, 15}, {45, 131, 209, 19}, {45, 131, 209, 20}, {45, 131, 209, 21}, {45, 131, 209, 25}, {45, 131, 209, 26}, {45, 131, 209, 29}}},
{Region: "Greenland", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 209, 7}, {45, 131, 209, 14}, {45, 131, 209, 16}, {45, 131, 209, 17}, {45, 131, 209, 18}, {45, 131, 209, 19}, {45, 131, 209, 20}, {45, 131, 209, 23}, {45, 131, 209, 26}, {45, 131, 209, 27}}},
{Region: "Hong Kong", Group: "Premium TCP Asia", IPs: []net.IP{{84, 17, 56, 131}, {84, 17, 56, 136}, {84, 17, 56, 152}, {84, 17, 56, 168}, {84, 17, 56, 170}, {84, 17, 56, 171}, {84, 17, 56, 174}, {84, 17, 56, 179}, {84, 17, 56, 180}, {84, 17, 56, 182}}},
{Region: "Hong Kong", Group: "Premium UDP Asia", IPs: []net.IP{{84, 17, 56, 135}, {84, 17, 56, 139}, {84, 17, 56, 140}, {84, 17, 56, 147}, {84, 17, 56, 148}, {84, 17, 56, 153}, {84, 17, 56, 164}, {84, 17, 56, 168}, {84, 17, 56, 179}, {84, 17, 56, 181}}},
{Region: "Hungary", Group: "Premium TCP Europe", IPs: []net.IP{{185, 104, 187, 83}, {185, 104, 187, 85}, {185, 104, 187, 89}, {185, 189, 114, 115}, {185, 189, 114, 116}, {185, 189, 114, 117}, {185, 189, 114, 118}, {185, 189, 114, 121}, {185, 189, 114, 124}, {185, 189, 114, 126}}},
{Region: "Hungary", Group: "Premium UDP Europe", IPs: []net.IP{{185, 104, 187, 85}, {185, 104, 187, 88}, {185, 104, 187, 91}, {185, 104, 187, 94}, {185, 189, 114, 116}, {185, 189, 114, 119}, {185, 189, 114, 120}, {185, 189, 114, 123}, {185, 189, 114, 124}, {185, 189, 114, 125}}},
{Region: "Iceland", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 193, 3}, {45, 133, 193, 4}, {45, 133, 193, 5}, {45, 133, 193, 6}, {45, 133, 193, 7}, {45, 133, 193, 8}, {45, 133, 193, 9}, {45, 133, 193, 10}, {45, 133, 193, 12}, {45, 133, 193, 13}}},
{Region: "Iceland", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 193, 3}, {45, 133, 193, 4}, {45, 133, 193, 5}, {45, 133, 193, 8}, {45, 133, 193, 9}, {45, 133, 193, 10}, {45, 133, 193, 11}, {45, 133, 193, 12}, {45, 133, 193, 13}, {45, 133, 193, 14}}},
{Region: "India", Group: "Premium UDP Europe", IPs: []net.IP{{43, 241, 71, 116}, {43, 241, 71, 118}, {43, 241, 71, 119}, {43, 241, 71, 120}, {43, 241, 71, 124}, {43, 241, 71, 125}, {43, 241, 71, 148}, {43, 241, 71, 150}, {43, 241, 71, 154}, {43, 241, 71, 155}}},
{Region: "India", Group: "Premium TCP Europe", IPs: []net.IP{{43, 241, 71, 116}, {43, 241, 71, 120}, {43, 241, 71, 121}, {43, 241, 71, 122}, {43, 241, 71, 125}, {43, 241, 71, 148}, {43, 241, 71, 151}, {43, 241, 71, 153}, {43, 241, 71, 155}, {43, 241, 71, 157}}},
{Region: "Indonesia", Group: "Premium UDP Asia", IPs: []net.IP{{113, 20, 29, 243}, {113, 20, 29, 244}, {113, 20, 29, 245}, {113, 20, 29, 246}, {113, 20, 29, 247}, {113, 20, 29, 249}, {113, 20, 29, 251}, {113, 20, 29, 252}, {113, 20, 29, 253}, {113, 20, 29, 254}}},
{Region: "Indonesia", Group: "Premium TCP Asia", IPs: []net.IP{{113, 20, 29, 243}, {113, 20, 29, 245}, {113, 20, 29, 246}, {113, 20, 29, 248}, {113, 20, 29, 249}, {113, 20, 29, 250}, {113, 20, 29, 251}, {113, 20, 29, 252}, {113, 20, 29, 253}, {113, 20, 29, 254}}},
{Region: "Iran", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 4, 10}, {45, 131, 4, 13}, {45, 131, 4, 14}, {45, 131, 4, 15}, {45, 131, 4, 17}, {45, 131, 4, 20}, {45, 131, 4, 21}, {45, 131, 4, 22}, {45, 131, 4, 24}, {45, 131, 4, 28}}},
{Region: "Iran", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 4, 7}, {45, 131, 4, 11}, {45, 131, 4, 12}, {45, 131, 4, 13}, {45, 131, 4, 19}, {45, 131, 4, 21}, {45, 131, 4, 26}, {45, 131, 4, 27}, {45, 131, 4, 28}, {45, 131, 4, 29}}},
{Region: "Ireland", Group: "Premium TCP Europe", IPs: []net.IP{{77, 81, 139, 36}, {77, 81, 139, 38}, {77, 81, 139, 39}, {77, 81, 139, 40}, {84, 247, 48, 3}, {84, 247, 48, 5}, {84, 247, 48, 11}, {84, 247, 48, 12}, {84, 247, 48, 27}, {84, 247, 48, 28}}},
{Region: "Ireland", Group: "Premium UDP Europe", IPs: []net.IP{{77, 81, 139, 42}, {77, 81, 139, 45}, {84, 247, 48, 3}, {84, 247, 48, 6}, {84, 247, 48, 7}, {84, 247, 48, 8}, {84, 247, 48, 20}, {84, 247, 48, 22}, {84, 247, 48, 24}, {84, 247, 48, 29}}},
{Region: "Isle of Man", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 140, 7}, {45, 132, 140, 8}, {45, 132, 140, 9}, {45, 132, 140, 11}, {45, 132, 140, 13}, {45, 132, 140, 15}, {45, 132, 140, 18}, {45, 132, 140, 20}, {45, 132, 140, 24}, {45, 132, 140, 25}}},
{Region: "Isle of Man", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 140, 7}, {45, 132, 140, 8}, {45, 132, 140, 15}, {45, 132, 140, 20}, {45, 132, 140, 21}, {45, 132, 140, 22}, {45, 132, 140, 23}, {45, 132, 140, 27}, {45, 132, 140, 28}, {45, 132, 140, 29}}},
{Region: "Israel", Group: "Premium TCP Europe", IPs: []net.IP{{160, 116, 0, 163}, {160, 116, 0, 165}, {160, 116, 0, 166}, {160, 116, 0, 167}, {160, 116, 0, 169}, {160, 116, 0, 170}, {160, 116, 0, 171}, {160, 116, 0, 172}, {160, 116, 0, 173}, {160, 116, 0, 174}}},
{Region: "Israel", Group: "Premium UDP Europe", IPs: []net.IP{{160, 116, 0, 163}, {160, 116, 0, 164}, {160, 116, 0, 165}, {160, 116, 0, 166}, {160, 116, 0, 167}, {160, 116, 0, 168}, {160, 116, 0, 169}, {160, 116, 0, 171}, {160, 116, 0, 172}, {160, 116, 0, 173}}},
{Region: "Italy", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 58, 5}, {84, 17, 58, 8}, {84, 17, 58, 22}, {84, 17, 58, 97}, {84, 17, 58, 121}, {87, 101, 94, 119}, {185, 217, 71, 135}, {185, 217, 71, 142}, {185, 217, 71, 147}, {212, 102, 55, 115}}},
{Region: "Italy", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 58, 9}, {84, 17, 58, 96}, {84, 17, 58, 97}, {84, 17, 58, 105}, {87, 101, 94, 115}, {87, 101, 94, 124}, {185, 217, 71, 135}, {185, 217, 71, 149}, {212, 102, 55, 98}, {212, 102, 55, 122}}},
{Region: "Japan", Group: "Premium UDP Asia", IPs: []net.IP{{156, 146, 35, 3}, {156, 146, 35, 6}, {156, 146, 35, 12}, {156, 146, 35, 21}, {156, 146, 35, 25}, {156, 146, 35, 30}, {156, 146, 35, 35}, {156, 146, 35, 39}, {156, 146, 35, 46}, {156, 146, 35, 50}}},
{Region: "Japan", Group: "Premium TCP Asia", IPs: []net.IP{{156, 146, 35, 3}, {156, 146, 35, 5}, {156, 146, 35, 17}, {156, 146, 35, 29}, {156, 146, 35, 32}, {156, 146, 35, 35}, {156, 146, 35, 36}, {156, 146, 35, 41}, {156, 146, 35, 47}, {156, 146, 35, 48}}},
{Region: "Kazakhstan", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 88, 10}, {45, 133, 88, 11}, {45, 133, 88, 13}, {45, 133, 88, 14}, {45, 133, 88, 15}, {45, 133, 88, 17}, {45, 133, 88, 20}, {45, 133, 88, 21}, {45, 133, 88, 27}, {45, 133, 88, 28}}},
{Region: "Kazakhstan", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 88, 6}, {45, 133, 88, 9}, {45, 133, 88, 11}, {45, 133, 88, 13}, {45, 133, 88, 14}, {45, 133, 88, 17}, {45, 133, 88, 20}, {45, 133, 88, 22}, {45, 133, 88, 25}, {45, 133, 88, 28}}},
{Region: "Kenya", Group: "Premium TCP Asia", IPs: []net.IP{{62, 12, 118, 195}, {62, 12, 118, 196}, {62, 12, 118, 197}, {62, 12, 118, 198}, {62, 12, 118, 199}, {62, 12, 118, 200}, {62, 12, 118, 201}, {62, 12, 118, 202}, {62, 12, 118, 203}, {62, 12, 118, 204}}},
{Region: "Kenya", Group: "Premium UDP Asia", IPs: []net.IP{{62, 12, 118, 195}, {62, 12, 118, 196}, {62, 12, 118, 197}, {62, 12, 118, 198}, {62, 12, 118, 199}, {62, 12, 118, 200}, {62, 12, 118, 201}, {62, 12, 118, 202}, {62, 12, 118, 203}, {62, 12, 118, 204}}},
{Region: "Korea", Group: "Premium UDP Asia", IPs: []net.IP{{27, 255, 75, 233}, {27, 255, 75, 236}, {27, 255, 75, 237}, {27, 255, 75, 244}, {27, 255, 75, 247}, {27, 255, 75, 249}, {27, 255, 75, 250}, {27, 255, 75, 252}, {27, 255, 75, 253}, {27, 255, 75, 254}}},
{Region: "Korea", Group: "Premium TCP Asia", IPs: []net.IP{{27, 255, 75, 228}, {27, 255, 75, 231}, {27, 255, 75, 233}, {27, 255, 75, 237}, {27, 255, 75, 243}, {27, 255, 75, 246}, {27, 255, 75, 248}, {27, 255, 75, 250}, {27, 255, 75, 252}, {27, 255, 75, 254}}},
{Region: "Latvia", Group: "Premium UDP Europe", IPs: []net.IP{{109, 248, 148, 244}, {109, 248, 148, 246}, {109, 248, 148, 252}, {109, 248, 148, 253}, {109, 248, 149, 19}, {109, 248, 149, 20}, {109, 248, 149, 23}, {109, 248, 149, 24}, {109, 248, 149, 27}, {109, 248, 149, 29}}},
{Region: "Latvia", Group: "Premium TCP Europe", IPs: []net.IP{{109, 248, 148, 243}, {109, 248, 148, 244}, {109, 248, 148, 251}, {109, 248, 148, 252}, {109, 248, 148, 254}, {109, 248, 149, 21}, {109, 248, 149, 24}, {109, 248, 149, 25}, {109, 248, 149, 28}, {109, 248, 149, 29}}},
{Region: "Liechtenstein", Group: "Premium TCP Europe", IPs: []net.IP{{45, 139, 48, 7}, {45, 139, 48, 8}, {45, 139, 48, 10}, {45, 139, 48, 11}, {45, 139, 48, 13}, {45, 139, 48, 19}, {45, 139, 48, 22}, {45, 139, 48, 24}, {45, 139, 48, 25}, {45, 139, 48, 29}}},
{Region: "Liechtenstein", Group: "Premium UDP Europe", IPs: []net.IP{{45, 139, 48, 7}, {45, 139, 48, 12}, {45, 139, 48, 13}, {45, 139, 48, 14}, {45, 139, 48, 16}, {45, 139, 48, 21}, {45, 139, 48, 22}, {45, 139, 48, 23}, {45, 139, 48, 25}, {45, 139, 48, 27}}},
{Region: "Lithuania", Group: "Premium UDP Europe", IPs: []net.IP{{85, 206, 162, 209}, {85, 206, 162, 211}, {85, 206, 162, 215}, {85, 206, 162, 218}, {85, 206, 162, 220}, {85, 206, 162, 222}, {85, 206, 165, 18}, {85, 206, 165, 20}, {85, 206, 165, 23}, {85, 206, 165, 26}}},
{Region: "Lithuania", Group: "Premium TCP Europe", IPs: []net.IP{{85, 206, 162, 210}, {85, 206, 162, 214}, {85, 206, 162, 215}, {85, 206, 162, 218}, {85, 206, 162, 219}, {85, 206, 162, 220}, {85, 206, 162, 221}, {85, 206, 165, 17}, {85, 206, 165, 18}, {85, 206, 165, 19}}},
{Region: "Luxembourg", Group: "Premium UDP Europe", IPs: []net.IP{{5, 253, 204, 8}, {5, 253, 204, 10}, {5, 253, 204, 11}, {5, 253, 204, 14}, {5, 253, 204, 19}, {5, 253, 204, 21}, {5, 253, 204, 23}, {5, 253, 204, 27}, {5, 253, 204, 29}, {5, 253, 204, 30}}},
{Region: "Luxembourg", Group: "Premium TCP Europe", IPs: []net.IP{{5, 253, 204, 5}, {5, 253, 204, 9}, {5, 253, 204, 10}, {5, 253, 204, 12}, {5, 253, 204, 13}, {5, 253, 204, 19}, {5, 253, 204, 22}, {5, 253, 204, 26}, {5, 253, 204, 27}, {5, 253, 204, 28}}},
{Region: "Macao", Group: "Premium UDP Asia", IPs: []net.IP{{45, 137, 197, 9}, {45, 137, 197, 10}, {45, 137, 197, 12}, {45, 137, 197, 14}, {45, 137, 197, 18}, {45, 137, 197, 25}, {45, 137, 197, 29}, {45, 137, 197, 30}, {45, 137, 197, 33}, {45, 137, 197, 45}}},
{Region: "Macao", Group: "Premium TCP Asia", IPs: []net.IP{{45, 137, 197, 1}, {45, 137, 197, 14}, {45, 137, 197, 16}, {45, 137, 197, 17}, {45, 137, 197, 26}, {45, 137, 197, 28}, {45, 137, 197, 30}, {45, 137, 197, 40}, {45, 137, 197, 42}, {45, 137, 197, 48}}},
{Region: "Macedonia", Group: "Premium UDP Europe", IPs: []net.IP{{185, 225, 28, 3}, {185, 225, 28, 4}, {185, 225, 28, 5}, {185, 225, 28, 6}, {185, 225, 28, 7}, {185, 225, 28, 8}, {185, 225, 28, 9}, {185, 225, 28, 10}, {185, 225, 28, 11}, {185, 225, 28, 12}}},
{Region: "Macedonia", Group: "Premium TCP Europe", IPs: []net.IP{{185, 225, 28, 3}, {185, 225, 28, 4}, {185, 225, 28, 5}, {185, 225, 28, 6}, {185, 225, 28, 7}, {185, 225, 28, 8}, {185, 225, 28, 9}, {185, 225, 28, 10}, {185, 225, 28, 11}, {185, 225, 28, 12}}},
{Region: "Malaysia", Group: "Premium UDP Asia", IPs: []net.IP{{139, 5, 177, 69}, {139, 5, 177, 70}, {139, 5, 177, 71}, {139, 5, 177, 72}, {139, 5, 177, 73}, {139, 5, 177, 74}, {139, 5, 177, 75}, {139, 5, 177, 76}, {139, 5, 177, 77}, {139, 5, 177, 78}}},
{Region: "Malaysia", Group: "Premium TCP Asia", IPs: []net.IP{{139, 5, 177, 69}, {139, 5, 177, 70}, {139, 5, 177, 71}, {139, 5, 177, 72}, {139, 5, 177, 73}, {139, 5, 177, 74}, {139, 5, 177, 75}, {139, 5, 177, 76}, {139, 5, 177, 77}, {139, 5, 177, 78}}},
{Region: "Malta", Group: "Premium UDP Europe", IPs: []net.IP{{45, 137, 198, 9}, {45, 137, 198, 11}, {45, 137, 198, 18}, {45, 137, 198, 20}, {45, 137, 198, 24}, {45, 137, 198, 25}, {45, 137, 198, 26}, {45, 137, 198, 27}, {45, 137, 198, 28}, {45, 137, 198, 29}}},
{Region: "Malta", Group: "Premium TCP Europe", IPs: []net.IP{{45, 137, 198, 8}, {45, 137, 198, 10}, {45, 137, 198, 12}, {45, 137, 198, 14}, {45, 137, 198, 17}, {45, 137, 198, 19}, {45, 137, 198, 20}, {45, 137, 198, 22}, {45, 137, 198, 27}, {45, 137, 198, 28}}},
{Region: "Mexico", Group: "Premium TCP USA", IPs: []net.IP{{45, 133, 180, 99}, {45, 133, 180, 100}, {45, 133, 180, 103}, {45, 133, 180, 104}, {45, 133, 180, 109}, {45, 133, 180, 115}, {45, 133, 180, 118}, {45, 133, 180, 119}, {45, 133, 180, 120}, {45, 133, 180, 123}}},
{Region: "Mexico", Group: "Premium UDP USA", IPs: []net.IP{{45, 133, 180, 101}, {45, 133, 180, 103}, {45, 133, 180, 106}, {45, 133, 180, 107}, {45, 133, 180, 109}, {45, 133, 180, 110}, {45, 133, 180, 119}, {45, 133, 180, 121}, {45, 133, 180, 122}, {45, 133, 180, 123}}},
{Region: "Moldova", Group: "Premium TCP Europe", IPs: []net.IP{{178, 175, 130, 243}, {178, 175, 130, 245}, {178, 175, 130, 246}, {178, 175, 130, 250}, {178, 175, 130, 251}, {178, 175, 130, 252}, {178, 175, 130, 254}, {178, 175, 142, 131}, {178, 175, 142, 133}, {178, 175, 142, 134}}},
{Region: "Moldova", Group: "Premium UDP Europe", IPs: []net.IP{{178, 175, 130, 243}, {178, 175, 130, 246}, {178, 175, 130, 250}, {178, 175, 130, 251}, {178, 175, 130, 253}, {178, 175, 130, 254}, {178, 175, 142, 131}, {178, 175, 142, 132}, {178, 175, 142, 133}, {178, 175, 142, 134}}},
{Region: "Monaco", Group: "Premium TCP Europe", IPs: []net.IP{{45, 137, 199, 6}, {45, 137, 199, 8}, {45, 137, 199, 11}, {45, 137, 199, 12}, {45, 137, 199, 13}, {45, 137, 199, 15}, {45, 137, 199, 16}, {45, 137, 199, 18}, {45, 137, 199, 23}, {45, 137, 199, 26}}},
{Region: "Monaco", Group: "Premium UDP Europe", IPs: []net.IP{{45, 137, 199, 6}, {45, 137, 199, 7}, {45, 137, 199, 10}, {45, 137, 199, 12}, {45, 137, 199, 13}, {45, 137, 199, 19}, {45, 137, 199, 20}, {45, 137, 199, 23}, {45, 137, 199, 25}, {45, 137, 199, 29}}},
{Region: "Mongolia", Group: "Premium UDP Asia", IPs: []net.IP{{45, 139, 51, 4}, {45, 139, 51, 11}, {45, 139, 51, 12}, {45, 139, 51, 15}, {45, 139, 51, 16}, {45, 139, 51, 27}, {45, 139, 51, 29}, {45, 139, 51, 32}, {45, 139, 51, 46}, {45, 139, 51, 48}}},
{Region: "Mongolia", Group: "Premium TCP Asia", IPs: []net.IP{{45, 139, 51, 5}, {45, 139, 51, 7}, {45, 139, 51, 15}, {45, 139, 51, 17}, {45, 139, 51, 18}, {45, 139, 51, 20}, {45, 139, 51, 21}, {45, 139, 51, 39}, {45, 139, 51, 41}, {45, 139, 51, 45}}},
{Region: "Montenegro", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 208, 8}, {45, 131, 208, 9}, {45, 131, 208, 10}, {45, 131, 208, 17}, {45, 131, 208, 19}, {45, 131, 208, 20}, {45, 131, 208, 23}, {45, 131, 208, 25}, {45, 131, 208, 26}, {45, 131, 208, 28}}},
{Region: "Montenegro", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 208, 6}, {45, 131, 208, 7}, {45, 131, 208, 8}, {45, 131, 208, 9}, {45, 131, 208, 13}, {45, 131, 208, 18}, {45, 131, 208, 21}, {45, 131, 208, 23}, {45, 131, 208, 27}, {45, 131, 208, 29}}},
{Region: "Morocco", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 211, 9}, {45, 131, 211, 11}, {45, 131, 211, 12}, {45, 131, 211, 16}, {45, 131, 211, 18}, {45, 131, 211, 19}, {45, 131, 211, 21}, {45, 131, 211, 24}, {45, 131, 211, 27}, {45, 131, 211, 28}}},
{Region: "Morocco", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 211, 8}, {45, 131, 211, 14}, {45, 131, 211, 15}, {45, 131, 211, 17}, {45, 131, 211, 19}, {45, 131, 211, 21}, {45, 131, 211, 22}, {45, 131, 211, 23}, {45, 131, 211, 25}, {45, 131, 211, 26}}},
{Region: "Netherlands", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 47, 53}, {84, 17, 47, 55}, {84, 17, 47, 64}, {84, 17, 47, 73}, {84, 17, 47, 102}, {84, 17, 47, 107}, {84, 17, 47, 110}, {84, 17, 47, 112}, {139, 28, 217, 200}, {195, 181, 172, 80}}},
{Region: "Netherlands", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 47, 45}, {84, 17, 47, 68}, {84, 17, 47, 75}, {84, 17, 47, 92}, {84, 17, 47, 105}, {195, 181, 172, 69}, {195, 181, 172, 70}, {195, 181, 172, 77}, {195, 181, 172, 78}, {195, 181, 172, 79}}},
{Region: "New Zealand", Group: "Premium TCP Asia", IPs: []net.IP{{114, 141, 194, 2}, {114, 141, 194, 4}, {114, 141, 194, 5}, {114, 141, 194, 7}, {114, 141, 194, 8}, {114, 141, 194, 9}, {114, 141, 194, 10}, {114, 141, 194, 11}, {114, 141, 194, 13}, {114, 141, 194, 14}}},
{Region: "New Zealand", Group: "Premium UDP Asia", IPs: []net.IP{{114, 141, 194, 2}, {114, 141, 194, 3}, {114, 141, 194, 4}, {114, 141, 194, 6}, {114, 141, 194, 7}, {114, 141, 194, 8}, {114, 141, 194, 9}, {114, 141, 194, 10}, {114, 141, 194, 12}, {114, 141, 194, 13}}},
{Region: "Nigeria", Group: "Premium UDP Europe", IPs: []net.IP{{45, 137, 196, 6}, {45, 137, 196, 10}, {45, 137, 196, 14}, {45, 137, 196, 15}, {45, 137, 196, 17}, {45, 137, 196, 20}, {45, 137, 196, 24}, {45, 137, 196, 26}, {45, 137, 196, 28}, {45, 137, 196, 29}}},
{Region: "Nigeria", Group: "Premium TCP Europe", IPs: []net.IP{{45, 137, 196, 6}, {45, 137, 196, 7}, {45, 137, 196, 8}, {45, 137, 196, 15}, {45, 137, 196, 16}, {45, 137, 196, 19}, {45, 137, 196, 23}, {45, 137, 196, 24}, {45, 137, 196, 27}, {45, 137, 196, 28}}},
{Region: "Norway", Group: "Premium TCP Europe", IPs: []net.IP{{45, 12, 223, 136}, {45, 12, 223, 139}, {45, 12, 223, 141}, {82, 102, 27, 93}, {185, 206, 225, 230}, {185, 206, 225, 232}, {185, 206, 225, 235}, {185, 253, 97, 235}, {185, 253, 97, 251}, {185, 253, 97, 253}}},
{Region: "Norway", Group: "Premium UDP Europe", IPs: []net.IP{{45, 12, 223, 134}, {82, 102, 27, 92}, {185, 206, 225, 29}, {185, 206, 225, 30}, {185, 206, 225, 231}, {185, 206, 225, 233}, {185, 206, 225, 234}, {185, 253, 97, 236}, {185, 253, 97, 243}, {185, 253, 97, 245}}},
{Region: "Pakistan", Group: "Premium UDP Europe", IPs: []net.IP{{103, 76, 3, 244}, {103, 76, 3, 245}, {103, 76, 3, 246}, {103, 76, 3, 247}, {103, 76, 3, 248}, {103, 76, 3, 249}, {103, 76, 3, 250}, {103, 76, 3, 251}, {103, 76, 3, 252}, {103, 76, 3, 253}}},
{Region: "Pakistan", Group: "Premium TCP Europe", IPs: []net.IP{{103, 76, 3, 244}, {103, 76, 3, 245}, {103, 76, 3, 246}, {103, 76, 3, 247}, {103, 76, 3, 248}, {103, 76, 3, 249}, {103, 76, 3, 250}, {103, 76, 3, 251}, {103, 76, 3, 252}, {103, 76, 3, 253}}},
{Region: "Panama", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 210, 6}, {45, 131, 210, 7}, {45, 131, 210, 10}, {45, 131, 210, 13}, {45, 131, 210, 14}, {45, 131, 210, 19}, {45, 131, 210, 24}, {45, 131, 210, 25}, {45, 131, 210, 28}, {45, 131, 210, 29}}},
{Region: "Panama", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 210, 7}, {45, 131, 210, 8}, {45, 131, 210, 9}, {45, 131, 210, 10}, {45, 131, 210, 12}, {45, 131, 210, 13}, {45, 131, 210, 16}, {45, 131, 210, 17}, {45, 131, 210, 20}, {45, 131, 210, 24}}},
{Region: "Philippines", Group: "Premium UDP Asia", IPs: []net.IP{{188, 214, 125, 35}, {188, 214, 125, 37}, {188, 214, 125, 40}, {188, 214, 125, 41}, {188, 214, 125, 44}, {188, 214, 125, 47}, {188, 214, 125, 54}, {188, 214, 125, 57}, {188, 214, 125, 58}, {188, 214, 125, 59}}},
{Region: "Philippines", Group: "Premium TCP Asia", IPs: []net.IP{{188, 214, 125, 35}, {188, 214, 125, 39}, {188, 214, 125, 40}, {188, 214, 125, 43}, {188, 214, 125, 44}, {188, 214, 125, 50}, {188, 214, 125, 57}, {188, 214, 125, 58}, {188, 214, 125, 60}, {188, 214, 125, 62}}},
{Region: "Poland", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 156, 8}, {37, 120, 156, 10}, {37, 120, 156, 12}, {37, 120, 156, 13}, {37, 120, 156, 19}, {37, 120, 156, 22}, {37, 120, 156, 23}, {37, 120, 156, 24}, {51, 75, 56, 37}, {51, 75, 56, 44}}},
{Region: "Poland", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 156, 6}, {37, 120, 156, 8}, {37, 120, 156, 10}, {37, 120, 156, 12}, {37, 120, 156, 13}, {37, 120, 156, 20}, {37, 120, 156, 21}, {37, 120, 156, 27}, {51, 75, 56, 34}, {51, 75, 56, 43}}},
{Region: "Portugal", Group: "Premium UDP Europe", IPs: []net.IP{{89, 26, 243, 2}, {89, 26, 243, 98}, {89, 26, 243, 100}, {89, 26, 243, 112}, {89, 26, 243, 113}, {89, 26, 243, 115}, {89, 26, 243, 194}, {89, 26, 243, 195}, {89, 26, 243, 198}, {89, 26, 243, 199}}},
{Region: "Portugal", Group: "Premium TCP Europe", IPs: []net.IP{{89, 26, 243, 1}, {89, 26, 243, 98}, {89, 26, 243, 100}, {89, 26, 243, 112}, {89, 26, 243, 113}, {89, 26, 243, 114}, {89, 26, 243, 194}, {89, 26, 243, 195}, {89, 26, 243, 197}, {89, 26, 243, 198}}},
{Region: "Qatar", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 7, 6}, {45, 131, 7, 10}, {45, 131, 7, 13}, {45, 131, 7, 15}, {45, 131, 7, 16}, {45, 131, 7, 22}, {45, 131, 7, 24}, {45, 131, 7, 26}, {45, 131, 7, 27}, {45, 131, 7, 28}}},
{Region: "Qatar", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 7, 7}, {45, 131, 7, 9}, {45, 131, 7, 13}, {45, 131, 7, 16}, {45, 131, 7, 17}, {45, 131, 7, 18}, {45, 131, 7, 19}, {45, 131, 7, 20}, {45, 131, 7, 26}, {45, 131, 7, 27}}},
{Region: "Romania", Group: "NoSpy UDP Europe", IPs: []net.IP{{85, 9, 20, 132}, {85, 9, 20, 133}, {85, 9, 20, 134}, {85, 9, 20, 139}, {85, 9, 20, 144}, {85, 9, 20, 145}, {85, 9, 20, 147}, {85, 9, 20, 148}, {85, 9, 20, 154}, {85, 9, 20, 249}}},
{Region: "Romania", Group: "Premium TCP Europe", IPs: []net.IP{{193, 176, 84, 43}, {193, 176, 84, 45}, {193, 176, 84, 47}, {193, 176, 84, 52}, {193, 176, 84, 120}, {193, 176, 85, 79}, {193, 176, 85, 91}, {193, 176, 85, 99}, {193, 176, 85, 105}, {193, 176, 85, 116}}},
{Region: "Romania", Group: "NoSpy TCP Europe", IPs: []net.IP{{85, 9, 20, 132}, {85, 9, 20, 134}, {85, 9, 20, 137}, {85, 9, 20, 148}, {85, 9, 20, 149}, {85, 9, 20, 150}, {85, 9, 20, 151}, {85, 9, 20, 155}, {85, 9, 20, 248}, {85, 9, 20, 249}}},
{Region: "Romania", Group: "Premium UDP Europe", IPs: []net.IP{{193, 176, 84, 84}, {193, 176, 84, 124}, {193, 176, 84, 126}, {193, 176, 85, 68}, {193, 176, 85, 72}, {193, 176, 85, 81}, {193, 176, 85, 85}, {193, 176, 85, 104}, {193, 176, 85, 108}, {193, 176, 85, 116}}},
{Region: "Russian Federation", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 192, 6}, {45, 132, 192, 16}, {45, 132, 192, 28}, {45, 132, 192, 46}, {45, 132, 192, 52}, {45, 132, 192, 57}, {45, 132, 192, 70}, {45, 132, 192, 71}, {45, 132, 192, 76}, {45, 132, 192, 92}}},
{Region: "Russian Federation", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 192, 9}, {45, 132, 192, 11}, {45, 132, 192, 22}, {45, 132, 192, 36}, {45, 132, 192, 39}, {45, 132, 192, 44}, {45, 132, 192, 51}, {45, 132, 192, 74}, {45, 132, 192, 79}, {45, 132, 192, 92}}},
{Region: "Saudi Arabia", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 6, 6}, {45, 131, 6, 7}, {45, 131, 6, 8}, {45, 131, 6, 15}, {45, 131, 6, 16}, {45, 131, 6, 18}, {45, 131, 6, 19}, {45, 131, 6, 22}, {45, 131, 6, 28}, {45, 131, 6, 29}}},
{Region: "Saudi Arabia", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 6, 10}, {45, 131, 6, 11}, {45, 131, 6, 13}, {45, 131, 6, 14}, {45, 131, 6, 16}, {45, 131, 6, 17}, {45, 131, 6, 24}, {45, 131, 6, 25}, {45, 131, 6, 27}, {45, 131, 6, 28}}},
{Region: "Serbia", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 193, 180}, {37, 120, 193, 181}, {37, 120, 193, 183}, {37, 120, 193, 184}, {37, 120, 193, 186}, {37, 120, 193, 187}, {141, 98, 103, 36}, {141, 98, 103, 38}, {141, 98, 103, 40}, {141, 98, 103, 46}}},
{Region: "Serbia", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 193, 181}, {37, 120, 193, 182}, {37, 120, 193, 183}, {37, 120, 193, 187}, {37, 120, 193, 189}, {141, 98, 103, 35}, {141, 98, 103, 36}, {141, 98, 103, 38}, {141, 98, 103, 44}, {141, 98, 103, 45}}},
{Region: "Singapore", Group: "Premium UDP Asia", IPs: []net.IP{{84, 17, 39, 162}, {84, 17, 39, 164}, {84, 17, 39, 166}, {84, 17, 39, 167}, {84, 17, 39, 169}, {84, 17, 39, 170}, {84, 17, 39, 174}, {84, 17, 39, 177}, {84, 17, 39, 179}, {84, 17, 39, 182}}},
{Region: "Singapore", Group: "Premium TCP Asia", IPs: []net.IP{{84, 17, 39, 163}, {84, 17, 39, 164}, {84, 17, 39, 165}, {84, 17, 39, 168}, {84, 17, 39, 170}, {84, 17, 39, 173}, {84, 17, 39, 177}, {84, 17, 39, 179}, {84, 17, 39, 181}, {84, 17, 39, 184}}},
{Region: "Slovakia", Group: "Premium UDP Europe", IPs: []net.IP{{185, 245, 85, 227}, {185, 245, 85, 228}, {185, 245, 85, 229}, {185, 245, 85, 230}, {185, 245, 85, 231}, {185, 245, 85, 232}, {185, 245, 85, 233}, {185, 245, 85, 234}, {185, 245, 85, 235}, {185, 245, 85, 236}}},
{Region: "Slovakia", Group: "Premium TCP Europe", IPs: []net.IP{{185, 245, 85, 227}, {185, 245, 85, 228}, {185, 245, 85, 229}, {185, 245, 85, 230}, {185, 245, 85, 231}, {185, 245, 85, 232}, {185, 245, 85, 233}, {185, 245, 85, 234}, {185, 245, 85, 235}, {185, 245, 85, 236}}},
{Region: "Slovenia", Group: "Premium UDP Europe", IPs: []net.IP{{146, 247, 25, 79}, {146, 247, 25, 80}, {146, 247, 25, 82}, {146, 247, 25, 83}, {146, 247, 25, 85}, {146, 247, 25, 86}, {146, 247, 25, 87}, {146, 247, 25, 88}, {146, 247, 25, 89}, {146, 247, 25, 90}}},
{Region: "Slovenia", Group: "Premium TCP Europe", IPs: []net.IP{{146, 247, 25, 79}, {146, 247, 25, 80}, {146, 247, 25, 81}, {146, 247, 25, 82}, {146, 247, 25, 83}, {146, 247, 25, 84}, {146, 247, 25, 85}, {146, 247, 25, 86}, {146, 247, 25, 87}, {146, 247, 25, 88}}},
{Region: "South Africa", Group: "Premium UDP Europe", IPs: []net.IP{{197, 85, 7, 26}, {197, 85, 7, 27}, {197, 85, 7, 28}, {197, 85, 7, 29}, {197, 85, 7, 30}, {197, 85, 7, 31}, {197, 85, 7, 131}, {197, 85, 7, 132}, {197, 85, 7, 133}, {197, 85, 7, 134}}},
{Region: "South Africa", Group: "Premium UDP Asia", IPs: []net.IP{{165, 73, 248, 214}, {165, 73, 248, 215}, {165, 73, 248, 216}, {165, 73, 248, 220}, {165, 73, 248, 221}, {165, 73, 248, 222}, {165, 73, 248, 229}, {165, 73, 248, 230}, {165, 73, 248, 232}, {165, 73, 248, 234}}},
{Region: "South Africa", Group: "Premium TCP Asia", IPs: []net.IP{{165, 73, 248, 211}, {165, 73, 248, 212}, {165, 73, 248, 216}, {165, 73, 248, 219}, {165, 73, 248, 227}, {165, 73, 248, 230}, {165, 73, 248, 232}, {165, 73, 248, 233}, {165, 73, 248, 234}, {165, 73, 248, 235}}},
{Region: "South Africa", Group: "Premium TCP Europe", IPs: []net.IP{{197, 85, 7, 26}, {197, 85, 7, 27}, {197, 85, 7, 28}, {197, 85, 7, 29}, {197, 85, 7, 30}, {197, 85, 7, 31}, {197, 85, 7, 131}, {197, 85, 7, 132}, {197, 85, 7, 133}, {197, 85, 7, 134}}},
{Region: "Spain", Group: "Premium UDP Europe", IPs: []net.IP{{37, 120, 142, 147}, {37, 120, 142, 155}, {37, 120, 142, 167}, {37, 120, 142, 169}, {37, 120, 142, 170}, {84, 17, 62, 131}, {84, 17, 62, 142}, {84, 17, 62, 145}, {84, 17, 62, 147}, {185, 93, 3, 113}}},
{Region: "Spain", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 62, 133}, {84, 17, 62, 141}, {84, 17, 62, 142}, {185, 93, 3, 105}, {185, 93, 3, 109}, {185, 93, 3, 111}, {185, 93, 182, 133}, {185, 93, 182, 136}, {185, 93, 182, 140}, {185, 93, 182, 141}}},
{Region: "Sri Lanka", Group: "Premium UDP Europe", IPs: []net.IP{{45, 132, 136, 7}, {45, 132, 136, 9}, {45, 132, 136, 11}, {45, 132, 136, 13}, {45, 132, 136, 16}, {45, 132, 136, 17}, {45, 132, 136, 20}, {45, 132, 136, 23}, {45, 132, 136, 26}, {45, 132, 136, 29}}},
{Region: "Sri Lanka", Group: "Premium TCP Europe", IPs: []net.IP{{45, 132, 136, 7}, {45, 132, 136, 9}, {45, 132, 136, 10}, {45, 132, 136, 11}, {45, 132, 136, 13}, {45, 132, 136, 17}, {45, 132, 136, 22}, {45, 132, 136, 25}, {45, 132, 136, 26}, {45, 132, 136, 29}}},
{Region: "Sweden", Group: "Premium TCP Europe", IPs: []net.IP{{46, 246, 65, 137}, {46, 246, 65, 139}, {46, 246, 65, 218}, {91, 132, 138, 60}, {188, 126, 64, 105}, {188, 126, 66, 10}, {188, 126, 66, 14}, {188, 126, 66, 29}, {188, 126, 73, 207}, {188, 126, 73, 209}}},
{Region: "Sweden", Group: "Premium UDP Europe", IPs: []net.IP{{46, 246, 65, 131}, {46, 246, 65, 140}, {46, 246, 65, 170}, {46, 246, 65, 189}, {46, 246, 65, 200}, {46, 246, 65, 203}, {46, 246, 65, 212}, {91, 132, 138, 52}, {188, 126, 73, 199}, {188, 126, 73, 220}}},
{Region: "Switzerland", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 52, 10}, {84, 17, 52, 14}, {84, 17, 52, 17}, {84, 17, 52, 21}, {185, 32, 222, 17}, {185, 32, 222, 18}, {185, 32, 222, 111}, {195, 225, 118, 45}, {195, 225, 118, 58}, {195, 225, 118, 61}}},
{Region: "Switzerland", Group: "Premium TCP Europe", IPs: []net.IP{{84, 17, 52, 5}, {84, 17, 52, 33}, {84, 17, 52, 45}, {84, 17, 52, 62}, {84, 17, 52, 69}, {84, 17, 52, 80}, {91, 132, 136, 171}, {185, 32, 222, 13}, {195, 225, 118, 44}, {195, 225, 118, 52}}},
{Region: "Taiwan", Group: "Premium UDP Asia", IPs: []net.IP{{45, 133, 181, 100}, {45, 133, 181, 104}, {45, 133, 181, 106}, {45, 133, 181, 109}, {45, 133, 181, 110}, {45, 133, 181, 114}, {45, 133, 181, 117}, {45, 133, 181, 119}, {45, 133, 181, 124}, {45, 133, 181, 125}}},
{Region: "Taiwan", Group: "Premium TCP Asia", IPs: []net.IP{{45, 133, 181, 99}, {45, 133, 181, 103}, {45, 133, 181, 104}, {45, 133, 181, 105}, {45, 133, 181, 107}, {45, 133, 181, 110}, {45, 133, 181, 112}, {45, 133, 181, 113}, {45, 133, 181, 116}, {45, 133, 181, 123}}},
{Region: "Thailand", Group: "Premium TCP Asia", IPs: []net.IP{{119, 59, 98, 214}, {119, 59, 98, 239}, {119, 59, 98, 240}, {119, 59, 98, 244}, {119, 59, 121, 162}, {119, 59, 121, 168}, {119, 59, 121, 169}, {119, 59, 121, 170}, {119, 59, 121, 171}, {119, 59, 121, 173}}},
{Region: "Thailand", Group: "Premium UDP Asia", IPs: []net.IP{{119, 59, 98, 238}, {119, 59, 98, 240}, {119, 59, 98, 244}, {119, 59, 98, 249}, {119, 59, 121, 166}, {119, 59, 121, 167}, {119, 59, 121, 168}, {119, 59, 121, 170}, {119, 59, 121, 172}, {119, 59, 121, 175}}},
{Region: "Turkey", Group: "Premium UDP Europe", IPs: []net.IP{{188, 213, 34, 5}, {188, 213, 34, 9}, {188, 213, 34, 14}, {188, 213, 34, 30}, {188, 213, 34, 35}, {188, 213, 34, 39}, {188, 213, 34, 42}, {188, 213, 34, 45}, {188, 213, 34, 103}, {188, 213, 34, 110}}},
{Region: "Turkey", Group: "Premium TCP Europe", IPs: []net.IP{{188, 213, 34, 10}, {188, 213, 34, 12}, {188, 213, 34, 25}, {188, 213, 34, 30}, {188, 213, 34, 36}, {188, 213, 34, 42}, {188, 213, 34, 102}, {188, 213, 34, 103}, {188, 213, 34, 104}, {188, 213, 34, 110}}},
{Region: "Ukraine", Group: "Premium TCP Europe", IPs: []net.IP{{31, 28, 161, 20}, {31, 28, 163, 40}, {31, 28, 163, 51}, {62, 149, 7, 168}, {62, 149, 29, 35}, {62, 149, 29, 40}, {62, 149, 29, 41}, {62, 149, 29, 48}, {62, 149, 29, 52}, {62, 149, 29, 56}}},
{Region: "Ukraine", Group: "Premium UDP Europe", IPs: []net.IP{{31, 28, 163, 35}, {31, 28, 163, 45}, {31, 28, 163, 51}, {31, 28, 163, 55}, {62, 149, 7, 167}, {62, 149, 29, 38}, {62, 149, 29, 40}, {62, 149, 29, 46}, {62, 149, 29, 47}, {62, 149, 29, 50}}},
{Region: "United Arab Emirates", Group: "Premium TCP Europe", IPs: []net.IP{{45, 131, 5, 6}, {45, 131, 5, 12}, {45, 131, 5, 13}, {45, 131, 5, 15}, {45, 131, 5, 17}, {45, 131, 5, 21}, {45, 131, 5, 22}, {45, 131, 5, 24}, {45, 131, 5, 28}, {45, 131, 5, 29}}},
{Region: "United Arab Emirates", Group: "Premium UDP Europe", IPs: []net.IP{{45, 131, 5, 6}, {45, 131, 5, 7}, {45, 131, 5, 10}, {45, 131, 5, 11}, {45, 131, 5, 12}, {45, 131, 5, 14}, {45, 131, 5, 17}, {45, 131, 5, 24}, {45, 131, 5, 26}, {45, 131, 5, 27}}},
{Region: "United Kingdom", Group: "Premium UDP Europe", IPs: []net.IP{{84, 17, 51, 18}, {84, 17, 51, 62}, {89, 238, 138, 245}, {89, 238, 167, 46}, {89, 238, 167, 56}, {95, 154, 200, 153}, {95, 154, 200, 155}, {95, 154, 200, 187}, {95, 154, 200, 188}, {141, 98, 100, 73}}},
{Region: "United Kingdom", Group: "Premium TCP Europe", IPs: []net.IP{{37, 120, 133, 165}, {84, 17, 51, 32}, {84, 17, 51, 106}, {84, 17, 51, 124}, {89, 238, 167, 45}, {95, 154, 200, 147}, {95, 154, 200, 165}, {95, 154, 200, 172}, {95, 154, 200, 179}, {141, 98, 100, 59}}},
{Region: "United States", Group: "Premium TCP USA", IPs: []net.IP{{23, 105, 191, 33}, {23, 106, 83, 26}, {37, 120, 157, 131}, {45, 89, 173, 221}, {84, 17, 40, 70}, {89, 187, 182, 6}, {91, 132, 137, 86}, {173, 234, 158, 179}, {173, 234, 158, 184}, {185, 250, 220, 39}}},
{Region: "United States", Group: "Premium UDP USA", IPs: []net.IP{{89, 187, 171, 143}, {108, 62, 235, 183}, {143, 244, 51, 169}, {156, 146, 37, 29}, {156, 146, 37, 106}, {156, 146, 37, 120}, {172, 255, 125, 138}, {173, 208, 44, 90}, {185, 242, 5, 120}, {185, 242, 5, 249}}},
{Region: "Venezuela", Group: "Premium TCP Europe", IPs: []net.IP{{45, 133, 89, 8}, {45, 133, 89, 10}, {45, 133, 89, 12}, {45, 133, 89, 16}, {45, 133, 89, 17}, {45, 133, 89, 18}, {45, 133, 89, 20}, {45, 133, 89, 22}, {45, 133, 89, 27}, {45, 133, 89, 28}}},
{Region: "Venezuela", Group: "Premium UDP Europe", IPs: []net.IP{{45, 133, 89, 6}, {45, 133, 89, 7}, {45, 133, 89, 15}, {45, 133, 89, 16}, {45, 133, 89, 17}, {45, 133, 89, 20}, {45, 133, 89, 22}, {45, 133, 89, 26}, {45, 133, 89, 28}, {45, 133, 89, 29}}},
{Region: "Vietnam", Group: "Premium UDP Asia", IPs: []net.IP{{45, 117, 79, 114}, {45, 117, 79, 118}, {45, 117, 79, 124}, {45, 117, 79, 125}, {103, 238, 214, 131}, {103, 238, 214, 132}, {103, 238, 214, 133}, {103, 238, 214, 134}, {103, 238, 214, 135}, {103, 238, 214, 137}}},
{Region: "Vietnam", Group: "Premium TCP Asia", IPs: []net.IP{{45, 117, 79, 114}, {45, 117, 79, 116}, {45, 117, 79, 124}, {45, 117, 79, 125}, {103, 238, 214, 131}, {103, 238, 214, 132}, {103, 238, 214, 133}, {103, 238, 214, 135}, {103, 238, 214, 136}, {103, 238, 214, 140}}},
} }
return makeUnique(choices)
}
// CyberghostServers returns a slice with the server information for each
// of the Cyberghost server.
func CyberghostServers() (servers []models.CyberghostServer) {
servers = make([]models.CyberghostServer, len(allServers.Cyberghost.Servers))
copy(servers, allServers.Cyberghost.Servers)
return servers
} }

98
internal/constants/dns.go Normal file
View File

@@ -0,0 +1,98 @@
package constants
import (
"net"
"github.com/qdm12/gluetun/internal/models"
)
const (
// Cloudflare is a DNS over TLS provider.
Cloudflare models.DNSProvider = "cloudflare"
// Google is a DNS over TLS provider.
Google models.DNSProvider = "google"
// Quad9 is a DNS over TLS provider.
Quad9 models.DNSProvider = "quad9"
// Quadrant is a DNS over TLS provider.
Quadrant models.DNSProvider = "quadrant"
// CleanBrowsing is a DNS over TLS provider.
CleanBrowsing models.DNSProvider = "cleanbrowsing"
)
// DNSProviderMapping returns a constant mapping of dns provider name
// to their data such as IP addresses or TLS host name.
func DNSProviderMapping() map[models.DNSProvider]models.DNSProviderData {
return map[models.DNSProvider]models.DNSProviderData{
Cloudflare: {
IPs: []net.IP{
{1, 1, 1, 1},
{1, 0, 0, 1},
{0x26, 0x6, 0x47, 0x0, 0x47, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11},
{0x26, 0x6, 0x47, 0x0, 0x47, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x01},
},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("cloudflare-dns.com"),
},
Google: {
IPs: []net.IP{
{8, 8, 8, 8},
{8, 8, 4, 4},
{0x20, 0x1, 0x48, 0x60, 0x48, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88, 0x88},
{0x20, 0x1, 0x48, 0x60, 0x48, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88, 0x44},
},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("dns.google"),
},
Quad9: {
IPs: []net.IP{
{9, 9, 9, 9},
{149, 112, 112, 112},
{0x26, 0x20, 0x0, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe},
{0x26, 0x20, 0x0, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9},
},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("dns.quad9.net"),
},
Quadrant: {
IPs: []net.IP{
{12, 159, 2, 159},
{0x20, 0x1, 0x18, 0x90, 0x14, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x59},
},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("dns-tls.qis.io"),
},
CleanBrowsing: {
IPs: []net.IP{
{185, 228, 168, 9},
{185, 228, 169, 9},
{0x2a, 0xd, 0x2a, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
{0x2a, 0xd, 0x2a, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
},
SupportsTLS: true,
SupportsIPv6: true,
Host: models.DNSHost("security-filter-dns.cleanbrowsing.org"),
},
}
}
// Block lists URLs.
//nolint:lll
const (
AdsBlockListHostnamesURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/ads-hostnames.updated"
AdsBlockListIPsURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/ads-ips.updated"
MaliciousBlockListHostnamesURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/malicious-hostnames.updated"
MaliciousBlockListIPsURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/malicious-ips.updated"
SurveillanceBlockListHostnamesURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/surveillance-hostnames.updated"
SurveillanceBlockListIPsURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/surveillance-ips.updated"
)
// DNS certificates to fetch.
// TODO obtain from source directly, see qdm12/updated).
const (
NamedRootURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/named.root.updated"
RootKeyURL models.URL = "https://raw.githubusercontent.com/qdm12/files/master/root.key.updated"
)

View File

@@ -1,36 +0,0 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll
const (
FastestvpnCertificate = "MIIFQjCCAyqgAwIBAgIIUfxepT+rr8owDQYJKoZIhvcNAQEMBQAwPzELMAkGA1UEBhMCS1kxEzARBgNVBAoTCkZhc3Rlc3RWUE4xGzAZBgNVBAMTEkZhc3Rlc3RWUE4gUm9vdCBDQTAeFw0xNzA5MTYwMDAxNDZaFw0yNzA5MTQwMDAxNDZaMD8xCzAJBgNVBAYTAktZMRMwEQYDVQQKEwpGYXN0ZXN0VlBOMRswGQYDVQQDExJGYXN0ZXN0VlBOIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC1Xj+WfPTozFynFqc+c3CVrggIllaXEl5bY5VgFynXkqCTM6lSrfC4pNjGXUbqWe6RnGJbM4/6kUn+lQDjFSQV1rzP2eDS8+r5+X2WXh4AoeNRUWhvSG+HiHD/B2EFK+Nd5BRSdUjpKWAtsCmT2bBt7nT0jN1OdeNrLJeyF8siAqv/oQzKznF9aIe/N01b2M8ZOFTzoXi2fZAckgGWui8NB/lzkVIJqSkAPRL8qiJLuRCPVOX1PFD8vV//R8/QumtfbcYBMo6vCk2HmWdrh5OQHPxb3KJtbtG+Z1j8x6HGEAe17djYepBiRMyCEQvYgfD6tvFylc4IquhqE9yaP60PJod5TxpWnRQ6HIGSeBm+S+rYSMalTZ8+pUqOOA+IQCYpfpx6EKIJL/VsW2C7cXdvudxDhXPI5lR/QidCb9Ohq3WkfxXaYwzrngdg2avmNqId9R4KESuM9GoHW0dszfyBCh5wYfeaffMElfDam3B92NUwyhZwtIiv623WVXY9PPz+EDjSJsIAu2Vi1vdJyA4nD4k9Lwmx/1zTc/UaYVLsiBqL2WdfvFTeoWoV+dNxQXSEPhB8gwi8x4O4lZW0cwVy/6fa8KMY8gZbcbSTr7U5bRERfW8l+jY+mYKQ/M/ccgpxaHiw1/+4LWfbJQ7VhJJrTyN0C36FQzY1URkSXg+53wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmVEL4x6xdCqiqu2OBLs27EA8xGYwDQYJKoZIhvcNAQEMBQADggIBABCpITvO1+R4T9v2+onHiFxU5JjtCZ0zkXqRCMp/Z0UIYbeo1p07pZCPAUjBfGPCkAaR++OiG9sysALdJf8Y6HQKcyuAcWUqQnaIhoZ2JcAP7EKq7uCqsMhcYZD/j3O/3RPtSW5UOx6ItDU+Ua0t9Edho9whNw0VQXmo1JjYoP3FzPjuKoDWTSO1q5eYlZfwcTcs55O2shNkFafPg/6cCm5j6v9nyHrM3sk4LjkrBPUXVx2m/aoz219t8O9Ha9/CdMKXsPO/8gTUzpgnzSgPnGnBmi5xr1nspVN8X4E2f3D+DKqBim3YgslD68NcuFQvJ0/BxZzWVbrr+QXoyzaiCgXuogpIDc2bB6oRXqFnHNz36d4QJmJdWdSaijiS/peQ6EOPgOZ1GuObLWlDCBZLNeQ+N6QaiJxVO4XUj/s22i1IRtwdz84TRHrbWiIpEymsqmb/Ep5r4xV5d6+791axclfOTH7tQrY/SbPtTJI4OEgNekI8YfadQifpelF82MsFFEZuaQn0lj+fvLGtE/zKh3OdLTxRc5TAgBB+0T81+JQosygNr2aFFG0hxar1eyw/gLeG8H+7Ie50pyPvXO4OgB6Key8rSExpilQXlvAT1qX0qS3/K1i/9QkSE9ftIPT6vtwLV2sVQzfyanI4IZgWC6ryhvNLsRn0NFnQclor0+aq"
FastestvpnOpenvpnStaticKeyV1 = "697fe793b32cb5091d30f2326d5d124a9412e93d0a44ef7361395d76528fcbfc82c3859dccea70a93cfa8fae409709bff75f844cf5ff0c237f426d0c20969233db0e706edb6bdf195ec3dc11b3f76bc807a77e74662d9a800c8cd1144ebb67b7f0d3f1281d1baf522bfe03b7c3f963b1364fc0769400e413b61ca7b43ab19fac9e0f77e41efd4bda7fd77b1de2d7d7855cbbe3e620cecceac72c21a825b243e651f44d90e290e09c3ad650de8fca99c858bc7caad584bc69b11e5c9fd9381c69c505ec487a65912c672d83ed0113b5a74ddfbd3ab33b3683cec593557520a72c4d6cce46111f56f3396cc3ce7183edce553c68ea0796cf6c4375fad00aaa2a42"
)
func FastestvpnCountriesChoices() (choices []string) {
servers := FastestvpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
}
return choices
}
func FastestvpnHostnameChoices() (choices []string) {
servers := FastestvpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
return choices
}
// FastestvpnServers returns the list of all VPN servers for FastestVPN.
func FastestvpnServers() (servers []models.FastestvpnServer) {
servers = make([]models.FastestvpnServer, len(allServers.Fastestvpn.Servers))
copy(servers, allServers.Fastestvpn.Servers)
return servers
}

View File

@@ -1,46 +0,0 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll
const (
HideMyAssCA = "MIIGVjCCBD6gAwIBAgIJAOmTY3hf1Bb6MA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVSzEPMA0GA1UECAwGTG9uZG9uMQ8wDQYDVQQHDAZMb25kb24xEzARBgNVBAoMClByaXZheCBMdGQxFDASBgNVBAsMC0hNQSBQcm8gVlBOMRYwFAYDVQQDDA1oaWRlbXlhc3MuY29tMR4wHAYJKoZIhvcNAQkBFg9pbmZvQHByaXZheC5jb20wHhcNMTYwOTE0MDk0MTUyWhcNMjYwOTEyMDk0MTUyWjCBkjELMAkGA1UEBhMCVUsxDzANBgNVBAgMBkxvbmRvbjEPMA0GA1UEBwwGTG9uZG9uMRMwEQYDVQQKDApQcml2YXggTHRkMRQwEgYDVQQLDAtITUEgUHJvIFZQTjEWMBQGA1UEAwwNaGlkZW15YXNzLmNvbTEeMBwGCSqGSIb3DQEJARYPaW5mb0Bwcml2YXguY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxWS4+bOnwzGsEZ2vyqfTg7OEJkdqlA+DmQB3UmeDxX8K+87FTe/htIudr4hQ19q2gaHU4PjN1QsJtkH+VxU6V5p5eeWVVCGpHOhkcI4XK0yodRGn6rhAPJYXI7pJHAronfmqfZz/XM+neTGHQ9VF9zW6Q1001mjT0YklFfpx+CPFiGYsQjqZ+ia9RvaXz5Eu1cQ0EWy4do1l7obmvmTrlqN26z4unmh3HfEKRuwtNeHsSyhdzFW20eT2GhvXniHItqWBDi93U55R84y2GNrQubm207UB6kqbJXPXYnlZifvQCxa1hz3sr+vUbRi4wIpj/Da2MK7BLHAuUbClKqFs9OSAffWo/PuhkhFyF5JhOYXjOMI1PhiTjeSfBmNdC5dFOGT3rStvYxYlB8rwuuyp9DuvInQRuCC62/Lew9pITULaPUPTU7TeKuk4Hqqn2LtnFTU7CSMRAVgZMxTWuC7PT+9sy+jM3nSqo+QaiVtMxbaWXmZD9UlLEMmM9IkMdHV08DXQonjIi4RnqHWLYRY6pDjJ2E4jleXlS2laIBKlmKIuyxZ/B5IyV2dLKrNAs7j9EC7J82giBBCHbZiHQjZ2CqIi+afHKjniFHhuJSVUe7DY+S/B/ePac7Xha8a5K2LmJ+jpPjvBjJd+2Tp2Eyt8wVn/6iSqKePDny5AZhbY+YkCAwEAAaOBrDCBqTAdBgNVHQ4EFgQU4MZR0iTa8SoTWOJeoOmtynuk8/cwHwYDVR0jBBgwFoAU4MZR0iTa8SoTWOJeoOmtynuk8/cwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAaYwEQYJYIZIAYb4QgEBBAQDAgEWMBoGA1UdEQQTMBGBD2luZm9AcHJpdmF4LmNvbTAaBgNVHRIEEzARgQ9pbmZvQHByaXZheC5jb20wDQYJKoZIhvcNAQELBQADggIBAG+QvRLNs41wHXeM7wq6tqSZl6UFStGc6gIzzVUkysVHwvAqqxj/8UncqEwFTxV3KiD/+wLMHZFkLwQgSAHwaTmBKGrK4I6DoUtK+52RwfyU3XA0s5dj6rKbZKPNdD0jusOTYgbXOCUa6JI2gmpyjk7lq3D66dATs11uP7S2uwjuO3ER5Cztm12RcsrAxjndH2igTgZVu4QQwnNZ39Raq6v5IayKxF0tP1wPxz/JafhIjdNxq6ReP4jsI5y0rJBuXuw+gWC8ePTP4rxWp908kI7vwmmVq9/iisGZelN6G5uEB2d3EiJBB0A3t9LCFT9fKznlp/38To4x1lQhfNbln8zC4qav/8fBfKu5MkuVcdV4ZmHq0bT7sfzsgHs00JaYOCadBslNu1xVtgooy+ARiGfnzVL9bArLhlVn476JfU22H57M0IaUF5iUTJOWKMSYHNMBWL/m+rgD4In1nEb8DITBW7c1JtC8Iql0UPq1PlxhqMyvXfW94njqcF4wQi6PsnJI9X7oHDy+pevRrCR+3R5xWB8C9jr8J80TmsRJRv8chDUOHH4HYjhF7ldJRDmvY+DK6e4jgBOIaqS5i2/PybVYWjBb7VuKDFkLQSqA5g/jELd6hpULyUgzpAgr7q3iJghthPkS4oxw9NtNvnbQweKIF37HIHiuJRsTRO4jhlX4"
HideMyAssCertificate = "MIIGMjCCBBqgAwIBAgICAQIwDQYJKoZIhvcNAQELBQAwgZIxCzAJBgNVBAYTAlVLMQ8wDQYDVQQIDAZMb25kb24xDzANBgNVBAcMBkxvbmRvbjETMBEGA1UECgwKUHJpdmF4IEx0ZDEUMBIGA1UECwwLSE1BIFBybyBWUE4xFjAUBgNVBAMMDWhpZGVteWFzcy5jb20xHjAcBgkqhkiG9w0BCQEWD2luZm9AcHJpdmF4LmNvbTAeFw0xNjEwMTgxNDE4MThaFw0yNjEwMTUxNDE4MThaMIGNMQswCQYDVQQGEwJVSzEPMA0GA1UECAwGTG9uZG9uMQ8wDQYDVQQHDAZMb25kb24xEzARBgNVBAoMClByaXZheCBMdGQxFDASBgNVBAsMC0hNQSBQcm8gVlBOMREwDwYDVQQDDAhobWF1c2VyMjEeMBwGCSqGSIb3DQEJARYPaW5mb0Bwcml2YXguY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5XY3ERJYWs/YIeBoybivNlu+M32rJs+CAZsh7BnnetTxytI4ngsMRoqXETuis8udp2hsqEHsglLR9tlk9C8yCuKhxbkpdrXFWdISmUq5sa7/wqg/zJF1AZm5Jy0oHNyTHfG6XW61I/h9IN5dmcR9YLir8DVDBNllbtt0z+DnvOhYJOqC30ENahWkTmNKl1cT7EBrR5slddiBJleAb08z77pwsD310e6jWTBySsBcPy+xu/Jj2QgVil/3mstZZDI+noFzs3SkTFBkha/lNTP7NODBQ6m39iaJxz6ZR1xE3v7XU0H5WnpZIcQ2+kmu5Krk2y1GYMKL+9oaotXFPz9v+QIDAQABo4IBkzCCAY8wCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCB4AwCwYDVR0PBAQDAgeAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU2LKFPHjFUzLfsHIMWi0VukhBgTEwgccGA1UdIwSBvzCBvIAU4MZR0iTa8SoTWOJeoOmtynuk8/ehgZikgZUwgZIxCzAJBgNVBAYTAlVLMQ8wDQYDVQQIDAZMb25kb24xDzANBgNVBAcMBkxvbmRvbjETMBEGA1UECgwKUHJpdmF4IEx0ZDEUMBIGA1UECwwLSE1BIFBybyBWUE4xFjAUBgNVBAMMDWhpZGVteWFzcy5jb20xHjAcBgkqhkiG9w0BCQEWD2luZm9AcHJpdmF4LmNvbYIJAOmTY3hf1Bb6MBoGA1UdEQQTMBGBD2luZm9AcHJpdmF4LmNvbTAaBgNVHRIEEzARgQ9pbmZvQHByaXZheC5jb20wEwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggIBAKeGVnbL3yu2fh1T0ATbgyHx9rnFGRW1o/xfF5ssfRInlopsGDejrk9goyJErVxuzSzLp8AhxSOrVZJp6Tlpssj3B4FbGB0BIH+LcrID9pb+r2LqrTeYfMwYo6zRLNQ5NmMyxQCf6XrdxihUTiZBV31LKlWNkhOLMlHr2eXwAEXjqYMXjYwN+WE8I7SlUm5WCwj7PTiF7BpdDP5Ut4y5Dj8A2m1zXt36rr5hxvbgo2JAeFwVEG4ch67PI+uM0G2GilxnjuK2wKgjBKFMAUfLs7tigzSgx8PEfYCc+bgWpPyfG5hYM9n94zd2VTDN4sam12Bxvhw8zn20L6eT+Skfa8BN7eesrV5opABt/IImZ4Q1HShKKc5EiBN8CKGDydojkNrXuFfsyv7S9VHch0e5cS+Annhr4ARaH0O5fPOD5PBVajdbV6/Rf7NzB5b/raJcUK5BD6KWWRCsmaNYzaabJjUpCmigrOMmkdAxeKCY/oEFpU3+7VeKfNyxBTIiGFt5RjNqTQXmMVjiRN97VN7fqAaFTQB2OF7E3hrtqU9jXkeN8Tvu/FF0LNyt87orewecC0Ujz7Hto9fchPH0roP+DVzoAEP8axD9RV5pM/kgubu3hMD6lLsbx4GOD11GQplvuygURxAYsyjbgFydbk1ZIpeE2OeGXXrfuQWFbNtjLJTu"
HideMyAssRSAPrivateKey = "MIIEpAIBAAKCAQEA5XY3ERJYWs/YIeBoybivNlu+M32rJs+CAZsh7BnnetTxytI4ngsMRoqXETuis8udp2hsqEHsglLR9tlk9C8yCuKhxbkpdrXFWdISmUq5sa7/wqg/zJF1AZm5Jy0oHNyTHfG6XW61I/h9IN5dmcR9YLir8DVDBNllbtt0z+DnvOhYJOqC30ENahWkTmNKl1cT7EBrR5slddiBJleAb08z77pwsD310e6jWTBySsBcPy+xu/Jj2QgVil/3mstZZDI+noFzs3SkTFBkha/lNTP7NODBQ6m39iaJxz6ZR1xE3v7XU0H5WnpZIcQ2+kmu5Krk2y1GYMKL+9oaotXFPz9v+QIDAQABAoIBAQCcMcssOMOiFWc3MC3EWo4SP4MKQ9n0Uj5Z34LI151FdJyehlj54+VYQ1Cv71tCbjED2sZUBoP69mtsT/EzcsjqtfiOwgrifrs2+BOm+0HKHKiGlcbP9peiHkT10PxEITWXpYtJvGlbcfOjIxqt6B28cBjCK09ShrVQL9ylAKBearRRUacszppntMNTMtN/uG48ZR9Wm+xAczImdG6CrG5sLI/++JwM5PDChLvn5JgMGyOfQZdjNe1oSOVLmqFeG5uu/FS4oMon9+HtfjHJr4ZgA1yQ2wQh3GvEjlP8zwHxEpRJYbxpj6ZbjHZJ2HLX/Gcd9/cXiN8+fQ2zPIYQyG9dAoGBAPUUmt2nJNvl7gj0GbZZ3XR9o+hvj7bJ74W2NhMrw6kjrrzHTAUQd1sBQS8szAQCLqf2ou1aw9AMMBdsLAHydXxvbH7IBAla7rKr23iethtSfjhTNSgQLJHVZlNHfp3hzNtCQZ7j0qVjrteNotrdVF7kKPHDXAK00ICy6SPNjvrXAoGBAO+vdnO15jLeZbbi3lQNS4r8oCadyqyX7ouKE6MtKNhiPsNPGqHKiGcKs/+QylVgYvSmm7TgpsCAiEYeLSPT+Yq3y7HtwVpULlpfAhEJXmvn/6hGpOizx1WNGWhw7nHPWPDzf+jqCGzHdhK0aEZR3MZZQ+U+uKfGiJ8vrvgB7eGvAoGAWxxp5nU48rcsIw/8bxpBhgkfYk33M5EnBqKSv9XJS5wEXhIJZOiWNrLktNEGl4boKXE7aNoRacreJhcE1UR6AOS7hPZ+6atwiePyF4mJUeb9HZtxa493wk9/Vv6BR9il++1Jz/QKX4oLef8hyBP4Rb60qgxirG7kBLR+j9zfhskCgYEAzA5y5xIeuIIU0H4XUDG9dcebxSSjbwsuYIgeLdb9pjMGQhsvjjyyoh8/nT20tLkJpkXN3FFCRjNnUWLRhWYrVkkh1wqWiYOPrwqh5MU4KN/sDWSPcznTY+drkTpMFoKzsvdrl2zf3VR3FneXKv742bkXj601Ykko+XWMHcLutisCgYBSq8IrsjzfaTQiTGI9a7WWsvzK92bq7Abnfq7swAXWcJd/bnjTQKLrrvt2bmwNvlWKAb3c69BFMn0X4t4PuN0iJQ39D6aQAEaM7HwWAmjf5TbodbmgbGxdsUB4xcCIQQ1mvTkigXWrCg0YAD2GZSoaslXAAVv6nR5qWEIa0Hx9GA=="
)
func HideMyAssCountryChoices() (choices []string) {
servers := HideMyAssServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
}
return makeUnique(choices)
}
func HideMyAssCityChoices() (choices []string) {
servers := HideMyAssServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
}
return makeUnique(choices)
}
func HideMyAssHostnameChoices() (choices []string) {
servers := HideMyAssServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
return makeUnique(choices)
}
// HideMyAssServers returns a slice of all the server information for HideMyAss.
func HideMyAssServers() (servers []models.HideMyAssServer) {
servers = make([]models.HideMyAssServer, len(allServers.HideMyAss.Servers))
copy(servers, allServers.HideMyAss.Servers)
return servers
}

View File

@@ -1,44 +0,0 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll
const (
IpvanishCA = "MIIErTCCA5WgAwIBAgIJAMYKzSS8uPKDMA0GCSqGSIb3DQEBDQUAMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCRkwxFDASBgNVBAcTC1dpbnRlciBQYXJrMREwDwYDVQQKEwhJUFZhbmlzaDEVMBMGA1UECxMMSVBWYW5pc2ggVlBOMRQwEgYDVQQDEwtJUFZhbmlzaCBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBpcHZhbmlzaC5jb20wHhcNMTIwMTExMTkzMjIwWhcNMjgxMTAyMTkzMjIwWjCBlTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkZMMRQwEgYDVQQHEwtXaW50ZXIgUGFyazERMA8GA1UEChMISVBWYW5pc2gxFTATBgNVBAsTDElQVmFuaXNoIFZQTjEUMBIGA1UEAxMLSVBWYW5pc2ggQ0ExIzAhBgkqhkiG9w0BCQEWFHN1cHBvcnRAaXB2YW5pc2guY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt9DBWNr/IKOuY3TmDP5x7vYZR0DGxLbXU8TyAzBbjUtFFMbhxlHiXVQrZHmgzih94x7BgXM7tWpmMKYVb+gNaqMdWE680Qm3nOwmhy/dulXDkEHAwD05i/iTx4ZaUdtV2vsKBxRg1vdC4AEiwD7bqV4HOi13xcG971aQ55Mj1KeCdA0aNvpat1LWx2jjWxsfI8s2Lv5Fkoi1HO1+vTnnaEsJZrBgAkLXpItqP29Lik3/OBIvkBIxlKrhiVPixE5qNiD+eSPirsmROvsyIonoJtuY4Dw5K6pcNlKyYiwo1IOFYU3YxffwFJk+bSW4WVBhsdf5dGxq/uOHmuz5gdwxCwIDAQABo4H9MIH6MAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFEv9FCWJHefBcIPX9p8RHCVOGe6uMIHKBgNVHSMEgcIwgb+AFEv9FCWJHefBcIPX9p8RHCVOGe6uoYGbpIGYMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCRkwxFDASBgNVBAcTC1dpbnRlciBQYXJrMREwDwYDVQQKEwhJUFZhbmlzaDEVMBMGA1UECxMMSVBWYW5pc2ggVlBOMRQwEgYDVQQDEwtJUFZhbmlzaCBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBpcHZhbmlzaC5jb22CCQDGCs0kvLjygzANBgkqhkiG9w0BAQ0FAAOCAQEAI2dkh/43ksV2fdYpVGhYaFZPVqCJoToCez0IvOmLeLGzow+EOSrY508oyjYeNP4VJEjApqo0NrMbKl8g/8bpLBcotOCF1c1HZ+y9v7648uumh01SMjsbBeHOuQcLb+7gX6c0pEmxWv8qj5JiW3/1L1bktnjW5Yp5oFkFSMXjOnIoYKHyKLjN2jtwH6XowUNYpg4qVtKU0CXPdOznWcd9/zSfa393HwJPeeVLbKYaFMC4IEbIUmKYtWyoJ9pJ58smU3pWsHZUg9Zc0LZZNjkNlBdQSLmUHAJ33Bd7pJS0JQeiWviC+4UTmzEWRKa7pDGnYRYNu2cUo0/voStphv8EVA=="
)
func IpvanishCountryChoices() (choices []string) {
servers := IpvanishServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
}
return makeUnique(choices)
}
func IpvanishCityChoices() (choices []string) {
servers := IpvanishServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
}
return makeUnique(choices)
}
func IpvanishHostnameChoices() (choices []string) {
servers := IpvanishServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
return makeUnique(choices)
}
// IpvanishServers returns a slice of all the server information for Ipvanish.
func IpvanishServers() (servers []models.IpvanishServer) {
servers = make([]models.IpvanishServer, len(allServers.Ipvanish.Servers))
copy(servers, allServers.Ipvanish.Servers)
return servers
}

View File

@@ -1,45 +0,0 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll
const (
IvpnCA = "MIIGoDCCBIigAwIBAgIJAJjvUclXmxtnMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJDSDEPMA0GA1UECAwGWnVyaWNoMQ8wDQYDVQQHDAZadXJpY2gxETAPBgNVBAoMCElWUE4ubmV0MQ0wCwYDVQQLDARJVlBOMRgwFgYDVQQDDA9JVlBOIFJvb3QgQ0EgdjIxHzAdBgkqhkiG9w0BCQEWEHN1cHBvcnRAaXZwbi5uZXQwHhcNMjAwMjI2MTA1MjI5WhcNNDAwMjIxMTA1MjI5WjCBjDELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBlp1cmljaDEPMA0GA1UEBwwGWnVyaWNoMREwDwYDVQQKDAhJVlBOLm5ldDENMAsGA1UECwwESVZQTjEYMBYGA1UEAwwPSVZQTiBSb290IENBIHYyMR8wHQYJKoZIhvcNAQkBFhBzdXBwb3J0QGl2cG4ubmV0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxHVeaQN3nYCLnGoEg6cY44AExbQ3W6XGKYwC9vI+HJbb1o0tAv56ryvc6eS6BdG5q9M8fHaHEE/jw9rtznioiXPwIEmqMqFPA9k1oRIQTGX73m+zHGtRpt9P4tGYhkvbqnN0OGI0H+j9R6cwKi7KpWIoTVibtyI7uuwgzC2nvDzVkLi63uvnCKRXcGy3VWC06uWFbqI9+QDrHHgdJA1F0wRfg0Iac7TE75yXItBMvNLbdZpge9SmplYWFQ2rVPG+n75KepJ+KW7PYfTP4Mh3R8A7h3/WRm03o3spf2aYw71t44voZ6agvslvwqGyczDytsLUny0U2zR7/mfEAyVbL8jqcWr2Df0m3TA0WxwdWvA51/RflVk9G96LncUkoxuBT56QSMtdjbMSqRgLfz1iPsglQEaCzUSqHfQExvONhXtNgy+Pr2+wGrEuSlLMee7aUEMTFEX/vHPZanCrUVYf5Vs8vDOirZjQSHJfgZfwj3nL5VLtIq6ekDhSAdrqCTILP3V2HbgdZGWPVQxl4YmQPKo0IJpse5Kb6TF2o0i90KhORcKg7qZA40sEbYLEwqTM7VBs1FahTXsOPAoMa7xZWV1TnigF5pdVS1l51dy5S8L4ErHFEnAp242BDuTClSLVnWDdofW0EZ0OkK7V9zKyVl75dlBgxMIS0y5MsK7IWicCAwEAAaOCAQEwgf4wHQYDVR0OBBYEFHUDcMOMo35yg2A/v0uYfkDE11CXMIHBBgNVHSMEgbkwgbaAFHUDcMOMo35yg2A/v0uYfkDE11CXoYGSpIGPMIGMMQswCQYDVQQGEwJDSDEPMA0GA1UECAwGWnVyaWNoMQ8wDQYDVQQHDAZadXJpY2gxETAPBgNVBAoMCElWUE4ubmV0MQ0wCwYDVQQLDARJVlBOMRgwFgYDVQQDDA9JVlBOIFJvb3QgQ0EgdjIxHzAdBgkqhkiG9w0BCQEWEHN1cHBvcnRAaXZwbi5uZXSCCQCY71HJV5sbZzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAABAjRMJy+mXFLezAZ8iUgxOjNtSqkCv1aU78K1XkYUzbwNNrSIVGKfP9cqOEiComXY6nniws7QEV2IWilcdPKm0x57recrr9TExGGOTVGB/WdmsFfn0g/HgmxNvXypzG3qulBk4qQTymICdsl9vIPb1l9FSjKw1KgUVuCPaYq7xiXbZ/kZdZX49xeKtoDBrXKKhXVYoWus/S+k2IS8iCxvcp599y7LQJg5DOGlbaxFhsW4R+kfGOaegyhPvpaznguv02i7NLd99XqJhpv2jTUF5F3T23Z4KkL/wTo4zxz09DKOlELrE4ai++ilCt/mXWECXNOSNXzgszpe6WAs0h9R++sH+AzJyhBfIGgPUTxHHHvxBVLj3k6VCgF7mRP2Y+rTWa6d8AGI2+RaeyV9DVVH9UeSoU0Hv2JHiZL6dRERnyg8dyzKeTCke8poLIjXF+gyvI+22/xsL8jcNHi9Kji3Vpc3i0Mxzx3gu2N+PL71CwJilgqBgxj0firr3k8sFcWVSGos6RJ3IvFvThxYx0p255WrWM01fR9TktPYEfjDT9qpIJ8OrGlNOhWhYj+a45qibXDpaDdb/uBEmf2sSXNifjSeUyqu6cKfZvMqB7pS3l/AhuAOTT80E4sXLEoDxkFD4C78swZ8wyWRKwsWGIGABGAHwXEAoDiZ/jjFrEZT0="
IvpnOpenvpnStaticKeyV1 = "ac470c93ff9f5602a8aab37dee84a52814d10f20490ad23c47d5d82120c1bf859e93d0696b455d4a1b8d55d40c2685c41ca1d0aef29a3efd27274c4ef09020a3978fe45784b335da6df2d12db97bbb838416515f2a96f04715fd28949c6fe296a925cfada3f8b8928ed7fc963c1563272f5cf46e5e1d9c845d7703ca881497b7e6564a9d1dea9358adffd435295479f47d5298fabf5359613ff5992cb57ff081a04dfb81a26513a6b44a9b5490ad265f8a02384832a59cc3e075ad545461060b7bcab49bac815163cb80983dd51d5b1fd76170ffd904d8291071e96efc3fb777856c717b148d08a510f5687b8a8285dcffe737b98916dd15ef6235dee4266d3b"
)
func IvpnCountryChoices() (choices []string) {
servers := IvpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
}
return makeUnique(choices)
}
func IvpnCityChoices() (choices []string) {
servers := IvpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
}
return makeUnique(choices)
}
func IvpnHostnameChoices() (choices []string) {
servers := IvpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
return makeUnique(choices)
}
// IvpnServers returns a slice of all the server information for Ivpn.
func IvpnServers() (servers []models.IvpnServer) {
servers = make([]models.IvpnServer, len(allServers.Ivpn.Servers))
copy(servers, allServers.Ivpn.Servers)
return servers
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,10 @@
package constants package constants
const ( import (
TUN = "tun0" "github.com/qdm12/gluetun/internal/models"
TAP = "tap0"
) )
const ( const (
AES128cbc = "aes-128-cbc" TUN models.VPNDevice = "tun0"
AES256cbc = "aes-256-cbc" TAP models.VPNDevice = "tap0"
AES128gcm = "aes-128-gcm"
AES256gcm = "aes-256-gcm"
SHA1 = "sha1"
SHA256 = "sha256"
SHA512 = "sha512"
)
const (
Openvpn24 = "2.4"
Openvpn25 = "2.5"
) )

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