Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f52df9747 | ||
|
|
f04fd845bb |
@@ -19,7 +19,6 @@ It works on Linux, Windows and OSX.
|
||||
|
||||
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. 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
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"vscode"
|
||||
],
|
||||
"shutdownAction": "stopCompose",
|
||||
"postCreateCommand": "source ~/.windows.sh && go mod download && go mod tidy",
|
||||
"postCreateCommand": "go mod download",
|
||||
"workspaceFolder": "/workspace",
|
||||
"extensions": [
|
||||
"golang.go",
|
||||
@@ -31,34 +31,25 @@
|
||||
"remote.extensionKind": {
|
||||
"ms-azuretools.vscode-docker": "workspace"
|
||||
},
|
||||
"editor.codeActionsOnSaveTimeout": 3000,
|
||||
"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.gotoSymbol.includeImports": true,
|
||||
"go.gotoSymbol.includeGoroot": true,
|
||||
"gopls": {
|
||||
"completeUnimported": true,
|
||||
"deepCompletion": true,
|
||||
"usePlaceholders": false
|
||||
},
|
||||
"go.lintTool": "golangci-lint",
|
||||
"go.buildOnSave": "workspace",
|
||||
"go.lintOnSave": "workspace",
|
||||
"go.vetOnSave": "workspace",
|
||||
"editor.formatOnSave": true,
|
||||
"[go]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": true
|
||||
}
|
||||
},
|
||||
"go.toolsEnvVars": {
|
||||
"GOFLAGS": "-tags=",
|
||||
// "CGO_ENABLED": 1 // for the race detector
|
||||
@@ -66,9 +57,7 @@
|
||||
"gopls.env": {
|
||||
"GOFLAGS": "-tags="
|
||||
},
|
||||
"go.testEnvVars": {
|
||||
"": ""
|
||||
},
|
||||
"go.testEnvVars": {},
|
||||
"go.testFlags": [
|
||||
"-v",
|
||||
// "-race"
|
||||
|
||||
@@ -8,17 +8,12 @@ services:
|
||||
- ../:/workspace
|
||||
# Docker socket to access Docker server
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# Docker configuration
|
||||
- ~/.docker:/root/.docker:z
|
||||
# SSH directory for Linux, OSX and WSL
|
||||
- ~/.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
|
||||
# SSH directory
|
||||
- ~/.ssh:/home/vscode/.ssh
|
||||
- ~/.ssh:/root/.ssh
|
||||
# Git config
|
||||
- ~/.gitconfig:/root/.gitconfig:z
|
||||
- ~/.gitconfig:/home/districter/.gitconfig
|
||||
- ~/.gitconfig:/root/.gitconfig
|
||||
environment:
|
||||
- TZ=
|
||||
cap_add:
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
.devcontainer
|
||||
.git
|
||||
.github
|
||||
.vscode
|
||||
cmd
|
||||
!cmd/gluetun
|
||||
doc
|
||||
docker-compose.yml
|
||||
Dockerfile
|
||||
LICENSE
|
||||
README.md
|
||||
title.svg
|
||||
|
||||
15
.github/CONTRIBUTING.md
vendored
15
.github/CONTRIBUTING.md
vendored
@@ -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. Create a new branch `git checkout -b my-branch-name`
|
||||
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. Push to your fork and [submit a pull request](https://github.com/qdm12/gluetun/compare)
|
||||
|
||||
## Resources
|
||||
|
||||
- [Gluetun guide on development](https://github.com/qdm12/gluetun/wiki/Development)
|
||||
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
|
||||
- [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)
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/bug.md
vendored
20
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -7,19 +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
|
||||
|
||||
<!---
|
||||
🚧 If this is about the Unraid template see https://github.com/qdm12/gluetun/discussions/550
|
||||
-->
|
||||
|
||||
**CPU arch** or **device name**: amd64
|
||||
**Is this urgent?**: No
|
||||
|
||||
**What VPN provider are you using**:
|
||||
|
||||
@@ -35,7 +25,9 @@ Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
|
||||
|
||||
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
|
||||
|
||||
@@ -43,7 +35,3 @@ PASTE YOUR LOGS
|
||||
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
|
||||
-->
|
||||
|
||||
22
.github/ISSUE_TEMPLATE/help.md
vendored
22
.github/ISSUE_TEMPLATE/help.md
vendored
@@ -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
|
||||
|
||||
**CPU arch** or **device name**: amd64
|
||||
**Is this urgent?**: No
|
||||
|
||||
**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
|
||||
|
||||
**Share your logs... (careful to remove in example tokens)**
|
||||
**Share your logs...**
|
||||
|
||||
...*careful to remove i.e. token information with PIA port forwarding*
|
||||
|
||||
```log
|
||||
|
||||
@@ -46,10 +36,6 @@ IN THERE
|
||||
|
||||
**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:
|
||||
|
||||
```yml
|
||||
|
||||
17
.github/ISSUE_TEMPLATE/provider.md
vendored
17
.github/ISSUE_TEMPLATE/provider.md
vendored
@@ -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.
|
||||
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
@@ -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
23
.github/labels.yml
vendored
@@ -18,18 +18,6 @@
|
||||
- name: ":cloud: Cyberghost"
|
||||
color: "cfe8d4"
|
||||
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"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
@@ -42,23 +30,12 @@
|
||||
- name: ":cloud: Privado"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: PrivateVPN"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: ProtonVPN"
|
||||
color: "cfe8d4"
|
||||
- name: ":cloud: PureVPN"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: Surfshark"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: Torguard"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: VPNUnlimited"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: Vyprvpn"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
|
||||
21
.github/workflows/build.yml
vendored
Normal file
21
.github/workflows/build.yml
vendored
Normal 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
39
.github/workflows/buildx-branch.yml
vendored
Normal 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
36
.github/workflows/buildx-latest.yml
vendored
Normal 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
36
.github/workflows/buildx-release.yml
vendored
Normal 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
|
||||
99
.github/workflows/ci.yml
vendored
99
.github/workflows/ci.yml
vendored
@@ -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
|
||||
4
.github/workflows/dockerhub-description.yml
vendored
4
.github/workflows/dockerhub-description.yml
vendored
@@ -10,9 +10,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2
|
||||
- name: Docker Hub Description
|
||||
uses: peter-evans/dockerhub-description@v2
|
||||
uses: peter-evans/dockerhub-description@v2.4.1
|
||||
with:
|
||||
username: qmcgaw
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
11
.github/workflows/labels.yml
vendored
11
.github/workflows/labels.yml
vendored
@@ -9,7 +9,10 @@ jobs:
|
||||
labeler:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.4
|
||||
- uses: crazy-max/ghaction-github-labeler@v3
|
||||
with:
|
||||
yaml-file: .github/labels.yml
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Labeler
|
||||
if: success()
|
||||
uses: crazy-max/ghaction-github-labeler@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
5
.github/workflows/misspell.yml
vendored
5
.github/workflows/misspell.yml
vendored
@@ -8,8 +8,9 @@ jobs:
|
||||
misspell:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.4
|
||||
- uses: reviewdog/action-misspell@v1
|
||||
- uses: actions/checkout@v2
|
||||
- uses: reviewdog/action-misspell@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
locale: "US"
|
||||
level: error
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +0,0 @@
|
||||
scratch.txt
|
||||
@@ -10,25 +10,10 @@ issues:
|
||||
linters:
|
||||
- dupl
|
||||
- maligned
|
||||
- goerr113
|
||||
- path: internal/server/
|
||||
- path: internal/unix/constants\.go
|
||||
linters:
|
||||
- dupl
|
||||
- path: internal/configuration/
|
||||
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
|
||||
- golint
|
||||
text: don't use ALL_CAPS in Go names; use CamelCase
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
@@ -48,32 +33,29 @@ linters:
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- godot
|
||||
- goerr113
|
||||
- goheader
|
||||
- goimports
|
||||
- golint
|
||||
- gomnd
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- importas
|
||||
- ineffassign
|
||||
- interfacer
|
||||
- lll
|
||||
- maligned
|
||||
- misspell
|
||||
- nakedret
|
||||
- nestif
|
||||
- nilerr
|
||||
- noctx
|
||||
- nolintlint
|
||||
- prealloc
|
||||
- predeclared
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- scopelint
|
||||
- sqlclosecheck
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- thelper
|
||||
- tparallel
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
|
||||
9
.vscode/extensions.json
vendored
Normal file
9
.vscode/extensions.json
vendored
Normal 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
91
.vscode/settings.json
vendored
Normal 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
|
||||
}
|
||||
121
Dockerfile
121
Dockerfile
@@ -1,64 +1,35 @@
|
||||
ARG ALPINE_VERSION=3.14
|
||||
ARG GO_ALPINE_VERSION=3.13
|
||||
ARG GO_VERSION=1.16
|
||||
ARG XCPUTRANSLATE_VERSION=v0.6.0
|
||||
ARG GOLANGCI_LINT_VERSION=v1.41.1
|
||||
ARG BUILDPLATFORM=linux/amd64
|
||||
ARG ALPINE_VERSION=3.12
|
||||
ARG GO_VERSION=1.15
|
||||
|
||||
FROM --platform=${BUILDPLATFORM} qmcgaw/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate
|
||||
FROM --platform=${BUILDPLATFORM} qmcgaw/binpot:golangci-lint-${GOLANGCI_LINT_VERSION} AS golangci-lint
|
||||
|
||||
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++
|
||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder
|
||||
RUN apk --update add git
|
||||
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
|
||||
COPY .golangci.yml .
|
||||
COPY go.mod go.sum ./
|
||||
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 CREATED="an unknown date"
|
||||
ARG BUILD_DATE="an unknown date"
|
||||
ARG COMMIT=unknown
|
||||
RUN GOARCH="$(xcputranslate translate -field arch -targetplatform ${TARGETPLATFORM})" \
|
||||
GOARM="$(xcputranslate translate -field arm -targetplatform ${TARGETPLATFORM})" \
|
||||
go build -trimpath -ldflags="-s -w \
|
||||
COPY cmd/gluetun/main.go .
|
||||
COPY internal/ ./internal/
|
||||
RUN go test ./...
|
||||
RUN golangci-lint run --timeout=10m
|
||||
RUN go build -trimpath -ldflags="-s -w \
|
||||
-X 'main.version=$VERSION' \
|
||||
-X 'main.created=$CREATED' \
|
||||
-X 'main.buildDate=$BUILD_DATE' \
|
||||
-X 'main.commit=$COMMIT' \
|
||||
" -o entrypoint cmd/gluetun/main.go
|
||||
" -o entrypoint main.go
|
||||
|
||||
FROM alpine:${ALPINE_VERSION}
|
||||
ARG VERSION=unknown
|
||||
ARG CREATED="an unknown date"
|
||||
ARG BUILD_DATE="an unknown date"
|
||||
ARG COMMIT=unknown
|
||||
LABEL \
|
||||
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.revision=$COMMIT \
|
||||
org.opencontainers.image.url="https://github.com/qdm12/gluetun" \
|
||||
@@ -69,55 +40,48 @@ LABEL \
|
||||
ENV VPNSP=pia \
|
||||
VERSION_INFORMATION=on \
|
||||
PROTOCOL=udp \
|
||||
OPENVPN_VERSION=2.5 \
|
||||
OPENVPN_VERBOSITY=1 \
|
||||
OPENVPN_FLAGS= \
|
||||
OPENVPN_ROOT=yes \
|
||||
OPENVPN_ROOT=no \
|
||||
OPENVPN_TARGET_IP= \
|
||||
OPENVPN_IPV6=off \
|
||||
OPENVPN_CUSTOM_CONFIG= \
|
||||
TZ= \
|
||||
PUID= \
|
||||
PGID= \
|
||||
PUBLICIP_FILE="/tmp/gluetun/ip" \
|
||||
# VPN provider settings
|
||||
# PIA, Windscribe, Surfshark, Cyberghost, Vyprvpn, NordVPN, PureVPN only
|
||||
OPENVPN_USER= \
|
||||
OPENVPN_PASSWORD= \
|
||||
USER_SECRETFILE=/run/secrets/openvpn_user \
|
||||
PASSWORD_SECRETFILE=/run/secrets/openvpn_password \
|
||||
REGION= \
|
||||
COUNTRY= \
|
||||
CITY= \
|
||||
PORT= \
|
||||
SERVER_HOSTNAME= \
|
||||
# Mullvad only:
|
||||
ISP= \
|
||||
OWNED=no \
|
||||
# Private Internet Access only:
|
||||
# PIA only
|
||||
PIA_ENCRYPTION=strong \
|
||||
PORT_FORWARDING=off \
|
||||
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" \
|
||||
OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt \
|
||||
OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey \
|
||||
# Nordvpn only:
|
||||
# NordVPN only
|
||||
SERVER_NUMBER= \
|
||||
# NordVPN and ProtonVPN only:
|
||||
SERVER_NAME= \
|
||||
# ProtonVPN only:
|
||||
FREE_ONLY= \
|
||||
# Openvpn
|
||||
OPENVPN_CIPHER= \
|
||||
OPENVPN_AUTH= \
|
||||
# Health
|
||||
HEALTH_OPENVPN_DURATION_INITIAL=6s \
|
||||
HEALTH_OPENVPN_DURATION_ADDITION=5s \
|
||||
HEALTH_SERVER_ADDRESS=127.0.0.1:9999 \
|
||||
# DNS over TLS
|
||||
DOT=on \
|
||||
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_DETAILS=0 \
|
||||
DOT_VALIDATION_LOGLEVEL=0 \
|
||||
@@ -147,22 +111,19 @@ ENV VPNSP=pia \
|
||||
# Shadowsocks
|
||||
SHADOWSOCKS=off \
|
||||
SHADOWSOCKS_LOG=off \
|
||||
SHADOWSOCKS_ADDRESS=":8388" \
|
||||
SHADOWSOCKS_PORT=8388 \
|
||||
SHADOWSOCKS_PASSWORD= \
|
||||
SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \
|
||||
SHADOWSOCKS_CIPHER=chacha20-ietf-poly1305 \
|
||||
SHADOWSOCKS_METHOD=chacha20-ietf-poly1305 \
|
||||
UPDATER_PERIOD=0
|
||||
ENTRYPOINT ["/entrypoint"]
|
||||
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
|
||||
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /entrypoint healthcheck
|
||||
ARG TARGETPLATFORM
|
||||
RUN apk add --no-cache --update -l apk-tools && \
|
||||
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 && \
|
||||
RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
|
||||
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* && \
|
||||
deluser openvpn && \
|
||||
deluser unbound && \
|
||||
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
102
README.md
@@ -1,42 +1,26 @@
|
||||
# Gluetun VPN client
|
||||
|
||||
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, FastestVPN,
|
||||
HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN,
|
||||
ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN and Windscribe VPN servers
|
||||
using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access,
|
||||
Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN and Privado VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||
|
||||
**ANNOUNCEMENT**:
|
||||
**ANNOUNCEMENT**: *New Docker image name `qmcgaw/gluetun`*
|
||||
|
||||

|
||||
<img height="250" src="https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg?sanitize=true">
|
||||
|
||||
[](https://github.com/qdm12/gluetun/actions/workflows/ci.yml)
|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun/tags?page=1&ordering=last_updated)
|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun/tags)
|
||||
|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||
|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||
[](https://hub.docker.com/r/qmcgaw/private-internet-access)
|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||
|
||||

|
||||

|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun/tags?page=1&ordering=last_updated)
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun/tags)
|
||||
|
||||
[](https://github.com/qdm12/gluetun/commits/master)
|
||||
[](https://github.com/qdm12/gluetun/graphs/contributors)
|
||||
[](https://github.com/qdm12/gluetun/pulls?q=is%3Apr+is%3Aclosed)
|
||||
[](https://github.com/qdm12/gluetun/issues)
|
||||
[](https://github.com/qdm12/gluetun/issues?q=is%3Aissue+is%3Aclosed)
|
||||
[](https://github.com/qdm12/gluetun/commits)
|
||||
|
||||
[](https://github.com/qdm12/gluetun)
|
||||

|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
## 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)
|
||||
- Donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
|
||||
- Drop me [an email](mailto:quentin.mcgaw@gmail.com)
|
||||
- Video:
|
||||
|
||||
[](https://youtu.be/0F6I03LQcI4)
|
||||
|
||||
## Features
|
||||
|
||||
- Based on Alpine 3.13 for a small Docker image of 54MB
|
||||
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers
|
||||
- Based on Alpine 3.12 for a small Docker image of 52MB
|
||||
- Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN**, **PureVPN** and **Privado** servers
|
||||
- Supports Openvpn only for now
|
||||
- DNS over TLS baked in with service provider(s) of your choice
|
||||
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
|
||||
@@ -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)
|
||||
- [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)
|
||||
- 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
|
||||
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
||||
- Subprograms all drop root privileges once launched
|
||||
@@ -75,17 +56,8 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||
|
||||
## Setup
|
||||
|
||||
1. Ensure your `tun` kernel module is 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. 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)
|
||||
1. Launch the container with:
|
||||
|
||||
```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:
|
||||
|
||||
```bash
|
||||
echo "your openvpn username" > openvpn_user
|
||||
echo "your openvpn password" > openvpn_password
|
||||
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.
|
||||
|
||||
- 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)
|
||||
- [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)
|
||||
- [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.
|
||||
- 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
|
||||
|
||||
[](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://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
|
||||
|
||||
[](https://github.com/qdm12/gluetun/commits)
|
||||
[](https://github.com/qdm12/gluetun/pulls?q=is%3Apr+is%3Aclosed)
|
||||
|
||||
[](https://github.com/qdm12/gluetun/issues)
|
||||
[](https://github.com/qdm12/gluetun/issues?q=is%3Aissue+is%3Aclosed)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
[](https://hub.docker.com/r/qmcgaw/gluetun)
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -2,31 +2,31 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
nativeos "os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/dns/pkg/unbound"
|
||||
"github.com/qdm12/gluetun/internal/alpine"
|
||||
"github.com/qdm12/gluetun/internal/cli"
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/dns"
|
||||
"github.com/qdm12/gluetun/internal/firewall"
|
||||
"github.com/qdm12/gluetun/internal/healthcheck"
|
||||
"github.com/qdm12/gluetun/internal/httpproxy"
|
||||
gluetunLogging "github.com/qdm12/gluetun/internal/logging"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/openvpn"
|
||||
"github.com/qdm12/gluetun/internal/portforward"
|
||||
"github.com/qdm12/gluetun/internal/params"
|
||||
"github.com/qdm12/gluetun/internal/publicip"
|
||||
"github.com/qdm12/gluetun/internal/routing"
|
||||
"github.com/qdm12/gluetun/internal/server"
|
||||
"github.com/qdm12/gluetun/internal/settings"
|
||||
"github.com/qdm12/gluetun/internal/shadowsocks"
|
||||
"github.com/qdm12/gluetun/internal/storage"
|
||||
"github.com/qdm12/gluetun/internal/unix"
|
||||
@@ -34,170 +34,101 @@ import (
|
||||
versionpkg "github.com/qdm12/gluetun/internal/version"
|
||||
"github.com/qdm12/golibs/command"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/params"
|
||||
"github.com/qdm12/goshutdown"
|
||||
"github.com/qdm12/gosplash"
|
||||
"github.com/qdm12/updated/pkg/dnscrypto"
|
||||
"github.com/qdm12/golibs/os"
|
||||
"github.com/qdm12/golibs/os/user"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
version = "unknown"
|
||||
commit = "unknown"
|
||||
created = "an unknown date"
|
||||
)
|
||||
|
||||
var (
|
||||
errSetupRouting = errors.New("cannot setup routing")
|
||||
errCreateUser = errors.New("cannot create user")
|
||||
version = "unknown"
|
||||
commit = "unknown"
|
||||
buildDate = "an unknown date"
|
||||
)
|
||||
|
||||
func main() {
|
||||
buildInfo := models.BuildInformation{
|
||||
Version: version,
|
||||
Commit: commit,
|
||||
Created: created,
|
||||
Version: version,
|
||||
Commit: commit,
|
||||
BuildDate: buildDate,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
logger := logging.NewParent(logging.Settings{
|
||||
Level: logging.LevelInfo,
|
||||
})
|
||||
|
||||
args := os.Args
|
||||
args := nativeos.Args
|
||||
os := os.New()
|
||||
osUser := user.New()
|
||||
unix := unix.New()
|
||||
cli := cli.New()
|
||||
env := params.NewEnv()
|
||||
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)
|
||||
nativeos.Exit(_main(ctx, buildInfo, args, os, osUser, unix, cli))
|
||||
}
|
||||
|
||||
var (
|
||||
errCommandUnknown = errors.New("command is unknown")
|
||||
)
|
||||
|
||||
//nolint:gocognit,gocyclo
|
||||
func _main(ctx context.Context, buildInfo models.BuildInformation,
|
||||
args []string, logger logging.ParentLogger, env params.Env,
|
||||
unix unix.Unix, cmder command.RunStarter, cli cli.CLIer) error {
|
||||
func _main(background context.Context, buildInfo models.BuildInformation,
|
||||
args []string, os os.OS, osUser user.OSUser, unix unix.Unix,
|
||||
cli cli.CLI) int {
|
||||
if len(args) > 1 { // cli operation
|
||||
var err error
|
||||
switch args[1] {
|
||||
case "healthcheck":
|
||||
return cli.HealthCheck(ctx, env, logger)
|
||||
err = cli.HealthCheck(background)
|
||||
case "clientkey":
|
||||
return cli.ClientKey(args[2:])
|
||||
err = cli.ClientKey(args[2:], os.OpenFile)
|
||||
case "openvpnconfig":
|
||||
return cli.OpenvpnConfig(logger)
|
||||
err = cli.OpenvpnConfig(os)
|
||||
case "update":
|
||||
return cli.Update(ctx, args[2:], logger)
|
||||
err = cli.Update(args[2:], os)
|
||||
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
|
||||
httpClient := &http.Client{Timeout: clientTimeout}
|
||||
// Create configurators
|
||||
alpineConf := alpine.New()
|
||||
ovpnConf := openvpn.NewConfigurator(
|
||||
logger.NewChild(logging.Settings{Prefix: "openvpn configurator: "}),
|
||||
unix, cmder)
|
||||
dnsCrypto := dnscrypto.New(httpClient, "", "")
|
||||
const cacertsPath = "/etc/ssl/certs/ca-certificates.crt"
|
||||
dnsConf := unbound.NewConfigurator(nil, cmder, dnsCrypto,
|
||||
"/etc/unbound", "/usr/sbin/unbound", cacertsPath)
|
||||
alpineConf := alpine.NewConfigurator(os.OpenFile, osUser)
|
||||
ovpnConf := openvpn.NewConfigurator(logger, os, unix)
|
||||
dnsConf := dns.NewConfigurator(logger, httpClient, os.OpenFile)
|
||||
routingConf := routing.NewRouting(logger)
|
||||
firewallConf := firewall.NewConfigurator(logger, routingConf, os.OpenFile)
|
||||
streamMerger := command.NewStreamMerger()
|
||||
|
||||
announcementExp, err := time.Parse(time.RFC3339, "2021-07-22T00:00:00Z")
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
paramsReader := params.NewReader(logger, os)
|
||||
fmt.Println(gluetunLogging.Splash(buildInfo))
|
||||
|
||||
err = printVersions(ctx, logger, []printVersionElement{
|
||||
{name: "Alpine", getVersion: alpineConf.Version},
|
||||
{name: "OpenVPN 2.4", getVersion: ovpnConf.Version24},
|
||||
{name: "OpenVPN 2.5", getVersion: ovpnConf.Version25},
|
||||
{name: "Unbound", getVersion: dnsConf.Version},
|
||||
{name: "IPtables", getVersion: func(ctx context.Context) (version string, err error) {
|
||||
return firewall.Version(ctx, cmder)
|
||||
}},
|
||||
printVersions(ctx, logger, map[string]func(ctx context.Context) (string, error){
|
||||
"OpenVPN": ovpnConf.Version,
|
||||
"Unbound": dnsConf.Version,
|
||||
"IPtables": firewallConf.Version,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var allSettings configuration.Settings
|
||||
err = allSettings.Read(env,
|
||||
logger.NewChild(logging.Settings{Prefix: "configuration: "}))
|
||||
allSettings, err := settings.GetAllSettings(paramsReader)
|
||||
if err != nil {
|
||||
return err
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
logger.Info(allSettings.String())
|
||||
|
||||
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
|
||||
return err
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
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
|
||||
storage := storage.New(
|
||||
logger.NewChild(logging.Settings{Prefix: "storage: "}),
|
||||
constants.ServersData)
|
||||
storage := storage.New(logger, os, constants.ServersData)
|
||||
allServers, err := storage.SyncServers(constants.GetAllServers())
|
||||
if err != nil {
|
||||
return err
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Should never change
|
||||
@@ -206,303 +137,313 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
||||
const defaultUsername = "nonrootuser"
|
||||
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %s", errCreateUser, err)
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
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 {
|
||||
return err
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
|
||||
firewallLogLevel := logging.LevelInfo
|
||||
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()
|
||||
if err != nil {
|
||||
return err
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
|
||||
localNetworks, err := routingConf.LocalNetworks()
|
||||
localSubnet, err := routingConf.LocalSubnet()
|
||||
if err != nil {
|
||||
return err
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
|
||||
defaultIP, err := routingConf.DefaultIP()
|
||||
if err != nil {
|
||||
return err
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
|
||||
firewallLogger := logger.NewChild(logging.Settings{
|
||||
Prefix: "firewall: ",
|
||||
Level: firewallLogLevel,
|
||||
})
|
||||
firewallConf := firewall.NewConfig(firewallLogger, cmder,
|
||||
defaultInterface, defaultGateway, localNetworks, defaultIP)
|
||||
firewallConf.SetNetworkInformation(defaultInterface, defaultGateway, localSubnet, defaultIP)
|
||||
|
||||
if err := routingConf.Setup(); err != nil {
|
||||
if strings.Contains(err.Error(), "operation not permitted") {
|
||||
logger.Warn("💡 Tip: Are you passing NET_ADMIN capability to gluetun?")
|
||||
}
|
||||
return fmt.Errorf("%w: %s", errSetupRouting, err)
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
defer func() {
|
||||
logger.Info("routing cleanup...")
|
||||
routingConf.SetVerbose(false)
|
||||
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 {
|
||||
return err
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
if err := routingConf.SetOutboundRoutes(allSettings.Firewall.OutboundSubnets); err != nil {
|
||||
return err
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
|
||||
if err := ovpnConf.CheckTUN(); err != nil {
|
||||
logger.Warn(err.Error())
|
||||
logger.Warn(err)
|
||||
err = ovpnConf.CreateTUN()
|
||||
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(dnsReadyCh)
|
||||
|
||||
if allSettings.Firewall.Enabled {
|
||||
err := firewallConf.SetEnabled(ctx, true) // disabled by default
|
||||
if err != nil {
|
||||
return err
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
for _, vpnPort := range allSettings.Firewall.VPNInputPorts {
|
||||
err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN))
|
||||
if err != nil {
|
||||
return err
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
for _, port := range allSettings.Firewall.InputPorts {
|
||||
err = firewallConf.SetAllowedPort(ctx, port, defaultInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
logger.Error(err)
|
||||
return 1
|
||||
}
|
||||
} // TODO move inside firewall?
|
||||
|
||||
// Shutdown settings
|
||||
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,
|
||||
}
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
controlGroupHandler := goshutdown.NewGroupHandler("control", defaultGroupSettings)
|
||||
tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupSettings)
|
||||
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupSettings)
|
||||
go collectStreamLines(ctx, streamMerger, logger, signalTunnelReady)
|
||||
|
||||
portForwardLogger := logger.NewChild(logging.Settings{Prefix: "port forwarding: "})
|
||||
portForwardLooper := portforward.NewLoop(allSettings.OpenVPN.Provider.PortForwarding,
|
||||
httpClient, firewallConf, portForwardLogger)
|
||||
portForwardHandler, portForwardCtx, portForwardDone := goshutdown.NewGoRoutineHandler(
|
||||
"port forwarding", goshutdown.GoRoutineSettings{Timeout: time.Second})
|
||||
go portForwardLooper.Run(portForwardCtx, portForwardDone)
|
||||
|
||||
openvpnLogger := logger.NewChild(logging.Settings{Prefix: "openvpn: "})
|
||||
openvpnLooper := openvpn.NewLoop(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers,
|
||||
ovpnConf, firewallConf, routingConf, portForwardLooper, openvpnLogger, httpClient, tunnelReadyCh)
|
||||
openvpnHandler, openvpnCtx, openvpnDone := goshutdown.NewGoRoutineHandler(
|
||||
"openvpn", goshutdown.GoRoutineSettings{Timeout: time.Second})
|
||||
openvpnLooper := openvpn.NewLooper(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers,
|
||||
ovpnConf, firewallConf, routingConf, logger, httpClient, os.OpenFile, streamMerger, cancel)
|
||||
wg.Add(1)
|
||||
// wait for restartOpenvpn
|
||||
go openvpnLooper.Run(openvpnCtx, openvpnDone)
|
||||
go openvpnLooper.Run(ctx, wg)
|
||||
|
||||
updaterLooper := updater.NewLooper(allSettings.Updater,
|
||||
allServers, storage, openvpnLooper.SetServers, httpClient,
|
||||
logger.NewChild(logging.Settings{Prefix: "updater: "}))
|
||||
updaterHandler, updaterCtx, updaterDone := goshutdown.NewGoRoutineHandler(
|
||||
"updater", defaultGoRoutineSettings)
|
||||
allServers, storage, openvpnLooper.SetServers, httpClient, logger)
|
||||
wg.Add(1)
|
||||
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
|
||||
go updaterLooper.Run(updaterCtx, updaterDone)
|
||||
tickersGroupHandler.Add(updaterHandler)
|
||||
go updaterLooper.Run(ctx, wg)
|
||||
|
||||
unboundLogger := logger.NewChild(logging.Settings{Prefix: "dns over tls: "})
|
||||
unboundLooper := dns.NewLoop(dnsConf, allSettings.DNS, httpClient,
|
||||
unboundLogger)
|
||||
dnsHandler, dnsCtx, dnsDone := goshutdown.NewGoRoutineHandler(
|
||||
"unbound", defaultGoRoutineSettings)
|
||||
unboundLooper := dns.NewLooper(dnsConf, allSettings.DNS, logger, streamMerger, nonRootUsername, puid, pgid)
|
||||
wg.Add(1)
|
||||
// wait for unboundLooper.Restart or its ticker launched with RunRestartTicker
|
||||
go unboundLooper.Run(dnsCtx, dnsDone)
|
||||
otherGroupHandler.Add(dnsHandler)
|
||||
go unboundLooper.Run(ctx, wg, signalDNSReady)
|
||||
|
||||
publicIPLooper := publicip.NewLoop(httpClient,
|
||||
logger.NewChild(logging.Settings{Prefix: "ip getter: "}),
|
||||
allSettings.PublicIP, puid, pgid)
|
||||
pubIPHandler, pubIPCtx, pubIPDone := goshutdown.NewGoRoutineHandler(
|
||||
"public IP", defaultGoRoutineSettings)
|
||||
go publicIPLooper.Run(pubIPCtx, pubIPDone)
|
||||
otherGroupHandler.Add(pubIPHandler)
|
||||
publicIPLooper := publicip.NewLooper(
|
||||
httpClient, logger, allSettings.PublicIP, puid, pgid, os)
|
||||
wg.Add(1)
|
||||
go publicIPLooper.Run(ctx, wg)
|
||||
wg.Add(1)
|
||||
go publicIPLooper.RunRestartTicker(ctx, wg)
|
||||
|
||||
pubIPTickerHandler, pubIPTickerCtx, pubIPTickerDone := goshutdown.NewGoRoutineHandler(
|
||||
"public IP", defaultGoRoutineSettings)
|
||||
go publicIPLooper.RunRestartTicker(pubIPTickerCtx, pubIPTickerDone)
|
||||
tickersGroupHandler.Add(pubIPTickerHandler)
|
||||
httpProxyLooper := httpproxy.NewLooper(logger, allSettings.HTTPProxy)
|
||||
wg.Add(1)
|
||||
go httpProxyLooper.Run(ctx, wg)
|
||||
|
||||
httpProxyLooper := httpproxy.NewLoop(
|
||||
logger.NewChild(logging.Settings{Prefix: "http proxy: "}),
|
||||
allSettings.HTTPProxy)
|
||||
httpProxyHandler, httpProxyCtx, httpProxyDone := goshutdown.NewGoRoutineHandler(
|
||||
"http proxy", defaultGoRoutineSettings)
|
||||
go httpProxyLooper.Run(httpProxyCtx, httpProxyDone)
|
||||
otherGroupHandler.Add(httpProxyHandler)
|
||||
shadowsocksLooper := shadowsocks.NewLooper(allSettings.ShadowSocks, logger)
|
||||
wg.Add(1)
|
||||
go shadowsocksLooper.Run(ctx, wg)
|
||||
|
||||
shadowsocksLooper := shadowsocks.NewLooper(allSettings.ShadowSocks,
|
||||
logger.NewChild(logging.Settings{Prefix: "shadowsocks: "}))
|
||||
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,
|
||||
wg.Add(1)
|
||||
go routeReadyEvents(ctx, wg, buildInfo, tunnelReadyCh, dnsReadyCh,
|
||||
unboundLooper, updaterLooper, publicIPLooper, routingConf, logger, httpClient,
|
||||
allSettings.VersionInformation)
|
||||
controlGroupHandler.Add(eventsRoutingHandler)
|
||||
|
||||
controlServerAddress := ":" + strconv.Itoa(int(allSettings.ControlServer.Port))
|
||||
allSettings.VersionInformation, allSettings.OpenVPN.Provider.PortForwarding.Enabled, openvpnLooper.PortForward,
|
||||
)
|
||||
controlServerAddress := fmt.Sprintf("0.0.0.0:%d", allSettings.ControlServer.Port)
|
||||
controlServerLogging := allSettings.ControlServer.Log
|
||||
httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler(
|
||||
"http server", defaultGoRoutineSettings)
|
||||
httpServer := server.New(httpServerCtx, controlServerAddress, controlServerLogging,
|
||||
logger.NewChild(logging.Settings{Prefix: "http server: "}),
|
||||
buildInfo, openvpnLooper, portForwardLooper, unboundLooper, updaterLooper, publicIPLooper)
|
||||
go httpServer.Run(httpServerCtx, httpServerDone)
|
||||
controlGroupHandler.Add(httpServerHandler)
|
||||
httpServer := server.New(controlServerAddress, controlServerLogging,
|
||||
logger, buildInfo, openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
|
||||
wg.Add(1)
|
||||
go httpServer.Run(ctx, wg)
|
||||
|
||||
healthLogger := logger.NewChild(logging.Settings{Prefix: "healthcheck: "})
|
||||
healthcheckServer := healthcheck.NewServer(allSettings.Health, healthLogger, openvpnLooper)
|
||||
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler(
|
||||
"HTTP health server", defaultGoRoutineSettings)
|
||||
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, portForwardHandler, otherGroupHandler)
|
||||
healthcheckServer := healthcheck.NewServer(
|
||||
constants.HealthcheckAddress, logger)
|
||||
wg.Add(1)
|
||||
go healthcheckServer.Run(ctx, wg)
|
||||
|
||||
// Start openvpn for the first time in a blocking call
|
||||
// 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()
|
||||
|
||||
return orderHandler.Shutdown(context.Background())
|
||||
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 {
|
||||
logger.Info("Clearing forwarded port status file %s", allSettings.OpenVPN.Provider.PortForwarding.Filepath)
|
||||
if err := os.Remove(string(allSettings.OpenVPN.Provider.PortForwarding.Filepath)); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
type printVersionElement struct {
|
||||
name string
|
||||
getVersion func(ctx context.Context) (version string, err error)
|
||||
func createLogger() logging.Logger {
|
||||
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return 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
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
for _, element := range elements {
|
||||
version, err := element.getVersion(ctx)
|
||||
for name, f := range versionFunctions {
|
||||
version, err := f(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
logger.Error(err)
|
||||
} else {
|
||||
logger.Info("%s version: %s", name, version)
|
||||
}
|
||||
logger.Info(element.name + " version: " + version)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func routeReadyEvents(ctx context.Context, done chan<- struct{}, buildInfo models.BuildInformation,
|
||||
tunnelReadyCh <-chan struct{},
|
||||
unboundLooper dns.Looper, updaterLooper updater.Looper, publicIPLooper publicip.Looper,
|
||||
routing routing.VPNGetter, logger logging.Logger, httpClient *http.Client,
|
||||
versionInformation bool) {
|
||||
defer close(done)
|
||||
//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, wg *sync.WaitGroup, buildInfo models.BuildInformation,
|
||||
tunnelReadyCh, dnsReadyCh <-chan struct{},
|
||||
unboundLooper dns.Looper, updaterLooper updater.Looper, publicIPLooper publicip.Looper,
|
||||
routing routing.Routing, logger logging.Logger, httpClient *http.Client,
|
||||
versionInformation, portForwardingEnabled bool, startPortForward func(vpnGateway net.IP)) {
|
||||
defer wg.Done()
|
||||
tickerWg := &sync.WaitGroup{}
|
||||
// for linters only
|
||||
var restartTickerContext context.Context
|
||||
var restartTickerCancel context.CancelFunc = func() {}
|
||||
|
||||
unboundTickerDone := make(chan struct{})
|
||||
close(unboundTickerDone)
|
||||
updaterTickerDone := make(chan struct{})
|
||||
close(updaterTickerDone)
|
||||
|
||||
first := true
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
restartTickerCancel() // for linters only
|
||||
<-unboundTickerDone
|
||||
<-updaterTickerDone
|
||||
tickerWg.Wait()
|
||||
return
|
||||
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()
|
||||
if err != nil {
|
||||
logger.Warn(err.Error())
|
||||
logger.Warn(err)
|
||||
} 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 portForwardingEnabled {
|
||||
// vpnGateway required only for PIA
|
||||
vpnGateway, err := routing.VPNLocalGatewayIP()
|
||||
if err != nil {
|
||||
logger.Error("cannot get version information: " + err.Error())
|
||||
} else {
|
||||
logger.Info(message)
|
||||
logger.Error(err)
|
||||
}
|
||||
logger.Info("VPN gateway IP address: %s", vpnGateway)
|
||||
startPortForward(vpnGateway)
|
||||
}
|
||||
|
||||
unboundTickerDone = make(chan struct{})
|
||||
updaterTickerDone = make(chan struct{})
|
||||
go unboundLooper.RunRestartTicker(restartTickerContext, unboundTickerDone)
|
||||
go updaterLooper.RunRestartTicker(restartTickerContext, updaterTickerDone)
|
||||
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
BIN
doc/paypal.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
BIN
doc/sponsors.jpg
Normal file
BIN
doc/sponsors.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
doc/windscribe.jpg
Normal file
BIN
doc/windscribe.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -14,11 +14,18 @@ services:
|
||||
# command:
|
||||
volumes:
|
||||
- /yourpath:/gluetun
|
||||
secrets:
|
||||
- openvpn_user
|
||||
- openvpn_password
|
||||
environment:
|
||||
# More variables are available, see the readme table
|
||||
- OPENVPN_USER=
|
||||
- OPENVPN_PASSWORD=
|
||||
- VPNSP=private internet access
|
||||
# Timezone for accurate logs times
|
||||
- TZ=
|
||||
restart: always
|
||||
|
||||
secrets:
|
||||
openvpn_user:
|
||||
file: ./openvpn_user
|
||||
openvpn_password:
|
||||
file: ./openvpn_password
|
||||
|
||||
20
go.mod
20
go.mod
@@ -1,18 +1,14 @@
|
||||
module github.com/qdm12/gluetun
|
||||
|
||||
go 1.16
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.12.0
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/qdm12/dns v1.11.0
|
||||
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2
|
||||
github.com/qdm12/goshutdown v0.1.0
|
||||
github.com/qdm12/gosplash v0.1.0
|
||||
github.com/qdm12/ss-server v0.3.0
|
||||
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/fatih/color v1.10.0
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||
github.com/qdm12/golibs v0.0.0-20210102015428-6e1d159e61a3
|
||||
github.com/qdm12/ss-server v0.1.0
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930
|
||||
)
|
||||
|
||||
178
go.sum
178
go.sum
@@ -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/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/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
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 h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
|
||||
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb h1:D4uzjWwKYQ5XnAvUbuvHW93esHg7F8N/OYeBBcJoTr0=
|
||||
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.17.0 h1:8JV+dzJJiK46XqGLqqLav8ZfEiJECp8jlOFhpiCdZ+0=
|
||||
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.2 h1:azEQ8Fnx0jmtFF2fxsnmd6I0x6rsweUF63qqSO1NmKk=
|
||||
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/jsonreference v0.17.0 h1:yJW3HCkTHg7NOA+gZ83IPHzUSnUzGXhGmsdiCcMexbA=
|
||||
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/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/spec v0.17.0 h1:XNvrt8FlSVP8T1WuhbAFF6QDhJc0zsoWzX4wXARhhpE=
|
||||
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/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
|
||||
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/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
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/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/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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
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/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/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/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/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/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/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
|
||||
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 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
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.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
|
||||
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/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/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/qdm12/dns v1.11.0 h1:jpcD5DZXXQSQe5a263PL09ghukiIdptvXFOZvyKEm6Q=
|
||||
github.com/qdm12/dns v1.11.0/go.mod h1:FmQsNOUcrrZ4UFzWAiED56AKXeNgaX3ySbmPwEfNjjE=
|
||||
github.com/qdm12/golibs v0.0.0-20210603202746-e5494e9c2ebb/go.mod h1:15RBzkun0i8XB7ADIoLJWp9ITRgsz3LroEI2FiOXLRg=
|
||||
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2 h1:FMeOhe/bGloI0T5Wb6QB7/rfOqgFeI//UF/N/f7PUCI=
|
||||
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.3.0 h1:BfKv4OU6dYb2KcDMYpTc7LIuO2jB73g3JCzy988GrLI=
|
||||
github.com/qdm12/ss-server v0.3.0/go.mod h1:ug+nWfuzKw/h5fxL1B6e9/OhkVuWJX4i2V1Pf0pJU1o=
|
||||
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/qdm12/golibs v0.0.0-20210102015428-6e1d159e61a3 h1:tnkjZkYZuAFNga7Wd/j3z3gPJLkv0OXow4q/YTkRdmE=
|
||||
github.com/qdm12/golibs v0.0.0-20210102015428-6e1d159e61a3/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
|
||||
github.com/qdm12/ss-server v0.1.0 h1:WV9MkHCDEWRwe4WpnYFeR/zcZAxYoTbfntLDnw9AQ50=
|
||||
github.com/qdm12/ss-server v0.1.0/go.mod h1:ABVUkxubboL3vqBkOwDV9glX1/x7SnYrckBe5d+M/zw=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
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/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/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg=
|
||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc=
|
||||
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=
|
||||
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
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/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-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-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-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-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
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/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/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-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
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 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/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
|
||||
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
inet.af/netaddr v0.0.0-20210511181906-37180328850c/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
|
||||
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722 h1:Qws2rZnQudC58cIagVucPQDLmMi3kAXgxscsgD0v6DU=
|
||||
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
@@ -1,29 +1,22 @@
|
||||
// Package alpine defines a configurator to interact with the Alpine operating system.
|
||||
package alpine
|
||||
|
||||
import (
|
||||
"os/user"
|
||||
"github.com/qdm12/golibs/os"
|
||||
"github.com/qdm12/golibs/os/user"
|
||||
)
|
||||
|
||||
var _ Alpiner = (*Alpine)(nil)
|
||||
|
||||
type Alpiner interface {
|
||||
UserCreater
|
||||
VersionGetter
|
||||
type Configurator interface {
|
||||
CreateUser(username string, uid int) (createdUsername string, err error)
|
||||
}
|
||||
|
||||
type Alpine struct {
|
||||
alpineReleasePath string
|
||||
passwdPath string
|
||||
lookupID func(uid string) (*user.User, error)
|
||||
lookup func(username string) (*user.User, error)
|
||||
type configurator struct {
|
||||
openFile os.OpenFileFunc
|
||||
osUser user.OSUser
|
||||
}
|
||||
|
||||
func New() *Alpine {
|
||||
return &Alpine{
|
||||
alpineReleasePath: "/etc/alpine-release",
|
||||
passwdPath: "/etc/passwd",
|
||||
lookupID: user.LookupId,
|
||||
lookup: user.Lookup,
|
||||
func NewConfigurator(openFile os.OpenFileFunc, osUser user.OSUser) Configurator {
|
||||
return &configurator{
|
||||
openFile: openFile,
|
||||
osUser: osUser,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,35 @@
|
||||
package alpine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"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.
|
||||
func (a *Alpine) CreateUser(username string, uid int) (createdUsername string, err error) {
|
||||
UIDStr := strconv.Itoa(uid)
|
||||
u, err := a.lookupID(UIDStr)
|
||||
func (c *configurator) CreateUser(username string, uid int) (createdUsername string, err error) {
|
||||
UIDStr := fmt.Sprintf("%d", uid)
|
||||
u, err := c.osUser.LookupID(UIDStr)
|
||||
_, unknownUID := err.(user.UnknownUserIdError)
|
||||
if err != nil && !unknownUID {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if u != nil {
|
||||
return "", fmt.Errorf("cannot create user: %w", err)
|
||||
} else if u != nil {
|
||||
if u.Username == username {
|
||||
return "", nil
|
||||
}
|
||||
return u.Username, nil
|
||||
}
|
||||
|
||||
u, err = a.lookup(username)
|
||||
u, err = c.osUser.Lookup(username)
|
||||
_, unknownUsername := err.(user.UnknownUserError)
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
file, err := c.openFile("/etc/passwd", os.O_APPEND|os.O_WRONLY, 0644)
|
||||
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)
|
||||
_, err = file.WriteString(s)
|
||||
@@ -53,6 +37,5 @@ func (a *Alpine) CreateUser(username string, uid int) (createdUsername string, e
|
||||
_ = file.Close()
|
||||
return "", err
|
||||
}
|
||||
|
||||
return username, file.Close()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package cli
|
||||
|
||||
import "context"
|
||||
|
||||
func (c *CLI) CI(context context.Context) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
// Package cli defines an interface CLI to run command line operations.
|
||||
package cli
|
||||
|
||||
var _ CLIer = (*CLI)(nil)
|
||||
import (
|
||||
"context"
|
||||
|
||||
type CLIer interface {
|
||||
ClientKeyFormatter
|
||||
HealthChecker
|
||||
OpenvpnConfigMaker
|
||||
Updater
|
||||
"github.com/qdm12/golibs/os"
|
||||
)
|
||||
|
||||
type CLI interface {
|
||||
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 {
|
||||
repoServersPath string
|
||||
}
|
||||
type cli struct{}
|
||||
|
||||
func New() *CLI {
|
||||
return &CLI{
|
||||
repoServersPath: "./internal/constants/servers.json",
|
||||
}
|
||||
func New() CLI {
|
||||
return &cli{}
|
||||
}
|
||||
|
||||
@@ -3,28 +3,24 @@ package cli
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/golibs/os"
|
||||
)
|
||||
|
||||
type ClientKeyFormatter interface {
|
||||
ClientKey(args []string) error
|
||||
}
|
||||
|
||||
func (c *CLI) ClientKey(args []string) error {
|
||||
func (c *cli) ClientKey(args []string, openFile os.OpenFileFunc) error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
file, err := os.OpenFile(*filepath, os.O_RDONLY, 0)
|
||||
file, err := openFile(*filepath, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := io.ReadAll(file)
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
_ = file.Close()
|
||||
return err
|
||||
|
||||
@@ -2,39 +2,19 @@ package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/healthcheck"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/params"
|
||||
)
|
||||
|
||||
type HealthChecker interface {
|
||||
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
|
||||
}
|
||||
|
||||
func (c *cli) HealthCheck(ctx context.Context) error {
|
||||
const timeout = 10 * time.Second
|
||||
httpClient := &http.Client{Timeout: timeout}
|
||||
client := healthcheck.NewClient(httpClient)
|
||||
healthchecker := healthcheck.NewChecker(httpClient)
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
url := "http://127.0.0.1:" + port
|
||||
return client.Check(ctx, url)
|
||||
const url = "http://" + constants.HealthcheckAddress
|
||||
return healthchecker.Check(ctx, url)
|
||||
}
|
||||
|
||||
@@ -5,25 +5,26 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/params"
|
||||
"github.com/qdm12/gluetun/internal/provider"
|
||||
"github.com/qdm12/gluetun/internal/settings"
|
||||
"github.com/qdm12/gluetun/internal/storage"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/params"
|
||||
"github.com/qdm12/golibs/os"
|
||||
)
|
||||
|
||||
type OpenvpnConfigMaker interface {
|
||||
OpenvpnConfig(logger logging.Logger) error
|
||||
}
|
||||
|
||||
func (c *CLI) OpenvpnConfig(logger logging.Logger) error {
|
||||
var allSettings configuration.Settings
|
||||
err := allSettings.Read(params.NewEnv(), logger)
|
||||
func (c *cli) OpenvpnConfig(os os.OS) error {
|
||||
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel)
|
||||
if err != nil {
|
||||
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())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -33,7 +34,15 @@ func (c *CLI) OpenvpnConfig(logger logging.Logger) error {
|
||||
if err != nil {
|
||||
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"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,110 +2,63 @@ package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"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/updater"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/os"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrModeUnspecified = errors.New("at least one of -enduser or -maintainers must be specified")
|
||||
ErrSyncServers = errors.New("cannot sync hardcoded and persisted servers")
|
||||
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
|
||||
func (c *cli) Update(args []string, os os.OS) error {
|
||||
options := settings.Updater{CLI: true}
|
||||
var flushToFile bool
|
||||
flagSet := flag.NewFlagSet("update", flag.ExitOnError)
|
||||
flagSet.BoolVar(&endUserMode, "enduser", false, "Write results to /gluetun/servers.json (for end users)")
|
||||
flagSet.BoolVar(&maintainerMode, "maintainer", false,
|
||||
"Write results to ./internal/constants/servers.json to modify the program (for maintainers)")
|
||||
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(&flushToFile, "file", false, "Write results to /gluetun/servers.json (for end users)")
|
||||
flagSet.BoolVar(&options.Stdout, "stdout", false, "Write results to console to modify the program (for maintainers)")
|
||||
flagSet.StringVar(&options.DNSAddress, "dns", "1.1.1.1", "DNS resolver address to use")
|
||||
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.Nordvpn, "nordvpn", false, "Update Nordvpn 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.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.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.Windscribe, "windscribe", false, "Update Windscribe servers")
|
||||
if err := flagSet.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
if !endUserMode && !maintainerMode {
|
||||
return ErrModeUnspecified
|
||||
logger, err := logging.NewLogger(logging.ConsoleEncoding, logging.InfoLevel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if updateAll {
|
||||
options.EnableAll()
|
||||
if !flushToFile && !options.Stdout {
|
||||
return fmt.Errorf("at least one of -file or -stdout must be specified")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
const clientTimeout = 10 * time.Second
|
||||
httpClient := &http.Client{Timeout: clientTimeout}
|
||||
storage := storage.New(logger, constants.ServersData)
|
||||
storage := storage.New(logger, os, constants.ServersData)
|
||||
currentServers, err := storage.SyncServers(constants.GetAllServers())
|
||||
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)
|
||||
allServers, err := updater.UpdateServers(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrUpdateServerInformation, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if endUserMode {
|
||||
if flushToFile {
|
||||
if err := storage.FlushToFile(allServers); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrWriteToFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
if maintainerMode {
|
||||
if err := writeToEmbeddedJSON(c.repoServersPath, allServers); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrWriteToFile, err)
|
||||
return fmt.Errorf("cannot update servers: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// Package configuration reads initial settings from environment variables
|
||||
// and secret files.
|
||||
package configuration
|
||||
@@ -1,6 +0,0 @@
|
||||
package configuration
|
||||
|
||||
const (
|
||||
lastIndent = "|--"
|
||||
indent = " "
|
||||
)
|
||||
@@ -1,71 +0,0 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
func (settings *Provider) cyberghostLines() (lines []string) {
|
||||
lines = append(lines, lastIndent+"Server groups: "+commaJoin(settings.ServerSelection.Groups))
|
||||
|
||||
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.Groups, err = r.env.CSVInside("CYBERGHOST_GROUP",
|
||||
constants.CyberghostGroupChoices())
|
||||
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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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,
|
||||
"groups": null,
|
||||
"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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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{
|
||||
Groups: []string{"group"},
|
||||
Regions: []string{"a", "El country"},
|
||||
},
|
||||
ExtraConfigOptions: ExtraConfigOptions{
|
||||
ClientKey: "a",
|
||||
ClientCertificate: "a",
|
||||
},
|
||||
},
|
||||
lines: []string{
|
||||
"|--Cyberghost settings:",
|
||||
" |--Network protocol: udp",
|
||||
" |--Server groups: 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
Groups []string `json:"groups"`
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/golibs/params"
|
||||
"github.com/qdm12/ss-server/pkg/tcpudp"
|
||||
)
|
||||
|
||||
// ShadowSocks contains settings to configure the Shadowsocks server.
|
||||
type ShadowSocks struct {
|
||||
Enabled bool
|
||||
tcpudp.Settings
|
||||
}
|
||||
|
||||
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 address: "+settings.Address)
|
||||
|
||||
lines = append(lines, indent+lastIndent+"Cipher: "+settings.CipherName)
|
||||
|
||||
if settings.LogAddresses {
|
||||
lines = append(lines, indent+lastIndent+"Log addresses: 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.LogAddresses, err = r.env.OnOff("SHADOWSOCKS_LOG", params.Default("off"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("environment variable SHADOWSOCKS_LOG: %w", err)
|
||||
}
|
||||
|
||||
settings.CipherName, err = r.env.Get("SHADOWSOCKS_CIPHER", params.Default("chacha20-ietf-poly1305"),
|
||||
params.RetroKeys([]string{"SHADOWSOCKS_METHOD"}, r.onRetroActive))
|
||||
if err != nil {
|
||||
return fmt.Errorf("environment variable SHADOWSOCKS_CIPHER (or SHADOWSOCKS_METHOD): %w", err)
|
||||
}
|
||||
|
||||
warning, err := settings.getAddress(r.env)
|
||||
if warning != "" {
|
||||
r.logger.Warn(warning)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (settings *ShadowSocks) getAddress(env params.Env) (
|
||||
warning string, err error) {
|
||||
address, err := env.Get("SHADOWSOCKS_LISTENING_ADDRESS")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("environment variable SHADOWSOCKS_LISTENING_ADDRESS: %w", err)
|
||||
}
|
||||
|
||||
if address != "" {
|
||||
address, warning, err := env.ListeningAddress("SHADOWSOCKS_LISTENING_ADDRESS")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("environment variable SHADOWSOCKS_LISTENING_ADDRESS: %w", err)
|
||||
}
|
||||
settings.Address = address
|
||||
return warning, nil
|
||||
}
|
||||
|
||||
// Retro-compatibility
|
||||
const retroWarning = "You are using the old environment variable " +
|
||||
"SHADOWSOCKS_PORT, please consider using " +
|
||||
"SHADOWSOCKS_LISTENING_ADDRESS instead"
|
||||
portStr, err := env.Get("SHADOWSOCKS_PORT")
|
||||
if err != nil {
|
||||
return retroWarning, fmt.Errorf("environment variable SHADOWSOCKS_PORT: %w", err)
|
||||
} else if portStr != "" {
|
||||
port, _, err := env.ListeningPort("SHADOWSOCKS_PORT")
|
||||
if err != nil {
|
||||
return retroWarning, fmt.Errorf("environment variable SHADOWSOCKS_PORT: %w", err)
|
||||
}
|
||||
settings.Address = ":" + fmt.Sprint(port)
|
||||
return retroWarning, nil
|
||||
}
|
||||
|
||||
// Default value
|
||||
settings.Address = ":8388"
|
||||
return "", nil
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
)
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
5
internal/constants/addresses.go
Normal file
5
internal/constants/addresses.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
HealthcheckAddress = "127.0.0.1:9999"
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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",
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sort"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
@@ -12,45 +13,219 @@ const (
|
||||
)
|
||||
|
||||
func CyberghostRegionChoices() (choices []string) {
|
||||
servers := CyberghostServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Region
|
||||
}
|
||||
return makeUnique(choices)
|
||||
}
|
||||
|
||||
func CyberghostGroupChoices() (choices []string) {
|
||||
servers := CyberghostServers()
|
||||
uniqueChoices := map[string]struct{}{}
|
||||
for _, server := range servers {
|
||||
uniqueChoices[server.Group] = struct{}{}
|
||||
for _, server := range CyberghostServers() {
|
||||
uniqueChoices[server.Region] = struct{}{}
|
||||
}
|
||||
|
||||
choices = make([]string, 0, len(uniqueChoices))
|
||||
for choice := range uniqueChoices {
|
||||
choices = append(choices, choice)
|
||||
}
|
||||
|
||||
sortable := sort.StringSlice(choices)
|
||||
sortable.Sort()
|
||||
|
||||
return sortable
|
||||
sort.Slice(choices, func(i, j int) bool {
|
||||
return choices[i] < choices[j]
|
||||
})
|
||||
return choices
|
||||
}
|
||||
|
||||
func CyberghostHostnameChoices() (choices []string) {
|
||||
servers := CyberghostServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Hostname
|
||||
func CyberghostGroupChoices() (choices []string) {
|
||||
uniqueChoices := map[string]struct{}{}
|
||||
for _, server := range CyberghostServers() {
|
||||
uniqueChoices[server.Group] = struct{}{}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
//nolint:lll
|
||||
func CyberghostServers() []models.CyberghostServer {
|
||||
return []models.CyberghostServer{
|
||||
{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}}},
|
||||
{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}}},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_CyberghostGroupChoices(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expected := []string{"Premium TCP Asia", "Premium TCP Europe",
|
||||
"Premium TCP USA", "Premium UDP Asia", "Premium UDP Europe",
|
||||
"Premium UDP USA"}
|
||||
choices := CyberghostGroupChoices()
|
||||
|
||||
assert.Equal(t, expected, choices)
|
||||
}
|
||||
98
internal/constants/dns.go
Normal file
98
internal/constants/dns.go
Normal 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"
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sort"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
@@ -10,44 +13,130 @@ const (
|
||||
)
|
||||
|
||||
func MullvadCountryChoices() (choices []string) {
|
||||
servers := MullvadServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Country
|
||||
uniqueChoices := map[string]struct{}{}
|
||||
for _, server := range MullvadServers() {
|
||||
uniqueChoices[server.Country] = struct{}{}
|
||||
}
|
||||
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) {
|
||||
servers := MullvadServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].City
|
||||
uniqueChoices := map[string]struct{}{}
|
||||
for _, server := range MullvadServers() {
|
||||
uniqueChoices[server.City] = struct{}{}
|
||||
}
|
||||
return makeUnique(choices)
|
||||
}
|
||||
|
||||
func MullvadHostnameChoices() (choices []string) {
|
||||
servers := MullvadServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Hostname
|
||||
for choice := range uniqueChoices {
|
||||
choices = append(choices, choice)
|
||||
}
|
||||
return makeUnique(choices)
|
||||
sort.Slice(choices, func(i, j int) bool {
|
||||
return choices[i] < choices[j]
|
||||
})
|
||||
return choices
|
||||
}
|
||||
|
||||
func MullvadISPChoices() (choices []string) {
|
||||
servers := MullvadServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].ISP
|
||||
uniqueChoices := map[string]struct{}{}
|
||||
for _, server := range MullvadServers() {
|
||||
uniqueChoices[server.ISP] = struct{}{}
|
||||
}
|
||||
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.
|
||||
func MullvadServers() (servers []models.MullvadServer) {
|
||||
servers = make([]models.MullvadServer, len(allServers.Mullvad.Servers))
|
||||
copy(servers, allServers.Mullvad.Servers)
|
||||
return servers
|
||||
//nolint:dupl,lll
|
||||
func MullvadServers() []models.MullvadServer {
|
||||
return []models.MullvadServer{
|
||||
{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}}},
|
||||
{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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user